From 844dd602028a0a5ece97fbc99dc59c90539c3670 Mon Sep 17 00:00:00 2001 From: mpilman Date: Thu, 20 Jun 2019 09:29:01 -0700 Subject: [PATCH 0001/1604] FDB compiling with intel compiler --- bindings/c/ThreadCleanup.cpp | 6 +- cmake/ConfigureCompiler.cmake | 11 ++- fdbcli/fdbcli.actor.cpp | 12 ++-- fdbclient/Atomic.h | 30 ++++----- fdbclient/FDBTypes.h | 22 +++--- fdbclient/FileBackupAgent.actor.cpp | 4 +- fdbclient/ManagementAPI.actor.cpp | 4 +- fdbclient/MonitorLeader.h | 4 ++ fdbclient/NativeAPI.actor.cpp | 18 ----- fdbclient/RYWIterator.cpp | 50 +++++++------- fdbclient/Status.h | 4 +- fdbclient/ThreadSafeTransaction.actor.cpp | 11 +-- fdbrpc/Locality.h | 4 +- fdbrpc/Net2FileSystem.cpp | 2 +- fdbrpc/crc32c.cpp | 47 ------------- fdbrpc/dsltest.actor.cpp | 8 ++- fdbrpc/libcoroutine/Coro.c | 9 ++- fdbserver/ApplyMetadataMutation.h | 6 +- fdbserver/KeyValueStoreSQLite.actor.cpp | 2 +- fdbserver/LeaderElection.h | 4 ++ fdbserver/SkipList.cpp | 81 ++++++++++++----------- fdbserver/Status.actor.cpp | 2 +- fdbserver/TLogServer.actor.cpp | 4 -- fdbserver/fdbserver.actor.cpp | 2 +- fdbserver/sqlite/btree.c | 4 +- fdbserver/storageserver.actor.cpp | 24 +++++++ fdbserver/workloads/BulkSetup.actor.h | 46 +------------ fdbserver/workloads/ReadWrite.actor.cpp | 47 +++++++++++++ fdbserver/workloads/UnitTests.actor.cpp | 6 +- flow/Arena.h | 2 +- flow/FastAlloc.cpp | 3 + flow/FastAlloc.h | 4 +- flow/ObjectSerializerTraits.h | 6 +- flow/flat_buffers.h | 14 ++-- flow/flow.h | 2 +- flow/genericactors.actor.cpp | 46 +++++++++++++ flow/genericactors.actor.h | 50 ++------------ flow/serialize.h | 4 +- 38 files changed, 306 insertions(+), 299 deletions(-) diff --git a/bindings/c/ThreadCleanup.cpp b/bindings/c/ThreadCleanup.cpp index 20b49cf8e5..966e38b800 100644 --- a/bindings/c/ThreadCleanup.cpp +++ b/bindings/c/ThreadCleanup.cpp @@ -34,6 +34,10 @@ BOOL WINAPI DllMain( HINSTANCE dll, DWORD reason, LPVOID reserved ) { #elif defined( __unixish__ ) +#ifdef __INTEL_COMPILER +#pragma warning ( disable:2415 ) +#endif + static pthread_key_t threadDestructorKey; static void threadDestructor(void*) { @@ -57,4 +61,4 @@ static int threadDestructorKeyInit = initThreadDestructorKey(); #else #error Port me! -#endif \ No newline at end of file +#endif diff --git a/cmake/ConfigureCompiler.cmake b/cmake/ConfigureCompiler.cmake index d989283033..c276fec24a 100644 --- a/cmake/ConfigureCompiler.cmake +++ b/cmake/ConfigureCompiler.cmake @@ -75,8 +75,11 @@ if(WIN32) else() set(GCC NO) set(CLANG NO) + set(ICC NO) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") set(CLANG YES) + elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel") + set(ICC YES) else() # This is not a very good test. However, as we do not really support many architectures # this is good enough for now @@ -155,13 +158,17 @@ else() else() add_compile_options(-Werror) endif() - add_compile_options($<$:-Wno-pragmas>) + if (GCC) + add_compile_options(-Wno-pragmas -fdiagnostics-color=always) + elseif(ICC) + add_compile_options(-wd1879 -wd1011) + elseif(CLANG) + endif() add_compile_options(-Wno-error=format -Wunused-variable -Wno-deprecated -fvisibility=hidden -Wreturn-type - -fdiagnostics-color=always -fPIC) if (GPERFTOOLS_FOUND AND GCC) add_compile_options( diff --git a/fdbcli/fdbcli.actor.cpp b/fdbcli/fdbcli.actor.cpp index 00327def46..1cd7d386a5 100644 --- a/fdbcli/fdbcli.actor.cpp +++ b/fdbcli/fdbcli.actor.cpp @@ -545,7 +545,7 @@ void initHelp() { void printVersion() { printf("FoundationDB CLI " FDB_VT_PACKAGE_NAME " (v" FDB_VT_VERSION ")\n"); printf("source version %s\n", getHGVersion()); - printf("protocol %" PRIx64 "\n", currentProtocolVersion); + printf("protocol %" PRIx64 "\n", currentProtocolVersion.versionWithFlags()); } void printHelpOverview() { @@ -1329,7 +1329,7 @@ void printStatus(StatusObjectReader statusObj, StatusClient::StatusLevel level, NetworkAddress parsedAddress; try { parsedAddress = NetworkAddress::parse(address); - } catch (Error& e) { + } catch (Error&) { // Groups all invalid IP address/port pair in the end of this detail group. line = format(" %-22s (invalid IP address or port)", address.c_str()); IPAddress::IPAddressStore maxIp; @@ -1847,10 +1847,10 @@ ACTOR Future fileConfigure(Database db, std::string filePath, bool isNewDa ACTOR Future coordinators( Database db, std::vector tokens, bool isClusterTLS ) { state StringRef setName; StringRef nameTokenBegin = LiteralStringRef("description="); - for(auto t = tokens.begin()+1; t != tokens.end(); ++t) - if (t->startsWith(nameTokenBegin)) { - setName = t->substr(nameTokenBegin.size()); - std::copy( t+1, tokens.end(), t ); + for(auto tok = tokens.begin()+1; tok != tokens.end(); ++tok) + if (tok->startsWith(nameTokenBegin)) { + setName = tok->substr(nameTokenBegin.size()); + std::copy( tok+1, tokens.end(), tok ); tokens.resize( tokens.size()-1 ); break; } diff --git a/fdbclient/Atomic.h b/fdbclient/Atomic.h index d9aecbe8a3..490485064c 100644 --- a/fdbclient/Atomic.h +++ b/fdbclient/Atomic.h @@ -24,7 +24,7 @@ #include "fdbclient/CommitTransaction.h" -static ValueRef doLittleEndianAdd(const Optional& existingValueOptional, const ValueRef& otherOperand, Arena& ar) { +inline ValueRef doLittleEndianAdd(const Optional& existingValueOptional, const ValueRef& otherOperand, Arena& ar) { const ValueRef& existingValue = existingValueOptional.present() ? existingValueOptional.get() : StringRef(); if(!existingValue.size()) return otherOperand; if(!otherOperand.size()) return otherOperand; @@ -47,7 +47,7 @@ static ValueRef doLittleEndianAdd(const Optional& existingValueOptiona return StringRef(buf, i); } -static ValueRef doAnd(const Optional& existingValueOptional, const ValueRef& otherOperand, Arena& ar) { +inline ValueRef doAnd(const Optional& existingValueOptional, const ValueRef& otherOperand, Arena& ar) { const ValueRef& existingValue = existingValueOptional.present() ? existingValueOptional.get() : StringRef(); if(!otherOperand.size()) return otherOperand; @@ -62,14 +62,14 @@ static ValueRef doAnd(const Optional& existingValueOptional, const Val return StringRef(buf, i); } -static ValueRef doAndV2(const Optional& existingValueOptional, const ValueRef& otherOperand, Arena& ar) { +inline ValueRef doAndV2(const Optional& existingValueOptional, const ValueRef& otherOperand, Arena& ar) { if (!existingValueOptional.present()) return otherOperand; return doAnd(existingValueOptional, otherOperand, ar); } -static ValueRef doOr(const Optional& existingValueOptional, const ValueRef& otherOperand, Arena& ar) { +inline ValueRef doOr(const Optional& existingValueOptional, const ValueRef& otherOperand, Arena& ar) { const ValueRef& existingValue = existingValueOptional.present() ? existingValueOptional.get() : StringRef(); if(!existingValue.size()) return otherOperand; if(!otherOperand.size()) return otherOperand; @@ -85,7 +85,7 @@ static ValueRef doOr(const Optional& existingValueOptional, const Valu return StringRef(buf, i); } -static ValueRef doXor(const Optional& existingValueOptional, const ValueRef& otherOperand, Arena& ar) { +inline ValueRef doXor(const Optional& existingValueOptional, const ValueRef& otherOperand, Arena& ar) { const ValueRef& existingValue = existingValueOptional.present() ? existingValueOptional.get() : StringRef(); if(!existingValue.size()) return otherOperand; if(!otherOperand.size()) return otherOperand; @@ -102,7 +102,7 @@ static ValueRef doXor(const Optional& existingValueOptional, const Val return StringRef(buf, i); } -static ValueRef doAppendIfFits(const Optional& existingValueOptional, const ValueRef& otherOperand, Arena& ar) { +inline ValueRef doAppendIfFits(const Optional& existingValueOptional, const ValueRef& otherOperand, Arena& ar) { const ValueRef& existingValue = existingValueOptional.present() ? existingValueOptional.get() : StringRef(); if(!existingValue.size()) return otherOperand; if(!otherOperand.size()) return existingValue; @@ -123,7 +123,7 @@ static ValueRef doAppendIfFits(const Optional& existingValueOptional, return StringRef(buf, i+j); } -static ValueRef doMax(const Optional& existingValueOptional, const ValueRef& otherOperand, Arena& ar) { +inline ValueRef doMax(const Optional& existingValueOptional, const ValueRef& otherOperand, Arena& ar) { const ValueRef& existingValue = existingValueOptional.present() ? existingValueOptional.get() : StringRef(); if (!existingValue.size()) return otherOperand; if (!otherOperand.size()) return otherOperand; @@ -155,7 +155,7 @@ static ValueRef doMax(const Optional& existingValueOptional, const Val return otherOperand; } -static ValueRef doByteMax(const Optional& existingValueOptional, const ValueRef& otherOperand, Arena& ar) { +inline ValueRef doByteMax(const Optional& existingValueOptional, const ValueRef& otherOperand, Arena& ar) { if (!existingValueOptional.present()) return otherOperand; const ValueRef& existingValue = existingValueOptional.get(); @@ -165,7 +165,7 @@ static ValueRef doByteMax(const Optional& existingValueOptional, const return otherOperand; } -static ValueRef doMin(const Optional& existingValueOptional, const ValueRef& otherOperand, Arena& ar) { +inline ValueRef doMin(const Optional& existingValueOptional, const ValueRef& otherOperand, Arena& ar) { if (!otherOperand.size()) return otherOperand; const ValueRef& existingValue = existingValueOptional.present() ? existingValueOptional.get() : StringRef(); @@ -203,14 +203,14 @@ static ValueRef doMin(const Optional& existingValueOptional, const Val return otherOperand; } -static ValueRef doMinV2(const Optional& existingValueOptional, const ValueRef& otherOperand, Arena& ar) { +inline ValueRef doMinV2(const Optional& existingValueOptional, const ValueRef& otherOperand, Arena& ar) { if (!existingValueOptional.present()) return otherOperand; return doMin(existingValueOptional, otherOperand, ar); } -static ValueRef doByteMin(const Optional& existingValueOptional, const ValueRef& otherOperand, Arena& ar) { +inline ValueRef doByteMin(const Optional& existingValueOptional, const ValueRef& otherOperand, Arena& ar) { if (!existingValueOptional.present()) return otherOperand; const ValueRef& existingValue = existingValueOptional.get(); @@ -220,7 +220,7 @@ static ValueRef doByteMin(const Optional& existingValueOptional, const return otherOperand; } -static Optional doCompareAndClear(const Optional& existingValueOptional, +inline Optional doCompareAndClear(const Optional& existingValueOptional, const ValueRef& otherOperand, Arena& ar) { if (!existingValueOptional.present() || existingValueOptional.get() == otherOperand) { // Clear the value. @@ -232,7 +232,7 @@ static Optional doCompareAndClear(const Optional& existingVa /* * Returns the range corresponding to the specified versionstamp key. */ -static KeyRangeRef getVersionstampKeyRange(Arena& arena, const KeyRef &key, const KeyRef &maxKey) { +inline KeyRangeRef getVersionstampKeyRange(Arena& arena, const KeyRef &key, const KeyRef &maxKey) { KeyRef begin(arena, key); KeyRef end(arena, key); @@ -255,7 +255,7 @@ static KeyRangeRef getVersionstampKeyRange(Arena& arena, const KeyRef &key, cons return KeyRangeRef(begin, std::min(end, maxKey)); } -static void placeVersionstamp( uint8_t* destination, Version version, uint16_t transactionNumber ) { +inline void placeVersionstamp( uint8_t* destination, Version version, uint16_t transactionNumber ) { version = bigEndian64(version); transactionNumber = bigEndian16(transactionNumber); static_assert( sizeof(version) == 8, "version size mismatch" ); @@ -264,7 +264,7 @@ static void placeVersionstamp( uint8_t* destination, Version version, uint16_t t memcpy( destination + sizeof(version), &transactionNumber, sizeof(transactionNumber) ); } -static void transformVersionstampMutation( MutationRef& mutation, StringRef MutationRef::* param, Version version, uint16_t transactionNumber ) { +inline void transformVersionstampMutation( MutationRef& mutation, StringRef MutationRef::* param, Version version, uint16_t transactionNumber ) { if ((mutation.*param).size() >= 4) { int32_t pos; memcpy(&pos, (mutation.*param).end() - sizeof(int32_t), sizeof(int32_t)); diff --git a/fdbclient/FDBTypes.h b/fdbclient/FDBTypes.h index edb83f5f92..b1143ba505 100644 --- a/fdbclient/FDBTypes.h +++ b/fdbclient/FDBTypes.h @@ -92,7 +92,7 @@ struct struct_like_traits : std::true_type { } template - static const void assign(Member& m, const Type& t) { + static void assign(Member& m, const Type& t) { if constexpr (i == 0) { m.id = t; } else { @@ -124,26 +124,26 @@ void uniquify( Collection& c ) { c.resize( std::unique(c.begin(), c.end()) - c.begin() ); } -static std::string describe( const Tag item ) { +inline std::string describe( const Tag item ) { return format("%d:%d", item.locality, item.id); } -static std::string describe( const int item ) { +inline std::string describe( const int item ) { return format("%d", item); } template -static std::string describe( Reference const& item ) { +std::string describe( Reference const& item ) { return item->toString(); } template -static std::string describe( T const& item ) { +std::string describe( T const& item ) { return item.toString(); } template -static std::string describe( std::map const& items, int max_items = -1 ) { +std::string describe( std::map const& items, int max_items = -1 ) { if(!items.size()) return "[no items]"; @@ -159,7 +159,7 @@ static std::string describe( std::map const& items, int max_items = -1 ) { } template -static std::string describeList( T const& items, int max_items ) { +std::string describeList( T const& items, int max_items ) { if(!items.size()) return "[no items]"; @@ -175,12 +175,12 @@ static std::string describeList( T const& items, int max_items ) { } template -static std::string describe( std::vector const& items, int max_items = -1 ) { +std::string describe( std::vector const& items, int max_items = -1 ) { return describeList(items, max_items); } template -static std::string describe( std::set const& items, int max_items = -1 ) { +std::string describe( std::set const& items, int max_items = -1 ) { return describeList(items, max_items); } @@ -492,7 +492,7 @@ struct KeyRangeWith : KeyRange { } }; template -static inline KeyRangeWith keyRangeWith( const KeyRangeRef& range, const Val& value ) { +KeyRangeWith keyRangeWith( const KeyRangeRef& range, const Val& value ) { return KeyRangeWith(range, value); } @@ -757,7 +757,7 @@ struct AddressExclusion { } }; -static bool addressExcluded( std::set const& exclusions, NetworkAddress const& addr ) { +inline bool addressExcluded( std::set const& exclusions, NetworkAddress const& addr ) { return exclusions.count( AddressExclusion(addr.ip, addr.port) ) || exclusions.count( AddressExclusion(addr.ip) ); } diff --git a/fdbclient/FileBackupAgent.actor.cpp b/fdbclient/FileBackupAgent.actor.cpp index efa53801c7..736fad10b0 100644 --- a/fdbclient/FileBackupAgent.actor.cpp +++ b/fdbclient/FileBackupAgent.actor.cpp @@ -572,8 +572,8 @@ namespace fileBackup { // Functions for consuming big endian (network byte order) integers. // Consumes a big endian number, swaps it to little endian, and returns it. - const int32_t consumeNetworkInt32() { return (int32_t)bigEndian32((uint32_t)consume< int32_t>());} - const uint32_t consumeNetworkUInt32() { return bigEndian32( consume());} + int32_t consumeNetworkInt32() { return (int32_t)bigEndian32((uint32_t)consume< int32_t>());} + uint32_t consumeNetworkUInt32() { return bigEndian32( consume());} bool eof() { return rptr == end; } diff --git a/fdbclient/ManagementAPI.actor.cpp b/fdbclient/ManagementAPI.actor.cpp index a371ac2624..f9680f25a8 100644 --- a/fdbclient/ManagementAPI.actor.cpp +++ b/fdbclient/ManagementAPI.actor.cpp @@ -104,8 +104,8 @@ std::map configForToken( std::string const& mode ) { // Add any new store types to fdbserver/workloads/ConfigureDatabase, too if (storeType.present()) { - out[p+"log_engine"] = format("%d", logType.get()); - out[p+"storage_engine"] = format("%d", storeType.get()); + out[p+"log_engine"] = format("%d", logType.get().operator KeyValueStoreType::StoreType()); + out[p+"storage_engine"] = format("%d", storeType.get().operator KeyValueStoreType::StoreType()); return out; } diff --git a/fdbclient/MonitorLeader.h b/fdbclient/MonitorLeader.h index 62fcd61427..6ec86570df 100644 --- a/fdbclient/MonitorLeader.h +++ b/fdbclient/MonitorLeader.h @@ -36,7 +36,9 @@ Future monitorLeader( Reference const& connFile, Re // of the current leader. If a leader is elected for long enough and communication with a quorum of // coordinators is possible, eventually outKnownLeader will be that leader's interface. +#ifndef __INTEL_COMPILER #pragma region Implementation +#endif Future monitorLeaderInternal( Reference const& connFile, Reference> const& outSerializedLeaderInfo, Reference> const& connectedCoordinatorsNum ); @@ -69,6 +71,8 @@ Future monitorLeader(Reference const& connFile, return m || deserializer( serializedInfo, outKnownLeader ); } +#ifndef __INTEL_COMPILER #pragma endregion +#endif #endif diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index 6fbf778997..76f051197e 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -252,24 +252,6 @@ ACTOR Future databaseLogger( DatabaseContext *cx ) { } } -ACTOR static Future > getSampleVersionStamp(Transaction *tr) { - loop{ - try { - tr->reset(); - tr->setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); - wait(success(tr->get(LiteralStringRef("\xff/StatusJsonTestKey62793")))); - state Future > vstamp = tr->getVersionstamp(); - tr->makeSelfConflicting(); - wait(tr->commit()); - Standalone val = wait(vstamp); - return val; - } - catch (Error& e) { - wait(tr->onError(e)); - } - } -} - struct TrInfoChunk { ValueRef value; Key key; diff --git a/fdbclient/RYWIterator.cpp b/fdbclient/RYWIterator.cpp index 3f8decfaab..7e9960b40f 100644 --- a/fdbclient/RYWIterator.cpp +++ b/fdbclient/RYWIterator.cpp @@ -334,31 +334,31 @@ ACTOR Standalone getRange( Transaction* tr, KeySelector begin, K -static void printWriteMap(WriteMap *p) { - WriteMap::iterator it(p); - for (it.skip(allKeys.begin); it.beginKey() < allKeys.end; ++it) { - if (it.is_cleared_range()) { - printf("CLEARED "); - } - if (it.is_conflict_range()) { - printf("CONFLICT "); - } - if (it.is_operation()) { - printf("OPERATION "); - printf(it.is_independent() ? "INDEPENDENT " : "DEPENDENT "); - } - if (it.is_unmodified_range()) { - printf("UNMODIFIED "); - } - if (it.is_unreadable()) { - printf("UNREADABLE "); - } - printf(": \"%s\" -> \"%s\"\n", - printable(it.beginKey().toStandaloneStringRef()).c_str(), - printable(it.endKey().toStandaloneStringRef()).c_str()); - } - printf("\n"); -} +//static void printWriteMap(WriteMap *p) { +// WriteMap::iterator it(p); +// for (it.skip(allKeys.begin); it.beginKey() < allKeys.end; ++it) { +// if (it.is_cleared_range()) { +// printf("CLEARED "); +// } +// if (it.is_conflict_range()) { +// printf("CONFLICT "); +// } +// if (it.is_operation()) { +// printf("OPERATION "); +// printf(it.is_independent() ? "INDEPENDENT " : "DEPENDENT "); +// } +// if (it.is_unmodified_range()) { +// printf("UNMODIFIED "); +// } +// if (it.is_unreadable()) { +// printf("UNREADABLE "); +// } +// printf(": \"%s\" -> \"%s\"\n", +// printable(it.beginKey().toStandaloneStringRef()).c_str(), +// printable(it.endKey().toStandaloneStringRef()).c_str()); +// } +// printf("\n"); +//} static int getWriteMapCount(WriteMap *p) { // printWriteMap(p); diff --git a/fdbclient/Status.h b/fdbclient/Status.h index 6d7384abfb..8a6e49ff25 100644 --- a/fdbclient/Status.h +++ b/fdbclient/Status.h @@ -68,7 +68,7 @@ struct StatusValue : json_spirit::mValue { StatusValue(json_spirit::mValue const& o) : json_spirit::mValue(o) {} }; -static StatusObject makeMessage(const char *name, const char *description) { +inline StatusObject makeMessage(const char *name, const char *description) { StatusObject out; out["name"] = name; out["description"] = description; @@ -88,7 +88,7 @@ template <> inline bool JSONDoc::get(const std::string path, StatusObje } // Takes an object by reference so make usage look clean and avoid the client doing object["messages"] which will create the key. -static bool findMessagesByName(StatusObjectReader object, std::set to_find) { +inline bool findMessagesByName(StatusObjectReader object, std::set to_find) { if (!object.has("messages") || object.last().type() != json_spirit::array_type) return false; diff --git a/fdbclient/ThreadSafeTransaction.actor.cpp b/fdbclient/ThreadSafeTransaction.actor.cpp index 130b1652ce..134e07fda2 100644 --- a/fdbclient/ThreadSafeTransaction.actor.cpp +++ b/fdbclient/ThreadSafeTransaction.actor.cpp @@ -53,9 +53,9 @@ Reference ThreadSafeDatabase::createTransaction() { void ThreadSafeDatabase::setOption( FDBDatabaseOptions::Option option, Optional value) { DatabaseContext *db = this->db; Standalone> passValue = value; - onMainThreadVoid( [db, option, passValue](){ + onMainThreadVoid( [db, option, passValue](){ db->checkDeferredError(); - db->setOption(option, passValue.contents()); + db->setOption(option, passValue.contents()); }, &db->deferredError ); } @@ -66,7 +66,7 @@ ThreadSafeDatabase::ThreadSafeDatabase(std::string connFilename, int apiVersion) // but run its constructor on the main thread DatabaseContext *db = this->db = DatabaseContext::allocateOnForeignThread(); - onMainThreadVoid([db, connFile, apiVersion](){ + onMainThreadVoid([db, connFile, apiVersion](){ try { Database::createDatabase(connFile, apiVersion, LocalityData(), db).extractPtr(); } @@ -312,7 +312,10 @@ void ThreadSafeTransaction::reset() { extern const char* getHGVersion(); -ThreadSafeApi::ThreadSafeApi() : apiVersion(-1), clientVersion(format("%s,%s,%llx", FDB_VT_VERSION, getHGVersion(), currentProtocolVersion)), transportId(0) {} +ThreadSafeApi::ThreadSafeApi() + : apiVersion(-1), + clientVersion(format("%s,%s,%llx", FDB_VT_VERSION, getHGVersion(), currentProtocolVersion.versionWithFlags())), + transportId(0) {} void ThreadSafeApi::selectApiVersion(int apiVersion) { this->apiVersion = apiVersion; diff --git a/fdbrpc/Locality.h b/fdbrpc/Locality.h index 759e59948c..ea6f4544a4 100644 --- a/fdbrpc/Locality.h +++ b/fdbrpc/Locality.h @@ -252,10 +252,10 @@ static std::string describe( } return s; } -static std::string describeZones( std::vector const& items, int max_items = -1 ) { +inline std::string describeZones( std::vector const& items, int max_items = -1 ) { return describe(items, LocalityData::keyZoneId, max_items); } -static std::string describeDataHalls( std::vector const& items, int max_items = -1 ) { +inline std::string describeDataHalls( std::vector const& items, int max_items = -1 ) { return describe(items, LocalityData::keyDataHallId, max_items); } diff --git a/fdbrpc/Net2FileSystem.cpp b/fdbrpc/Net2FileSystem.cpp index 31ce9f6095..cb33c1c84b 100644 --- a/fdbrpc/Net2FileSystem.cpp +++ b/fdbrpc/Net2FileSystem.cpp @@ -107,7 +107,7 @@ Net2FileSystem::Net2FileSystem(double ioTimeout, std::string fileSystemPath) criticalError(FDB_EXIT_ERROR, "FileSystemError", format("`%s' is not a mount point", fileSystemPath.c_str()).c_str()); } } - } catch (Error& e) { + } catch (Error&) { criticalError(FDB_EXIT_ERROR, "FileSystemError", format("Could not get device id from `%s'", fileSystemPath.c_str()).c_str()); } } diff --git a/fdbrpc/crc32c.cpp b/fdbrpc/crc32c.cpp index 899a0b88e4..9c0eb397b4 100644 --- a/fdbrpc/crc32c.cpp +++ b/fdbrpc/crc32c.cpp @@ -38,53 +38,6 @@ #include "generated-constants.cpp" #pragma GCC target("sse4.2") -static uint32_t append_trivial(uint32_t crc, const uint8_t * input, size_t length) -{ - for (size_t i = 0; i < length; ++i) - { - crc = crc ^ input[i]; - for (int j = 0; j < 8; j++) - crc = (crc >> 1) ^ 0x80000000 ^ ((~crc & 1) * POLY); - } - return crc; -} - -/* Table-driven software version as a fall-back. This is about 15 times slower - than using the hardware instructions. This assumes little-endian integers, - as is the case on Intel processors that the assembler code here is for. */ -static uint32_t append_adler_table(uint32_t crci, const uint8_t * input, size_t length) -{ - const uint8_t * next = input; - uint64_t crc; - - crc = crci ^ 0xffffffff; - while (length && ((uintptr_t)next & 7) != 0) - { - crc = table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8); - --length; - } - while (length >= 8) - { - crc ^= *(uint64_t *)next; - crc = table[7][crc & 0xff] - ^ table[6][(crc >> 8) & 0xff] - ^ table[5][(crc >> 16) & 0xff] - ^ table[4][(crc >> 24) & 0xff] - ^ table[3][(crc >> 32) & 0xff] - ^ table[2][(crc >> 40) & 0xff] - ^ table[1][(crc >> 48) & 0xff] - ^ table[0][crc >> 56]; - next += 8; - length -= 8; - } - while (length) - { - crc = table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8); - --length; - } - return (uint32_t)crc ^ 0xffffffff; -} - /* Table-driven software version as a fall-back. This is about 15 times slower than using the hardware instructions. This assumes little-endian integers, as is the case on Intel processors that the assembler code here is for. */ diff --git a/fdbrpc/dsltest.actor.cpp b/fdbrpc/dsltest.actor.cpp index fce4617fa8..eaaac40907 100644 --- a/fdbrpc/dsltest.actor.cpp +++ b/fdbrpc/dsltest.actor.cpp @@ -262,14 +262,20 @@ Future switchTest( FutureStream as, Future oneb ) { class TestBuffer : public ReferenceCounted { public: static TestBuffer* create( int length ) { +#if defined(__INTEL_COMPILER) + return new TestBuffer(length); +#else auto b = (TestBuffer*)new int[ (length+7)/4 ]; new (b) TestBuffer(length); return b; +#endif } +#if !defined(__INTEL_COMPILER) void operator delete( void* buf ) { cout << "Freeing buffer" << endl; delete[] (int*)buf; } +#endif int size() const { return length; } uint8_t* begin() { return data; } @@ -278,7 +284,7 @@ public: const uint8_t* end() const { return data+length; } private: - TestBuffer(int length) throw () : length(length) {} + TestBuffer(int length) noexcept : length(length) {} int length; uint8_t data[1]; }; diff --git a/fdbrpc/libcoroutine/Coro.c b/fdbrpc/libcoroutine/Coro.c index e72990f4d9..67330972e1 100644 --- a/fdbrpc/libcoroutine/Coro.c +++ b/fdbrpc/libcoroutine/Coro.c @@ -75,8 +75,6 @@ typedef struct CallbackBlock CoroStartCallback *func; } CallbackBlock; -static CallbackBlock globalCallbackBlock; - Coro *Coro_new(void) { Coro *self = (Coro *)io_calloc(1, sizeof(Coro)); @@ -286,6 +284,9 @@ void Coro_Start(void) } */ #else + +static CallbackBlock globalCallbackBlock; + void Coro_StartWithArg(CallbackBlock *block) { setProfilingEnabled(1); @@ -421,6 +422,8 @@ void Coro_setup(Coro *self, void *arg) #define buf (self->env) +static CallbackBlock globalCallbackBlock; + void Coro_setup(Coro *self, void *arg) { setjmp(buf); @@ -456,6 +459,8 @@ void Coro_setup(Coro *self, void *arg) #define setjmp _setjmp #define longjmp _longjmp +static CallbackBlock globalCallbackBlock; + void Coro_setup(Coro *self, void *arg) { size_t *sp = (size_t *)(((intptr_t)Coro_stack(self) diff --git a/fdbserver/ApplyMetadataMutation.h b/fdbserver/ApplyMetadataMutation.h index 6756f15a3e..a01ea7467b 100644 --- a/fdbserver/ApplyMetadataMutation.h +++ b/fdbserver/ApplyMetadataMutation.h @@ -30,7 +30,7 @@ #include "fdbserver/LogSystem.h" #include "fdbserver/LogProtocolMessage.h" -static bool isMetadataMutation(MutationRef const& m) { +inline bool isMetadataMutation(MutationRef const& m) { // FIXME: This is conservative - not everything in system keyspace is necessarily processed by applyMetadataMutations return (m.type == MutationRef::SetValue && m.param1.size() && m.param1[0] == systemKeys.begin[0] && !m.param1.startsWith(nonMetadataSystemKeys.begin)) || (m.type == MutationRef::ClearRange && m.param2.size() && m.param2[0] == systemKeys.begin[0] && !nonMetadataSystemKeys.contains(KeyRangeRef(m.param1, m.param2)) ); @@ -42,7 +42,7 @@ struct applyMutationsData { Reference> keyVersion; }; -static Reference getStorageInfo(UID id, std::map>* storageCache, IKeyValueStore* txnStateStore) { +inline Reference getStorageInfo(UID id, std::map>* storageCache, IKeyValueStore* txnStateStore) { Reference storageInfo; auto cacheItr = storageCache->find(id); if(cacheItr == storageCache->end()) { @@ -59,7 +59,7 @@ static Reference getStorageInfo(UID id, std::map const& mutations, IKeyValueStore* txnStateStore, LogPushData* toCommit, bool *confChange, Reference logSystem = Reference(), Version popVersion = 0, +inline void applyMetadataMutations(UID const& dbgid, Arena &arena, VectorRef const& mutations, IKeyValueStore* txnStateStore, LogPushData* toCommit, bool *confChange, Reference logSystem = Reference(), Version popVersion = 0, KeyRangeMap >* vecBackupKeys = NULL, KeyRangeMap* keyInfo = NULL, std::map* uid_applyMutationsData = NULL, RequestStream commit = RequestStream(), Database cx = Database(), NotifiedVersion* commitVersion = NULL, std::map>* storageCache = NULL, std::map* tag_popped = NULL, bool initialCommit = false ) { for (auto const& m : mutations) { diff --git a/fdbserver/KeyValueStoreSQLite.actor.cpp b/fdbserver/KeyValueStoreSQLite.actor.cpp index e53fa5a29a..3e831d85a8 100644 --- a/fdbserver/KeyValueStoreSQLite.actor.cpp +++ b/fdbserver/KeyValueStoreSQLite.actor.cpp @@ -1426,7 +1426,7 @@ struct ThreadSafeCounter { ThreadSafeCounter() : counter(0) {} void operator ++() { interlockedIncrement64(&counter); } void operator --() { interlockedDecrement64(&counter); } - operator const int64_t() const { return counter; } + operator int64_t() const { return counter; } }; class KeyValueStoreSQLite : public IKeyValueStore { diff --git a/fdbserver/LeaderElection.h b/fdbserver/LeaderElection.h index 8e90c53034..3140466a58 100644 --- a/fdbserver/LeaderElection.h +++ b/fdbserver/LeaderElection.h @@ -47,7 +47,9 @@ Future tryBecomeLeader( ServerCoordinators const& coordinators, Future changeLeaderCoordinators( ServerCoordinators const& coordinators, Value const& forwardingInfo ); // Inform all the coordinators that they have been replaced with a new connection string +#ifndef __INTEL_COMPILER #pragma region Implementation +#endif // __INTEL_COMPILER Future tryBecomeLeaderInternal( ServerCoordinators const& coordinators, Value const& proposedSerializedInterface, Reference> const& outSerializedLeader, bool const& hasConnected, Reference> const& asyncPriorityInfo ); @@ -66,6 +68,8 @@ Future tryBecomeLeader( ServerCoordinators const& coordinators, return m || asyncDeserialize(serializedInfo, outKnownLeader, g_network->useObjectSerializer()); } +#ifndef __INTEL_COMPILER #pragma endregion +#endif // __INTEL_COMPILER #endif diff --git a/fdbserver/SkipList.cpp b/fdbserver/SkipList.cpp index 62f22a66b9..e570db08e7 100644 --- a/fdbserver/SkipList.cpp +++ b/fdbserver/SkipList.cpp @@ -88,7 +88,7 @@ void SlowConflictSet::add( const VectorRef& clearRanges, const Vect } -PerfDoubleCounter +PerfDoubleCounter g_buildTest("Build", skc), g_add("Add", skc), g_add_sort("A.Sort", skc), @@ -163,7 +163,7 @@ force_inline bool getCharacter(const KeyInfo& ki, int character, int &outputChar // termination if (character == ki.key.size()){ outputCharacter = 0; - return false; + return false; } if (character == ki.key.size()+1) { @@ -313,8 +313,8 @@ private: uint8_t* value() { return end() + nPointers*(sizeof(Node*)+sizeof(Version)); } int length() { return valueLength; } Node* getNext(int i) { return *((Node**)end() + i); } - void setNext(int i, Node* n) { - *((Node**)end() + i) = n; + void setNext(int i, Node* n) { + *((Node**)end() + i) = n; #if defined(_DEBUG) || 1 /*if (n && n->level() < i) *(volatile int*)0 = 0;*/ @@ -438,7 +438,7 @@ public: // Returns true if we have advanced to the next level force_inline bool advance() { Node* next = x->getNext(level-1); - + if (next == alreadyChecked || !less(next->value(), next->length(), value.begin(), value.size())) { alreadyChecked = next; level--; @@ -464,7 +464,7 @@ public: Node *n = finger[0]->getNext(0); // or alreadyChecked, but that is more easily invalidated if (n && n->length() == value.size() && !memcmp(n->value(), value.begin(), value.size())) return n; - else + else return NULL; } @@ -477,9 +477,9 @@ public: int count() { int count = 0; Node* x = header->getNext(0); - while (x) { - x = x->getNext(0); - count++; + while (x) { + x = x->getNext(0); + count++; } return count; } @@ -561,7 +561,7 @@ public: void partition( StringRef* begin, int splitCount, SkipList* output ) { for(int i=splitCount-1; i>=0; i--) { Finger f( header, begin[i] ); - while (!f.finished()) + while (!f.finished()) f.nextLevel(); split(f, output[i+1]); } @@ -585,7 +585,7 @@ public: } void find( const StringRef* values, Finger* results, int* temp, int count ) { - // Relying on the ordering of values, descend until the values aren't all in the + // Relying on the ordering of values, descend until the values aren't all in the // same part of the tree // vtune: 11 parts @@ -674,7 +674,7 @@ public: while (nodeCount--) { Node* x = f.finger[0]->getNext(0); if (!x) break; - + // double prefetch gives +25% speed (single threaded) Node* next = x->getNext(0); _mm_prefetch( (const char*)next, _MM_HINT_T0 ); @@ -703,7 +703,7 @@ public: private: void remove( const Finger& start, const Finger& end ) { - if (start.finger[0] == end.finger[0]) + if (start.finger[0] == end.finger[0]) return; Node *x = start.finger[0]->getNext(0); @@ -792,17 +792,17 @@ private: return conflict(); } state = 1; - case 1: + case 1: { // check the end side of the pyramid Node *e = end.finger[end.level]; while (e->getMaxVersion(end.level) > version) { - if (end.finished()) + if (end.finished()) return conflict(); end.nextLevel(); Node *f = end.finger[end.level]; while (e != f){ - if (e->getMaxVersion(end.level) > version) + if (e->getMaxVersion(end.level) > version) return conflict(); e = e->getNext(end.level); } @@ -814,11 +814,11 @@ private: Node *nextS = start.finger[start.level]->getNext(start.level); Node *p = nextS; while (p != s){ - if (p->getMaxVersion(start.level) > version) + if (p->getMaxVersion(start.level) > version) return conflict(); p = p->getNext(start.level); } - if (start.finger[start.level]->getMaxVersion(start.level) <= version) + if (start.finger[start.level]->getMaxVersion(start.level) <= version) return noConflict(); s = nextS; if (start.finished()) { @@ -854,7 +854,7 @@ private: Node* node = header; for(int l=MaxLevels-1; l>=0; l--) { Node* next; - while ( (next=node->getNext(l)) != NULL ) + while ( (next=node->getNext(l)) != NULL ) node = next; end.finger[l] = node; } @@ -866,7 +866,7 @@ private: } }; -struct Action { +struct Action { virtual void operator()() = 0; // self-destructs }; typedef Action* PAction; @@ -1184,7 +1184,7 @@ void ConflictBatch::detectConflicts(Version now, Version newOldestVersion, std:: t = timer(); mergeWriteConflictRanges(now); g_merge += timer()-t; - + for (int i = 0; i < transactionCount; i++) { if (!transactionConflictStatus[i]) @@ -1198,7 +1198,7 @@ void ConflictBatch::detectConflicts(Version now, Version newOldestVersion, std:: t = timer(); if (newOldestVersion > cs->oldestVersion) { cs->oldestVersion = newOldestVersion; - SkipList::Finger finger; + SkipList::Finger finger; int temp; cs->versionHistory.find( &cs->removalKey, &finger, &temp, 1 ); cs->versionHistory.removeBefore( cs->oldestVersion, finger, combinedWriteConflictRanges.size()*3 + 10 ); @@ -1208,28 +1208,29 @@ void ConflictBatch::detectConflicts(Version now, Version newOldestVersion, std:: } void ConflictBatch::checkReadConflictRanges() { - if (!combinedReadConflictRanges.size()) + if (!combinedReadConflictRanges.size()) return; - if (PARALLEL_THREAD_COUNT) { - Event done[PARALLEL_THREAD_COUNT?PARALLEL_THREAD_COUNT:1]; - for(int t=0; tworker_nextAction[t] = action( [&,t] { +#if PARALLEL_THREAD_COUNT + Event done[PARALLEL_THREAD_COUNT ? PARALLEL_THREAD_COUNT : 1]; + for (int t = 0; t < PARALLEL_THREAD_COUNT; t++) { + cs->worker_nextAction[t] = action([&, t] { #pragma GCC diagnostic push -DISABLE_ZERO_DIVISION_FLAG - auto begin = &combinedReadConflictRanges[0] + t*combinedReadConflictRanges.size()/PARALLEL_THREAD_COUNT; - auto end = &combinedReadConflictRanges[0] + (t+1)*combinedReadConflictRanges.size()/PARALLEL_THREAD_COUNT; + DISABLE_ZERO_DIVISION_FLAG + auto begin = &combinedReadConflictRanges[0] + t * combinedReadConflictRanges.size() / PARALLEL_THREAD_COUNT; + auto end = + &combinedReadConflictRanges[0] + (t + 1) * combinedReadConflictRanges.size() / PARALLEL_THREAD_COUNT; #pragma GCC diagnostic pop - cs->versionHistory.detectConflicts( begin, end-begin, transactionConflictStatus ); - done[t].set(); - }); - cs->worker_ready[t]->set(); - } - for(int i=0; iversionHistory.detectConflicts( &combinedReadConflictRanges[0], combinedReadConflictRanges.size(), transactionConflictStatus ); + cs->versionHistory.detectConflicts(begin, end - begin, transactionConflictStatus); + done[t].set(); + }); + cs->worker_ready[t]->set(); } + for (int i = 0; i < PARALLEL_THREAD_COUNT; i++) done[i].block(); +#else + cs->versionHistory.detectConflicts(&combinedReadConflictRanges[0], combinedReadConflictRanges.size(), + transactionConflictStatus); +#endif } void ConflictBatch::addConflictRanges(Version now, std::vector< std::pair >::iterator begin, std::vector< std::pair >::iterator end,SkipList* part) { @@ -1258,7 +1259,7 @@ void ConflictBatch::addConflictRanges(Version now, std::vector< std::pair clusterGetStatus( state JsonBuilderObject qos; state JsonBuilderObject data_overlay; - statusObj["protocol_version"] = format("%llx", currentProtocolVersion); + statusObj["protocol_version"] = format("%llx", currentProtocolVersion.versionWithFlags()); statusObj["connection_string"] = coordinators.ccf->getConnectionString().toString(); state Optional configuration; diff --git a/fdbserver/TLogServer.actor.cpp b/fdbserver/TLogServer.actor.cpp index 52d0079ab7..4e38bdb685 100644 --- a/fdbserver/TLogServer.actor.cpp +++ b/fdbserver/TLogServer.actor.cpp @@ -254,10 +254,6 @@ static StringRef stripTagMessagesKey( StringRef key ) { return key.substr( sizeof(UID) + sizeof(Tag) + persistTagMessagesKeys.begin.size() ); } -static StringRef stripTagMessageRefsKey( StringRef key ) { - return key.substr( sizeof(UID) + sizeof(Tag) + persistTagMessageRefsKeys.begin.size() ); -} - static Version decodeTagMessagesKey( StringRef key ) { return bigEndian64( BinaryReader::fromStringRef( stripTagMessagesKey(key), Unversioned() ) ); } diff --git a/fdbserver/fdbserver.actor.cpp b/fdbserver/fdbserver.actor.cpp index 4d7f58796e..a3c5f927a1 100644 --- a/fdbserver/fdbserver.actor.cpp +++ b/fdbserver/fdbserver.actor.cpp @@ -521,7 +521,7 @@ void* parentWatcher(void *arg) { static void printVersion() { printf("FoundationDB " FDB_VT_PACKAGE_NAME " (v" FDB_VT_VERSION ")\n"); printf("source version %s\n", getHGVersion()); - printf("protocol %" PRIx64 "\n", currentProtocolVersion); + printf("protocol %" PRIx64 "\n", currentProtocolVersion.versionWithFlags()); } static void printHelpTeaser( const char *name ) { diff --git a/fdbserver/sqlite/btree.c b/fdbserver/sqlite/btree.c index 28390d6163..c2e21ea5dc 100644 --- a/fdbserver/sqlite/btree.c +++ b/fdbserver/sqlite/btree.c @@ -2561,7 +2561,9 @@ static int newDatabase(BtShared *pBt){ ** proceed. */ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag){ +#ifndef SQLITE_OMIT_SHARED_CACHE sqlite3 *pBlock = 0; +#endif BtShared *pBt = p->pBt; int rc = SQLITE_OK; @@ -4644,10 +4646,10 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( goto moveto_finish; } - int partial_c = c; c = sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey, (SQLITE3_BTREE_FORCE_FULL_COMPARISONS ? 0 : nextStartField), NULL); #if SQLITE3_BTREE_FORCE_FULL_COMPARISONS + int partial_c = c; /* If more data was NOT required but the partial comparison produced a different result than full * then something is wrong, log stuff and abort */ if(!moreDataRequired && partial_c != c) { diff --git a/fdbserver/storageserver.actor.cpp b/fdbserver/storageserver.actor.cpp index 28d47fa9cb..58ec60a84c 100644 --- a/fdbserver/storageserver.actor.cpp +++ b/fdbserver/storageserver.actor.cpp @@ -57,7 +57,9 @@ using std::pair; using std::make_pair; +#ifndef __INTEL_COMPILER #pragma region Data Structures +#endif #define SHORT_CIRCUT_ACTUAL_STORAGE 0 @@ -668,10 +670,14 @@ void StorageServer::byteSampleApplyMutation( MutationRef const& m, Version ver ) ASSERT(false); // Mutation of unknown type modfying byte sample } +#ifndef __INTEL_COMPILER #pragma endregion +#endif /////////////////////////////////// Validation /////////////////////////////////////// +#ifndef __INTEL_COMPILER #pragma region Validation +#endif bool validateRange( StorageServer::VersionedData::ViewAtVersion const& view, KeyRangeRef range, Version version, UID id, Version minInsertVersion ) { // * Nonoverlapping: No clear overlaps a set or another clear, or adjoins another clear. // * Old mutations are erased: All items in versionedData.atLatest() have insertVersion() > durableVersion() @@ -742,7 +748,9 @@ void validate(StorageServer* data, bool force = false) { throw; } } +#ifndef __INTEL_COMPILER #pragma endregion +#endif void updateProcessStats(StorageServer* self) @@ -763,7 +771,9 @@ updateProcessStats(StorageServer* self) } ///////////////////////////////////// Queries ///////////////////////////////// +#ifndef __INTEL_COMPILER #pragma region Queries +#endif ACTOR Future waitForVersion( StorageServer* data, Version version ) { // This could become an Actor transparently, but for now it just does the lookup if (version == latestVersion) @@ -1521,10 +1531,14 @@ void getQueuingMetrics( StorageServer* self, StorageQueuingMetricsRequest const& req.reply.send( reply ); } +#ifndef __INTEL_COMPILER #pragma endregion +#endif /////////////////////////// Updates //////////////////////////////// +#ifndef __INTEL_COMPILER #pragma region Updates +#endif ACTOR Future doEagerReads( StorageServer* data, UpdateEagerReadInfo* eager ) { eager->finishKeyBegin(); @@ -2940,10 +2954,14 @@ ACTOR Future updateStorage(StorageServer* data) { } } +#ifndef __INTEL_COMPILER #pragma endregion +#endif ////////////////////////////////// StorageServerDisk /////////////////////////////////////// +#ifndef __INTEL_COMPILER #pragma region StorageServerDisk +#endif void StorageServerDisk::makeNewStorageServerDurable() { storage->set( persistFormat ); @@ -3409,10 +3427,14 @@ Future StorageServerMetrics::waitMetrics(WaitMetricsRequest req, Future metricsCore( StorageServer* self, StorageServerInterface ssi ) { state Future doPollMetrics = Void(); @@ -3778,7 +3800,9 @@ ACTOR Future storageServer( IKeyValueStore* persistentData, StorageServerI } } +#ifndef __INTEL_COMPILER #pragma endregion +#endif /* 4 Reference count diff --git a/fdbserver/workloads/BulkSetup.actor.h b/fdbserver/workloads/BulkSetup.actor.h index 327d6b13fa..e4a248eec9 100644 --- a/fdbserver/workloads/BulkSetup.actor.h +++ b/fdbserver/workloads/BulkSetup.actor.h @@ -155,51 +155,7 @@ Future setupRangeWorker( Database cx, T* workload, std::vector > > trackInsertionCount(Database cx, std::vector countsOfInterest, double checkInterval) -{ - state KeyRange keyPrefix = KeyRangeRef(std::string("keycount"), std::string("keycount") + char(255)); - state KeyRange bytesPrefix = KeyRangeRef(std::string("bytesstored"), std::string("bytesstored") + char(255)); - state Transaction tr(cx); - state uint64_t lastInsertionCount = 0; - state int currentCountIndex = 0; - - state std::vector > countInsertionRates; - - state double startTime = now(); - - while(currentCountIndex < countsOfInterest.size()) - { - try - { - state Future> countFuture = tr.getRange(keyPrefix, 1000000000); - state Future> bytesFuture = tr.getRange(bytesPrefix, 1000000000); - wait(success(countFuture) && success(bytesFuture)); - - Standalone counts = countFuture.get(); - Standalone bytes = bytesFuture.get(); - - uint64_t numInserted = 0; - for(int i = 0; i < counts.size(); i++) - numInserted += *(uint64_t*)counts[i].value.begin(); - - uint64_t bytesInserted = 0; - for(int i = 0; i < bytes.size(); i++) - bytesInserted += *(uint64_t*)bytes[i].value.begin(); - - while(currentCountIndex < countsOfInterest.size() && countsOfInterest[currentCountIndex] > lastInsertionCount && countsOfInterest[currentCountIndex] <= numInserted) - countInsertionRates.emplace_back(countsOfInterest[currentCountIndex++], bytesInserted / (now() - startTime)); - - lastInsertionCount = numInserted; - wait(delay(checkInterval)); - } - catch(Error& e) - { - wait(tr.onError(e)); - } - } - - return countInsertionRates; -} +ACTOR Future > > trackInsertionCount(Database cx, std::vector countsOfInterest, double checkInterval); ACTOR template Future bulkSetup(Database cx, T* workload, uint64_t nodeCount, Promise setupTime, diff --git a/fdbserver/workloads/ReadWrite.actor.cpp b/fdbserver/workloads/ReadWrite.actor.cpp index e5f8a0eb0d..8819a616da 100644 --- a/fdbserver/workloads/ReadWrite.actor.cpp +++ b/fdbserver/workloads/ReadWrite.actor.cpp @@ -679,5 +679,52 @@ struct ReadWriteWorkload : KVWorkload { } }; +ACTOR Future > > trackInsertionCount(Database cx, std::vector countsOfInterest, double checkInterval) +{ + state KeyRange keyPrefix = KeyRangeRef(std::string("keycount"), std::string("keycount") + char(255)); + state KeyRange bytesPrefix = KeyRangeRef(std::string("bytesstored"), std::string("bytesstored") + char(255)); + state Transaction tr(cx); + state uint64_t lastInsertionCount = 0; + state int currentCountIndex = 0; + + state std::vector > countInsertionRates; + + state double startTime = now(); + + while(currentCountIndex < countsOfInterest.size()) + { + try + { + state Future> countFuture = tr.getRange(keyPrefix, 1000000000); + state Future> bytesFuture = tr.getRange(bytesPrefix, 1000000000); + wait(success(countFuture) && success(bytesFuture)); + + Standalone counts = countFuture.get(); + Standalone bytes = bytesFuture.get(); + + uint64_t numInserted = 0; + for(int i = 0; i < counts.size(); i++) + numInserted += *(uint64_t*)counts[i].value.begin(); + + uint64_t bytesInserted = 0; + for(int i = 0; i < bytes.size(); i++) + bytesInserted += *(uint64_t*)bytes[i].value.begin(); + + while(currentCountIndex < countsOfInterest.size() && countsOfInterest[currentCountIndex] > lastInsertionCount && countsOfInterest[currentCountIndex] <= numInserted) + countInsertionRates.emplace_back(countsOfInterest[currentCountIndex++], bytesInserted / (now() - startTime)); + + lastInsertionCount = numInserted; + wait(delay(checkInterval)); + } + catch(Error& e) + { + wait(tr.onError(e)); + } + } + + return countInsertionRates; +} + + WorkloadFactory ReadWriteWorkloadFactory("ReadWrite"); diff --git a/fdbserver/workloads/UnitTests.actor.cpp b/fdbserver/workloads/UnitTests.actor.cpp index 0599218c2b..5d955e69cc 100644 --- a/fdbserver/workloads/UnitTests.actor.cpp +++ b/fdbserver/workloads/UnitTests.actor.cpp @@ -64,10 +64,10 @@ struct UnitTestWorkload : TestWorkload { ACTOR static Future runUnitTests(UnitTestWorkload* self) { state std::vector tests; - for (auto t = g_unittests.tests; t != NULL; t = t->next) { - if (StringRef(t->name).startsWith(self->testPattern)) { + for (auto test = g_unittests.tests; test != NULL; test = test->next) { + if (StringRef(test->name).startsWith(self->testPattern)) { ++self->testsAvailable; - tests.push_back(t); + tests.push_back(test); } } fprintf(stdout, "Found %zu tests\n", tests.size()); diff --git a/flow/Arena.h b/flow/Arena.h index 2028bd1f6b..7697d5cc2d 100644 --- a/flow/Arena.h +++ b/flow/Arena.h @@ -457,7 +457,7 @@ struct union_like_traits> : std::true_type { } template - static const void assign(Member& member, const T& t) { + static void assign(Member& member, const T& t) { member = t; } }; diff --git a/flow/FastAlloc.cpp b/flow/FastAlloc.cpp index e909c470ae..b29c29b7ba 100644 --- a/flow/FastAlloc.cpp +++ b/flow/FastAlloc.cpp @@ -47,6 +47,9 @@ #pragma warning (disable: 4073) #pragma init_seg(lib) #define INIT_SEG +#elif defined(__INTEL_COMPILER) +// intel compiler ignored INIT_SEG for thread local variables +#define INIT_SEG #elif defined(__GNUG__) #ifdef __linux__ #define INIT_SEG __attribute__ ((init_priority (1000))) diff --git a/flow/FastAlloc.h b/flow/FastAlloc.h index 1959816e54..94e76c82be 100644 --- a/flow/FastAlloc.h +++ b/flow/FastAlloc.h @@ -203,7 +203,7 @@ public: static void operator delete( void*, void* ) { } }; -static void* allocateFast(int size) { +inline void* allocateFast(int size) { if (size <= 16) return FastAllocator<16>::allocate(); if (size <= 32) return FastAllocator<32>::allocate(); if (size <= 64) return FastAllocator<64>::allocate(); @@ -214,7 +214,7 @@ static void* allocateFast(int size) { return new uint8_t[size]; } -static void freeFast(int size, void* ptr) { +inline void freeFast(int size, void* ptr) { if (size <= 16) return FastAllocator<16>::release(ptr); if (size <= 32) return FastAllocator<32>::release(ptr); if (size <= 64) return FastAllocator<64>::release(ptr); diff --git a/flow/ObjectSerializerTraits.h b/flow/ObjectSerializerTraits.h index 3301214e76..dc15cd9874 100644 --- a/flow/ObjectSerializerTraits.h +++ b/flow/ObjectSerializerTraits.h @@ -154,7 +154,7 @@ struct union_like_traits : std::false_type { static const index_t& get(const Member&); template - static const void assign(Member&, const Alternative&); + static void assign(Member&, const Alternative&); template static void done(Member&, Context&); @@ -171,7 +171,7 @@ struct struct_like_traits : std::false_type { static const index_t& get(const Member&); template - static const void assign(Member&, const index_t&); + static void assign(Member&, const index_t&); template static void done(Member&, Context&); @@ -190,7 +190,7 @@ struct union_like_traits> : std::true_type { } template - static const void assign(Member& member, const Alternative& a) { + static void assign(Member& member, const Alternative& a) { static_assert(std::is_same_v, Alternative>); member = a; } diff --git a/flow/flat_buffers.h b/flow/flat_buffers.h index a7ff261358..79fd1dcfcc 100644 --- a/flow/flat_buffers.h +++ b/flow/flat_buffers.h @@ -80,7 +80,7 @@ struct struct_like_traits> : std::true_type { } template - static const void assign(Member& m, const Type& t) { + static void assign(Member& m, const Type& t) { std::get(m) = t; } }; @@ -262,6 +262,11 @@ private: } else { return struct_offset_impl) + fb_scalar_size, index - 1, Ts...>::offset; } +#ifdef __INTEL_COMPILER + // ICC somehow things that this method does not return + // see: https://software.intel.com/en-us/forums/intel-c-compiler/topic/799473 + return 1; +#endif } public: @@ -685,15 +690,15 @@ struct SaveVisitorLambda { auto typeVectorWriter = writer.getMessageWriter(num_entries); // type tags are one byte auto offsetVectorWriter = writer.getMessageWriter(num_entries * sizeof(RelativeOffset)); auto iter = VectorTraits::begin(member); - for (int i = 0; i < num_entries; ++i) { + for (int j = 0; j < num_entries; ++j) { uint8_t type_tag = UnionTraits::index(*iter); uint8_t fb_type_tag = UnionTraits::empty(*iter) ? 0 : type_tag + 1; // Flatbuffers indexes from 1. - typeVectorWriter.write(&fb_type_tag, i, sizeof(fb_type_tag)); + typeVectorWriter.write(&fb_type_tag, j, sizeof(fb_type_tag)); if (!UnionTraits::empty(*iter)) { RelativeOffset offset = (SaveAlternative{ writer, vtableset }).save(type_tag, *iter); - offsetVectorWriter.write(&offset, i * sizeof(offset), sizeof(offset)); + offsetVectorWriter.write(&offset, j * sizeof(offset), sizeof(offset)); } ++iter; } @@ -1110,4 +1115,3 @@ struct EnsureTable { private: object_construction t; }; - diff --git a/flow/flow.h b/flow/flow.h index 7ce23eade7..04b68ed04b 100644 --- a/flow/flow.h +++ b/flow/flow.h @@ -221,7 +221,7 @@ struct union_like_traits> : std::true_type { } template - static const void assign(Member& m, const Alternative& a) { + static void assign(Member& m, const Alternative& a) { if constexpr (i == 0) { m = a; } else { diff --git a/flow/genericactors.actor.cpp b/flow/genericactors.actor.cpp index fd24381e3c..e5d200a25b 100644 --- a/flow/genericactors.actor.cpp +++ b/flow/genericactors.actor.cpp @@ -83,3 +83,49 @@ ACTOR Future quorumEqualsTrue( std::vector> futures, int requ } } } + +ACTOR Future shortCircuitAny( std::vector> f ) +{ + std::vector> sc; + for(Future fut : f) { + sc.push_back(returnIfTrue(fut)); + } + + choose { + when( wait( waitForAll( f ) ) ) { + // Handle a possible race condition? If the _last_ term to + // be evaluated triggers the waitForAll before bubbling + // out of the returnIfTrue quorum + for ( auto fut : f ) { + if ( fut.get() ) { + return true; + } + } + return false; + } + when( wait( waitForAny( sc ) ) ) { + return true; + } + } +} + +Future orYield( Future f ) { + if(f.isReady()) { + if(f.isError()) + return tagError(yield(), f.getError()); + else + return yield(); + } + else + return f; +} + +ACTOR Future returnIfTrue( Future f ) +{ + bool b = wait( f ); + if ( b ) { + return Void(); + } + wait( Never() ); + throw internal_error(); +} diff --git a/flow/genericactors.actor.h b/flow/genericactors.actor.h index 7b577b2e4c..0b3302517c 100644 --- a/flow/genericactors.actor.h +++ b/flow/genericactors.actor.h @@ -410,15 +410,7 @@ Future map( FutureStream input, F func, PromiseStream returnIfTrue( Future f ) -{ - bool b = wait( f ); - if ( b ) { - return Void(); - } - wait( Never() ); - throw internal_error(); -} +ACTOR Future returnIfTrue( Future f ); //Returns if the future, when waited on and then evaluated with the predicate, returns true, otherwise waits forever template @@ -972,30 +964,7 @@ Future waitForAny( std::vector> const& results ) { return quorum( results, 1 ); } -ACTOR static Future shortCircuitAny( std::vector> f ) -{ - std::vector> sc; - for(Future fut : f) { - sc.push_back(returnIfTrue(fut)); - } - - choose { - when( wait( waitForAll( f ) ) ) { - // Handle a possible race condition? If the _last_ term to - // be evaluated triggers the waitForAll before bubbling - // out of the returnIfTrue quorum - for ( auto fut : f ) { - if ( fut.get() ) { - return true; - } - } - return false; - } - when( wait( waitForAny( sc ) ) ) { - return true; - } - } -} +ACTOR Future shortCircuitAny( std::vector> f ); ACTOR template Future> getAll( std::vector> input ) { @@ -1132,16 +1101,7 @@ Future orYield( Future f ) { return f; } -static Future orYield( Future f ) { - if(f.isReady()) { - if(f.isError()) - return tagError(yield(), f.getError()); - else - return yield(); - } - else - return f; -} +Future orYield( Future f ); ACTOR template Future chooseActor( Future lhs, Future rhs ) { choose { @@ -1153,7 +1113,7 @@ ACTOR template Future chooseActor( Future lhs, Future rhs ) { // set && set -> set // error && x -> error // all others -> unset -static Future operator &&( Future const& lhs, Future const& rhs ) { +inline Future operator &&( Future const& lhs, Future const& rhs ) { if(lhs.isReady()) { if(lhs.isError()) return lhs; else return rhs; @@ -1428,7 +1388,7 @@ struct YieldedFutureActor : SAV, ActorCallback yieldedFuture(Future f) { +inline Future yieldedFuture(Future f) { if (f.isReady()) return yield(); else diff --git a/flow/serialize.h b/flow/serialize.h index e7431e7205..cde6e027e9 100644 --- a/flow/serialize.h +++ b/flow/serialize.h @@ -282,7 +282,7 @@ struct _IncludeVersion { ar >> v; if (!v.isValid()) { auto err = incompatible_protocol_version(); - TraceEvent(SevError, "InvalidSerializationVersion").error(err).detailf("Version", "%llx", v); + TraceEvent(SevError, "InvalidSerializationVersion").error(err).detailf("Version", "%llx", v.versionWithFlags()); throw err; } if (v > currentProtocolVersion) { @@ -290,7 +290,7 @@ struct _IncludeVersion { // particular data structures (e.g. to support mismatches between client and server versions when the client // must deserialize zookeeper and database structures) auto err = incompatible_protocol_version(); - TraceEvent(SevError, "FutureProtocolVersion").error(err).detailf("Version", "%llx", v); + TraceEvent(SevError, "FutureProtocolVersion").error(err).detailf("Version", "%llx", v.versionWithFlags()); throw err; } ar.setProtocolVersion(v); From ab019fbe41bf40df30170fef8685bae8739ef951 Mon Sep 17 00:00:00 2001 From: mpilman Date: Thu, 20 Jun 2019 14:28:31 -0700 Subject: [PATCH 0002/1604] More minor fixes, removed snapshots --- FDBLibTLS/FDBLibTLSPolicy.cpp | 2 +- bindings/c/test/test.h | 2 +- cmake/ConfigureCompiler.cmake | 1 + fdbserver/FDBExecHelper.actor.cpp | 4 ++-- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/FDBLibTLS/FDBLibTLSPolicy.cpp b/FDBLibTLS/FDBLibTLSPolicy.cpp index d22f7d8f67..1fb9f65277 100644 --- a/FDBLibTLS/FDBLibTLSPolicy.cpp +++ b/FDBLibTLS/FDBLibTLSPolicy.cpp @@ -300,7 +300,7 @@ bool FDBLibTLSPolicy::set_verify_peers(int count, const uint8_t* verify_peers[], } Reference verify = Reference(new FDBLibTLSVerify(verifyString.substr(start))); verify_rules.push_back(verify); - } catch ( const std::runtime_error& e ) { + } catch ( const std::runtime_error& ) { verify_rules.clear(); std::string verifyString((const char*)verify_peers[i], verify_peers_len[i]); TraceEvent(SevError, "FDBLibTLSVerifyPeersParseError").detail("Config", verifyString); diff --git a/bindings/c/test/test.h b/bindings/c/test/test.h index cecb76b10c..b63e15c95b 100644 --- a/bindings/c/test/test.h +++ b/bindings/c/test/test.h @@ -236,7 +236,7 @@ void* runNetwork() { FDBDatabase* openDatabase(struct ResultSet *rs, pthread_t *netThread) { checkError(fdb_setup_network(), "setup network", rs); - pthread_create(netThread, NULL, &runNetwork, NULL); + pthread_create(netThread, NULL, (void*)(&runNetwork), NULL); FDBDatabase *db; checkError(fdb_create_database(NULL, &db), "create database", rs); diff --git a/cmake/ConfigureCompiler.cmake b/cmake/ConfigureCompiler.cmake index c276fec24a..5e1f5c83bb 100644 --- a/cmake/ConfigureCompiler.cmake +++ b/cmake/ConfigureCompiler.cmake @@ -162,6 +162,7 @@ else() add_compile_options(-Wno-pragmas -fdiagnostics-color=always) elseif(ICC) add_compile_options(-wd1879 -wd1011) + add_link_options(-static-intel) elseif(CLANG) endif() add_compile_options(-Wno-error=format diff --git a/fdbserver/FDBExecHelper.actor.cpp b/fdbserver/FDBExecHelper.actor.cpp index 763cc25fe4..ea3eb57d3a 100644 --- a/fdbserver/FDBExecHelper.actor.cpp +++ b/fdbserver/FDBExecHelper.actor.cpp @@ -1,4 +1,4 @@ -#if !defined(_WIN32) && !defined(__APPLE__) +#if !defined(_WIN32) && !defined(__APPLE__) && !defined(__INTEL_COMPILER) #define BOOST_SYSTEM_NO_LIB #define BOOST_DATE_TIME_NO_LIB #define BOOST_REGEX_NO_LIB @@ -83,7 +83,7 @@ void ExecCmdValueString::dbgPrint() { return; } -#if defined(_WIN32) || defined(__APPLE__) +#if defined(_WIN32) || defined(__APPLE__) || defined(__INTEL_COMPILER) ACTOR Future spawnProcess(std::string binPath, std::vector paramList, double maxWaitTime, bool isSync) { wait(delay(0.0)); From 923a89748cf0ab250ae936b5430a7bc69c558942 Mon Sep 17 00:00:00 2001 From: mpilman Date: Thu, 20 Jun 2019 14:34:23 -0700 Subject: [PATCH 0003/1604] removed dead code --- bindings/flow/tester/Tester.actor.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/bindings/flow/tester/Tester.actor.cpp b/bindings/flow/tester/Tester.actor.cpp index a20718b15d..71242ec2d5 100644 --- a/bindings/flow/tester/Tester.actor.cpp +++ b/bindings/flow/tester/Tester.actor.cpp @@ -214,19 +214,19 @@ ACTOR Future< Standalone > getRange(Reference tr, K } } -ACTOR static Future debugPrintRange(Reference tr, std::string subspace, std::string msg) { - if (!tr) - return Void(); - - Standalone results = wait(getRange(tr, KeyRange(KeyRangeRef(subspace + '\x00', subspace + '\xff')))); - printf("==================================================DB:%s:%s, count:%d\n", msg.c_str(), - StringRef(subspace).printable().c_str(), results.size()); - for (auto & s : results) { - printf("=====key:%s, value:%s\n", StringRef(s.key).printable().c_str(), StringRef(s.value).printable().c_str()); - } - - return Void(); -} +//ACTOR static Future debugPrintRange(Reference tr, std::string subspace, std::string msg) { +// if (!tr) +// return Void(); +// +// Standalone results = wait(getRange(tr, KeyRange(KeyRangeRef(subspace + '\x00', subspace + '\xff')))); +// printf("==================================================DB:%s:%s, count:%d\n", msg.c_str(), +// StringRef(subspace).printable().c_str(), results.size()); +// for (auto & s : results) { +// printf("=====key:%s, value:%s\n", StringRef(s.key).printable().c_str(), StringRef(s.value).printable().c_str()); +// } +// +// return Void(); +//} ACTOR Future stackSub(FlowTesterStack* stack) { if (stack->data.size() < 2) From 77751d0127b217a8b85f4f885195382f12e47786 Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Wed, 3 Jul 2019 09:51:57 -0700 Subject: [PATCH 0004/1604] Fixed typo Co-Authored-By: A.J. Beamon --- flow/flat_buffers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/flat_buffers.h b/flow/flat_buffers.h index 79fd1dcfcc..51e2c261cc 100644 --- a/flow/flat_buffers.h +++ b/flow/flat_buffers.h @@ -263,7 +263,7 @@ private: return struct_offset_impl) + fb_scalar_size, index - 1, Ts...>::offset; } #ifdef __INTEL_COMPILER - // ICC somehow things that this method does not return + // ICC somehow thinks that this method does not return // see: https://software.intel.com/en-us/forums/intel-c-compiler/topic/799473 return 1; #endif From b242760adf1449c96fb6642958d788879da3b89b Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Fri, 19 Jul 2019 09:58:40 -0700 Subject: [PATCH 0005/1604] Use functional cast instead of explicit operator call --- fdbclient/ManagementAPI.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbclient/ManagementAPI.actor.cpp b/fdbclient/ManagementAPI.actor.cpp index f9680f25a8..28ea8686ec 100644 --- a/fdbclient/ManagementAPI.actor.cpp +++ b/fdbclient/ManagementAPI.actor.cpp @@ -105,7 +105,7 @@ std::map configForToken( std::string const& mode ) { if (storeType.present()) { out[p+"log_engine"] = format("%d", logType.get().operator KeyValueStoreType::StoreType()); - out[p+"storage_engine"] = format("%d", storeType.get().operator KeyValueStoreType::StoreType()); + out[p+"storage_engine"] = format("%d", KeyValueStoreType::StoreType(storeType.get())); return out; } From a031a12150984ba8b02d7dc58e27eb070f29d958 Mon Sep 17 00:00:00 2001 From: Ryan Worl Date: Mon, 29 Jul 2019 08:30:10 -0400 Subject: [PATCH 0006/1604] Remove finalizers in favor of copying and destroying the native resources immediately. Except for futureKeyValueArray, all futures which allocate native heap resources (other than the futures themselves) will allocate Go resources and copy from native to Go, then destroy the native heap resources. --- bindings/go/src/fdb/database.go | 13 +-- bindings/go/src/fdb/futures.go | 138 ++++++++++++++++++++--------- bindings/go/src/fdb/range.go | 13 +++ bindings/go/src/fdb/transaction.go | 12 ++- 4 files changed, 125 insertions(+), 51 deletions(-) diff --git a/bindings/go/src/fdb/database.go b/bindings/go/src/fdb/database.go index aca709e3d4..4246c2933e 100644 --- a/bindings/go/src/fdb/database.go +++ b/bindings/go/src/fdb/database.go @@ -27,7 +27,7 @@ package fdb import "C" import ( - "runtime" + "sync" ) // Database is a handle to a FoundationDB database. Database is a lightweight @@ -74,13 +74,14 @@ func (d Database) CreateTransaction() (Transaction, error) { return Transaction{}, Error{int(err)} } - t := &transaction{outt, d} - runtime.SetFinalizer(t, (*transaction).destroy) + t := &transaction{outt, d, sync.Once{}} return Transaction{t}, nil } -func retryable(wrapped func() (interface{}, error), onError func(Error) FutureNil) (ret interface{}, e error) { +func retryable(t Transaction, wrapped func() (interface{}, error), onError func(Error) FutureNil) (ret interface{}, e error) { + defer t.Close() + for { ret, e = wrapped() @@ -140,7 +141,7 @@ func (d Database) Transact(f func(Transaction) (interface{}, error)) (interface{ return } - return retryable(wrapped, tr.OnError) + return retryable(tr, wrapped, tr.OnError) } // ReadTransact runs a caller-provided function inside a retry loop, providing @@ -180,7 +181,7 @@ func (d Database) ReadTransact(f func(ReadTransaction) (interface{}, error)) (in return } - return retryable(wrapped, tr.OnError) + return retryable(tr, wrapped, tr.OnError) } // Options returns a DatabaseOptions instance suitable for setting options diff --git a/bindings/go/src/fdb/futures.go b/bindings/go/src/fdb/futures.go index 4894ee40ea..2c35f90ece 100644 --- a/bindings/go/src/fdb/futures.go +++ b/bindings/go/src/fdb/futures.go @@ -39,7 +39,6 @@ package fdb import "C" import ( - "runtime" "sync" "unsafe" ) @@ -75,9 +74,7 @@ type future struct { } func newFuture(ptr *C.FDBFuture) *future { - f := &future{ptr} - runtime.SetFinalizer(f, func(f *future) { C.fdb_future_destroy(f.ptr) }) - return f + return &future{ptr} } func fdb_future_block_until_ready(f *C.FDBFuture) { @@ -99,17 +96,14 @@ func fdb_future_block_until_ready(f *C.FDBFuture) { } func (f *future) BlockUntilReady() { - defer runtime.KeepAlive(f) fdb_future_block_until_ready(f.ptr) } func (f *future) IsReady() bool { - defer runtime.KeepAlive(f) return C.fdb_future_is_ready(f.ptr) != 0 } func (f *future) Cancel() { - defer runtime.KeepAlive(f) C.fdb_future_cancel(f.ptr) } @@ -141,7 +135,7 @@ type futureByteSlice struct { func (f *futureByteSlice) Get() ([]byte, error) { f.o.Do(func() { - defer runtime.KeepAlive(f.future) + defer C.fdb_future_destroy(f.ptr) var present C.fdb_bool_t var value *C.uint8_t @@ -155,10 +149,14 @@ func (f *futureByteSlice) Get() ([]byte, error) { } if present != 0 { - f.v = C.GoBytes(unsafe.Pointer(value), length) - } + // Copy the native `value` into a Go byte slice so the underlying + // native Future can be freed. This avoids the need for finalizers. + valueDestination := make([]byte, length) + valueSource := C.GoBytes(unsafe.Pointer(value), length) + copy(valueDestination, valueSource) - C.fdb_future_release_memory(f.ptr) + f.v = valueDestination + } }) return f.v, f.e @@ -198,7 +196,7 @@ type futureKey struct { func (f *futureKey) Get() (Key, error) { f.o.Do(func() { - defer runtime.KeepAlive(f.future) + defer C.fdb_future_destroy(f.ptr) var value *C.uint8_t var length C.int @@ -210,8 +208,11 @@ func (f *futureKey) Get() (Key, error) { return } - f.k = C.GoBytes(unsafe.Pointer(value), length) - C.fdb_future_release_memory(f.ptr) + keySource := C.GoBytes(unsafe.Pointer(value), length) + keyDestination := make([]byte, length) + copy(keyDestination, keySource) + + f.k = keyDestination }) return f.k, f.e @@ -244,17 +245,21 @@ type FutureNil interface { type futureNil struct { *future + o sync.Once + e error } func (f *futureNil) Get() error { - defer runtime.KeepAlive(f.future) + f.o.Do(func() { + defer C.fdb_future_destroy(f.ptr) - f.BlockUntilReady() - if err := C.fdb_future_get_error(f.ptr); err != 0 { - return Error{int(err)} - } + f.BlockUntilReady() + if err := C.fdb_future_get_error(f.ptr); err != 0 { + f.e = Error{int(err)} + } + }) - return nil + return f.e } func (f *futureNil) MustGet() { @@ -292,13 +297,42 @@ func (f *futureKeyValueArray) Get() ([]KeyValue, bool, error) { return nil, false, Error{int(err)} } + // To minimize the number of individual allocations, we first calculate the + // final size used by all keys and values returned from this iteration, + // then perform one larger allocation and slice within it. + + poolSize := 0 + for i := 0; i < int(count); i++ { + kvptr := unsafe.Pointer(uintptr(unsafe.Pointer(kvs)) + uintptr(i*24)) + + poolSize += len(stringRefToSlice(kvptr)) + poolSize += len(stringRefToSlice(unsafe.Pointer(uintptr(kvptr) + 12))) + } + + poolOffset := 0 + pool := make([]byte, poolSize) + ret := make([]KeyValue, int(count)) for i := 0; i < int(count); i++ { kvptr := unsafe.Pointer(uintptr(unsafe.Pointer(kvs)) + uintptr(i*24)) - ret[i].Key = stringRefToSlice(kvptr) - ret[i].Value = stringRefToSlice(unsafe.Pointer(uintptr(kvptr) + 12)) + keySource := stringRefToSlice(kvptr) + valueSource := stringRefToSlice(unsafe.Pointer(uintptr(kvptr) + 12)) + + keyDestination := pool[poolOffset : poolOffset+len(keySource)] + poolOffset += len(keySource) + + valueDestination := pool[poolOffset : poolOffset+len(valueSource)] + poolOffset += len(valueSource) + + copy(keyDestination, keySource) + copy(valueDestination, valueSource) + + ret[i] = KeyValue{ + Key: keyDestination, + Value: valueDestination, + } } return ret, (more != 0), nil @@ -323,19 +357,28 @@ type FutureInt64 interface { type futureInt64 struct { *future + o sync.Once + e error + v int64 } func (f *futureInt64) Get() (int64, error) { - defer runtime.KeepAlive(f.future) + f.o.Do(func() { + defer C.fdb_future_destroy(f.ptr) - f.BlockUntilReady() + f.BlockUntilReady() - var ver C.int64_t - if err := C.fdb_future_get_int64(f.ptr, &ver); err != 0 { - return 0, Error{int(err)} - } + var ver C.int64_t + if err := C.fdb_future_get_version(f.ptr, &ver); err != 0 { + f.v = 0 + f.e = Error{int(err)} + return + } - return int64(ver), nil + f.v = int64(ver) + }) + + return f.v, f.e } func (f *futureInt64) MustGet() int64 { @@ -366,27 +409,40 @@ type FutureStringSlice interface { type futureStringSlice struct { *future + o sync.Once + e error + v []string } func (f *futureStringSlice) Get() ([]string, error) { - defer runtime.KeepAlive(f.future) + f.o.Do(func() { + defer C.fdb_future_destroy(f.ptr) - f.BlockUntilReady() + f.BlockUntilReady() - var strings **C.char - var count C.int + var strings **C.char + var count C.int - if err := C.fdb_future_get_string_array(f.ptr, (***C.char)(unsafe.Pointer(&strings)), &count); err != 0 { - return nil, Error{int(err)} - } + if err := C.fdb_future_get_string_array(f.ptr, (***C.char)(unsafe.Pointer(&strings)), &count); err != 0 { + f.e = Error{int(err)} + return + } - ret := make([]string, int(count)) + ret := make([]string, int(count)) - for i := 0; i < int(count); i++ { - ret[i] = C.GoString((*C.char)(*(**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(strings)) + uintptr(i*8))))) - } + for i := 0; i < int(count); i++ { + source := C.GoString((*C.char)(*(**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(strings)) + uintptr(i*8))))) - return ret, nil + destination := make([]byte, len(source)) + copy(destination, source) + + ret[i] = string(destination) + } + + f.v = ret + }) + + return f.v, f.e } func (f *futureStringSlice) MustGet() []string { diff --git a/bindings/go/src/fdb/range.go b/bindings/go/src/fdb/range.go index 8273fe37fe..6fbb131619 100644 --- a/bindings/go/src/fdb/range.go +++ b/bindings/go/src/fdb/range.go @@ -28,6 +28,7 @@ import "C" import ( "fmt" + "sync" ) // KeyValue represents a single key-value pair in the database. @@ -206,6 +207,18 @@ type RangeIterator struct { index int err error snapshot bool + o sync.Once +} + +// Close releases the underlying native resources for all the `KeyValue`s +// ever returned by this iterator. The `KeyValue`s themselves are copied +// before they're returned, so they are still safe to use after calling +// this function. This is instended to be called with `defer` inside +// your transaction function. +func (ri *RangeIterator) Close() { + ri.o.Do(func() { + C.fdb_future_destroy(ri.f.ptr) + }) } // Advance attempts to advance the iterator to the next key-value pair. Advance diff --git a/bindings/go/src/fdb/transaction.go b/bindings/go/src/fdb/transaction.go index 274aeee867..4d11526d06 100644 --- a/bindings/go/src/fdb/transaction.go +++ b/bindings/go/src/fdb/transaction.go @@ -25,6 +25,7 @@ package fdb // #define FDB_API_VERSION 620 // #include import "C" +import "sync" // A ReadTransaction can asynchronously read from a FoundationDB // database. Transaction and Snapshot both satisfy the ReadTransaction @@ -69,6 +70,7 @@ type Transaction struct { type transaction struct { ptr *C.FDBTransaction db Database + o sync.Once } // TransactionOptions is a handle with which to set options that affect a @@ -84,16 +86,18 @@ func (opt TransactionOptions) setOpt(code int, param []byte) error { }, param) } -func (t *transaction) destroy() { - C.fdb_transaction_destroy(t.ptr) -} - // GetDatabase returns a handle to the database with which this transaction is // interacting. func (t Transaction) GetDatabase() Database { return t.transaction.db } +func (t Transaction) Close() { + t.o.Do(func() { + C.fdb_transaction_destroy(t.ptr) + }) +} + // Transact executes the caller-provided function, passing it the Transaction // receiver object. // From fbb83a98dbf8dfcd128add84af765dda966cf30f Mon Sep 17 00:00:00 2001 From: Ryan Worl Date: Mon, 29 Jul 2019 08:40:15 -0400 Subject: [PATCH 0007/1604] Remove stray KeepAlive, use new Close method to free RangeIterator resources --- bindings/go/src/fdb/directory/directoryLayer.go | 2 ++ bindings/go/src/fdb/fdb_test.go | 1 + bindings/go/src/fdb/futures.go | 2 -- bindings/go/src/fdb/range.go | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/bindings/go/src/fdb/directory/directoryLayer.go b/bindings/go/src/fdb/directory/directoryLayer.go index 63574d9148..5be70e5dd1 100644 --- a/bindings/go/src/fdb/directory/directoryLayer.go +++ b/bindings/go/src/fdb/directory/directoryLayer.go @@ -417,6 +417,7 @@ func (dl directoryLayer) subdirNames(rtr fdb.ReadTransaction, node subspace.Subs rr := rtr.GetRange(sd, fdb.RangeOptions{}) ri := rr.Iterator() + defer ri.Close() var ret []string @@ -442,6 +443,7 @@ func (dl directoryLayer) subdirNodes(tr fdb.Transaction, node subspace.Subspace) rr := tr.GetRange(sd, fdb.RangeOptions{}) ri := rr.Iterator() + defer ri.Close() var ret []subspace.Subspace diff --git a/bindings/go/src/fdb/fdb_test.go b/bindings/go/src/fdb/fdb_test.go index ed9478878a..2c10100e30 100644 --- a/bindings/go/src/fdb/fdb_test.go +++ b/bindings/go/src/fdb/fdb_test.go @@ -246,6 +246,7 @@ func ExampleRangeIterator() { rr := tr.GetRange(fdb.KeyRange{fdb.Key(""), fdb.Key{0xFF}}, fdb.RangeOptions{}) ri := rr.Iterator() + defer ri.Close() // Advance will return true until the iterator is exhausted for ri.Advance() { diff --git a/bindings/go/src/fdb/futures.go b/bindings/go/src/fdb/futures.go index 2c35f90ece..4cf2463575 100644 --- a/bindings/go/src/fdb/futures.go +++ b/bindings/go/src/fdb/futures.go @@ -285,8 +285,6 @@ func stringRefToSlice(ptr unsafe.Pointer) []byte { } func (f *futureKeyValueArray) Get() ([]KeyValue, bool, error) { - defer runtime.KeepAlive(f.future) - f.BlockUntilReady() var kvs *C.FDBKeyValue diff --git a/bindings/go/src/fdb/range.go b/bindings/go/src/fdb/range.go index 6fbb131619..832305591d 100644 --- a/bindings/go/src/fdb/range.go +++ b/bindings/go/src/fdb/range.go @@ -140,6 +140,7 @@ func (rr RangeResult) GetSliceWithError() ([]KeyValue, error) { var ret []KeyValue ri := rr.Iterator() + defer ri.Close() if rr.options.Limit != 0 { ri.options.Mode = StreamingModeExact From d3bdb32ad1b4fbab9a6b0f6fe09da5a7518050b4 Mon Sep 17 00:00:00 2001 From: Ryan Worl Date: Mon, 29 Jul 2019 08:51:27 -0400 Subject: [PATCH 0008/1604] Updated release notes for Go bindings changes --- documentation/sphinx/source/release-notes.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/documentation/sphinx/source/release-notes.rst b/documentation/sphinx/source/release-notes.rst index 600f2f4d3b..2269fdefd8 100644 --- a/documentation/sphinx/source/release-notes.rst +++ b/documentation/sphinx/source/release-notes.rst @@ -46,6 +46,8 @@ Bindings * C: Applications linking to libfdb_c can now use ``pkg-config foundationdb-client`` or ``find_package(FoundationDB-Client ...)`` (for cmake) to get the proper flags for compiling and linking. `(PR #1636) `_. * Go: The Go bindings now require Go version 1.11 or later. * Go: Fix issue with finalizers running too early that could lead to undefined behavior. `(PR #1451) `_. +* Go: Added a `Close` function to `RangeIterator` which **must** be called to free resources returned from `Transaction.GetRange`. `(PR #1910) `_. +* Go: Finalizers are no longer used to clean up native resources. `Future` results are now copied from the native heap to the Go heap, and native resources are freed immediately. `(PR #1910) `_. * Added transaction option to control the field length of keys and values in debug transaction logging in order to avoid truncation. `(PR #1844) `_. Other Changes From eeb2da5c9d1e017dd35147449808beed87afc8ab Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Mon, 9 Sep 2019 22:56:19 -0700 Subject: [PATCH 0009/1604] WIP - simple example compiles --- fdbrpc/FlowTests.actor.cpp | 15 +++++++++++++ flow/actorcompiler/ActorCompiler.cs | 33 +++++++++++++---------------- flow/actorcompiler/ActorParser.cs | 32 +++++++++++++++++++++++++--- flow/actorcompiler/ParseTree.cs | 2 ++ 4 files changed, 61 insertions(+), 21 deletions(-) diff --git a/fdbrpc/FlowTests.actor.cpp b/fdbrpc/FlowTests.actor.cpp index cb43aaed7b..f90a49d930 100644 --- a/fdbrpc/FlowTests.actor.cpp +++ b/fdbrpc/FlowTests.actor.cpp @@ -1207,3 +1207,18 @@ TEST_CASE("/fdbrpc/flow/wait_expression_after_cancel") ASSERT( a == 1 ); return Void(); } + +class Foo { +public: + explicit Foo(int x) : x(x) {} + Future foo() { return fooActor(this); } + ACTOR static Future fooActor(Foo* self); + +private: + int x; +}; + +ACTOR Future Foo::fooActor(Foo* self) { + wait(Future()); + return self->x; +} diff --git a/flow/actorcompiler/ActorCompiler.cs b/flow/actorcompiler/ActorCompiler.cs index 85437910d1..9377f8bf57 100644 --- a/flow/actorcompiler/ActorCompiler.cs +++ b/flow/actorcompiler/ActorCompiler.cs @@ -213,7 +213,6 @@ namespace actorcompiler public void Write(TextWriter writer, out int lines) { lines = 0; - //if (isTopLevel) writer.WriteLine("namespace {"); writer.WriteLine(memberIndentStr + "template<> struct Descriptor {{", descr.name); writer.WriteLine(memberIndentStr + "\tstatic StringRef typeName() {{ return LiteralStringRef(\"{0}\"); }}", descr.name); @@ -265,7 +264,6 @@ namespace actorcompiler lines++; } - //if (isTopLevel) writer.WriteLine("}"); // namespace } } @@ -276,7 +274,6 @@ namespace actorcompiler string sourceFile; List state; List callbacks = new List(); - bool isTopLevel; const string loopDepth0 = "int loopDepth=0"; const string loopDepth = "int loopDepth"; const int codeIndent = +2; @@ -287,12 +284,11 @@ namespace actorcompiler string This; bool generateProbes; - public ActorCompiler(Actor actor, string sourceFile, bool isTopLevel, bool lineNumbersEnabled, bool generateProbes) + public ActorCompiler(Actor actor, string sourceFile, bool lineNumbersEnabled, bool generateProbes) { this.actor = actor; this.sourceFile = sourceFile; - this.isTopLevel = isTopLevel; - this.LineNumbersEnabled = lineNumbersEnabled; + this.LineNumbersEnabled = false; this.generateProbes = generateProbes; FindState(); @@ -302,21 +298,13 @@ namespace actorcompiler string fullReturnType = actor.returnType != null ? string.Format("Future<{0}>", actor.returnType) : "void"; - if (actor.isForwardDeclaration) { - foreach (string attribute in actor.attributes) { - writer.Write(attribute + " "); - } - if (actor.isStatic) writer.Write("static "); - writer.WriteLine("{0} {3}{1}( {2} );", fullReturnType, actor.name, string.Join(", ", ParameterList()), actor.nameSpace==null ? "" : actor.nameSpace + "::"); - return; - } for (int i = 0; ; i++) { className = string.Format("{0}{1}Actor{2}", actor.name.Substring(0, 1).ToUpper(), actor.name.Substring(1), i!=0 ? i.ToString() : ""); - if (usedClassNames.Add(className)) + if (actor.isForwardDeclaration || usedClassNames.Add(className)) break; } @@ -326,6 +314,18 @@ namespace actorcompiler stateClassName = className + "State"; var fullStateClassName = stateClassName + GetTemplateActuals(new VarDeclaration { type = "class", name = fullClassName }); + if (actor.isForwardDeclaration) { + foreach (string attribute in actor.attributes) { + writer.Write(attribute + " "); + } + if (actor.isStatic) writer.Write("static "); + writer.WriteLine("{0} {3}{1}( {2} );", fullReturnType, actor.name, string.Join(", ", ParameterList()), actor.nameSpace==null ? "" : actor.nameSpace + "::"); + if (actor.enclosingClass.Length > 0) { + writer.WriteLine("template friend class {0};", stateClassName); + } + return; + } + var body = getFunction("", "body", loopDepth0); var bodyContext = new Context { target = body, @@ -353,8 +353,6 @@ namespace actorcompiler } bodyContext.catchFErr.WriteLine("loopDepth = 0;"); - if (isTopLevel) writer.WriteLine("namespace {"); - // The "State" class contains all state and user code, to make sure that state names are accessible to user code but // inherited members of Actor, Callback etc are not. writer.WriteLine("// This generated class is to be used only via {0}()", actor.name); @@ -399,7 +397,6 @@ namespace actorcompiler //WriteStartFunc(body, writer); WriteCancelFunc(writer); writer.WriteLine("};"); - if (isTopLevel) writer.WriteLine("}"); // namespace WriteTemplate(writer); LineNumber(writer, actor.SourceLine); foreach (string attribute in actor.attributes) { diff --git a/flow/actorcompiler/ActorParser.cs b/flow/actorcompiler/ActorParser.cs index f85e3ffa9b..2ff02fc176 100644 --- a/flow/actorcompiler/ActorParser.cs +++ b/flow/actorcompiler/ActorParser.cs @@ -255,6 +255,11 @@ namespace actorcompiler //showTokens(); } + class ClassContext { + public string name; + public int inBlocks; + } + public void Write(System.IO.TextWriter writer, string destFileName) { writer.NewLine = "\n"; @@ -266,6 +271,7 @@ namespace actorcompiler outLine++; } int inBlocks = 0; + Stack classContextStack = new Stack(); for(int i=0; i 0 ? classContextStack.Peek().name : ""; var actorWriter = new System.IO.StringWriter(); actorWriter.NewLine = "\n"; - new ActorCompiler(actor, sourceFile, inBlocks==0, LineNumbersEnabled, generateProbes).Write(actorWriter); + new ActorCompiler(actor, sourceFile, LineNumbersEnabled, generateProbes).Write(actorWriter); string[] actorLines = actorWriter.ToString().Split('\n'); bool hasLineNumber = false; @@ -322,10 +329,29 @@ namespace actorcompiler outLine++; } } + else if (tokens[i].Value == "class" || tokens[i].Value == "struct") + { + writer.Write(tokens[i].Value); + var toks = range(i+1, tokens.Length).SkipWhile(Whitespace); + if (!toks.IsEmpty) + { + classContextStack.Push(new ClassContext{name = toks.First().Value, inBlocks = inBlocks }); + } + } else { - if (tokens[i].Value == "{") inBlocks++; - else if (tokens[i].Value == "}") inBlocks--; + if (tokens[i].Value == "{") + { + inBlocks++; + } + else if (tokens[i].Value == "}") + { + inBlocks--; + if (classContextStack.Count > 0 && classContextStack.Peek().inBlocks == inBlocks) + { + classContextStack.Pop(); + } + } writer.Write(tokens[i].Value); outLine += tokens[i].Value.Count(c => c == '\n'); } diff --git a/flow/actorcompiler/ParseTree.cs b/flow/actorcompiler/ParseTree.cs index 4f69d33e6e..8c44ed7aa7 100644 --- a/flow/actorcompiler/ParseTree.cs +++ b/flow/actorcompiler/ParseTree.cs @@ -226,6 +226,8 @@ namespace actorcompiler public List attributes = new List(); public string returnType; public string name; + // "" if there is not enclosing class + public string enclosingClass; public VarDeclaration[] parameters; public VarDeclaration[] templateFormals; //< null if not a template public CodeBlock body; From c487f021f00ff4f92901a0cec1cfe34639f87afd Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Tue, 10 Sep 2019 13:09:37 -0700 Subject: [PATCH 0010/1604] WIP - seems to work for 1 level of nesting --- fdbrpc/FlowTests.actor.cpp | 62 +++++++++++++++++++++-- flow/actorcompiler/ActorCompiler.cs | 11 +++-- flow/actorcompiler/ActorParser.cs | 76 +++++++++++++++++++++++++++-- flow/actorcompiler/ParseTree.cs | 2 +- 4 files changed, 136 insertions(+), 15 deletions(-) diff --git a/fdbrpc/FlowTests.actor.cpp b/fdbrpc/FlowTests.actor.cpp index f90a49d930..10072f332c 100644 --- a/fdbrpc/FlowTests.actor.cpp +++ b/fdbrpc/FlowTests.actor.cpp @@ -1208,17 +1208,69 @@ TEST_CASE("/fdbrpc/flow/wait_expression_after_cancel") return Void(); } -class Foo { +// Tests for https://github.com/apple/foundationdb/issues/1226 + +template +struct ShouldNotGoIntoClassContextStack; + +ACTOR static Future shouldNotHaveFriends(); + +class Foo1 { public: - explicit Foo(int x) : x(x) {} + explicit Foo1(int x) : x(x) {} Future foo() { return fooActor(this); } - ACTOR static Future fooActor(Foo* self); + ACTOR static Future fooActor(Foo1* self); private: int x; }; - -ACTOR Future Foo::fooActor(Foo* self) { +ACTOR Future Foo1::fooActor(Foo1* self) { wait(Future()); return self->x; } + +class [[nodiscard]] Foo2 { +public: + explicit Foo2(int x) : x(x) {} + Future foo() { return fooActor(this); } + ACTOR static Future fooActor(Foo2 * self); + +private: + int x; +}; +ACTOR Future Foo2::fooActor(Foo2* self) { + wait(Future()); + return self->x; +} + +class alignas(4) Foo3 { +public: + explicit Foo3(int x) : x(x) {} + Future foo() { return fooActor(this); } + ACTOR static Future fooActor(Foo3* self); + +private: + int x; +}; +ACTOR Future Foo3::fooActor(Foo3* self) { + wait(Future()); + return self->x; +} + +struct Super {}; + +class Foo4 : Super { +public: + explicit Foo4(int x) : x(x) {} + Future foo() { return fooActor(this); } + ACTOR static Future fooActor(Foo4* self); + +private: + int x; +}; +ACTOR Future Foo4::fooActor(Foo4* self) { + wait(Future()); + return self->x; +} + +ACTOR static Future shouldNotHaveFriends2(); diff --git a/flow/actorcompiler/ActorCompiler.cs b/flow/actorcompiler/ActorCompiler.cs index 9377f8bf57..8dca52a661 100644 --- a/flow/actorcompiler/ActorCompiler.cs +++ b/flow/actorcompiler/ActorCompiler.cs @@ -288,7 +288,7 @@ namespace actorcompiler { this.actor = actor; this.sourceFile = sourceFile; - this.LineNumbersEnabled = false; + this.LineNumbersEnabled = lineNumbersEnabled; this.generateProbes = generateProbes; FindState(); @@ -300,10 +300,13 @@ namespace actorcompiler : "void"; for (int i = 0; ; i++) { - className = string.Format("{0}{1}Actor{2}", + className = string.Format("{3}{0}{1}Actor{2}", actor.name.Substring(0, 1).ToUpper(), actor.name.Substring(1), - i!=0 ? i.ToString() : ""); + i != 0 ? i.ToString() : "", + actor.enclosingClass != null ? actor.enclosingClass + "_" + : actor.nameSpace != null ? actor.nameSpace + "_" + : ""); if (actor.isForwardDeclaration || usedClassNames.Add(className)) break; } @@ -320,7 +323,7 @@ namespace actorcompiler } if (actor.isStatic) writer.Write("static "); writer.WriteLine("{0} {3}{1}( {2} );", fullReturnType, actor.name, string.Join(", ", ParameterList()), actor.nameSpace==null ? "" : actor.nameSpace + "::"); - if (actor.enclosingClass.Length > 0) { + if (actor.enclosingClass != null) { writer.WriteLine("template friend class {0};", stateClassName); } return; diff --git a/flow/actorcompiler/ActorParser.cs b/flow/actorcompiler/ActorParser.cs index 2ff02fc176..ff3bf0d7c9 100644 --- a/flow/actorcompiler/ActorParser.cs +++ b/flow/actorcompiler/ActorParser.cs @@ -260,6 +260,66 @@ namespace actorcompiler public int inBlocks; } + private bool ParseClassContext(TokenRange toks, out string name) + { + name = ""; + if (toks.Begin == toks.End) + { + return false; + } + + // http://nongnu.org/hcb/#attribute-specifier-seq + Token first; + while (true) + { + first = toks.First(NonWhitespace); + if (first.Value == "[") + { + var contents = first.GetMatchingRangeIn(toks); + toks = range(contents.End + 1, toks.End); + } + else if (first.Value == "alignas") + { + toks = range(first.Position + 1, toks.End); + first = toks.First(NonWhitespace); + first.Assert("Expected ( after alignas", t => t.Value == "("); + var contents = first.GetMatchingRangeIn(toks); + toks = range(contents.End + 1, toks.End); + } + else + { + break; + } + } + + // http://nongnu.org/hcb/#class-head-name + first = toks.First(NonWhitespace); + if (!identifierPattern.Match(first.Value).Success) { + return false; + } + while (true) { + first.Assert("Expected identifier", t=>identifierPattern.Match(t.Value).Success); + name += first.Value; + toks = range(first.Position + 1, toks.End); + if (toks.First(NonWhitespace).Value == "::") { + name += "::"; + toks = toks.SkipWhile(Whitespace).Skip(1); + } else { + break; + } + first = toks.First(NonWhitespace); + } + // http://nongnu.org/hcb/#class-virt-specifier-seq + toks = toks.SkipWhile(t => Whitespace(t) || t.Value == "final" || t.Value == "explicit"); + + first = toks.First(NonWhitespace); + if (first.Value == ":" || first.Value == "{") { + // At this point we've confirmed that this is a class. + return true; + } + return false; + } + public void Write(System.IO.TextWriter writer, string destFileName) { writer.NewLine = "\n"; @@ -282,7 +342,10 @@ namespace actorcompiler { int end; var actor = ParseActor(i, out end); - actor.enclosingClass = classContextStack.Count > 0 ? classContextStack.Peek().name : ""; + if (classContextStack.Count > 0) + { + actor.enclosingClass = classContextStack.Peek().name; + } var actorWriter = new System.IO.StringWriter(); actorWriter.NewLine = "\n"; new ActorCompiler(actor, sourceFile, LineNumbersEnabled, generateProbes).Write(actorWriter); @@ -329,13 +392,13 @@ namespace actorcompiler outLine++; } } - else if (tokens[i].Value == "class" || tokens[i].Value == "struct") + else if (tokens[i].Value == "class" || tokens[i].Value == "struct" || tokens[i].Value == "union") { writer.Write(tokens[i].Value); - var toks = range(i+1, tokens.Length).SkipWhile(Whitespace); - if (!toks.IsEmpty) + string name; + if (ParseClassContext(range(i+1, tokens.Length), out name)) { - classContextStack.Push(new ClassContext{name = toks.First().Value, inBlocks = inBlocks }); + classContextStack.Push(new ClassContext { name = name, inBlocks = inBlocks}); } } else @@ -1044,6 +1107,8 @@ namespace actorcompiler } } + readonly Regex identifierPattern = new Regex(@"\G[a-zA-Z_][a-zA-Z_0-9]*", RegexOptions.Singleline); + readonly Regex[] tokenExpressions = (new string[] { @"\{", @"\}", @@ -1059,6 +1124,7 @@ namespace actorcompiler @"\r\n", @"\n", @"::", + @":", @"." }).Select( x=>new Regex(@"\G"+x, RegexOptions.Singleline) ).ToArray(); diff --git a/flow/actorcompiler/ParseTree.cs b/flow/actorcompiler/ParseTree.cs index 8c44ed7aa7..ee9572adf3 100644 --- a/flow/actorcompiler/ParseTree.cs +++ b/flow/actorcompiler/ParseTree.cs @@ -227,7 +227,7 @@ namespace actorcompiler public string returnType; public string name; // "" if there is not enclosing class - public string enclosingClass; + public string enclosingClass = null; public VarDeclaration[] parameters; public VarDeclaration[] templateFormals; //< null if not a template public CodeBlock body; From 9d531db985d38c1a836d1cb43e0e84b449a9b547 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Tue, 10 Sep 2019 13:25:58 -0700 Subject: [PATCH 0011/1604] Handle nested classes --- fdbrpc/FlowTests.actor.cpp | 16 ++++++++++++++++ flow/actorcompiler/ActorCompiler.cs | 4 ++-- flow/actorcompiler/ActorParser.cs | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/fdbrpc/FlowTests.actor.cpp b/fdbrpc/FlowTests.actor.cpp index 10072f332c..643f2df027 100644 --- a/fdbrpc/FlowTests.actor.cpp +++ b/fdbrpc/FlowTests.actor.cpp @@ -1273,4 +1273,20 @@ ACTOR Future Foo4::fooActor(Foo4* self) { return self->x; } +struct Outer { + class Foo5 : Super { + public: + explicit Foo5(int x) : x(x) {} + Future foo() { return fooActor(this); } + ACTOR static Future fooActor(Foo5* self); + + private: + int x; + }; +}; +ACTOR Future Outer::Foo5::fooActor(Outer::Foo5* self) { + wait(Future()); + return self->x; +} + ACTOR static Future shouldNotHaveFriends2(); diff --git a/flow/actorcompiler/ActorCompiler.cs b/flow/actorcompiler/ActorCompiler.cs index 8dca52a661..0150750990 100644 --- a/flow/actorcompiler/ActorCompiler.cs +++ b/flow/actorcompiler/ActorCompiler.cs @@ -304,8 +304,8 @@ namespace actorcompiler actor.name.Substring(0, 1).ToUpper(), actor.name.Substring(1), i != 0 ? i.ToString() : "", - actor.enclosingClass != null ? actor.enclosingClass + "_" - : actor.nameSpace != null ? actor.nameSpace + "_" + actor.enclosingClass != null ? actor.enclosingClass.Replace("::", "_") + "_" + : actor.nameSpace != null ? actor.nameSpace.Replace("::", "_") + "_" : ""); if (actor.isForwardDeclaration || usedClassNames.Add(className)) break; diff --git a/flow/actorcompiler/ActorParser.cs b/flow/actorcompiler/ActorParser.cs index ff3bf0d7c9..228ac17106 100644 --- a/flow/actorcompiler/ActorParser.cs +++ b/flow/actorcompiler/ActorParser.cs @@ -344,7 +344,7 @@ namespace actorcompiler var actor = ParseActor(i, out end); if (classContextStack.Count > 0) { - actor.enclosingClass = classContextStack.Peek().name; + actor.enclosingClass = String.Join("::", classContextStack.Reverse().Select(t => t.name)); } var actorWriter = new System.IO.StringWriter(); actorWriter.NewLine = "\n"; From f983b3c786450f3c54e9edab582cb6a80800816b Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Tue, 10 Sep 2019 14:29:16 -0700 Subject: [PATCH 0012/1604] Remove stale comment --- flow/actorcompiler/ParseTree.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/flow/actorcompiler/ParseTree.cs b/flow/actorcompiler/ParseTree.cs index ee9572adf3..8e94773bc7 100644 --- a/flow/actorcompiler/ParseTree.cs +++ b/flow/actorcompiler/ParseTree.cs @@ -226,7 +226,6 @@ namespace actorcompiler public List attributes = new List(); public string returnType; public string name; - // "" if there is not enclosing class public string enclosingClass = null; public VarDeclaration[] parameters; public VarDeclaration[] templateFormals; //< null if not a template From ce4393b4be646bb7e211367d8415404c3c3b8656 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Tue, 10 Sep 2019 17:37:23 -0700 Subject: [PATCH 0013/1604] Add design doc on TLog spilling --- design/tlog-spilling.md.html | 612 +++++++++++++++++++++++++++++++++++ 1 file changed, 612 insertions(+) create mode 100644 design/tlog-spilling.md.html diff --git a/design/tlog-spilling.md.html b/design/tlog-spilling.md.html new file mode 100644 index 0000000000..c55f84997a --- /dev/null +++ b/design/tlog-spilling.md.html @@ -0,0 +1,612 @@ + + +In what will be FDB 6.1, a new way for TLogs to spill will be introduced. The +code to this will be tricking in as a series of PRs over the next week or two. +It's currently still undetermined if this will be left as only used for +satellite TLogs in Multi-DC configurations (what it was originally written +for), or set as the default for all transaction logs in 6.1. + + +I'm posting this document to: + +1. Make people aware of the coming changes +2. Document how it works, for anyone changing or debugging related code +3. Win "longest post" award. +4. Solicit feedback on the operational side of this work. + +And my personal motivations are largely the last one. I'm interested in comments on things like: + +1. Are all the metrics required for monitoring the new spilling method mentioned? +2. Are all the behaviors that you'd be interested mentioned as knobs or configurable? +3. Does this work introduce new opportunities for problems that weren't mentioned, or insufficiently mitigated. + +--- + +# TLog Spill-By-Reference Design + +## Background + +(This assumes a basic familiarity with [FoundationDB's architecture][fdbsummit-technical-overview].) + +Transaction logs are a distributed Write-Ahead-Log for FoundationDB. They +receive commits from proxies that are written to a sequential *disk queue* in +version order. A commit is sent as a list of tagged mutations, where a *tag* +is a small identifier that represents one storage server as a destination. The +transaction logs are then later *peeked* by storage servers to receive data +destined for their tag. This is how committed data becomes available to +clients for reading. + +Transaction logs internally handle commits via performing two operations +concurrently. First, they walk through each mutation in the commit, and push +the mutation onto an in-memory queue of mutations destined for that tag. +Second, they include the data in the next batch of pages to durably persist to +disk. These queues are popped from when the corresponding storage server has +persisted the data to its own disk. + +TLogs will need to hold the last 5-7 seconds of mutations. In normal +operation, the default 1.5GB of memory is enough such that the last 5-7 seconds +of commits should almost always fit in memory. However, in the presence of +failures, the transaction log can be required to buffer significantly more +data. Most notably, when a storage server fails, its tag isn't popped until +data distribution is able to re-replicate all of the shards that storage server +was responsible for to other storage servers. Before that happens, mutations +will accumulate on the TLog destined for the failed storage server, in case it +comes back and is able to rejoin the cluster. + +When this accumulation causes the memory required to hold all the unpopped data +to exceed `TLOG_SPILL_THREASHOLD` bytes, the transaction log offloads the +oldest data to disk. This writing of data to disk to reduce TLog memory +pressure is referred to as *spilling*. + +Previously, spilling would work by writing the data to a SQLite B-tree. The +key would be `(tag, version)`, and the value would be all the mutations +destined for the given tag at the given version. Peek requests have a start +version, that is the latest version for which the storage server knows about, +and the TLog responds by range-reading the B-tree from the start version. Pop +requests allow the TLog to forget all mutations for a tag until a specific +version, and the TLog thus issues a range clear from `(tag, 0)` to +`(tag, pop_version)`. After spilling, the durably written data in the disk +queue would be trimmed to only include from the spilled version on, as any +required data is now entirely, durably held in the B-tree. As the entire value +is copied into the B-tree, this method of spilling will be referred to as +*spill-by-value* in the rest of this document. + +************************************************************** +* Transaction Log * +* * +* * +* +------------------+ appends +------------+ * +* | Incoming Commits |----------->| Disk Queue | +------+ * +* +------------------+ +------------+ |SQLite| * +* | ^ +------+ * +* | | ^ * +* | pops | * +* +------+--------------+ | writes * +* | | | | | * +* v v v +----------+ * +* in-memory +---+ +---+ +---+ |Spill Loop| * +* queues | 1 | | 2 | | 3 | +----------+ * +* per-tag | | | | | | ^ * +* |...| |...| |...| | * +* | | | | * +* v v v | * +* +-------+------+--------------+ * +* queues spilled on overflow * +* * +************************************************************** + +Unfortunately, it turned out that spilling in this fashion greatly impacts TLog +performance. A write bandwidth saturation test was run against a cluster, with +a modification to the transaction logs to have them act as if there was one +storage server that was permanently failed; it never sent pop requests to allow +the TLog to remove data from memory. After 15min, the write bandwidth had +reduced to 30% of its baseline. After 30min, that became 10%. After 60min, +that became 5%. (This is an intentional hyperbole due to the saturating write +load. See experiments at the end for more detail and data.) + +With the recent multi-DC/multi-region work, a failure of a remote data center +would cause transaction logs to need to buffer all commits, as every commit is +tagged as destined for the remote datacenter. This would rapidly push +transaction logs into a spilling regime, and thus write bandwidth would begin +to rapidly degrade. It is unacceptable for a remote datacenter failure to so +drastically affect the primary datacenter's performance in the case of a +failure, so a more performant way of spilling data is required. + +## Overview + +Whereas spill-by-value copied the entire mutation into the B-tree and removes +it from the disk queue, spill-by-reference leaves the mutations in the disk +queue and writes a pointer to it into the B-tree. Performance experiments +revealed that the TLog's performance while spilling was dictated more by the +number of writes done to the SQLite B-tree, than by the size of those writes. +Thus, "spill-by-reference" being able to do a significantly better batching +with its writes to the B-tree is more important than that it writes less data +in aggregate. Spill-by-reference significantly reduces the volume of data +written to the B-tree, and the less data that we write, the more we can batch +versions to be written together. + +************************************************************************ +* DiskQueue * +* * +* -------- Index on disk -------- ---- Index in memory ---- * +* / \ / \ * +* +-----------------------------------+-----------------------------+ * +* | Spilled Data | Most Recent Data | * +* +-----------------------------------+-----------------------------+ * +* lowest version highest version * +* * +************************************************************************ + +Spill-by-reference works by taking a larger range of versions, and building a +single key-value pair per tag that describes where in the disk queue is every +relevant commit for that tag. Concretely, this takes the form +`(tag, last_version) -> [(version, start, end, mutation bytes)]`, where... + + * `tag` is the small integer representing the storage server this mutation batch is destined for. + * `last_version` is the last/maximum version contained in the value's batch. + * `version` is the version of the commit that this index entry points to. + * `start` is an index into the disk queue of where to find the beginning of the commit. + * `end` is an index into the disk queue of where the end of the commit is. + * `mutation_bytes` is the number of bytes in the commit that are relevant for this tag. + +And then writing only once per tag spilled into the B-tree for each iteration +through spilling. + +Note that each tuple in the list represents a commit, and not a mutation. This +means that peeking spilled commits will involve reading mutations unrelated to +the requested tag. Alternatively, one could have each tuple represent a +mutation within a commit, to prevent over-reading when peeking. There exist +pathological workloads for each strategy. The purpose of this work is most +importantly to support spilling of log router tags. These exist on every +mutation, so that it will get copied to other datacenters. This is the exact +pathological workload for recording each mutation individually, because it only +increases the number of IO operations used to read the same amount of data. +For a wider set of workloads, there's room to establish a heuristic as to when +to record mutation(s) versus the entire commit, but performance testing hasn't +surfaced this as important enough to include in the initial version of this +work. + +Peeking now works by issuing a range read to the B-tree from `(tag, peek_begin)` +to `(tag, infinity)`. This is why the key contains the last version of the +batch, rather than the beginning, so that a range read from the peek request's +version will always return all relevant batches. For each batched tuple, if +the version is greater than our peek request's version, then we read the commit +containing that mutation from disk, extract the relevant mutations, and append +them to our response. There is a target size of the response, 150KB by +default. As we iterate through the tuples, we sum `mutation_bytes`, which +already informs us how many bytes of relevant mutations we'll get from a given +commit. This allows us to make sure we won't waste disk IOs on reads that will +end up being discarded as unnecessary. + +Popping works similarly to before, but now requires recovering information from +disk. Previously, we would maintain a map from version to location in the disk +queue for every version we hadn't yet spilled. Once spilling has copied the +value into the B-tree, knowing where the commit was in the disk queue is +useless to us, and is removed. In spill-by-reference, that information is +still needed to know how to map "pop until version 7" to "pop until byte 87" in +the disk queue. Unfortunately, keeping this information in memory would result +in TLogs slowly consuming more and more memory[^versionmap-memory] as more data +is spilled. Instead, we issue a range read of the B-tree from `(tag, pop_version)` +to `(tag, infinity)` and look at the first commit we find with a version +greater than our own. We then use its starting disk queue location as the +limit of what we could pop the disk queue until for this tag. + +[^versionmap-memory]: Pessimistic assumptions would suggest that a TLog spilling 1TB of data would require ~50GB of memory to hold this map, which isn't acceptable. + +## Detailed Implementation + +The rough outline of concrete changes proposed looks like: + +0. Allow a new TLog and old TLog to co-exist and be configurable, upgradeable, and recoverable +0. Modify spilling in new TLogServer +0. Modify peeking in new TLogServer +0. Modify popping in new TLogServer +0. Spill txsTag specially + +### Spilling + +In spill-by-reference, spilling is now the act of persisting the index of + +### Peeking + +A `TLogPeekRequest` contains a `Tag` and a `Version`, and is a request for all +commits with the specified tag with a commit version greater than or equal to +the given version. The goal is to return a 150KB block of mutations. + +************************************************************************** +* * +* +---------+ Tag +---------+ Tag +--------+ * +* | Peek |-------->| Spilled | ...------------->| Memory | * +* | Request | Version | Index | Version | Index | * +* +---------+ +---------+ +--------+ * +* | | * +* +-----------------+-----------------+ | * +* / \ Start=100 _/ \_ Start=500 + Start=900 + Ptr=0xF00 * +* / \ Length=50 / \ Length=70 / \ Length=30 / \ Length=30 * +* +------------------------------------------------+------------------+ * +* | Disk Queue | Also In Memory | * +* +------------------------------------------------+------------------+ * +* * +************************************************************************** + +Spill-by-value and memory storage engine only ever read from DiskQueue when +recovering, and read the entire file linearly. Therefore, `IDiskQueue` had no +API for random reads to the DiskQueue. That ability is now required for +peeking, and thus, `IDiskQueue`'s API has been enhanced correspondingly: + +``` CPP +enum class CheckHashes { NO, YES }; + +class IDiskQueue { + // ... + Future> read(location start, location end, CheckHashes ch); + // ... +}; +``` + +Internally, the DiskQueue adds page headers every 4K, which are stripped out +from the returned data. Therefore, the length of the result will not be the +same as `end-start`, intentionally. For this reason, the API is `(start, end)` +and not `(start, length)`. + +Spilled data, when using spill-by-value, was resistent to bitrot via data being +checksummed interally within SQLite's B-tree. Now that reads can be done +directly, the responsibility for verifing data integrity falls upon the +DiskQueue. `CheckHashes::YES` will cause the DiskQueue to use the checksum in +each DiskQueue page to verify data integrity. If an externally maintained +checksums exists to verify the returned data, then `CheckHashes::NO` can be +used to elide the checksumming. + +### Popping + +As storage servers persist data, they send `pop(tag, version)` requests to the +transaction log to notify it that it is allowed to discard data for `tag` up +through `version`. Once all the tags have been popped from the oldest commit +in the DiskQueue, the tail of the DiskQueue can be discarded to reclaim space. + + +Each time FoundationDB goes through a recovery, it will recruit a new +generation of transaction logs. This new generation of transaction logs will +often be recruited on the same worker that hosted the previous generation's +transaction log. The old generation of transaction logs will only shut down +once all the data that they have has been fully popped. This means that there +can be multiple instances of a transaction log + +********************************************************* +* SharedTLog * +* * +* +--------+--------+--------+--------+--------+ * +* | TLog 1 | TLog 2 | TLog 3 | TLog 4 | TLog 5 | * +* +--------+--------+--------+--------+--------+ * +* ^ popping ^spilling ^committing * +********************************************************* + + + + +### Transaction State Store + +For FDB to perform a recovery, there is information that it needs to know about +the database, such as the configuration, worker exclusions, backup status, etc. +These values are stored into the database in the `\xff` system keyspace. +However, during a recovery, FDB can't read this data from the storage servers, +because recovery hasn't completed, so it doesn't know who the storage servers +are yet. Thus, a copy of this data is held in-memory on every proxy in the +*transaction state store*, and durably persisted as a part of commits on the +transaction logs. Being durably stored on the transaction logs means the list +of transaction logs can be fetched from the coordinators, and then used to load +the rest of the information about the database. + +The in-memory storage engine writes an equal amount of mutations and snapshot +data to a queue, an when a full snapshot of the data has been written, deletes +the preceeding snapshot and begins writing a new one. When backing an +in-memory storage engine with the transaction logs, the +`LogSystemDiskQueueAdapter` implements writing to a queue as committing +mutations to the transaction logs with a special tag of `txsTag`, and deleting +the preceeding snapshot as popping the transaction logs for the tag of `txsTag` +until the version where the last full snapshot began. + +This means that unlike every other commit that is tagged and stored on the +transaction logs, `txsTag` signifies data that is: + +1. Committed to infrequently +2. Only peeked on recovery +3. Popped infrequently, and a large portion of the data is popped at once +4. A small total volume of data + +The most problematic of these is the infrequent popping. Unpopped data will be +spilled after some time, and if `txsTag` data is spilled and not popped, it +will prevent the DiskQueue from being popped as well. This will cause the +DiskQueue to grow continuously. The infrequent commits and small data volume +means that there benefits of spill-by-reference over spill-by-value don't apply +for this tag. + +Thus, even when configured to spill-by-reference, `txsTag` is spilled by value. + +### Disk Queue Recovery + +If a transaction log dies and restarts, all commits that were in memory at the +time of the crash must be loaded back into memory. Recovery is blocked on this +process, as there might have been a commit to the transaction state store +immediately before crashing, and that data needs to be fully readable during a +recovery. + +In spill-by-value, the DiskQueue only ever contained commits that were also +held in memory, and thus recovery would need to read up to 1.5GB of data. With +spill-by-reference, the DiskQueue could theoretically contain terrabytes of +data. To keep recovery times boundedly low, FDB must still only read the +commits that need to be loaded back into memory. + +This is done by persisting the location in the DiskQueue of the last spilled +commit to the SQLite B-Tree. This is done in the same transaction as the +spilling of that commit. This provides an always accurate pointer to where +data that needs to be loaded into memory begins. The pointer is to the +beginning of the last commit rather than the end, to make sure that the pointer +is always contained within the DiskQueue. This provides extra sanity checking +on the validity of the DiskQueue's contents at recovery, at the cost of +potentially reading 10MB more than what would be required. + +## Testing + +Correctness bugs in spilling would manifest as data corruption, which is well covered by simulation. +The only special testing code added was to enable changing `log_spill` in `ConfigureTest`. +This covers switching between spilling methods in the presence of faults. + +An `ASSERT` was added to simulation that verifies that commits read from the +DiskQueue on recovery are only the commits which have not been spilled. + +The rest of the testing is to take a physical cluster and try the extremes that +can only happen at scale: + +* Verify that recovery times are not impacted when a large amount of data is spilled +* Verify that long running tests hit a steady state of memory usage (and thus there are likely no leaks). +* Plot how quickly (MB/s) a remote datacenter can catch up in old vs new spilling strategy +* See what happens when there's 1 tlog and more than 100 storage servers. + * Verify that peek requests get limited + * See if tlog commits can get starved by excessive peeking + +[fdbsummit-technical-overview]: https://www.youtube.com/watch?v=EMwhsGsxfPU + +# TLog Spill-By-Reference Operational Guide + +## Notable Behavior Changes + +TL;DR: Spilling involves less IOPS and is faster. Peeking involves more IOPS and is slower. Popping involves >0 IOPS. + +### Spilling + +The most notable effect of the spilling changes is that the Disk Queue files +will now grow to potentially terrabytes in size. + + 1. Spilling will occur in larger batches, which will result in a more +sawtooth-like `BytesInput - BytesDurable` value. I'm not aware that this will have any meaningful impact. + + * Disk queue files will grow when spilling is happening + * Alerting based on DQ file size is no longer appropriate + +As a curious aside, throughput decreases as spilled volume increases, which +quite possibly worked as accidental backpressure. As a feature, this no longer +exists, but means write-heavy workloads can drown storage servers faster than +before. + +### Peeking + +Peeking has seen tremendous changes. Its involves more IO operations and memory usage. + +The expected implication of this are: + +1. A peek of spilled data will involve a burst of IO operations. + + Theoretically, this burst can drown out queued write operations to disk, + thus and slowing down TLog commits. This hasn't been observed in testing. + + Low IOPS devices, such as HDD or network attached storage, would struggle + more here than locally attached SSD. + +2. Generating a peek response of 150KB could require reading 100MB of data, and allocating buffers to hold that 100MB. + + OOMs were observed in early testing. Code has been added to specifically + limit how much memory can be allocated for serving a signle peek request + and all concurrent peek requests, with knobs to allow tuning this per + deployment configuration. + +### Popping + +Popping will transition from being an only in-memory operation to one that +involves reads from disk. + +Due to a strange quirk, TLogs will allocate up to 2GB of memory as a read cache +for SQLite's B-tree. The expected maximum size of the B-tree has drastically +reduced, so these reads should almost never actually hit disk. The number of +writes to disk will stay the same, so performance should stay unchanged. + +### Disk Queues + +This work should have a minimal impact on recovery times, which is why recovery +hasn't been significantly mentioned in this document. However, there are two +minor impacts on recovery times: + +1. Larger disk queue file means more file to zero out in the case of recovery. + + This should be negligable when fallocate `ZERO_RANGE` is available, because then it's only a metadata operation. + +2. A larger file means more bisection iterations to find the first page. + + If we say Disk Queue files are typically ~4GB now, and people are unlikely + to have more than 4TB drives, then this means in the worst case, another 8 + sequential IOs will need to be done when first recovering a disk queue file + to find the most recent page with a binary search. + + If this turns out to be an issue, it's trivial to address. There's no + reason to do only a binary search when drives support parallel requests. A + 32-way search could reasonably be done, and would would make a 4TB Disk + Queue file faster to recover than a 4GB one currently. + +3. Disk queue files can now shrink. + + The particular logic currently used is that: + + If one file is significantly larger than the other file, then it will be + truncated to the size of the other file. This resolves situations where a + particular storage server or remote DC being down causes one DiskQueue file + to be grown to a massive size, and then the data is rapidly popped. + + Otherwise, If the files are of reasonably similar size, then we'll take + `pushLocation - popLocation` as the number of "active" bytes, and then + shrink the file by `TLOG_DISK_QUEUE_SHRINK_BYTES` bytes if the file is + larger than `active + TLOG_DISK_QUEUE_EXTENSION_BYTES + TLOG_DISK_QUEUE_SHRINK_BYTES`. + +!!! note + While writing this, I've realized it's probably a good idea to limit that + the disk queue can't shrink under 4GB of size, to prevent size thrashing on + bursty workloads. + +## Knobs + +`REFERENCE_SPILL_UPDATE_STORAGE_BYTE_LIMIT` +: How many bytes of mutations should be spilled at once in a spill-by-reference TLog.
+ Increasing it could increase throughput in spilling regimes.
+ Decreasing it will decrease how sawtooth-like TLog memory usage is.
+ +`TLOG_UPDATE_STORAGE` +: How many bytes of mutations should be spilled at once in a spill-by-value TLog.
+ This knob is pre-existing, and has only been "changed" to only apply to spill-by-value.
+ +`TLOG_SPILL_REFERENCE_MAX_BATCHES_PER_PEEK` +: How many batches of spilled data index batches should be read from disk to serve one peek request.
+ Increasing it will potentially increase the throughput of peek requests.
+ Decreasing it will decrease the number of read IOs done per peek request.
+ +`TLOG_SPILL_REFERENCE_MAX_BYTES_PER_BATCH` +: How many bytes a batch of spilled data indexes can be.
+ Increasing it will increase TLog throughput while spilling.
+ Decreasing it will decrease the latency and increase the throughput of peek requests.
+ +`TLOG_SPILL_REFERENCE_MAX_PEEK_MEMORY_BYTES` +: How many bytes of memory can be allocated to hold the results of reads from disk to respond to peek requests.
+ Increasing it will increase the number of parallel peek requests a TLog can handle at once.
+ Decreasing it will reduce TLog memory usage.
+ If increased, `--max_memory` should be increased by the same amount.
+ +`TLOG_DISK_QUEUE_EXTENSION_BYTES` +: When a DiskQueue needs to extend a file, by how many bytes should it extend the file.
+ Increasing it will reduce metadata operations done to the drive, and likely tail commit latency.
+ Decreasing it will reduce allocated but unused space in the DiskQueue files.
+ Note that this was previously hardcoded to 20MB, and is only being promoted to a knob.
+ +`TLOG_DISK_QUEUE_SHRINK_BYTES` +: If a DiskQueue file has extra space left when switching to the other file, by how many bytes should it be shrunk.
+ Increasing this will cause disk space to be returned to the OS faster.
+ Decreasing this will decrease TLog tail latency due to filesystem metadata updates.
+ +## Observability + +With the new changes, we must ensure that sufficent information has been exposed such that: + +1. If something goes wrong in production, we can understand what and why from trace logs. +2. We can understand if the TLog is performing suboptimally, and if so, which knob we should change and by how much. + +All of the below are planned to be additions to TLogMetrics. + +### Spilling + +!!! warning + Only metrics above this line have been implemented. + +`SpillReferenceBatchSize` +: Stats about the total size of batches written to the B-tree, excluding `txsTag`. + +`SpillReferenceTagCount` +: Stats about the number of distinct tags that have been spilled on each loop iteration, excluding `txsTag`. + +`SpillReferenceIterationCount` +: The number of times we committed data to the B-tree to spill. + +### Peeking + +`PeekMemoryRequestsStalled` +: The number of peek requests that are blocked on acquiring memory for reads. + +`PeekMemoryReserved` +: The amount of memory currently reserved for serving peek requests. + +!!! warning + Only metrics above this line have been implemented. + +`PeekMemoryAverage` +: The average amount of memory allocated per peek of spilled data. + +`PeekMemoryLimitHit` +: The number of times a peek was cut short due to hitting the maximum memory limit. + +`PeekReferenceSpilledCount` +: The number of times a peek request required reading spilled data from the disk queue. + +`PeekReferenceReadAmp` +: Stats about the read amplification encountered. + +### Popping + +!!! warning + Only metrics above this line have been implemented. + +`DQOldestVersion` +: The oldest version that's still useful. + +`BytesPopped` +: The total bytes discarded from the queue *and* the `IDiskQueue::location` of the first useful byte. + +`OldestUnpoppedTag` +: The tag that's preventing the DiskQueue from being further popped. + +### Disk Queue + +!!! warning + Only metrics above this line have been implemented. + +`DiskQueueExcessBytes` +: The number of bytes that the disk queue doesn't need, and will truncate to free over time. This should be roughly equal to `BytesInput - BytesPopped`, but computed at a different layer. + +## Monitoring and Alerting + +To answer questions like: + +1. What new graphs should exist? +2. What old graphs might exist that would no longer be meaningful? +3. What alerts might exist that need to be changed? +4. What alerts should be created? + +Of which I'm aware of: + +* Any current alerts on "Disk Queue files more than [constant size] GB" will need to be removed. +* Any alerting or monitoring of `log*.sqlite` as an indication of spilling will no longer be effective. + + +* A graph of `BytesInput - BytesPopped` will give an idea of the number of "active" bytes in the DiskQueue file. + + + +# Appendix + +## Experiments + +### SQLite B-tree costs + +| Experiment | result | +|-------------------------------|---------| +| Baseline | 60 MB/s | +| Write value to B-tree | 20 MB/s | +| Write pointer to B-tree | 20 MB/s | +| " once per spill loop | 40 MB/s | +| " spill 10x the data per loop | 60 MB/s | + +### Spilling speeds + +2. speed over time with 1 storage server dead + + + + + + + From 61f6aed19395cc5902bcf72f227125ea3488a69c Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Tue, 10 Sep 2019 17:40:56 -0700 Subject: [PATCH 0014/1604] Remove the header meant for the forums --- design/tlog-spilling.md.html | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/design/tlog-spilling.md.html b/design/tlog-spilling.md.html index c55f84997a..3d0eb5c14a 100644 --- a/design/tlog-spilling.md.html +++ b/design/tlog-spilling.md.html @@ -1,27 +1,5 @@ -In what will be FDB 6.1, a new way for TLogs to spill will be introduced. The -code to this will be tricking in as a series of PRs over the next week or two. -It's currently still undetermined if this will be left as only used for -satellite TLogs in Multi-DC configurations (what it was originally written -for), or set as the default for all transaction logs in 6.1. - - -I'm posting this document to: - -1. Make people aware of the coming changes -2. Document how it works, for anyone changing or debugging related code -3. Win "longest post" award. -4. Solicit feedback on the operational side of this work. - -And my personal motivations are largely the last one. I'm interested in comments on things like: - -1. Are all the metrics required for monitoring the new spilling method mentioned? -2. Are all the behaviors that you'd be interested mentioned as knobs or configurable? -3. Does this work introduce new opportunities for problems that weren't mentioned, or insufficiently mitigated. - ---- - # TLog Spill-By-Reference Design ## Background From 703e7fd9446531a3f1131656a42920224ecf75f0 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Wed, 11 Sep 2019 16:26:40 -0700 Subject: [PATCH 0015/1604] Put generated code back in anonymous namespace where possible --- fdbserver/Resolver.actor.cpp | 2 +- flow/actorcompiler/ActorCompiler.cs | 11 ++++++++--- flow/actorcompiler/ActorParser.cs | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/fdbserver/Resolver.actor.cpp b/fdbserver/Resolver.actor.cpp index 41834bb163..dbafe83513 100644 --- a/fdbserver/Resolver.actor.cpp +++ b/fdbserver/Resolver.actor.cpp @@ -66,7 +66,7 @@ struct Resolver : ReferenceCounted { Version debugMinRecentStateVersion; }; -} +} // namespace ACTOR Future resolveBatch( Reference self, diff --git a/flow/actorcompiler/ActorCompiler.cs b/flow/actorcompiler/ActorCompiler.cs index 0150750990..eab91e56a7 100644 --- a/flow/actorcompiler/ActorCompiler.cs +++ b/flow/actorcompiler/ActorCompiler.cs @@ -274,6 +274,7 @@ namespace actorcompiler string sourceFile; List state; List callbacks = new List(); + bool isTopLevel; const string loopDepth0 = "int loopDepth=0"; const string loopDepth = "int loopDepth"; const int codeIndent = +2; @@ -284,10 +285,11 @@ namespace actorcompiler string This; bool generateProbes; - public ActorCompiler(Actor actor, string sourceFile, bool lineNumbersEnabled, bool generateProbes) + public ActorCompiler(Actor actor, string sourceFile, bool isTopLevel, bool lineNumbersEnabled, bool generateProbes) { this.actor = actor; this.sourceFile = sourceFile; + this.isTopLevel = isTopLevel; this.LineNumbersEnabled = lineNumbersEnabled; this.generateProbes = generateProbes; @@ -304,8 +306,8 @@ namespace actorcompiler actor.name.Substring(0, 1).ToUpper(), actor.name.Substring(1), i != 0 ? i.ToString() : "", - actor.enclosingClass != null ? actor.enclosingClass.Replace("::", "_") + "_" - : actor.nameSpace != null ? actor.nameSpace.Replace("::", "_") + "_" + actor.enclosingClass != null && actor.isForwardDeclaration ? actor.enclosingClass.Replace("::", "_") + "_" + : actor.nameSpace != null ? actor.nameSpace.Replace("::", "_") + "_" : ""); if (actor.isForwardDeclaration || usedClassNames.Add(className)) break; @@ -356,6 +358,8 @@ namespace actorcompiler } bodyContext.catchFErr.WriteLine("loopDepth = 0;"); + if (isTopLevel && actor.nameSpace == null) writer.WriteLine("namespace {"); + // The "State" class contains all state and user code, to make sure that state names are accessible to user code but // inherited members of Actor, Callback etc are not. writer.WriteLine("// This generated class is to be used only via {0}()", actor.name); @@ -400,6 +404,7 @@ namespace actorcompiler //WriteStartFunc(body, writer); WriteCancelFunc(writer); writer.WriteLine("};"); + if (isTopLevel && actor.nameSpace == null) writer.WriteLine("}"); // namespace WriteTemplate(writer); LineNumber(writer, actor.SourceLine); foreach (string attribute in actor.attributes) { diff --git a/flow/actorcompiler/ActorParser.cs b/flow/actorcompiler/ActorParser.cs index 228ac17106..d92bba9d53 100644 --- a/flow/actorcompiler/ActorParser.cs +++ b/flow/actorcompiler/ActorParser.cs @@ -348,7 +348,7 @@ namespace actorcompiler } var actorWriter = new System.IO.StringWriter(); actorWriter.NewLine = "\n"; - new ActorCompiler(actor, sourceFile, LineNumbersEnabled, generateProbes).Write(actorWriter); + new ActorCompiler(actor, sourceFile, inBlocks == 0, LineNumbersEnabled, generateProbes).Write(actorWriter); string[] actorLines = actorWriter.ToString().Split('\n'); bool hasLineNumber = false; From 36fd1ec0e4cc5358fb2c064c4b47c3eb8b3e3593 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Thu, 12 Sep 2019 10:53:17 -0700 Subject: [PATCH 0016/1604] Add sealed as a valid class-virt-specifier --- flow/actorcompiler/ActorParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/actorcompiler/ActorParser.cs b/flow/actorcompiler/ActorParser.cs index d92bba9d53..7c95344952 100644 --- a/flow/actorcompiler/ActorParser.cs +++ b/flow/actorcompiler/ActorParser.cs @@ -310,7 +310,7 @@ namespace actorcompiler first = toks.First(NonWhitespace); } // http://nongnu.org/hcb/#class-virt-specifier-seq - toks = toks.SkipWhile(t => Whitespace(t) || t.Value == "final" || t.Value == "explicit"); + toks = toks.SkipWhile(t => Whitespace(t) || t.Value == "final" || t.Value == "explicit" || t.Value == "sealed"); first = toks.First(NonWhitespace); if (first.Value == ":" || first.Value == "{") { From 50d43cff15ca337d6ebccc20c154f28ee21d6fd4 Mon Sep 17 00:00:00 2001 From: Tapasweni Pathak Date: Thu, 26 Sep 2019 23:03:13 +0530 Subject: [PATCH 0017/1604] Add comments to explain functions in ReplicationUtils.cpp --- fdbrpc/ReplicationUtils.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/fdbrpc/ReplicationUtils.cpp b/fdbrpc/ReplicationUtils.cpp index e91bf475ea..ac43064367 100644 --- a/fdbrpc/ReplicationUtils.cpp +++ b/fdbrpc/ReplicationUtils.cpp @@ -26,6 +26,12 @@ #include "fdbrpc/Replication.h" +/** + * ratePolicy takes localitySet and ReplicationPolicy as arguments. + * localitySet is used for setting the logServerSet defining using WorkerDetails. + * Iterating nTestTotal number of times the replication is performed for the items. + */ + double ratePolicy( Reference & localitySet, Reference const& policy, @@ -82,6 +88,12 @@ double ratePolicy( return rating; } +/** + * findBestPolicySet takes bestResults, localitySet, ReplicationPolicy, number of Min Iterms + * number of Select Test and number of Policy Tests as arguments and find the best + * from a locality set defined. The bestRate has value less than 0.0 + **/ + bool findBestPolicySet( std::vector& bestResults, Reference & localitySet, @@ -158,6 +170,11 @@ bool findBestPolicySet( return bSucceeded; } +/** + * findBestUniquePolicySet takes mainluy localityUniquenessKey. Random unique items + * are compared with results, the output is returned. + **/ + bool findBestUniquePolicySet( std::vector& bestResults, Reference & localitySet, From 2e5e168d0130de58fe6efbbd51c4404a636746a4 Mon Sep 17 00:00:00 2001 From: canardleteer Date: Tue, 15 Oct 2019 11:50:12 -0700 Subject: [PATCH 0018/1604] Add PackWithVersionstamp to Go Subpace & Directory bindings. --- .../src/fdb/directory/directoryPartition.go | 4 ++ bindings/go/src/fdb/subspace/subspace.go | 8 +++ bindings/go/src/fdb/subspace/subspace_test.go | 49 +++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 bindings/go/src/fdb/subspace/subspace_test.go diff --git a/bindings/go/src/fdb/directory/directoryPartition.go b/bindings/go/src/fdb/directory/directoryPartition.go index d6e0275f02..7702bd3e04 100644 --- a/bindings/go/src/fdb/directory/directoryPartition.go +++ b/bindings/go/src/fdb/directory/directoryPartition.go @@ -45,6 +45,10 @@ func (dp directoryPartition) Pack(t tuple.Tuple) fdb.Key { panic("cannot pack keys using the root of a directory partition") } +func (dp directoryPartition) PackWithVersionstamp(t tuple.Tuple) (fdb.Key, error) { + panic("cannot pack keys using the root of a directory partition") +} + func (dp directoryPartition) Unpack(k fdb.KeyConvertible) (tuple.Tuple, error) { panic("cannot unpack keys using the root of a directory partition") } diff --git a/bindings/go/src/fdb/subspace/subspace.go b/bindings/go/src/fdb/subspace/subspace.go index b779d5a9f7..c525f03a2b 100644 --- a/bindings/go/src/fdb/subspace/subspace.go +++ b/bindings/go/src/fdb/subspace/subspace.go @@ -54,6 +54,10 @@ type Subspace interface { // Subspace prepended. Pack(t tuple.Tuple) fdb.Key + // PackWithVersionstamp is similar to Pack, but afford for an + // IncompleteVersionstamp in the tuple + PackWithVersionstamp(t tuple.Tuple) (fdb.Key, error) + // Unpack returns the Tuple encoded by the given key with the prefix of this // Subspace removed. Unpack will return an error if the key is not in this // Subspace or does not encode a well-formed Tuple. @@ -108,6 +112,10 @@ func (s subspace) Pack(t tuple.Tuple) fdb.Key { return fdb.Key(concat(s.b, t.Pack()...)) } +func (s subspace) PackWithVersionstamp(t tuple.Tuple) (fdb.Key, error) { + return t.PackWithVersionstamp(s.b) +} + func (s subspace) Unpack(k fdb.KeyConvertible) (tuple.Tuple, error) { key := k.FDBKey() if !bytes.HasPrefix(key, s.b) { diff --git a/bindings/go/src/fdb/subspace/subspace_test.go b/bindings/go/src/fdb/subspace/subspace_test.go new file mode 100644 index 0000000000..cb4d52aca7 --- /dev/null +++ b/bindings/go/src/fdb/subspace/subspace_test.go @@ -0,0 +1,49 @@ +package subspace + +import ( + "github.com/apple/foundationdb/bindings/go/src/fdb" + "github.com/apple/foundationdb/bindings/go/src/fdb/tuple" + "testing" +) + +// TestSubspacePackWithVersionstamp confirms that packing Versionstamps +// in subspaces work by setting, then preparing to read back a key. +func TestSubspacePackWithVersionstamp(t *testing.T) { + + // I assume this can be lowered, but I have not tested it. + fdb.MustAPIVersion(610) + db := fdb.MustOpenDefault() + + var sub Subspace + sub = FromBytes([]byte("testspace")) + + tup := tuple.Tuple{tuple.IncompleteVersionstamp(uint16(0))} + key, err := sub.PackWithVersionstamp(tup) + + if err != nil { + t.Errorf("PackWithVersionstamp failed: %s", err) + } + + ret, err := db.Transact(func(tr fdb.Transaction) (interface{}, error) { + tr.SetVersionstampedKey(key, []byte("blahblahbl")) + return tr.GetVersionstamp(), nil + }) + + if err != nil { + t.Error("Transaction failed") + } + + fvs := ret.(fdb.FutureKey) + + _, err = fvs.Get() + + if err != nil { + t.Error("Failed to get the written Versionstamp") + } + + // It would be nice to include a read back of the key here, but when + // I started writing that part of the test, most of it was spent + // on writing Versionstamp management in Go, which isn't really + // fleshed out in the Go binding... So I'm going to leave that for + // when that aspect of the binding is more developed. +} \ No newline at end of file From 795c951b7b873d608f0041d835e7c4c9ee67a4a6 Mon Sep 17 00:00:00 2001 From: Tapasweni Pathak Date: Thu, 17 Oct 2019 22:04:32 +0530 Subject: [PATCH 0019/1604] Add function documentation --- fdbrpc/ReplicationUtils.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/fdbrpc/ReplicationUtils.h b/fdbrpc/ReplicationUtils.h index f9f1987e78..2a569be590 100644 --- a/fdbrpc/ReplicationUtils.h +++ b/fdbrpc/ReplicationUtils.h @@ -27,9 +27,10 @@ typedef std::string repTestType; + //string value defining test type extern repTestType convertToTestType(int iValue); - + //converts integer value to a test type extern int testReplication(); @@ -37,6 +38,12 @@ extern double ratePolicy( Reference & localitySet, Reference const& policy, unsigned int nSelectTests); + //returns the value for the rate policy + //given a localitySet, replication policy and number of selected tests, apply the + //policy and return the rating + //rating can be -1 there are no unique results failing while applying the replication + //policy, otherwise largest mode from the items per unique set of locaility entry + //are returned. extern bool findBestPolicySet( std::vector& bestResults, @@ -45,6 +52,11 @@ extern bool findBestPolicySet( unsigned int nMinItems, unsigned int nSelectTests, unsigned int nPolicyTests); + //returns the best policy set + //given locality set, replication policy, number of min items, number of select + //test, number of policy tests, find the best from locality set, including few + //random items, get the rate policy having test rate, best rate and returning + //the success state. extern bool findBestUniquePolicySet( std::vector& bestResults, From 4000ddadc0ebadd07e32e706fa3ac1ea3f0c8fab Mon Sep 17 00:00:00 2001 From: Tapasweni Pathak Date: Thu, 17 Oct 2019 22:07:30 +0530 Subject: [PATCH 0020/1604] remove comments from ReplicationUtils.cpp file --- fdbrpc/ReplicationUtils.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/fdbrpc/ReplicationUtils.cpp b/fdbrpc/ReplicationUtils.cpp index ac43064367..e91bf475ea 100644 --- a/fdbrpc/ReplicationUtils.cpp +++ b/fdbrpc/ReplicationUtils.cpp @@ -26,12 +26,6 @@ #include "fdbrpc/Replication.h" -/** - * ratePolicy takes localitySet and ReplicationPolicy as arguments. - * localitySet is used for setting the logServerSet defining using WorkerDetails. - * Iterating nTestTotal number of times the replication is performed for the items. - */ - double ratePolicy( Reference & localitySet, Reference const& policy, @@ -88,12 +82,6 @@ double ratePolicy( return rating; } -/** - * findBestPolicySet takes bestResults, localitySet, ReplicationPolicy, number of Min Iterms - * number of Select Test and number of Policy Tests as arguments and find the best - * from a locality set defined. The bestRate has value less than 0.0 - **/ - bool findBestPolicySet( std::vector& bestResults, Reference & localitySet, @@ -170,11 +158,6 @@ bool findBestPolicySet( return bSucceeded; } -/** - * findBestUniquePolicySet takes mainluy localityUniquenessKey. Random unique items - * are compared with results, the output is returned. - **/ - bool findBestUniquePolicySet( std::vector& bestResults, Reference & localitySet, From 0fab0d1a2531893f4d8d0534e17c86c60a3d6ddb Mon Sep 17 00:00:00 2001 From: Tapasweni Pathak Date: Thu, 17 Oct 2019 22:09:11 +0530 Subject: [PATCH 0021/1604] remove whitespaces --- fdbrpc/ReplicationUtils.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fdbrpc/ReplicationUtils.cpp b/fdbrpc/ReplicationUtils.cpp index e91bf475ea..791947f0e3 100644 --- a/fdbrpc/ReplicationUtils.cpp +++ b/fdbrpc/ReplicationUtils.cpp @@ -294,10 +294,10 @@ bool validateAllCombinations( for (int i = 0; i < newItems.size(); ++i) { localGroup->add(newItems[i]); } - + std::string bitmask(nCombinationSize, 1); // K leading 1's bitmask.resize(newItems.size(), 0); // N-K trailing 0's - + std::vector resultEntries; do { From 7503f2f46cbab16d1e087d4def95f70c5c680e21 Mon Sep 17 00:00:00 2001 From: canardleteer Date: Sat, 19 Oct 2019 14:11:05 -0700 Subject: [PATCH 0022/1604] Remove unnecessary test --- bindings/go/src/fdb/subspace/subspace_test.go | 49 ------------------- 1 file changed, 49 deletions(-) delete mode 100644 bindings/go/src/fdb/subspace/subspace_test.go diff --git a/bindings/go/src/fdb/subspace/subspace_test.go b/bindings/go/src/fdb/subspace/subspace_test.go deleted file mode 100644 index cb4d52aca7..0000000000 --- a/bindings/go/src/fdb/subspace/subspace_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package subspace - -import ( - "github.com/apple/foundationdb/bindings/go/src/fdb" - "github.com/apple/foundationdb/bindings/go/src/fdb/tuple" - "testing" -) - -// TestSubspacePackWithVersionstamp confirms that packing Versionstamps -// in subspaces work by setting, then preparing to read back a key. -func TestSubspacePackWithVersionstamp(t *testing.T) { - - // I assume this can be lowered, but I have not tested it. - fdb.MustAPIVersion(610) - db := fdb.MustOpenDefault() - - var sub Subspace - sub = FromBytes([]byte("testspace")) - - tup := tuple.Tuple{tuple.IncompleteVersionstamp(uint16(0))} - key, err := sub.PackWithVersionstamp(tup) - - if err != nil { - t.Errorf("PackWithVersionstamp failed: %s", err) - } - - ret, err := db.Transact(func(tr fdb.Transaction) (interface{}, error) { - tr.SetVersionstampedKey(key, []byte("blahblahbl")) - return tr.GetVersionstamp(), nil - }) - - if err != nil { - t.Error("Transaction failed") - } - - fvs := ret.(fdb.FutureKey) - - _, err = fvs.Get() - - if err != nil { - t.Error("Failed to get the written Versionstamp") - } - - // It would be nice to include a read back of the key here, but when - // I started writing that part of the test, most of it was spent - // on writing Versionstamp management in Go, which isn't really - // fleshed out in the Go binding... So I'm going to leave that for - // when that aspect of the binding is more developed. -} \ No newline at end of file From d715e2909ce5037b34fa75f4db9e367345a2797b Mon Sep 17 00:00:00 2001 From: canardleteer Date: Mon, 21 Oct 2019 19:57:01 -0700 Subject: [PATCH 0023/1604] Use the python module to guide PackWithVersionstamp's documentation. --- bindings/go/src/fdb/subspace/subspace.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/bindings/go/src/fdb/subspace/subspace.go b/bindings/go/src/fdb/subspace/subspace.go index c525f03a2b..353d377e42 100644 --- a/bindings/go/src/fdb/subspace/subspace.go +++ b/bindings/go/src/fdb/subspace/subspace.go @@ -54,8 +54,13 @@ type Subspace interface { // Subspace prepended. Pack(t tuple.Tuple) fdb.Key - // PackWithVersionstamp is similar to Pack, but afford for an - // IncompleteVersionstamp in the tuple + // PackWithVersionstamp returns the key encoding the specified tuple in + // the subspace so that it may be used as the key in fdb.Transaction's + // SetVersionstampedKey() method. The passed tuple must contain exactly + // one incomplete tuple.Versionstamp instance or the method will return + // with an error. The behavior here is the same as if one used the + // tuple.PackWithVersionstamp() method to appropriately pack together this + // subspace and the passed tuple. PackWithVersionstamp(t tuple.Tuple) (fdb.Key, error) // Unpack returns the Tuple encoded by the given key with the prefix of this From 4e404e34e5522d823d414dbfaa686210a2617e97 Mon Sep 17 00:00:00 2001 From: Stephen Atherton Date: Sun, 17 Nov 2019 17:09:24 -0800 Subject: [PATCH 0024/1604] Added prefix size comparison test which generates records with a configurable prefix pattern and compares storage size between Redwood and the SQLite storage engine. --- fdbserver/VersionedBTree.actor.cpp | 270 ++++++++++++++++++++++++- tests/CMakeLists.txt | 2 + tests/RedwoodPerfPrefixCompression.txt | 6 + tests/RedwoodPerfSet.txt | 6 + 4 files changed, 276 insertions(+), 8 deletions(-) create mode 100644 tests/RedwoodPerfPrefixCompression.txt create mode 100644 tests/RedwoodPerfSet.txt diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index b4facd88f2..0984903904 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -84,7 +84,7 @@ std::string toString(const T *begin, const T *end) { template std::string toString(const std::vector &v) { - return toString(v.begin(), v.end()); + return toString(&v.front(), &v.back() + 1); } template @@ -1540,12 +1540,12 @@ public: g_network->getDiskBytes(parentDirectory(filename), free, total); int64_t pagerSize = pHeader->pageCount * physicalPageSize; - // It is not exactly known how many pages on the delayed free list are usable as of right now. It could be, - // if each commit delayed entries that were freeable were shuffled from the delayed free queue to the free queue. - // but this doesn't seem necessary most of the time. + // It is not exactly known how many pages on the delayed free list are usable as of right now. It could be known, + // if each commit delayed entries that were freeable were shuffled from the delayed free queue to the free queue, + // but this doesn't seem necessary. int64_t reusable = (freeList.numEntries + delayedFreeList.numEntries) * physicalPageSize; - return StorageBytes(free, total, pagerSize, free + reusable); + return StorageBytes(free, total, pagerSize - reusable, free + reusable); } ACTOR static Future getUserPageCount_cleanup(DWALPager *self) { @@ -3337,8 +3337,9 @@ private: ASSERT(ib != m_pBuffer->end()); // If we found the boundary we are looking for, return its iterator - if(ib->first == boundary) + if(ib->first == boundary) { return ib; + } // ib is our insert hint. Insert the new boundary and set ib to its entry ib = m_pBuffer->insert(ib, {boundary, RangeMutation()}); @@ -4853,7 +4854,7 @@ public: } void set( KeyValueRef keyValue, const Arena* arena = NULL ) { - debug_printf("SET %s\n", keyValue.key.printable().c_str()); + debug_printf("SET %s\n", printable(keyValue).c_str()); m_tree->set(keyValue); } @@ -6126,6 +6127,7 @@ TEST_CASE("!/redwood/performance/set") { state int minValueSize = 0; state int maxValueSize = 500; state int maxConsecutiveRun = 10; + state int minConsecutiveRun = 1000; state char firstKeyChar = 'a'; state char lastKeyChar = 'b'; @@ -6135,6 +6137,7 @@ TEST_CASE("!/redwood/performance/set") { printf("maxChangesPerVersion: %d\n", maxChangesPerVersion); printf("minKeyPrefixBytes: %d\n", minKeyPrefixBytes); printf("maxKeyPrefixBytes: %d\n", maxKeyPrefixBytes); + printf("minConsecutiveRun: %d\n", minConsecutiveRun); printf("maxConsecutiveRun: %d\n", maxConsecutiveRun); printf("minValueSize: %d\n", minValueSize); printf("maxValueSize: %d\n", maxValueSize); @@ -6165,7 +6168,7 @@ TEST_CASE("!/redwood/performance/set") { KeyValue kv; kv.key = randomString(kv.arena(), deterministicRandom()->randomInt(minKeyPrefixBytes + sizeof(uint32_t), maxKeyPrefixBytes + sizeof(uint32_t) + 1), firstKeyChar, lastKeyChar); int32_t index = deterministicRandom()->randomInt(0, nodeCount); - int runLength = deterministicRandom()->randomInt(1, maxConsecutiveRun + 1); + int runLength = deterministicRandom()->randomInt(minConsecutiveRun, maxConsecutiveRun + 1); while(runLength > 0 && changes > 0) { *(uint32_t *)(kv.key.end() - sizeof(uint32_t)) = bigEndian32(index++); @@ -6263,3 +6266,254 @@ TEST_CASE("!/redwood/performance/set") { return Void(); } + +struct PrefixSegment { + int length; + int cardinality; + + std::string toString() const { + return format("{%d bytes, %d choices}", length, cardinality); + } +}; + +// Utility class for generating kv pairs under a prefix pattern +// It currently uses std::string in an abstraction breaking way. +struct KVSource { + KVSource() {} + + typedef VectorRef PrefixRef; + typedef Standalone Prefix; + + std::vector desc; + std::vector> segments; + std::vector prefixes; + std::vector prefixesSorted; + std::string valueData; + int prefixLen; + int lastIndex; + + KVSource(const std::vector &desc, int numPrefixes = 0) : desc(desc) { + if(numPrefixes == 0) { + numPrefixes = 1; + for(auto &p : desc) { + numPrefixes *= p.cardinality; + } + } + + prefixLen = 0; + for(auto &s : desc) { + prefixLen += s.length; + std::vector parts; + while(parts.size() < s.cardinality) { + parts.push_back(deterministicRandom()->randomAlphaNumeric(s.length)); + } + std::sort(parts.begin(), parts.end()); + segments.push_back(std::move(parts)); + } + + while(prefixes.size() < numPrefixes) { + std::string p; + for(auto &s : segments) { + p.append(s[deterministicRandom()->randomInt(0, s.size())]); + } + prefixes.push_back(PrefixRef((uint8_t *)p.data(), p.size())); + prefixesSorted.push_back(KeyRef((uint8_t *)p.data(), p.size())); + } + std::sort(prefixesSorted.begin(), prefixesSorted.end()); + valueData = deterministicRandom()->randomAlphaNumeric(100000); + lastIndex = 0; + } + + // Expands the chosen prefix in the prefix list to hold suffix, + // fills suffix with random bytes, and returns a reference to the string + KeyRef getKeyRef(int suffixLen) { + return makeKey(randomPrefix(), suffixLen); + } + + // Like getKeyRef but uses the same prefix as the last randomly chosen prefix + KeyRef getAnotherKeyRef(int suffixLen) { + return makeKey(prefixes[lastIndex], suffixLen); + } + + // Get a KeyRangeRef covering the given number of adjacent prefixes + KeyRangeRef getRangeRef(int prefixesCovered) { + prefixesCovered = std::min(prefixesCovered, prefixes.size()); + int i = deterministicRandom()->randomInt(0, prefixesSorted.size() - prefixesCovered); + KeyRef begin = prefixesSorted[i]; + KeyRef end = prefixesSorted[i + prefixesCovered]; + return KeyRangeRef(begin, end); + } + + KeyRef getValue(int len) { + return KeyRef(valueData).substr(0, len); + } + + // Move lastIndex to the next position, wrapping around to 0 + void nextPrefix() { + ++lastIndex; + if(lastIndex == prefixes.size()) { + lastIndex = 0; + } + } + + Prefix & randomPrefix() { + lastIndex = deterministicRandom()->randomInt(0, prefixes.size()); + return prefixes[lastIndex]; + } + + static KeyRef makeKey(Prefix &p, int suffixLen) { + p.reserve(p.arena(), p.size() + suffixLen); + uint8_t *wptr = p.end(); + for(int i = 0; i < suffixLen; ++i) { + *wptr++ = (uint8_t)deterministicRandom()->randomAlphaNumeric(); + } + return KeyRef(p.begin(), p.size() + suffixLen); + } + + int numPrefixes() const { + return prefixes.size(); + }; + + std::string toString() const { + return format("{prefixLen=%d prefixes=%d format=%s}", prefixLen, numPrefixes(), ::toString(desc).c_str()); + } +}; + +std::string toString(const StorageBytes &sb) { + return format("{%.2f MB total, %.2f MB free, %.2f MB available, %.2f MB used}", sb.total / 1e6, sb.free / 1e6, sb.available / 1e6, sb.used / 1e6); +} + +ACTOR Future getStableStorageBytes(IKeyValueStore *kvs) { + state StorageBytes sb = kvs->getStorageBytes(); + + // Wait for StorageBytes used metric to stabilize + loop { + wait(kvs->commit()); + StorageBytes sb2 = kvs->getStorageBytes(); + bool stable = sb2.used == sb.used; + sb = sb2; + if(stable) { + break; + } + } + + return sb; +} + +ACTOR Future prefixClusteredInsert(IKeyValueStore *kvs, int suffixSize, int valueSize, KVSource source, int recordCountTarget) { + state int commitTarget = 5e6; + + state int recordSize = source.prefixLen + suffixSize + valueSize; + state int64_t kvBytesTarget = (int64_t)recordCountTarget * recordSize; + state int recordsPerPrefix = recordCountTarget / source.numPrefixes(); + + printf("\nstoreType: %d\n", kvs->getType()); + printf("commitTarget: %d\n", commitTarget); + printf("prefixSource: %s\n", source.toString().c_str()); + printf("suffixSize: %d\n", suffixSize); + printf("valueSize: %d\n", valueSize); + printf("recordSize: %d\n", recordSize); + printf("recordsPerPrefix: %d\n", recordsPerPrefix); + printf("recordCountTarget: %d\n", recordCountTarget); + printf("kvBytesTarget: %" PRId64 "\n", kvBytesTarget); + + state int64_t kvBytes = 0; + state int64_t kvBytesTotal = 0; + state int records = 0; + state Future commit = Void(); + state std::string value = deterministicRandom()->randomAlphaNumeric(1e6); + + wait(kvs->init()); + + state double intervalStart = timer(); + state double start = intervalStart; + + state std::function stats = [&]() { + double elapsed = timer() - start; + printf("Cumulative stats: %.2f seconds %.2f MB keyValue bytes %d records %.2f MB/s %.2f rec/s\r", elapsed, kvBytesTotal / 1e6, records, kvBytesTotal / elapsed / 1e6, records / elapsed); + fflush(stdout); + }; + + while(kvBytesTotal < kvBytesTarget) { + wait(yield()); + + state int i; + for(i = 0; i < recordsPerPrefix; ++i) { + KeyValueRef kv(source.getAnotherKeyRef(4), source.getValue(valueSize)); + kvs->set(kv); + kvBytes += kv.expectedSize(); + ++records; + + if(kvBytes >= commitTarget) { + wait(commit); + stats(); + commit = kvs->commit(); + kvBytesTotal += kvBytes; + if(kvBytesTotal >= kvBytesTarget) { + break; + } + kvBytes = 0; + } + } + + // Use every prefix, one at a time, random order + source.nextPrefix(); + } + + wait(commit); + stats(); + printf("\n"); + + intervalStart = timer(); + StorageBytes sb = wait(getStableStorageBytes(kvs)); + printf("storageBytes: %s (stable after %.2f seconds)\n", toString(sb).c_str(), timer() - intervalStart); + + printf("Clearing all keys\n"); + intervalStart = timer(); + kvs->clear(KeyRangeRef(LiteralStringRef(""), LiteralStringRef("\xff"))); + state StorageBytes sbClear = wait(getStableStorageBytes(kvs)); + printf("Cleared all keys in %.2f seconds, final storageByte: %s\n", timer() - intervalStart, toString(sbClear).c_str()); + + return Void(); +} + +Future closeKVS(IKeyValueStore *kvs) { + Future closed = kvs->onClosed(); + kvs->close(); + return closed; +} + +ACTOR Future doPrefixInsertComparison(int suffixSize, int valueSize, int recordCountTarget, KVSource source) { + VersionedBTree::counts.clear(); + + deleteFile("test.sqlite"); + deleteFile("test.sqlite-wal"); + wait(delay(5)); + state IKeyValueStore *sqlite = openKVStore(KeyValueStoreType::SSD_BTREE_V2, "test.sqlite", UID(), 0); + wait(prefixClusteredInsert(sqlite, suffixSize, valueSize, source, recordCountTarget)); + wait(closeKVS(sqlite)); + printf("\n"); + + deleteFile("test.redwood"); + wait(delay(5)); + state IKeyValueStore *redwood = openKVStore(KeyValueStoreType::SSD_REDWOOD_V1, "test.redwood", UID(), 0); + wait(prefixClusteredInsert(redwood, suffixSize, valueSize, source, recordCountTarget)); + wait(closeKVS(redwood)); + printf("\n"); + + return Void(); +} + +TEST_CASE("!/redwood/performance/prefixSizeComparison") { + state int suffixSize = 4; + state int valueSize = 16; + state int recordCountTarget = 40e6; + + wait(doPrefixInsertComparison(suffixSize, valueSize, recordCountTarget, KVSource({{3, 100000}}))); + wait(doPrefixInsertComparison(suffixSize, valueSize, recordCountTarget, KVSource({{16, 100000}}))); + wait(doPrefixInsertComparison(suffixSize, valueSize, recordCountTarget, KVSource({{32, 100000}}))); + wait(doPrefixInsertComparison(suffixSize, valueSize, recordCountTarget, KVSource({{4, 5}, {12, 1000}, {8, 5}, {8, 4}}))); + + return Void(); +} + diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c16b36a1f1..a2d8dee922 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -68,6 +68,8 @@ add_fdb_test(TEST_FILES RedwoodCorrectnessPager.txt IGNORE) add_fdb_test(TEST_FILES fast/RedwoodCorrectnessBTree.txt IGNORE) add_fdb_test(TEST_FILES RedwoodCorrectness.txt IGNORE) add_fdb_test(TEST_FILES RedwoodPerfTests.txt IGNORE) +add_fdb_test(TEST_FILES RedwoodPerfSet.txt IGNORE) +add_fdb_test(TEST_FILES RedwoodPerfPrefixCompression.txt IGNORE) add_fdb_test(TEST_FILES SimpleExternalTest.txt) add_fdb_test(TEST_FILES SlowTask.txt IGNORE) add_fdb_test(TEST_FILES SpecificUnitTest.txt IGNORE) diff --git a/tests/RedwoodPerfPrefixCompression.txt b/tests/RedwoodPerfPrefixCompression.txt new file mode 100644 index 0000000000..7d526702c6 --- /dev/null +++ b/tests/RedwoodPerfPrefixCompression.txt @@ -0,0 +1,6 @@ +testTitle=UnitTests +testName=UnitTests +startDelay=0 +useDB=false +maxTestCases=0 +testsMatching=!/redwood/performance/prefixSizeComparison diff --git a/tests/RedwoodPerfSet.txt b/tests/RedwoodPerfSet.txt new file mode 100644 index 0000000000..206b52dbf5 --- /dev/null +++ b/tests/RedwoodPerfSet.txt @@ -0,0 +1,6 @@ +testTitle=UnitTests +testName=UnitTests +startDelay=0 +useDB=false +maxTestCases=0 +testsMatching=!/redwood/performance/set From 9e1e0d731d827be4fafc481585ce83c0e1169678 Mon Sep 17 00:00:00 2001 From: Stephen Atherton Date: Mon, 18 Nov 2019 02:34:37 -0800 Subject: [PATCH 0025/1604] Incremental subtree deletion now processes pages in parallel. --- fdbserver/VersionedBTree.actor.cpp | 89 +++++++++++++++++------------- 1 file changed, 52 insertions(+), 37 deletions(-) diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index 0984903904..0d60de567e 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -2844,56 +2844,71 @@ public: m_latestCommit = m_init; } - ACTOR static Future incrementalSubtreeClear(VersionedBTree *self, bool *pStop = nullptr, unsigned int minPages = 0, int maxPages = std::numeric_limits::max()) { + ACTOR static Future incrementalSubtreeClear(VersionedBTree *self, bool *pStop = nullptr, int batchSize = 10, unsigned int minPages = 0, int maxPages = std::numeric_limits::max()) { // TODO: Is it contractually okay to always to read at the latest version? state Reference snapshot = self->m_pager->getReadSnapshot(self->m_pager->getLatestVersion()); state int freedPages = 0; + loop { - // take a page from front of queue - state Optional q = wait(self->m_lazyDeleteQueue.pop()); - debug_printf("LazyDelete: popped %s\n", toString(q).c_str()); - if(!q.present()) { + state std::vector>>> entries; + + // Take up to batchSize pages from front of queue + while(entries.size() < batchSize) { + Optional q = wait(self->m_lazyDeleteQueue.pop()); + debug_printf("LazyDelete: popped %s\n", toString(q).c_str()); + if(!q.present()) { + break; + } + // Start reading the page, without caching + entries.push_back(std::make_pair(q.get(), self->readPage(snapshot, q.get().pageID, nullptr, nullptr, true))); + } + + if(entries.empty()) { break; } - // Read the page without caching - Reference p = wait(self->readPage(snapshot, q.get().pageID, nullptr, nullptr, true)); - const BTreePage &btPage = *(BTreePage *)p->begin(); + state int i; + for(i = 0; i < entries.size(); ++i) { + Reference p = wait(entries[i].second); + const LazyDeleteQueueEntry &entry = entries[i].first; + const BTreePage &btPage = *(BTreePage *)p->begin(); + debug_printf("LazyDelete: processing %s\n", toString(entry).c_str()); - // Level 1 (leaf) nodes should never be in the lazy delete queue - ASSERT(btPage.height > 1); - - // Iterate over page entries, skipping key decoding using BTreePage::ValueTree which uses - // RedwoodRecordRef::DeltaValueOnly as the delta type type to skip key decoding - BTreePage::ValueTree::Reader reader(&btPage.valueTree(), &dbBegin, &dbEnd); - auto c = reader.getCursor(); - ASSERT(c.moveFirst()); - Version v = q.get().version; - while(1) { - if(c.get().value.present()) { - BTreePageID btChildPageID = c.get().getChildPage(); - // If this page is height 2, then the children are leaves so free - if(btPage.height == 2) { - debug_printf("LazyDelete: freeing child %s\n", toString(btChildPageID).c_str()); - self->freeBtreePage(btChildPageID, v); - freedPages += btChildPageID.size(); + // Level 1 (leaf) nodes should never be in the lazy delete queue + ASSERT(btPage.height > 1); + + // Iterate over page entries, skipping key decoding using BTreePage::ValueTree which uses + // RedwoodRecordRef::DeltaValueOnly as the delta type type to skip key decoding + BTreePage::ValueTree::Reader reader(&btPage.valueTree(), &dbBegin, &dbEnd); + auto c = reader.getCursor(); + ASSERT(c.moveFirst()); + Version v = entry.version; + while(1) { + if(c.get().value.present()) { + BTreePageID btChildPageID = c.get().getChildPage(); + // If this page is height 2, then the children are leaves so free + if(btPage.height == 2) { + debug_printf("LazyDelete: freeing child %s\n", toString(btChildPageID).c_str()); + self->freeBtreePage(btChildPageID, v); + freedPages += btChildPageID.size(); + } + else { + // Otherwise, queue them for lazy delete. + debug_printf("LazyDelete: queuing child %s\n", toString(btChildPageID).c_str()); + self->m_lazyDeleteQueue.pushFront(LazyDeleteQueueEntry{v, btChildPageID}); + } } - else { - // Otherwise, queue them for lazy delete. - debug_printf("LazyDelete: queuing child %s\n", toString(btChildPageID).c_str()); - self->m_lazyDeleteQueue.pushFront(LazyDeleteQueueEntry{v, btChildPageID}); + if(!c.moveNext()) { + break; } } - if(!c.moveNext()) { - break; - } + + // Free the page, now that its children have either been freed or queued + debug_printf("LazyDelete: freeing queue entry %s\n", toString(entry.pageID).c_str()); + self->freeBtreePage(entry.pageID, v); + freedPages += entry.pageID.size(); } - // Free the page, now that its children have either been freed or queued - debug_printf("LazyDelete: freeing queue entry %s\n", toString(q.get().pageID).c_str()); - self->freeBtreePage(q.get().pageID, v); - freedPages += q.get().pageID.size(); - // If stop is set and we've freed the minimum number of pages required, or the maximum is exceeded, return. if((freedPages >= minPages && pStop != nullptr && *pStop) || freedPages >= maxPages) { break; From 8d973ce762905d2b34a359201b1f74fb72e3d8f1 Mon Sep 17 00:00:00 2001 From: Stephen Atherton Date: Tue, 19 Nov 2019 23:42:00 -0800 Subject: [PATCH 0026/1604] Bug fix: Any time the least recently used ObjectCache entry is not evictable the effective size of the cache would grow by one entry for each read and will never shrink even once there are enough evictable pages to return the cache to its configured size. This has probably never actually happened because evictability of Redwood pages is currently based on having no pending IO, but it would be more of a problem if evictability were redefined to require a reference count of 1. --- fdbserver/VersionedBTree.actor.cpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index 0d60de567e..373f34b9b7 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -743,10 +743,11 @@ class ObjectCache : NonCopyable { }; public: - ObjectCache(int sizeLimit = 0) : sizeLimit(sizeLimit), cacheHits(0), cacheMisses(0), noHitEvictions(0) { + ObjectCache(int sizeLimit = 1) : sizeLimit(sizeLimit), cacheHits(0), cacheMisses(0), noHitEvictions(0) { } void setSizeLimit(int n) { + ASSERT(n > 0); sizeLimit = n; } @@ -784,12 +785,20 @@ public: // Insert the newly created Entry at the back of the eviction order evictionOrder.push_back(entry); - // If the cache is too big, try to evict the first Entry in the eviction order - if(cache.size() > sizeLimit) { + // While the cache is too big, evict the oldest entry until the oldest entry can't be evicted. + while(cache.size() > sizeLimit) { Entry &toEvict = evictionOrder.front(); debug_printf("Trying to evict %s to make room for %s\n", toString(toEvict.index).c_str(), toString(index).c_str()); - // Don't evict the entry that was just added as then we can't return a reference to it. - if(toEvict.index != index && toEvict.item.evictable()) { + + // It's critical that we do not evict the item we just added (or the reference we return would be invalid) but + // since sizeLimit must be > 0, entry was just added to the end of the evictionOrder, and this loop will end + // if we move anything to the end of the eviction order, we can be guaraunted that entry != toEvict, so we + // do not need to check. + if(!toEvict.item.evictable()) { + evictionOrder.erase(evictionOrder.iterator_to(toEvict)); + evictionOrder.push_back(toEvict); + break; + } else { if(toEvict.hits == 0) { ++noHitEvictions; } @@ -810,6 +819,9 @@ public: state boost::intrusive::list evictionOrder; // Swap cache contents to local state vars + // After this, no more entries will be added to or read from these + // structures so we know for sure that no page will become unevictable + // after it is either evictable or onEvictable() is ready. cache.swap(self->cache); evictionOrder.swap(self->evictionOrder); From d91d744fd7638882f097eee68654aef52c4c1471 Mon Sep 17 00:00:00 2001 From: Stephen Atherton Date: Wed, 20 Nov 2019 03:20:23 -0800 Subject: [PATCH 0027/1604] Typedefs to simplify ObjectCache a bit. --- fdbserver/VersionedBTree.actor.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index 373f34b9b7..ecda176032 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -742,6 +742,9 @@ class ObjectCache : NonCopyable { int hits; }; + typedef std::unordered_map CacheT; + typedef boost::intrusive::list EvictionOrderT; + public: ObjectCache(int sizeLimit = 1) : sizeLimit(sizeLimit), cacheHits(0), cacheMisses(0), noHitEvictions(0) { } @@ -815,8 +818,8 @@ public: // Clears the cache, saving the entries, and then waits for eachWaits for each item to be evictable and evicts it. // The cache should not be Evicts all evictable entries ACTOR static Future clear_impl(ObjectCache *self) { - state std::unordered_map cache; - state boost::intrusive::list evictionOrder; + state ObjectCache::CacheT cache; + state EvictionOrderT evictionOrder; // Swap cache contents to local state vars // After this, no more entries will be added to or read from these @@ -825,8 +828,8 @@ public: cache.swap(self->cache); evictionOrder.swap(self->evictionOrder); - state typename boost::intrusive::list::iterator i = evictionOrder.begin(); - state typename boost::intrusive::list::iterator iEnd = evictionOrder.begin(); + state typename EvictionOrderT::iterator i = evictionOrder.begin(); + state typename EvictionOrderT::iterator iEnd = evictionOrder.begin(); while(i != iEnd) { if(!i->item.evictable()) { @@ -856,9 +859,8 @@ private: int64_t cacheMisses; int64_t noHitEvictions; - // TODO: Use boost intrusive unordered set instead, with a comparator that only considers entry.index - std::unordered_map cache; - boost::intrusive::list evictionOrder; + CacheT cache; + EvictionOrderT evictionOrder; }; ACTOR template Future forwardError(Future f, Promise target) { From a9af2de1d21ca315a10dec0d3b79d07fb570bdae Mon Sep 17 00:00:00 2001 From: Stephen Atherton Date: Wed, 20 Nov 2019 15:55:59 -0800 Subject: [PATCH 0028/1604] Added option in redwood prefixed set test to use randomly generated prefixes in sorted order. --- fdbserver/VersionedBTree.actor.cpp | 68 +++++++++++++++++------------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index ecda176032..7a228f5cf4 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -6316,7 +6316,7 @@ struct KVSource { std::vector desc; std::vector> segments; std::vector prefixes; - std::vector prefixesSorted; + std::vector prefixesSorted; std::string valueData; int prefixLen; int lastIndex; @@ -6336,7 +6336,6 @@ struct KVSource { while(parts.size() < s.cardinality) { parts.push_back(deterministicRandom()->randomAlphaNumeric(s.length)); } - std::sort(parts.begin(), parts.end()); segments.push_back(std::move(parts)); } @@ -6346,9 +6345,15 @@ struct KVSource { p.append(s[deterministicRandom()->randomInt(0, s.size())]); } prefixes.push_back(PrefixRef((uint8_t *)p.data(), p.size())); - prefixesSorted.push_back(KeyRef((uint8_t *)p.data(), p.size())); } - std::sort(prefixesSorted.begin(), prefixesSorted.end()); + + for(auto &p : prefixes) { + prefixesSorted.push_back(&p); + } + std::sort(prefixesSorted.begin(), prefixesSorted.end(), [](const Prefix *a, const Prefix *b) { + return KeyRef((uint8_t *)a->begin(), a->size()) < KeyRef((uint8_t *)b->begin(), b->size()); + }); + valueData = deterministicRandom()->randomAlphaNumeric(100000); lastIndex = 0; } @@ -6360,17 +6365,18 @@ struct KVSource { } // Like getKeyRef but uses the same prefix as the last randomly chosen prefix - KeyRef getAnotherKeyRef(int suffixLen) { - return makeKey(prefixes[lastIndex], suffixLen); + KeyRef getAnotherKeyRef(int suffixLen, bool sorted = false) { + Prefix &p = sorted ? *prefixesSorted[lastIndex] : prefixes[lastIndex]; + return makeKey(p, suffixLen); } - // Get a KeyRangeRef covering the given number of adjacent prefixes - KeyRangeRef getRangeRef(int prefixesCovered) { + // Like getKeyRef but gets a KeyRangeRef for two keys covering the given number of sorted adjacent prefixes + KeyRangeRef getRangeRef(int prefixesCovered, int suffixLen) { prefixesCovered = std::min(prefixesCovered, prefixes.size()); int i = deterministicRandom()->randomInt(0, prefixesSorted.size() - prefixesCovered); - KeyRef begin = prefixesSorted[i]; - KeyRef end = prefixesSorted[i + prefixesCovered]; - return KeyRangeRef(begin, end); + Prefix *begin = prefixesSorted[i]; + Prefix *end = prefixesSorted[i + prefixesCovered]; + return KeyRangeRef(makeKey(*begin, suffixLen), makeKey(*end, suffixLen)); } KeyRef getValue(int len) { @@ -6429,7 +6435,7 @@ ACTOR Future getStableStorageBytes(IKeyValueStore *kvs) { return sb; } -ACTOR Future prefixClusteredInsert(IKeyValueStore *kvs, int suffixSize, int valueSize, KVSource source, int recordCountTarget) { +ACTOR Future prefixClusteredInsert(IKeyValueStore *kvs, int suffixSize, int valueSize, KVSource source, int recordCountTarget, bool usePrefixesInOrder) { state int commitTarget = 5e6; state int recordSize = source.prefixLen + suffixSize + valueSize; @@ -6439,6 +6445,7 @@ ACTOR Future prefixClusteredInsert(IKeyValueStore *kvs, int suffixSize, in printf("\nstoreType: %d\n", kvs->getType()); printf("commitTarget: %d\n", commitTarget); printf("prefixSource: %s\n", source.toString().c_str()); + printf("usePrefixesInOrder: %d\n", usePrefixesInOrder); printf("suffixSize: %d\n", suffixSize); printf("valueSize: %d\n", valueSize); printf("recordSize: %d\n", recordSize); @@ -6468,7 +6475,7 @@ ACTOR Future prefixClusteredInsert(IKeyValueStore *kvs, int suffixSize, in state int i; for(i = 0; i < recordsPerPrefix; ++i) { - KeyValueRef kv(source.getAnotherKeyRef(4), source.getValue(valueSize)); + KeyValueRef kv(source.getAnotherKeyRef(4, usePrefixesInOrder), source.getValue(valueSize)); kvs->set(kv); kvBytes += kv.expectedSize(); ++records; @@ -6485,7 +6492,7 @@ ACTOR Future prefixClusteredInsert(IKeyValueStore *kvs, int suffixSize, in } } - // Use every prefix, one at a time, random order + // Use every prefix, one at a time source.nextPrefix(); } @@ -6512,36 +6519,37 @@ Future closeKVS(IKeyValueStore *kvs) { return closed; } -ACTOR Future doPrefixInsertComparison(int suffixSize, int valueSize, int recordCountTarget, KVSource source) { +ACTOR Future doPrefixInsertComparison(int suffixSize, int valueSize, int recordCountTarget, bool usePrefixesInOrder, KVSource source) { VersionedBTree::counts.clear(); + deleteFile("test.redwood"); + wait(delay(5)); + state IKeyValueStore *redwood = openKVStore(KeyValueStoreType::SSD_REDWOOD_V1, "test.redwood", UID(), 0); + wait(prefixClusteredInsert(redwood, suffixSize, valueSize, source, recordCountTarget, usePrefixesInOrder)); + wait(closeKVS(redwood)); + printf("\n"); + deleteFile("test.sqlite"); deleteFile("test.sqlite-wal"); wait(delay(5)); state IKeyValueStore *sqlite = openKVStore(KeyValueStoreType::SSD_BTREE_V2, "test.sqlite", UID(), 0); - wait(prefixClusteredInsert(sqlite, suffixSize, valueSize, source, recordCountTarget)); + wait(prefixClusteredInsert(sqlite, suffixSize, valueSize, source, recordCountTarget, usePrefixesInOrder)); wait(closeKVS(sqlite)); printf("\n"); - deleteFile("test.redwood"); - wait(delay(5)); - state IKeyValueStore *redwood = openKVStore(KeyValueStoreType::SSD_REDWOOD_V1, "test.redwood", UID(), 0); - wait(prefixClusteredInsert(redwood, suffixSize, valueSize, source, recordCountTarget)); - wait(closeKVS(redwood)); - printf("\n"); - return Void(); } TEST_CASE("!/redwood/performance/prefixSizeComparison") { - state int suffixSize = 4; - state int valueSize = 16; - state int recordCountTarget = 40e6; + state int suffixSize = 12; + state int valueSize = 100; + state int recordCountTarget = 100e6; + state int usePrefixesInOrder = false; - wait(doPrefixInsertComparison(suffixSize, valueSize, recordCountTarget, KVSource({{3, 100000}}))); - wait(doPrefixInsertComparison(suffixSize, valueSize, recordCountTarget, KVSource({{16, 100000}}))); - wait(doPrefixInsertComparison(suffixSize, valueSize, recordCountTarget, KVSource({{32, 100000}}))); - wait(doPrefixInsertComparison(suffixSize, valueSize, recordCountTarget, KVSource({{4, 5}, {12, 1000}, {8, 5}, {8, 4}}))); + wait(doPrefixInsertComparison(suffixSize, valueSize, recordCountTarget, usePrefixesInOrder, KVSource({{10, 100000}}))); + wait(doPrefixInsertComparison(suffixSize, valueSize, recordCountTarget, usePrefixesInOrder, KVSource({{16, 100000}}))); + wait(doPrefixInsertComparison(suffixSize, valueSize, recordCountTarget, usePrefixesInOrder, KVSource({{32, 100000}}))); + wait(doPrefixInsertComparison(suffixSize, valueSize, recordCountTarget, usePrefixesInOrder, KVSource({{4, 5}, {12, 1000}, {8, 5}, {8, 4}}))); return Void(); } From 5f1644f2931d7092c60063c080651a527ebe5c74 Mon Sep 17 00:00:00 2001 From: Stephen Atherton Date: Sat, 23 Nov 2019 00:09:11 -0800 Subject: [PATCH 0029/1604] DeltaTree::Reader is now DeltaTree::Mirror and supports insertion into a DeltaTree. DeltaTrees now support an item count, so BTreePage no longer has an item count, so the VersionedBTree format version has been bumped. --- fdbserver/DeltaTree.h | 123 +++++++++++++++++++++++++---- fdbserver/VersionedBTree.actor.cpp | 79 +++++++++++------- 2 files changed, 160 insertions(+), 42 deletions(-) diff --git a/fdbserver/DeltaTree.h b/fdbserver/DeltaTree.h index b1eb53dfff..06fdb2df86 100644 --- a/fdbserver/DeltaTree.h +++ b/fdbserver/DeltaTree.h @@ -167,12 +167,12 @@ struct DeltaTree { Node * rightChild() const { //printf("Node(%p): leftOffset=%d rightOffset=%d deltaSize=%d\n", this, (int)leftChildOffset, (int)rightChildOffset, (int)delta().size()); - return rightChildOffset == 0 ? nullptr : (Node *)((uint8_t *)&delta() + rightChildOffset); + return rightChildOffset == 0 ? nullptr : (Node *)((uint8_t *)this + rightChildOffset); } Node * leftChild() const { //printf("Node(%p): leftOffset=%d rightOffset=%d deltaSize=%d\n", this, (int)leftChildOffset, (int)rightChildOffset, (int)delta().size()); - return leftChildOffset == 0 ? nullptr : (Node *)((uint8_t *)&delta() + leftChildOffset); + return leftChildOffset == 0 ? nullptr : (Node *)((uint8_t *)this + leftChildOffset); } int size() const { @@ -181,8 +181,10 @@ struct DeltaTree { }; struct { - OffsetT nodeBytes; // Total size of all Nodes including the root - uint8_t initialDepth; // Levels in the tree as of the last rebuild + OffsetT numItems; // Number of items in the tree. + OffsetT nodeBytes; // Total size of all Nodes including the root + uint8_t initialHeight; // Height of tree as originally built + uint8_t maxHeight; // Maximum height of tree after any insertion. Value of 0 means no insertions done. }; #pragma pack(pop) @@ -198,6 +200,10 @@ struct DeltaTree { return sizeof(DeltaTree) + nodeBytes; } + inline Node & newNode() { + return *(Node *)((uint8_t *)this + size()); + } + public: // Get count of total overhead bytes (everything but the user-formatted Delta) for a tree given size n static inline int GetTreeOverhead(int n = 0) { @@ -221,6 +227,40 @@ public: //printf("DecodedNode2 raw=%p delta=%s\n", raw, raw->delta().toString().c_str()); } + // Add newItem to tree and create a DecodedNode for it, linked to parent via the left or right child link + DecodedNode(DeltaTree *tree, const T &newItem, DecodedNode *parent, bool left, Arena &arena) + : parent(parent), raw(&tree->newNode()), left(nullptr), right(nullptr), + prev(left ? parent->prev : &parent->item), + next(left ? &parent->item : parent->next), + item(arena, newItem) + { + raw->leftChildOffset = 0; + raw->rightChildOffset = 0; + + // TODO: Get subtreeCommon in here somehow. + int commonWithPrev = newItem.getCommonPrefixLen(*prev, 0); + int commonWithNext = newItem.getCommonPrefixLen(*next, 0); + + bool prefixSourcePrev; + int commonPrefix; + const T *base; + if(commonWithPrev >= commonWithNext) { + prefixSourcePrev = true; + commonPrefix = commonWithPrev; + base = prev; + } + else { + prefixSourcePrev = false; + commonPrefix = commonWithNext; + base = next; + } + + int deltaSize = newItem.writeDelta(raw->delta(), *base, commonPrefix); + raw->delta().setPrefixSource(prefixSourcePrev); + tree->nodeBytes += sizeof(Node) + deltaSize; + ++tree->numItems; + } + Node *raw; DecodedNode *parent; DecodedNode *left; @@ -252,11 +292,12 @@ public: struct Cursor; - // A Reader is used to read a Tree by getting cursors into it. - // Any node decoded by any cursor is placed in cache for use - // by other cursors. - struct Reader : FastAllocated { - Reader(const void *treePtr = nullptr, const T *lowerBound = nullptr, const T *upperBound = nullptr) + // A Mirror is an accessor for a DeltaTree which allows insertion and reading. Both operations are done + // using cursors which point to and share nodes in an tree that is built on-demand and mirrors the compressed + // structure but with fully reconstituted items (which reference DeltaTree bytes or Arena bytes, based + // on the behavior of T::Delta::apply()) + struct Mirror : FastAllocated { + Mirror(const void *treePtr = nullptr, const T *lowerBound = nullptr, const T *upperBound = nullptr) : tree((DeltaTree *)treePtr), lower(lowerBound), upper(upperBound) { // TODO: Remove these copies into arena and require users of Reader to keep prev and next alive during its lifetime @@ -283,6 +324,58 @@ public: Cursor getCursor() { return Cursor(this); } + + // Insert k into the DeltaTree, updating nodeBytes and initialHeight. + // It's up to the caller to know that it will fit in the space available. + void insert(const T &k) { + int height = 1; + DecodedNode *n = root; + + while(n != nullptr) { + int cmp = k.compare(n->item); + + if(cmp >= 0) { + DecodedNode *right = n->getRight(arena); + + if(right == nullptr) { + // Set the right child of the decoded node to a new decoded node that points to a newly + // allocated/written raw node in the tree. DecodedNode() will write the new node + // and update nodeBytes + n->right = new (arena) DecodedNode(tree, k, n, false, arena); + n->raw->rightChildOffset = (uint8_t *)n->right->raw - (uint8_t *)n->raw; + //printf("inserted %s at offset %d\n", k.toString().c_str(), n->raw->rightChildOffset); + + // Update max height of the tree if necessary + if(height > tree->maxHeight) { + tree->maxHeight = height; + } + + return; + } + + n = right; + } + else { + DecodedNode *left = n->getLeft(arena); + + if(left == nullptr) { + // See right side case above for comments + n->left = new (arena) DecodedNode(tree, k, n, true, arena); + n->raw->leftChildOffset = (uint8_t *)n->left->raw - (uint8_t *)n->raw; + //printf("inserted %s at offset %d\n", k.toString().c_str(), n->raw->leftChildOffset); + + if(height > tree->maxHeight) { + tree->maxHeight = height; + } + + return; + } + + n = left; + } + ++height; + } + } }; // Cursor provides a way to seek into a DeltaTree and iterate over its contents @@ -291,10 +384,10 @@ public: Cursor() : reader(nullptr), node(nullptr) { } - Cursor(Reader *r) : reader(r), node(reader->root) { + Cursor(Mirror *r) : reader(r), node(reader->root) { } - Reader *reader; + Mirror *reader; DecodedNode *node; bool valid() const { @@ -414,7 +507,9 @@ public: int build(const T *begin, const T *end, const T *prev, const T *next) { //printf("tree size: %d node size: %d\n", sizeof(DeltaTree), sizeof(Node)); int count = end - begin; - initialDepth = (uint8_t)log2(count) + 1; + numItems = count; + initialHeight = (uint8_t)log2(count) + 1; + maxHeight = 0; // The boundary leading to the new page acts as the last time we branched right if(begin != end) { @@ -464,7 +559,7 @@ private: // Serialize left child if(count > 1) { wptr += build(*(Node *)wptr, begin, begin + mid, prev, &item, commonWithPrev); - root.leftChildOffset = deltaSize; + root.leftChildOffset = sizeof(Node) + deltaSize; } else { root.leftChildOffset = 0; @@ -472,7 +567,7 @@ private: // Serialize right child if(count > 2) { - root.rightChildOffset = wptr - (uint8_t *)&root.delta(); + root.rightChildOffset = wptr - (uint8_t *)&root; wptr += build(*(Node *)wptr, begin + mid + 1, end, &item, next, commonWithNext); } else { diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index 7a228f5cf4..509c034eb2 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -2489,7 +2489,6 @@ struct BTreePage { #pragma pack(push,1) struct { uint8_t height; - uint16_t itemCount; uint32_t kvBytes; }; #pragma pack(pop) @@ -2518,12 +2517,12 @@ struct BTreePage { std::string toString(bool write, BTreePageID id, Version ver, const RedwoodRecordRef *lowerBound, const RedwoodRecordRef *upperBound) const { std::string r; r += format("BTreePage op=%s %s @%" PRId64 " ptr=%p height=%d count=%d kvBytes=%d\n lowerBound: %s\n upperBound: %s\n", - write ? "write" : "read", ::toString(id).c_str(), ver, this, height, (int)itemCount, (int)kvBytes, + write ? "write" : "read", ::toString(id).c_str(), ver, this, height, (int)tree().numItems, (int)kvBytes, lowerBound->toString().c_str(), upperBound->toString().c_str()); try { - if(itemCount > 0) { + if(tree().numItems > 0) { // This doesn't use the cached reader for the page but it is only for debugging purposes - BinaryTree::Reader reader(&tree(), lowerBound, upperBound); + BinaryTree::Mirror reader(&tree(), lowerBound, upperBound); BinaryTree::Cursor c = reader.getCursor(); c.moveFirst(); @@ -2564,12 +2563,11 @@ static void makeEmptyRoot(Reference page) { BTreePage *btpage = (BTreePage *)page->begin(); btpage->height = 1; btpage->kvBytes = 0; - btpage->itemCount = 0; btpage->tree().build(nullptr, nullptr, nullptr, nullptr); } -BTreePage::BinaryTree::Reader * getReader(Reference page) { - return (BTreePage::BinaryTree::Reader *)page->userData; +BTreePage::BinaryTree::Mirror * getReader(Reference page) { + return (BTreePage::BinaryTree::Mirror *)page->userData; } struct BoundaryRefAndPage { @@ -2665,7 +2663,7 @@ public: #pragma pack(push, 1) struct MetaKey { - static constexpr int FORMAT_VERSION = 2; + static constexpr int FORMAT_VERSION = 3; // This serves as the format version for the entire tree, individual pages will not be versioned uint16_t formatVersion; uint8_t height; @@ -2893,7 +2891,7 @@ public: // Iterate over page entries, skipping key decoding using BTreePage::ValueTree which uses // RedwoodRecordRef::DeltaValueOnly as the delta type type to skip key decoding - BTreePage::ValueTree::Reader reader(&btPage.valueTree(), &dbBegin, &dbEnd); + BTreePage::ValueTree::Mirror reader(&btPage.valueTree(), &dbBegin, &dbEnd); auto c = reader.getCursor(); ASSERT(c.moveFirst()); Version v = entry.version; @@ -3505,7 +3503,6 @@ private: btPage->height = height; btPage->kvBytes = kvBytes; - btPage->itemCount = i - start; int written = btPage->tree().build(&entries[start], &entries[i], &pageLowerBound, &pageUpperBound); if(written > pageSize) { @@ -3680,8 +3677,8 @@ private: if(!forLazyDelete && page->userData == nullptr) { debug_printf("readPage() Creating Reader for %s @%" PRId64 " lower=%s upper=%s\n", toString(id).c_str(), snapshot->getVersion(), lowerBound->toString().c_str(), upperBound->toString().c_str()); - page->userData = new BTreePage::BinaryTree::Reader(&pTreePage->tree(), lowerBound, upperBound); - page->userDataDestructor = [](void *ptr) { delete (BTreePage::BinaryTree::Reader *)ptr; }; + page->userData = new BTreePage::BinaryTree::Mirror(&pTreePage->tree(), lowerBound, upperBound); + page->userDataDestructor = [](void *ptr) { delete (BTreePage::BinaryTree::Mirror *)ptr; }; } if(!forLazyDelete) { @@ -4283,8 +4280,8 @@ private: } // Multiple InternalCursors can share a Page - BTreePage::BinaryTree::Reader & getReader() const { - return *(BTreePage::BinaryTree::Reader *)page->userData; + BTreePage::BinaryTree::Mirror & getReader() const { + return *(BTreePage::BinaryTree::Mirror *)page->userData; } bool isLeaf() const { @@ -5319,13 +5316,21 @@ struct IntIntPair { int compare(const IntIntPair &rhs) const { //printf("compare %s to %s\n", toString().c_str(), rhs.toString().c_str()); - return k - rhs.k; + int cmp = k - rhs.k; + if(cmp == 0) { + cmp = v - rhs.v; + } + return cmp; } bool operator==(const IntIntPair &rhs) const { return k == rhs.k; } + bool operator<(const IntIntPair &rhs) const { + return compare(rhs) < 0; + } + int getCommonPrefixLen(const IntIntPair &other, int skip) const { return 0; } @@ -5628,14 +5633,14 @@ TEST_CASE("!/redwood/correctness/unit/deltaTree/RedwoodRecordRef") { tree->build(&items[0], &items[items.size()], &prev, &next); - printf("Count=%d Size=%d InitialDepth=%d\n", (int)items.size(), (int)tree->size(), (int)tree->initialDepth); + printf("Count=%d Size=%d InitialHeight=%d\n", (int)items.size(), (int)tree->size(), (int)tree->initialHeight); debug_printf("Data(%p): %s\n", tree, StringRef((uint8_t *)tree, tree->size()).toHexString().c_str()); - DeltaTree::Reader r(tree, &prev, &next); + DeltaTree::Mirror r(tree, &prev, &next); DeltaTree::Cursor fwd = r.getCursor(); DeltaTree::Cursor rev = r.getCursor(); - DeltaTree::Reader rValuesOnly(tree, &prev, &next); + DeltaTree::Mirror rValuesOnly(tree, &prev, &next); DeltaTree::Cursor fwdValueOnly = rValuesOnly.getCursor(); ASSERT(fwd.moveFirst()); @@ -5699,23 +5704,41 @@ TEST_CASE("!/redwood/correctness/unit/deltaTree/IntIntPair") { IntIntPair prev = {0, 0}; IntIntPair next = {1000, 0}; + state std::function randomPair = []() { + return IntIntPair({deterministicRandom()->randomInt(0, 1000), deterministicRandom()->randomInt(0, 1000)}); + }; + + // Build a sorted vector of N items std::vector items; for(int i = 0; i < N; ++i) { - items.push_back({i*10, i*1000}); + items.push_back(randomPair()); //printf("i=%d %s\n", i, items.back().toString().c_str()); } + std::sort(items.begin(), items.end()); - DeltaTree *tree = (DeltaTree *) new uint8_t[10000]; + // Build tree of items + int bufferSize = N * 2 * 20; + DeltaTree *tree = (DeltaTree *) new uint8_t[bufferSize]; + int builtSize = tree->build(&items[0], &items[items.size()], &prev, &next); + ASSERT(builtSize <= bufferSize); - tree->build(&items[0], &items[items.size()], &prev, &next); - - printf("Count=%d Size=%d InitialDepth=%d\n", (int)items.size(), (int)tree->size(), (int)tree->initialDepth); - debug_printf("Data(%p): %s\n", tree, StringRef((uint8_t *)tree, tree->size()).toHexString().c_str()); - - DeltaTree::Reader r(tree, &prev, &next); + DeltaTree::Mirror r(tree, &prev, &next); DeltaTree::Cursor fwd = r.getCursor(); DeltaTree::Cursor rev = r.getCursor(); + // Insert N more items into the tree and add them to items and sort again + for(int i = 0; i < N; ++i) { + IntIntPair p = randomPair(); + items.push_back(p); + r.insert(p); + ASSERT(tree->size() < bufferSize); + //printf("Inserted %s size=%d\n", items.back().toString().c_str(), tree->size()); + } + std::sort(items.begin(), items.end()); + + printf("Count=%d Size=%d InitialHeight=%d MaxHeight=%d\n", (int)items.size(), (int)tree->size(), (int)tree->initialHeight, (int)tree->maxHeight); + debug_printf("Data(%p): %s\n", tree, StringRef((uint8_t *)tree, tree->size()).toHexString().c_str()); + ASSERT(fwd.moveFirst()); ASSERT(rev.moveLast()); int i = 0; @@ -5741,12 +5764,12 @@ TEST_CASE("!/redwood/correctness/unit/deltaTree/IntIntPair") { double start = timer(); for(int i = 0; i < 20000000; ++i) { - IntIntPair p({deterministicRandom()->randomInt(0, items.size() * 10), 0}); + IntIntPair &p = items[deterministicRandom()->randomInt(0, items.size())]; if(!c.seekLessThanOrEqual(p)) { printf("Not found! query=%s\n", p.toString().c_str()); ASSERT(false); } - if(c.get().k != (p.k - (p.k % 10))) { + if(c.get() != p) { printf("Found incorrect node! query=%s found=%s\n", p.toString().c_str(), c.get().toString().c_str()); ASSERT(false); } From 04b2338e60671d16b4cbaec80e3dc7f141bff3c7 Mon Sep 17 00:00:00 2001 From: Stephen Atherton Date: Sat, 23 Nov 2019 00:40:58 -0800 Subject: [PATCH 0030/1604] Added sequential insert speed test. --- fdbserver/VersionedBTree.actor.cpp | 77 ++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index 509c034eb2..7d1cee5bdf 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -6536,6 +6536,68 @@ ACTOR Future prefixClusteredInsert(IKeyValueStore *kvs, int suffixSize, in return Void(); } +ACTOR Future sequentialInsert(IKeyValueStore *kvs, int prefixLen, int valueSize, int recordCountTarget) { + state int commitTarget = 5e6; + + state KVSource source({{prefixLen, 1}}); + state int recordSize = source.prefixLen + sizeof(uint64_t) + valueSize; + state int64_t kvBytesTarget = (int64_t)recordCountTarget * recordSize; + + printf("\nstoreType: %d\n", kvs->getType()); + printf("commitTarget: %d\n", commitTarget); + printf("valueSize: %d\n", valueSize); + printf("recordSize: %d\n", recordSize); + printf("recordCountTarget: %d\n", recordCountTarget); + printf("kvBytesTarget: %" PRId64 "\n", kvBytesTarget); + + state int64_t kvBytes = 0; + state int64_t kvBytesTotal = 0; + state int records = 0; + state Future commit = Void(); + state std::string value = deterministicRandom()->randomAlphaNumeric(1e6); + + wait(kvs->init()); + + state double intervalStart = timer(); + state double start = intervalStart; + + state std::function stats = [&]() { + double elapsed = timer() - start; + printf("Cumulative stats: %.2f seconds %.2f MB keyValue bytes %d records %.2f MB/s %.2f rec/s\r", elapsed, kvBytesTotal / 1e6, records, kvBytesTotal / elapsed / 1e6, records / elapsed); + fflush(stdout); + }; + + state uint64_t c = 0; + state Key key = source.getKeyRef(sizeof(uint64_t)); + + while(kvBytesTotal < kvBytesTarget) { + wait(yield()); + *(uint64_t *)(key.end() - sizeof(uint64_t)) = bigEndian64(c); + KeyValueRef kv(key, source.getValue(valueSize)); + kvs->set(kv); + kvBytes += kv.expectedSize(); + ++records; + + if(kvBytes >= commitTarget) { + wait(commit); + stats(); + commit = kvs->commit(); + kvBytesTotal += kvBytes; + if(kvBytesTotal >= kvBytesTarget) { + break; + } + kvBytes = 0; + } + ++c; + } + + wait(commit); + stats(); + printf("\n"); + + return Void(); +} + Future closeKVS(IKeyValueStore *kvs) { Future closed = kvs->onClosed(); kvs->close(); @@ -6577,3 +6639,18 @@ TEST_CASE("!/redwood/performance/prefixSizeComparison") { return Void(); } +TEST_CASE("!/redwood/performance/sequentialInsert") { + state int prefixLen = 30; + state int valueSize = 100; + state int recordCountTarget = 100e6; + + deleteFile("test.redwood"); + wait(delay(5)); + state IKeyValueStore *redwood = openKVStore(KeyValueStoreType::SSD_REDWOOD_V1, "test.redwood", UID(), 0); + wait(sequentialInsert(redwood, prefixLen, valueSize, recordCountTarget)); + wait(closeKVS(redwood)); + printf("\n"); + + return Void(); +} + From 887acae74a69df75e5a579b484637f30fb3a0751 Mon Sep 17 00:00:00 2001 From: Stephen Atherton Date: Sun, 1 Dec 2019 22:28:50 -0800 Subject: [PATCH 0031/1604] DeltaTree cursor equality only needs to check the DecodedNode pointer. --- fdbserver/DeltaTree.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fdbserver/DeltaTree.h b/fdbserver/DeltaTree.h index 06fdb2df86..8fe091cc7d 100644 --- a/fdbserver/DeltaTree.h +++ b/fdbserver/DeltaTree.h @@ -402,6 +402,14 @@ public: return valid() ? node->item : *reader->upperBound(); } + bool operator==(const Cursor &rhs) const { + return node == rhs.node; + } + + bool operator!=(const Cursor &rhs) const { + return node != rhs.node; + } + // Moves the cursor to the node with the greatest key less than or equal to s. If successful, // returns true, otherwise returns false and the cursor will be at the node with the next key // greater than s. From 545a12533a3ccbeb0becf17df49cf256bd1a0dd6 Mon Sep 17 00:00:00 2001 From: Stephen Atherton Date: Sun, 1 Dec 2019 23:40:59 -0800 Subject: [PATCH 0032/1604] Added redwood sequential insert unit test. --- tests/CMakeLists.txt | 1 + tests/RedwoodPerfSequentialInsert.txt | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 tests/RedwoodPerfSequentialInsert.txt diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a2d8dee922..b4d1f6ef3e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -70,6 +70,7 @@ add_fdb_test(TEST_FILES RedwoodCorrectness.txt IGNORE) add_fdb_test(TEST_FILES RedwoodPerfTests.txt IGNORE) add_fdb_test(TEST_FILES RedwoodPerfSet.txt IGNORE) add_fdb_test(TEST_FILES RedwoodPerfPrefixCompression.txt IGNORE) +add_fdb_test(TEST_FILES RedwoodPerfSequentialInsert.txt IGNORE) add_fdb_test(TEST_FILES SimpleExternalTest.txt) add_fdb_test(TEST_FILES SlowTask.txt IGNORE) add_fdb_test(TEST_FILES SpecificUnitTest.txt IGNORE) diff --git a/tests/RedwoodPerfSequentialInsert.txt b/tests/RedwoodPerfSequentialInsert.txt new file mode 100644 index 0000000000..d489fa359f --- /dev/null +++ b/tests/RedwoodPerfSequentialInsert.txt @@ -0,0 +1,6 @@ +testTitle=UnitTests +testName=UnitTests +startDelay=0 +useDB=false +maxTestCases=0 +testsMatching=!/redwood/performance/sequentialInsert From edf52e8c97050e62f0b9f7d07688ab3ca7ce6b65 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Thu, 10 Oct 2019 15:42:52 -0700 Subject: [PATCH 0033/1604] First version for reporting conflicting keys --- bindings/flow/tester/Tester.actor.cpp | 2 ++ fdbclient/CommitTransaction.h | 9 ++++-- fdbclient/MasterProxyInterface.h | 16 +++++++--- fdbclient/NativeAPI.actor.cpp | 16 +++++++++- fdbclient/NativeAPI.actor.h | 3 ++ fdbclient/ReadYourWrites.actor.cpp | 18 +++++++++++ fdbclient/vexillographer/fdb.options | 5 +++ fdbserver/ConflictSet.h | 3 +- fdbserver/MasterProxyServer.actor.cpp | 35 +++++++++++++++++++-- fdbserver/Resolver.actor.cpp | 5 ++- fdbserver/ResolverInterface.h | 3 +- fdbserver/SkipList.cpp | 44 +++++++++++++++++++-------- fdbserver/workloads/Mako.actor.cpp | 16 ++++++++-- tests/Mako.txt | 6 ++-- 14 files changed, 150 insertions(+), 31 deletions(-) diff --git a/bindings/flow/tester/Tester.actor.cpp b/bindings/flow/tester/Tester.actor.cpp index 52d193320e..508d3ae30f 100644 --- a/bindings/flow/tester/Tester.actor.cpp +++ b/bindings/flow/tester/Tester.actor.cpp @@ -1584,6 +1584,7 @@ struct UnitTestsFunc : InstructionFunc { data->db->setDatabaseOption(FDBDatabaseOption::FDB_DB_OPTION_TRANSACTION_RETRY_LIMIT, Optional(StringRef((const uint8_t*)&noRetryLimit, 8))); data->db->setDatabaseOption(FDBDatabaseOption::FDB_DB_OPTION_TRANSACTION_CAUSAL_READ_RISKY); data->db->setDatabaseOption(FDBDatabaseOption::FDB_DB_OPTION_TRANSACTION_INCLUDE_PORT_IN_ADDRESS); + data->db->setDatabaseOption(FDBDatabaseOption::FDB_DB_OPTION_TRANSACTION_REPORT_CONFLICTING_KEYS); state Reference tr = data->db->createTransaction(); tr->setOption(FDBTransactionOption::FDB_TR_OPTION_PRIORITY_SYSTEM_IMMEDIATE); @@ -1603,6 +1604,7 @@ struct UnitTestsFunc : InstructionFunc { 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"))); tr->cancel(); diff --git a/fdbclient/CommitTransaction.h b/fdbclient/CommitTransaction.h index 5ebb245c72..700cf75a4f 100644 --- a/fdbclient/CommitTransaction.h +++ b/fdbclient/CommitTransaction.h @@ -137,21 +137,23 @@ static inline bool isNonAssociativeOp(MutationRef::Type mutationType) { } struct CommitTransactionRef { - CommitTransactionRef() : read_snapshot(0) {} + CommitTransactionRef() : read_snapshot(0), report_conflicting_keys(false) {} CommitTransactionRef(Arena &a, const CommitTransactionRef &from) : read_conflict_ranges(a, from.read_conflict_ranges), write_conflict_ranges(a, from.write_conflict_ranges), mutations(a, from.mutations), - read_snapshot(from.read_snapshot) { + read_snapshot(from.read_snapshot), + report_conflicting_keys(from.report_conflicting_keys) { } VectorRef< KeyRangeRef > read_conflict_ranges; VectorRef< KeyRangeRef > write_conflict_ranges; VectorRef< MutationRef > mutations; Version read_snapshot; + bool report_conflicting_keys; template force_inline void serialize( Ar& ar ) { - serializer(ar, read_conflict_ranges, write_conflict_ranges, mutations, read_snapshot); + serializer(ar, read_conflict_ranges, write_conflict_ranges, mutations, read_snapshot, report_conflicting_keys); } // Convenience for internal code required to manipulate these without the Native API @@ -161,6 +163,7 @@ struct CommitTransactionRef { } void clear( Arena& arena, KeyRangeRef const& keys ) { + // TODO: check do I need to clear flag here mutations.push_back_deep(arena, MutationRef(MutationRef::ClearRange, keys.begin, keys.end)); write_conflict_ranges.push_back_deep(arena, keys); } diff --git a/fdbclient/MasterProxyInterface.h b/fdbclient/MasterProxyInterface.h index 5b00fd5008..ae0e76ce36 100644 --- a/fdbclient/MasterProxyInterface.h +++ b/fdbclient/MasterProxyInterface.h @@ -103,26 +103,30 @@ struct CommitID { constexpr static FileIdentifier file_identifier = 14254927; Version version; // returns invalidVersion if transaction conflicts uint16_t txnBatchId; - Optional metadataVersion; + Optional metadataVersion; + // TODO : data structure okay here ? + Optional>> conflictingKeyRanges; template void serialize(Ar& ar) { - serializer(ar, version, txnBatchId, metadataVersion); + serializer(ar, version, txnBatchId, metadataVersion, conflictingKeyRanges); } CommitID() : version(invalidVersion), txnBatchId(0) {} - CommitID( Version version, uint16_t txnBatchId, const Optional& metadataVersion ) : version(version), txnBatchId(txnBatchId), metadataVersion(metadataVersion) {} + CommitID( Version version, uint16_t txnBatchId, const Optional& metadataVersion, const Optional>>& conflictingKeyRanges = Optional>>() ) : version(version), txnBatchId(txnBatchId), metadataVersion(metadataVersion), conflictingKeyRanges(conflictingKeyRanges) {} }; struct CommitTransactionRequest : TimedRequest { constexpr static FileIdentifier file_identifier = 93948; enum { FLAG_IS_LOCK_AWARE = 0x1, - FLAG_FIRST_IN_BATCH = 0x2 + FLAG_FIRST_IN_BATCH = 0x2, + FLAG_REPORT_CONFLICTING_KEYS = 0x4 }; bool isLockAware() const { return (flags & FLAG_IS_LOCK_AWARE) != 0; } bool firstInBatch() const { return (flags & FLAG_FIRST_IN_BATCH) != 0; } + bool isReportConflictingKeys() const { return (flags & FLAG_REPORT_CONFLICTING_KEYS) != 0; } Arena arena; CommitTransactionRef transaction; @@ -136,6 +140,10 @@ struct CommitTransactionRequest : TimedRequest { void serialize(Ar& ar) { serializer(ar, transaction, reply, arena, flags, debugID); } + + void reportConflictingKeys(){ + transaction.report_conflicting_keys = true; + } }; static inline int getBytes( CommitTransactionRequest const& r ) { diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index 34bbc60ed3..6195434854 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -2613,7 +2613,7 @@ ACTOR static Future tryCommit( Database cx, Reference proxy_memory_limit_exceeded(), commit_unknown_result()}); } - + try { Version v = wait( readVersion ); req.transaction.read_snapshot = v; @@ -2673,6 +2673,10 @@ ACTOR static Future tryCommit( Database cx, Reference } return Void(); } else { + if (ci.conflictingKeyRanges.present()){ + tr->info.conflictingKeyRanges.push_back_deep(tr->info.conflictingKeyRanges.arena(), ci.conflictingKeyRanges.get()); + } + if (info.debugID.present()) TraceEvent(interval.end()).detail("Conflict", 1); @@ -2784,6 +2788,11 @@ Future Transaction::commitMutations() { if(options.firstInBatch) { tr.flags = tr.flags | CommitTransactionRequest::FLAG_FIRST_IN_BATCH; } + if(options.reportConflictingKeys) { + // TODO : Is it better to keep it as a flag? + tr.flags = tr.flags | CommitTransactionRequest::FLAG_REPORT_CONFLICTING_KEYS; + tr.reportConflictingKeys(); + } Future commitResult = tryCommit( cx, trLogInfo, tr, readVersion, info, &this->committedVersion, this, options ); @@ -2974,6 +2983,11 @@ void Transaction::setOption( FDBTransactionOptions::Option option, Optional debugID; TaskPriority taskID; bool useProvisionalProxies; + Standalone>> conflictingKeyRanges; explicit TransactionInfo( TaskPriority taskID ) : taskID(taskID), useProvisionalProxies(false) {} }; @@ -271,6 +273,7 @@ public: void reset(); void fullReset(); double getBackoff(int errCode); + void debugTransaction(UID dID) { info.debugID = dID; } Future commitMutations(); diff --git a/fdbclient/ReadYourWrites.actor.cpp b/fdbclient/ReadYourWrites.actor.cpp index c41739d907..e459d05af8 100644 --- a/fdbclient/ReadYourWrites.actor.cpp +++ b/fdbclient/ReadYourWrites.actor.cpp @@ -23,6 +23,7 @@ #include "fdbclient/DatabaseContext.h" #include "fdbclient/StatusClient.h" #include "fdbclient/MonitorLeader.h" +#include "fdbclient/JsonBuilder.h" #include "flow/Util.h" #include "flow/actorcompiler.h" // This must be the last #include. @@ -1228,6 +1229,23 @@ Future< Optional > ReadYourWritesTransaction::get( const Key& key, bool s return Optional(); } + // TODO : add conflict keys to special key space + if (key == LiteralStringRef("\xff\xff/conflicting_keys/json")){ + if (!tr.info.conflictingKeyRanges.empty()){ + // TODO : return a json value which represents all the values + JsonBuilderArray conflictingKeysArray; + for (auto & cKR : tr.info.conflictingKeyRanges) { + for (auto & kr : cKR) { + conflictingKeysArray.push_back(format("[%s, %s)", kr.begin.toString().c_str(), kr.end.toString().c_str())); + } + } + Optional output = StringRef(conflictingKeysArray.getJson()); + return output; + } else { + return Optional(); + } + } + if(checkUsedDuringCommit()) { return used_during_commit(); } diff --git a/fdbclient/vexillographer/fdb.options b/fdbclient/vexillographer/fdb.options index 890dea4864..035335e2a2 100644 --- a/fdbclient/vexillographer/fdb.options +++ b/fdbclient/vexillographer/fdb.options @@ -174,6 +174,9 @@ description is not currently required but encouraged.
^ With the cursor located where the ^ is pointing. --- fdbcli/FlowLineNoise.actor.cpp | 4 +- fdbcli/fdbcli.actor.cpp | 10 ++++- fdbcli/linenoise/linenoise.c | 70 +++++++++++++++++++++++++++++++++- fdbcli/linenoise/linenoise.h | 7 ++++ 4 files changed, 86 insertions(+), 5 deletions(-) diff --git a/fdbcli/FlowLineNoise.actor.cpp b/fdbcli/FlowLineNoise.actor.cpp index 85ae2c0bfb..6c101ca666 100644 --- a/fdbcli/FlowLineNoise.actor.cpp +++ b/fdbcli/FlowLineNoise.actor.cpp @@ -113,7 +113,7 @@ LineNoise::LineNoise( for( auto const& c : completions ) linenoiseAddCompletion( lc, c.c_str() ); }); - /*linenoiseSetHintsCallback( [](const char* line, int* color, int*bold) -> const char* { + linenoiseSetHintsCallback( [](const char* line, int* color, int*bold) -> char* { Hint h = onMainThread( [line]() -> Future { return hint_callback(line); }).getBlocking(); @@ -122,7 +122,7 @@ LineNoise::LineNoise( *bold = h.bold; return strdup( h.text.c_str() ); }); - linenoiseSetFreeHintsCallback( free );*/ + linenoiseSetFreeHintsCallback( free ); #endif threadPool->addThread(reader); diff --git a/fdbcli/fdbcli.actor.cpp b/fdbcli/fdbcli.actor.cpp index ef55970ead..18cd4f0633 100644 --- a/fdbcli/fdbcli.actor.cpp +++ b/fdbcli/fdbcli.actor.cpp @@ -540,7 +540,7 @@ void initHelp() { "attempts to kill one or more processes in the cluster", "If no addresses are specified, populates the list of processes which can be killed. Processes cannot be killed before this list has been populated.\n\nIf `all' is specified, attempts to kill all known processes.\n\nIf `list' is specified, displays all known processes. This is only useful when the database is unresponsive.\n\nFor each IP:port pair in
*, attempt to kill the specified process."); helpMap["profile"] = CommandHelp( - " ", + "profile ", "namespace for all the profiling-related commands.", "Different types support different actions. Run `profile` to get a list of types, and iteratively explore the help.\n"); helpMap["force_recovery_with_data_loss"] = CommandHelp( @@ -3654,6 +3654,14 @@ ACTOR Future runCli(CLIOptions opt) { fdbcli_comp_cmd(line, completions); }, [](std::string const& line)->LineNoise::Hint { + int firstWordIdx = line.find(' '); + if (firstWordIdx == std::string::npos) { + firstWordIdx = line.size(); + } + auto iter = helpMap.find(line.substr(0, firstWordIdx)); + if (iter != helpMap.end()) { + return LineNoise::Hint(iter->second.usage.substr(firstWordIdx), 0, false); + } return LineNoise::Hint(); }, 1000, diff --git a/fdbcli/linenoise/linenoise.c b/fdbcli/linenoise/linenoise.c index 30d64ececf..10ffd71c35 100644 --- a/fdbcli/linenoise/linenoise.c +++ b/fdbcli/linenoise/linenoise.c @@ -111,6 +111,7 @@ #include #include #include +#include #include #include #include @@ -120,6 +121,8 @@ #define LINENOISE_MAX_LINE 4096 static char *unsupported_term[] = {"dumb","cons25","emacs",NULL}; static linenoiseCompletionCallback *completionCallback = NULL; +static linenoiseHintsCallback *hintsCallback = NULL; +static linenoiseFreeHintsCallback *freeHintsCallback = NULL; static struct termios orig_termios; /* In order to restore at exit.*/ static int rawmode = 0; /* For atexit() function to check if restore is needed*/ @@ -407,6 +410,18 @@ void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) { completionCallback = fn; } +/* Register a hits function to be called to show hits to the user at the + * right of the prompt. */ +void linenoiseSetHintsCallback(linenoiseHintsCallback *fn) { + hintsCallback = fn; +} + +/* Register a function to free the hints returned by the hints callback + * registered with linenoiseSetHintsCallback(). */ +void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) { + freeHintsCallback = fn; +} + /* This function is used by the callback function registered by the user * in order to add completion options given the input string when the * user typed . See the example.c source code for a very easy to @@ -456,6 +471,32 @@ static void abFree(struct abuf *ab) { free(ab->b); } +/* Helper of refreshSingleLine() and refreshMultiLine() to show hints + * to the right of the prompt. */ +void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int plen) { + char seq[64]; + if (hintsCallback && plen+l->len < l->cols) { + int color = -1, bold = 0; + char *hint = hintsCallback(l->buf,&color,&bold); + if (hint) { + int hintlen = strlen(hint); + int hintmaxlen = l->cols-(plen+l->len); + if (hintlen > hintmaxlen) hintlen = hintmaxlen; + if (bold == 1 && color == -1) color = 37; + if (color != -1 || bold != 0) + snprintf(seq,64,"\033[%d;%d;49m",bold,color); + else + seq[0] = '\0'; + abAppend(ab,seq,strlen(seq)); + abAppend(ab,hint,hintlen); + if (color != -1 || bold != 0) + abAppend(ab,"\033[0m",4); + /* Call the function to free the hint returned. */ + if (freeHintsCallback) freeHintsCallback(hint); + } + } +} + /* Single line low level line refresh. * * Rewrite the currently edited line accordingly to the buffer content, @@ -485,6 +526,8 @@ static void refreshSingleLine(struct linenoiseState *l) { /* Write the prompt and the current buffer content */ abAppend(&ab,l->prompt,strlen(l->prompt)); abAppend(&ab,buf,len); + /* Show hits if any. */ + refreshShowHints(&ab,l,plen); /* Erase to right */ snprintf(seq,64,"\x1b[0K"); abAppend(&ab,seq,strlen(seq)); @@ -538,6 +581,9 @@ static void refreshMultiLine(struct linenoiseState *l) { abAppend(&ab,l->prompt,strlen(l->prompt)); abAppend(&ab,l->buf,l->len); + /* Show hits if any. */ + refreshShowHints(&ab,l,plen); + /* If we are at the very end of the screen with our prompt, we need to * emit a newline and move the prompt to the first column. */ if (l->pos && @@ -598,7 +644,7 @@ int linenoiseEditInsert(struct linenoiseState *l, char c) { l->pos++; l->len++; l->buf[l->len] = '\0'; - if ((!mlmode && l->plen+l->len < l->cols) /* || mlmode */) { + if ((!mlmode && l->plen+l->len < l->cols && !hintsCallback)) { /* Avoid a full update of the line in the * trivial case. */ if (write(l->ofd,&c,1) == -1) return -1; @@ -772,6 +818,14 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, history_len--; free(history[history_len]); if (mlmode) linenoiseEditMoveEnd(&l); + if (hintsCallback) { + /* Force a refresh without hints to leave the previous + * line as the user typed it after a newline. */ + linenoiseHintsCallback *hc = hintsCallback; + hintsCallback = NULL; + refreshLine(&l); + hintsCallback = hc; + } return (int)l.len; case CTRL_C: /* ctrl-c */ errno = EAGAIN; @@ -1010,6 +1064,14 @@ char *linenoise(const char *prompt) { } } +/* This is just a wrapper the user may want to call in order to make sure + * the linenoise returned buffer is freed with the same allocator it was + * created with. Useful when the main program is using an alternative + * allocator. */ +void linenoiseFree(void *ptr) { + free(ptr); +} + /* ================================ History ================================= */ /* Free the history, but does not reset it. Only used when we have to @@ -1101,10 +1163,14 @@ int linenoiseHistorySetMaxLen(int len) { /* Save the history in the specified file. On success 0 is returned * otherwise -1 is returned. */ int linenoiseHistorySave(const char *filename) { - FILE *fp = fopen(filename,"w"); + mode_t old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO); + FILE *fp; int j; + fp = fopen(filename,"w"); + umask(old_umask); if (fp == NULL) return -1; + chmod(filename,S_IRUSR|S_IWUSR); for (j = 0; j < history_len; j++) fprintf(fp,"%s\n",history[j]); fclose(fp); diff --git a/fdbcli/linenoise/linenoise.h b/fdbcli/linenoise/linenoise.h index fbb01cfaad..c388e25a4f 100644 --- a/fdbcli/linenoise/linenoise.h +++ b/fdbcli/linenoise/linenoise.h @@ -39,6 +39,8 @@ #ifndef __LINENOISE_H #define __LINENOISE_H +#include + #ifdef __cplusplus extern "C" { #endif @@ -49,10 +51,15 @@ typedef struct linenoiseCompletions { } linenoiseCompletions; typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *); +typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold); +typedef void(linenoiseFreeHintsCallback)(void *); void linenoiseSetCompletionCallback(linenoiseCompletionCallback *); +void linenoiseSetHintsCallback(linenoiseHintsCallback *); +void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *); void linenoiseAddCompletion(linenoiseCompletions *, const char *); char *linenoise(const char *prompt); +void linenoiseFree(void *ptr); int linenoiseHistoryAdd(const char *line); int linenoiseHistorySetMaxLen(int len); int linenoiseHistorySave(const char *filename); From 62a1983c6255712edda1162921490f97cefae53e Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Fri, 21 Feb 2020 17:48:34 -0800 Subject: [PATCH 0680/1604] reStructuredText uses ``x`` as code font and not `x`. --- documentation/sphinx/source/backups.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/sphinx/source/backups.rst b/documentation/sphinx/source/backups.rst index 01de988614..1a30a6e4b1 100644 --- a/documentation/sphinx/source/backups.rst +++ b/documentation/sphinx/source/backups.rst @@ -162,7 +162,7 @@ The Blob Credential File format is JSON with the following schema: TLS Support =========== -In-flight traffic for blob store or disaster recovery backups can be encrypted with the following environment variables. They are also offered as command-line flags or can be specified in `foundationdb.conf` for backup agents. +In-flight traffic for blob store or disaster recovery backups can be encrypted with the following environment variables. They are also offered as command-line flags or can be specified in ``foundationdb.conf`` for backup agents. ============================ ==================================================== Environment Variable Purpose @@ -180,7 +180,7 @@ Environment Variable Purpose certificates and sessions. ============================ ==================================================== -Blob store backups can be configured to use HTTPS/TLS by setting the `secure_connection` or `sc` backup URL option to `1`, which is the default. Disaster recovery backups are secured by using TLS for both the source and target clusters and setting the TLS options for the `fdbdr` and `dr_agent` commands. +Blob store backups can be configured to use HTTPS/TLS by setting the ``secure_connection`` or ``sc`` backup URL option to ``1``, which is the default. Disaster recovery backups are secured by using TLS for both the source and target clusters and setting the TLS options for the ``fdbdr`` and ``dr_agent`` commands. ``fdbbackup`` command line tool =============================== From 65fbe0d0bc286213e089b5e3b1c5b7cee9daa202 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Fri, 21 Feb 2020 19:22:14 -0800 Subject: [PATCH 0681/1604] revert AcceptSocket priority change because of bad performance results --- fdbrpc/FlowTransport.actor.cpp | 2 +- fdbserver/Knobs.cpp | 2 +- flow/Knobs.cpp | 1 + flow/Knobs.h | 1 + flow/network.h | 1 - 5 files changed, 4 insertions(+), 3 deletions(-) diff --git a/fdbrpc/FlowTransport.actor.cpp b/fdbrpc/FlowTransport.actor.cpp index b00ca240c7..83d40b1753 100644 --- a/fdbrpc/FlowTransport.actor.cpp +++ b/fdbrpc/FlowTransport.actor.cpp @@ -997,7 +997,7 @@ ACTOR static Future listen( TransportData* self, NetworkAddress listenAddr .detail("ListenAddress", listenAddr.toString()); incoming.add( connectionIncoming(self, conn) ); } - wait(delay(0, TaskPriority::AcceptSocket)); + wait(delay(0) || delay(FLOW_KNOBS->CONNECTION_ACCEPT_DELAY, TaskPriority::WriteSocket)); } } catch (Error& e) { TraceEvent(SevError, "ListenError").error(e); diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index b0539e1d82..992f84625c 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -318,7 +318,7 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs, bool isSimula init( ALWAYS_CAUSAL_READ_RISKY, false ); init( MAX_COMMIT_UPDATES, 2000 ); if( randomize && BUGGIFY ) MAX_COMMIT_UPDATES = 1; init( MIN_PROXY_COMPUTE, 0.001 ); - init( PROXY_COMPUTE_BUCKETS, 5000 ); + init( PROXY_COMPUTE_BUCKETS, 20000 ); init( PROXY_COMPUTE_GROWTH_RATE, 0.01 ); // Master Server diff --git a/flow/Knobs.cpp b/flow/Knobs.cpp index 1529b58a6b..aa714551a0 100644 --- a/flow/Knobs.cpp +++ b/flow/Knobs.cpp @@ -67,6 +67,7 @@ FlowKnobs::FlowKnobs(bool randomize, bool isSimulated) { init( MAX_RECONNECTION_TIME, 0.5 ); init( RECONNECTION_TIME_GROWTH_RATE, 1.2 ); init( RECONNECTION_RESET_TIME, 5.0 ); + init( CONNECTION_ACCEPT_DELAY, 0.5 ); init( USE_OBJECT_SERIALIZER, 1 ); init( TOO_MANY_CONNECTIONS_CLOSED_RESET_DELAY, 5.0 ); init( TOO_MANY_CONNECTIONS_CLOSED_TIMEOUT, 20.0 ); diff --git a/flow/Knobs.h b/flow/Knobs.h index 2db4ebd9ce..358fc82be0 100644 --- a/flow/Knobs.h +++ b/flow/Knobs.h @@ -87,6 +87,7 @@ public: double MAX_RECONNECTION_TIME; double RECONNECTION_TIME_GROWTH_RATE; double RECONNECTION_RESET_TIME; + double CONNECTION_ACCEPT_DELAY; int USE_OBJECT_SERIALIZER; int TLS_CERT_REFRESH_DELAY_SECONDS; diff --git a/flow/network.h b/flow/network.h index d3dd9aa026..02898797dd 100644 --- a/flow/network.h +++ b/flow/network.h @@ -44,7 +44,6 @@ enum class TaskPriority { DiskIOComplete = 9150, LoadBalancedEndpoint = 9000, ReadSocket = 9000, - AcceptSocket = 8950, Handshake = 8900, CoordinationReply = 8810, Coordination = 8800, From 9c9e64333429f51c9b17b8cc65c6a878f3fc9fc4 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Fri, 21 Feb 2020 20:05:48 -0800 Subject: [PATCH 0682/1604] Fix some various indentation issues that caused weird formatting in the documentation output. --- .../sphinx/source/administration.rst | 36 +++++++-------- documentation/sphinx/source/api-c.rst | 14 +++--- documentation/sphinx/source/api-ruby.rst | 44 +++++++++---------- documentation/sphinx/source/cap-theorem.rst | 6 +-- documentation/sphinx/source/configuration.rst | 44 +++++++++---------- documentation/sphinx/source/data-modeling.rst | 20 ++++----- .../old-release-notes/release-notes-100.rst | 26 +++++------ documentation/sphinx/source/tls.rst | 12 ++--- 8 files changed, 99 insertions(+), 103 deletions(-) diff --git a/documentation/sphinx/source/administration.rst b/documentation/sphinx/source/administration.rst index 7275c0c4a7..e34413b9f3 100644 --- a/documentation/sphinx/source/administration.rst +++ b/documentation/sphinx/source/administration.rst @@ -177,7 +177,7 @@ You can add new machines to a cluster at any time: 5) If you have previously :ref:`excluded ` a machine from the cluster, you will need to take it off the exclusion list using the ``include `` command of fdbcli before it can be a full participant in the cluster. - .. note:: Addresses have the form ``IP``:``PORT``. This form is used even if TLS is enabled. +.. note:: Addresses have the form ``IP``:``PORT``. This form is used even if TLS is enabled. .. _removing-machines-from-a-cluster: @@ -192,26 +192,26 @@ To temporarily or permanently remove one or more machines from a FoundationDB cl 3) Use the ``exclude`` command in ``fdbcli`` on the machines you plan to remove: - :: +:: - user@host1$ fdbcli - Using cluster file `/etc/foundationdb/fdb.cluster'. + user@host1$ fdbcli + Using cluster file `/etc/foundationdb/fdb.cluster'. - The database is available. + The database is available. - Welcome to the fdbcli. For help, type `help'. - fdb> exclude 1.2.3.4 1.2.3.5 1.2.3.6 - Waiting for state to be removed from all excluded servers. This may take a while. - It is now safe to remove these machines or processes from the cluster. + Welcome to the fdbcli. For help, type `help'. + fdb> exclude 1.2.3.4 1.2.3.5 1.2.3.6 + Waiting for state to be removed from all excluded servers. This may take a while. + It is now safe to remove these machines or processes from the cluster. - - ``exclude`` can be used to exclude either machines (by specifying an IP address) or individual processes (by specifying an ``IP``:``PORT`` pair). - .. note:: Addresses have the form ``IP``:``PORT``. This form is used even if TLS is enabled. - - Excluding a server doesn't shut it down immediately; data on the machine is first moved away. When the ``exclude`` command completes successfully (by returning control to the command prompt), the machines that you specified are no longer required to maintain the configured redundancy mode. A large amount of data might need to be transferred first, so be patient. When the process is complete, the excluded machine or process can be shut down without fault tolerance or availability consequences. - - If you interrupt the exclude command with Ctrl-C after seeing the "waiting for state to be removed" message, the exclusion work will continue in the background. Repeating the command will continue waiting for the exclusion to complete. To reverse the effect of the ``exclude`` command, use the ``include`` command. +``exclude`` can be used to exclude either machines (by specifying an IP address) or individual processes (by specifying an ``IP``:``PORT`` pair). + +.. note:: Addresses have the form ``IP``:``PORT``. This form is used even if TLS is enabled. + +Excluding a server doesn't shut it down immediately; data on the machine is first moved away. When the ``exclude`` command completes successfully (by returning control to the command prompt), the machines that you specified are no longer required to maintain the configured redundancy mode. A large amount of data might need to be transferred first, so be patient. When the process is complete, the excluded machine or process can be shut down without fault tolerance or availability consequences. + +If you interrupt the exclude command with Ctrl-C after seeing the "waiting for state to be removed" message, the exclusion work will continue in the background. Repeating the command will continue waiting for the exclusion to complete. To reverse the effect of the ``exclude`` command, use the ``include`` command. 4) On each removed machine, stop the FoundationDB server and prevent it from starting at the next boot. Follow the :ref:`instructions for your platform `. For example, on Ubuntu:: @@ -316,9 +316,9 @@ Running backups Number of backups currently running. Different backups c Running DRs Number of DRs currently running. Different DRs could be streaming different prefixes and/or to different DR clusters. ====================== ========================================================================================================== -The "Memory availability" is a conservative estimate of the minimal RAM available to any ``fdbserver`` process across all machines in the cluster. This value is calculated in two steps. Memory available per process is first calculated *for each machine* by taking: +The "Memory availability" is a conservative estimate of the minimal RAM available to any ``fdbserver`` process across all machines in the cluster. This value is calculated in two steps. Memory available per process is first calculated *for each machine* by taking:: - availability = ((total - committed) + sum(processSize)) / processes + availability = ((total - committed) + sum(processSize)) / processes where: diff --git a/documentation/sphinx/source/api-c.rst b/documentation/sphinx/source/api-c.rst index 6b5d41bf60..7cb4a0d04d 100644 --- a/documentation/sphinx/source/api-c.rst +++ b/documentation/sphinx/source/api-c.rst @@ -538,31 +538,31 @@ Applications must provide error handling and an appropriate retry loop around th ``FDB_STREAMING_MODE_ITERATOR`` - The caller is implementing an iterator (most likely in a binding to a higher level language). The amount of data returned depends on the value of the ``iteration`` parameter to :func:`fdb_transaction_get_range()`. + The caller is implementing an iterator (most likely in a binding to a higher level language). The amount of data returned depends on the value of the ``iteration`` parameter to :func:`fdb_transaction_get_range()`. ``FDB_STREAMING_MODE_SMALL`` - Data is returned in small batches (not much more expensive than reading individual key-value pairs). + Data is returned in small batches (not much more expensive than reading individual key-value pairs). ``FDB_STREAMING_MODE_MEDIUM`` - Data is returned in batches between _SMALL and _LARGE. + Data is returned in batches between _SMALL and _LARGE. ``FDB_STREAMING_MODE_LARGE`` - Data is returned in batches large enough to be, in a high-concurrency environment, nearly as efficient as possible. If the caller does not need the entire range, some disk and network bandwidth may be wasted. The batch size may be still be too small to allow a single client to get high throughput from the database. + Data is returned in batches large enough to be, in a high-concurrency environment, nearly as efficient as possible. If the caller does not need the entire range, some disk and network bandwidth may be wasted. The batch size may be still be too small to allow a single client to get high throughput from the database. ``FDB_STREAMING_MODE_SERIAL`` - Data is returned in batches large enough that an individual client can get reasonable read bandwidth from the database. If the caller does not need the entire range, considerable disk and network bandwidth may be wasted. + Data is returned in batches large enough that an individual client can get reasonable read bandwidth from the database. If the caller does not need the entire range, considerable disk and network bandwidth may be wasted. ``FDB_STREAMING_MODE_WANT_ALL`` - The caller intends to consume the entire range and would like it all transferred as early as possible. + The caller intends to consume the entire range and would like it all transferred as early as possible. ``FDB_STREAMING_MODE_EXACT`` - The caller has passed a specific row limit and wants that many rows delivered in a single batch. + The caller has passed a specific row limit and wants that many rows delivered in a single batch. .. function:: void fdb_transaction_set(FDBTransaction* transaction, uint8_t const* key_name, int key_name_length, uint8_t const* value, int value_length) diff --git a/documentation/sphinx/source/api-ruby.rst b/documentation/sphinx/source/api-ruby.rst index 6fdf866a40..c3b44c0413 100644 --- a/documentation/sphinx/source/api-ruby.rst +++ b/documentation/sphinx/source/api-ruby.rst @@ -211,21 +211,21 @@ Key selectors Creates a key selector with the given reference key, equality flag, and offset. It is usually more convenient to obtain a key selector with one of the following methods: - .. classmethod:: last_less_than(key) -> KeySelector + .. classmethod:: last_less_than(key) -> KeySelector - Returns a key selector referencing the last (greatest) key in the database less than the specified key. + Returns a key selector referencing the last (greatest) key in the database less than the specified key. - .. classmethod:: KeySelector.last_less_or_equal(key) -> KeySelector + .. classmethod:: KeySelector.last_less_or_equal(key) -> KeySelector - Returns a key selector referencing the last (greatest) key less than, or equal to, the specified key. + Returns a key selector referencing the last (greatest) key less than, or equal to, the specified key. - .. classmethod:: KeySelector.first_greater_than(key) -> KeySelector + .. classmethod:: KeySelector.first_greater_than(key) -> KeySelector - Returns a key selector referencing the first (least) key greater than the specified key. + Returns a key selector referencing the first (least) key greater than the specified key. - .. classmethod:: KeySelector.first_greater_or_equal(key) -> KeySelector + .. classmethod:: KeySelector.first_greater_or_equal(key) -> KeySelector - Returns a key selector referencing the first key greater than, or equal to, the specified key. + Returns a key selector referencing the first key greater than, or equal to, the specified key. .. method:: KeySelector.+(offset) -> KeySelector @@ -281,16 +281,16 @@ A |database-blurb1| |database-blurb2| The ``options`` hash accepts the following optional parameters: - ``:limit`` - Only the first ``limit`` keys (and their values) in the range will be returned. + ``:limit`` + Only the first ``limit`` keys (and their values) in the range will be returned. - ``:reverse`` - If ``true``, then the keys in the range will be returned in reverse order. Reading ranges in reverse is supported natively by the database and should have minimal extra cost. + ``:reverse`` + If ``true``, then the keys in the range will be returned in reverse order. Reading ranges in reverse is supported natively by the database and should have minimal extra cost. - If ``:limit`` is also specified, the *last* ``limit`` keys in the range will be returned in reverse order. + If ``:limit`` is also specified, the *last* ``limit`` keys in the range will be returned in reverse order. - ``:streaming_mode`` - A valid |streaming-mode|, which provides a hint to FoundationDB about how to retrieve the specified range. This option should generally not be specified, allowing FoundationDB to retrieve the full range very efficiently. + ``:streaming_mode`` + A valid |streaming-mode|, which provides a hint to FoundationDB about how to retrieve the specified range. This option should generally not be specified, allowing FoundationDB to retrieve the full range very efficiently. .. method:: Database.get_range(begin, end, options={}) {|kv| block } -> nil @@ -459,16 +459,16 @@ Reading data The ``options`` hash accepts the following optional parameters: - ``:limit`` - Only the first ``limit`` keys (and their values) in the range will be returned. + ``:limit`` + Only the first ``limit`` keys (and their values) in the range will be returned. - ``:reverse`` - If ``true``, then the keys in the range will be returned in reverse order. Reading ranges in reverse is supported natively by the database and should have minimal extra cost. + ``:reverse`` + If ``true``, then the keys in the range will be returned in reverse order. Reading ranges in reverse is supported natively by the database and should have minimal extra cost. - If ``:limit`` is also specified, the *last* ``limit`` keys in the range will be returned in reverse order. + If ``:limit`` is also specified, the *last* ``limit`` keys in the range will be returned in reverse order. - ``:streaming_mode`` - A valid |streaming-mode|, which provides a hint to FoundationDB about how the returned enumerable is likely to be used. The default is ``:iterator``. + ``:streaming_mode`` + A valid |streaming-mode|, which provides a hint to FoundationDB about how the returned enumerable is likely to be used. The default is ``:iterator``. .. method:: Transaction.get_range(begin, end, options={}) {|kv| block } -> nil diff --git a/documentation/sphinx/source/cap-theorem.rst b/documentation/sphinx/source/cap-theorem.rst index c5c3c64d55..42942d2f8c 100644 --- a/documentation/sphinx/source/cap-theorem.rst +++ b/documentation/sphinx/source/cap-theorem.rst @@ -9,9 +9,9 @@ What is the CAP Theorem? In 2000, Eric Brewer conjectured that a distributed system cannot simultaneously provide all three of the following desirable properties: - * Consistency: A read sees all previously completed writes. - * Availability: Reads and writes always succeed. - * Partition tolerance: Guaranteed properties are maintained even when network failures prevent some machines from communicating with others. +* Consistency: A read sees all previously completed writes. +* Availability: Reads and writes always succeed. +* Partition tolerance: Guaranteed properties are maintained even when network failures prevent some machines from communicating with others. In 2002, Gilbert and Lynch proved this in the asynchronous and partially synchronous network models, so it is now commonly called the `CAP Theorem `_. diff --git a/documentation/sphinx/source/configuration.rst b/documentation/sphinx/source/configuration.rst index b2a6b9de70..9e9c520193 100644 --- a/documentation/sphinx/source/configuration.rst +++ b/documentation/sphinx/source/configuration.rst @@ -397,7 +397,7 @@ Datacenter-aware mode In addition to the more commonly used modes listed above, this version of FoundationDB has support for redundancy across multiple datacenters. - .. note:: When using the datacenter-aware mode, all ``fdbserver`` processes should be passed a valid datacenter identifier on the command line. +.. note:: When using the datacenter-aware mode, all ``fdbserver`` processes should be passed a valid datacenter identifier on the command line. ``three_datacenter`` mode *(for 5+ machines in 3 datacenters)* @@ -624,23 +624,23 @@ The ``satellite_redundancy_mode`` is configured per region, and specifies how ma ``one_satellite_single`` mode - Keep one copy of the mutation log in the satellite datacenter with the highest priority. If the highest priority satellite is unavailable it will put the transaction log in the satellite datacenter with the next highest priority. +Keep one copy of the mutation log in the satellite datacenter with the highest priority. If the highest priority satellite is unavailable it will put the transaction log in the satellite datacenter with the next highest priority. ``one_satellite_double`` mode - Keep two copies of the mutation log in the satellite datacenter with the highest priority. +Keep two copies of the mutation log in the satellite datacenter with the highest priority. ``one_satellite_triple`` mode - Keep three copies of the mutation log in the satellite datacenter with the highest priority. +Keep three copies of the mutation log in the satellite datacenter with the highest priority. ``two_satellite_safe`` mode - Keep two copies of the mutation log in each of the two satellite datacenters with the highest priorities, for a total of four copies of each mutation. This mode will protect against the simultaneous loss of both the primary and one of the satellite datacenters. If only one satellite is available, it will fall back to only storing two copies of the mutation log in the remaining datacenter. +Keep two copies of the mutation log in each of the two satellite datacenters with the highest priorities, for a total of four copies of each mutation. This mode will protect against the simultaneous loss of both the primary and one of the satellite datacenters. If only one satellite is available, it will fall back to only storing two copies of the mutation log in the remaining datacenter. ``two_satellite_fast`` mode - Keep two copies of the mutation log in each of the two satellite datacenters with the highest priorities, for a total of four copies of each mutation. FoundationDB will only synchronously wait for one of the two satellite datacenters to make the mutations durable before considering a commit successful. This will reduce tail latencies caused by network issues between datacenters. If only one satellite is available, it will fall back to only storing two copies of the mutation log in the remaining datacenter. +Keep two copies of the mutation log in each of the two satellite datacenters with the highest priorities, for a total of four copies of each mutation. FoundationDB will only synchronously wait for one of the two satellite datacenters to make the mutations durable before considering a commit successful. This will reduce tail latencies caused by network issues between datacenters. If only one satellite is available, it will fall back to only storing two copies of the mutation log in the remaining datacenter. .. warning:: In release 6.0 this is implemented by waiting for all but 2 of the transaction logs. If ``satellite_logs`` is set to more than 4, FoundationDB will still need to wait for replies from both datacenters. @@ -698,17 +698,17 @@ Migrating a database to use a region configuration To configure an existing database to regions, do the following steps: - 1. Ensure all processes have their dcid locality set on the command line. All processes should exist in the same datacenter. If converting from a ``three_datacenter`` configuration, first configure down to using a single datacenter by changing the replication mode. Then exclude the machines in all datacenters but the one that will become the initial active region. +1. Ensure all processes have their dcid locality set on the command line. All processes should exist in the same datacenter. If converting from a ``three_datacenter`` configuration, first configure down to using a single datacenter by changing the replication mode. Then exclude the machines in all datacenters but the one that will become the initial active region. - 2. Configure the region configuration. The datacenter with all the existing processes should have a non-negative priority. The region which will eventually store the remote replica should be added with a negative priority. +2. Configure the region configuration. The datacenter with all the existing processes should have a non-negative priority. The region which will eventually store the remote replica should be added with a negative priority. - 3. Add processes to the cluster in the remote region. These processes will not take data yet, but need to be added to the cluster. If they are added before the region configuration is set they will be assigned data like any other FoundationDB process, which will lead to high latencies. +3. Add processes to the cluster in the remote region. These processes will not take data yet, but need to be added to the cluster. If they are added before the region configuration is set they will be assigned data like any other FoundationDB process, which will lead to high latencies. - 4. Configure ``usable_regions=2``. This will cause the cluster to start copying data between the regions. +4. Configure ``usable_regions=2``. This will cause the cluster to start copying data between the regions. - 5. Watch ``status`` and wait until data movement is complete. This will signal that the remote datacenter has a full replica of all of the data in the database. +5. Watch ``status`` and wait until data movement is complete. This will signal that the remote datacenter has a full replica of all of the data in the database. - 6. Change the region configuration to have a non-negative priority for the primary datacenters in both regions. This will enable automatic failover between regions. +6. Change the region configuration to have a non-negative priority for the primary datacenters in both regions. This will enable automatic failover between regions. Handling datacenter failures ---------------------------- @@ -719,9 +719,9 @@ When a primary datacenter fails, the cluster will go into a degraded state. It w To drop the dead datacenter do the following steps: - 1. Configure the region configuration so that the dead datacenter has a negative priority. +1. Configure the region configuration so that the dead datacenter has a negative priority. - 2. Configure ``usable_regions=1``. +2. Configure ``usable_regions=1``. If you are running in a configuration without a satellite datacenter, or you have lost all machines in a region simultaneously, the ``force_recovery_with_data_loss`` command from ``fdbcli`` allows you to force a recovery to the other region. This will discard the portion of the mutation log which did not make it across the WAN. Once the database has recovered, immediately follow the previous steps to drop the dead region the normal way. @@ -730,13 +730,10 @@ Region change safety The steps described above for both adding and removing replicas are enforced by ``fdbcli``. The following are the specific conditions checked by ``fdbcli``: - * You cannot change the ``regions`` configuration while also changing ``usable_regions``. - - * You can only change ``usable_regions`` when exactly one region has priority >= 0. - - * When ``usable_regions`` > 1, all regions with priority >= 0 must have a full replica of the data. - - * All storage servers must be in one of the regions specified by the region configuration. +* You cannot change the ``regions`` configuration while also changing ``usable_regions``. +* You can only change ``usable_regions`` when exactly one region has priority >= 0. +* When ``usable_regions`` > 1, all regions with priority >= 0 must have a full replica of the data. +* All storage servers must be in one of the regions specified by the region configuration. Monitoring ---------- @@ -772,9 +769,8 @@ Known limitations The 6.2 release still has a number of rough edges related to region configuration. This is a collection of all the issues that have been pointed out in the sections above. These issues should be significantly improved in future releases of FoundationDB: - * FoundationDB supports replicating data to at most two regions. - - * ``two_satellite_fast`` does not hide latency properly when configured with more than 4 satellite transaction logs. +* FoundationDB supports replicating data to at most two regions. +* ``two_satellite_fast`` does not hide latency properly when configured with more than 4 satellite transaction logs. .. _guidelines-process-class-config: diff --git a/documentation/sphinx/source/data-modeling.rst b/documentation/sphinx/source/data-modeling.rst index ad8363ce83..ca0983ec8f 100644 --- a/documentation/sphinx/source/data-modeling.rst +++ b/documentation/sphinx/source/data-modeling.rst @@ -543,25 +543,25 @@ How you map your application data to keys and values can have a dramatic impact * Structure keys so that range reads can efficiently retrieve the most frequently accessed data. - * If you perform a range read that is, in total, much more than 1 kB, try to restrict your range as much as you can while still retrieving the needed data. + * If you perform a range read that is, in total, much more than 1 kB, try to restrict your range as much as you can while still retrieving the needed data. * Structure keys so that no single key needs to be updated too frequently, which can cause transaction conflicts. - * If a key is updated more than 10-100 times per second, try to split it into multiple keys. - * For example, if a key is storing a counter, split the counter into N separate counters that are randomly incremented by clients. The total value of the counter can then read by adding up the N individual ones. + * If a key is updated more than 10-100 times per second, try to split it into multiple keys. + * For example, if a key is storing a counter, split the counter into N separate counters that are randomly incremented by clients. The total value of the counter can then read by adding up the N individual ones. * Keep key sizes small. - * Try to keep key sizes below 1 kB. (Performance will be best with key sizes below 32 bytes and *cannot* be more than 10 kB.) - * When using the tuple layer to encode keys (as is recommended), select short strings or small integers for tuple elements. Small integers will encode to just two bytes. - * If your key sizes are above 1 kB, try either to move data from the key to the value, split the key into multiple keys, or encode the parts of the key more efficiently (remembering to preserve any important ordering). + * Try to keep key sizes below 1 kB. (Performance will be best with key sizes below 32 bytes and *cannot* be more than 10 kB.) + * When using the tuple layer to encode keys (as is recommended), select short strings or small integers for tuple elements. Small integers will encode to just two bytes. + * If your key sizes are above 1 kB, try either to move data from the key to the value, split the key into multiple keys, or encode the parts of the key more efficiently (remembering to preserve any important ordering). * Keep value sizes moderate. - * Try to keep value sizes below 10 kB. (Value sizes *cannot* be more than 100 kB.) - * If your value sizes are above 10 kB, consider splitting the value across multiple keys. - * If you read values with sizes above 1 kB but use only a part of each value, consider splitting the values using multiple keys. - * If you frequently perform individual reads on a set of values that total to fewer than 200 bytes, try either to combine the values into a single value or to store the values in adjacent keys and use a range read. + * Try to keep value sizes below 10 kB. (Value sizes *cannot* be more than 100 kB.) + * If your value sizes are above 10 kB, consider splitting the value across multiple keys. + * If you read values with sizes above 1 kB but use only a part of each value, consider splitting the values using multiple keys. + * If you frequently perform individual reads on a set of values that total to fewer than 200 bytes, try either to combine the values into a single value or to store the values in adjacent keys and use a range read. Large Values and Blobs ---------------------- diff --git a/documentation/sphinx/source/old-release-notes/release-notes-100.rst b/documentation/sphinx/source/old-release-notes/release-notes-100.rst index e313cc9fc1..e82bf84069 100644 --- a/documentation/sphinx/source/old-release-notes/release-notes-100.rst +++ b/documentation/sphinx/source/old-release-notes/release-notes-100.rst @@ -5,7 +5,7 @@ Release Notes 1.0.1 ===== - * Fix segmentation fault in client when there are a very large number of dependent operations in a transaction and certain errors occur. +* Fix segmentation fault in client when there are a very large number of dependent operations in a transaction and certain errors occur. 1.0.0 ===== @@ -21,34 +21,34 @@ There are only minor technical differences between this release and the 0.3.0 re Java ---- - * ``clear(Range)`` replaces the now deprecated ``clearRangeStartsWith()``. +* ``clear(Range)`` replaces the now deprecated ``clearRangeStartsWith()``. Python ------ - * Windows installer supports Python 3. +* Windows installer supports Python 3. Node and Ruby ------------- - * String option parameters are converted to UTF-8. +* String option parameters are converted to UTF-8. All --- - * API version updated to 100. See the :ref:`API version upgrade guide ` for upgrade details. - * Runs on Mac OS X 10.7. - * Improvements to installation packages, including package paths and directory modes. - * Eliminated cases of excessive resource usage in the locality API. - * Watches are disabled when read-your-writes functionality is disabled. - * Fatal error paths now call ``_exit()`` instead instead of ``exit()``. +* API version updated to 100. See the :ref:`API version upgrade guide ` for upgrade details. +* Runs on Mac OS X 10.7. +* Improvements to installation packages, including package paths and directory modes. +* Eliminated cases of excessive resource usage in the locality API. +* Watches are disabled when read-your-writes functionality is disabled. +* Fatal error paths now call ``_exit()`` instead instead of ``exit()``. Fixes ----- - * A few Python API entry points failed to respect the ``as_foundationdb_key()`` convenience interface. - * ``fdbcli`` could print commit version numbers incorrectly in Windows. - * Multiple watches set on the same key were not correctly triggered by a subsequent write in the same transaction. +* A few Python API entry points failed to respect the ``as_foundationdb_key()`` convenience interface. +* ``fdbcli`` could print commit version numbers incorrectly in Windows. +* Multiple watches set on the same key were not correctly triggered by a subsequent write in the same transaction. Earlier release notes --------------------- diff --git a/documentation/sphinx/source/tls.rst b/documentation/sphinx/source/tls.rst index d527f8887c..bfdac3fc88 100644 --- a/documentation/sphinx/source/tls.rst +++ b/documentation/sphinx/source/tls.rst @@ -128,9 +128,9 @@ Certificate file default location The default behavior when the certificate or key file is not specified is to look for a file named ``fdb.pem`` in the current working directory. If this file is not present, an attempt is made to load a file from a system-dependent location as follows: - * Linux: ``/etc/foundationdb/fdb.pem`` - * macOS: ``/usr/local/etc/foundationdb/fdb.pem`` - * Windows: ``C:\ProgramData\foundationdb\fdb.pem`` +* Linux: ``/etc/foundationdb/fdb.pem`` +* macOS: ``/usr/local/etc/foundationdb/fdb.pem`` +* Windows: ``C:\ProgramData\foundationdb\fdb.pem`` Default Peer Verification ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -152,9 +152,9 @@ Automatic TLS certificate refresh The TLS certificate will be automatically refreshed on a configurable cadence. The server will inspect the CA, certificate, and key files in the specified locations periodically, and will begin using the new versions if following criterion were met: - * They are changed, judging by the last modified time. - * They are valid certificates. - * The key file matches the certificate file. +* They are changed, judging by the last modified time. +* They are valid certificates. +* The key file matches the certificate file. The refresh rate is controlled by ``--knob_tls_cert_refresh_delay_seconds``. Setting it to 0 will disable the refresh. From 648040b070f32483753dcbf241edc7ec984d5fda Mon Sep 17 00:00:00 2001 From: Steve Atherton Date: Sat, 22 Feb 2020 20:21:06 -0800 Subject: [PATCH 0683/1604] Removed versioned records from the mutation buffer and commit path because support for this was incomplete and has high overhead costs when only exact committed versions are being read. The BTree could still contain versioned records in the future as the read cursors still support this, but some optimizations were added for when all internal records are at version 0 which is the case and has been for quite some time. Mutation buffer now stores keys and values in one arena per buffer. --- fdbserver/VersionedBTree.actor.cpp | 491 +++++++++++++---------------- 1 file changed, 221 insertions(+), 270 deletions(-) diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index d8f9fc55ae..f6b6780346 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -2812,53 +2812,17 @@ public: // A write shall not become durable until the following call to commit() begins, and shall be durable once the following call to commit() returns void set(KeyValueRef keyValue) { ++counts.sets; - SingleKeyMutationsByVersion &changes = insertMutationBoundary(keyValue.key)->second.startKeyMutations; - - if(singleVersion) { - if(changes.empty()) { - changes[0] = SingleKeyMutation(keyValue.value); - } - else { - changes.begin()->second = SingleKeyMutation(keyValue.value); - } - } - else { - // Add the set if the changes set is empty or the last entry isn't a set to exactly the same value - if(changes.empty() || !changes.rbegin()->second.equalToSet(keyValue.value)) { - changes[m_writeVersion] = SingleKeyMutation(keyValue.value); - } - } + m_pBuffer->insertMutationBoundary(keyValue.key)->second.setBoundaryValue(ValueRef(m_pBuffer->arena, keyValue.value)); } - void clear(KeyRangeRef range) { + + void clear(KeyRangeRef clearedRange) { ++counts.clears; - MutationBufferT::iterator iBegin = insertMutationBoundary(range.begin); - MutationBufferT::iterator iEnd = insertMutationBoundary(range.end); + MutationBuffer::MutationsT::iterator iBegin = m_pBuffer->insertMutationBoundary(clearedRange.begin); + MutationBuffer::MutationsT::iterator iEnd = m_pBuffer->insertMutationBoundary(clearedRange.end); - // In single version mode, clear all pending updates in the affected range - if(singleVersion) { - RangeMutation &range = iBegin->second; - range.startKeyMutations.clear(); - range.startKeyMutations[0] = SingleKeyMutation(); - range.rangeClearVersion = 0; - ++iBegin; - m_pBuffer->erase(iBegin, iEnd); - } - else { - // For each boundary in the cleared range - while(iBegin != iEnd) { - RangeMutation &range = iBegin->second; - - // Set the rangeClearedVersion if not set - if(!range.rangeClearVersion.present()) - range.rangeClearVersion = m_writeVersion; - - // Add a clear to the startKeyMutations map if it's empty or the last item is not a clear - if(range.startKeyMutations.empty() || !range.startKeyMutations.rbegin()->second.isClear()) - range.startKeyMutations[m_writeVersion] = SingleKeyMutation(); - - ++iBegin; - } - } + iBegin->second.clearAll(); + ++iBegin; + m_pBuffer->mutations.erase(iBegin, iEnd); } void mutate(int op, StringRef param1, StringRef param2) NOT_IMPLEMENTED @@ -2885,13 +2849,12 @@ public: return m_lastCommittedVersion; } - VersionedBTree(IPager2 *pager, std::string name, bool singleVersion = false) + VersionedBTree(IPager2 *pager, std::string name) : m_pager(pager), m_writeVersion(invalidVersion), m_lastCommittedVersion(invalidVersion), m_pBuffer(nullptr), - m_name(name), - singleVersion(singleVersion) + m_name(name) { m_init = init_impl(this); m_latestCommit = m_init; @@ -3027,17 +2990,14 @@ public: Reference readAtVersion(Version v) { // Only committed versions can be read. - Version recordVersion = singleVersion ? 0 : v; ASSERT(v <= m_lastCommittedVersion); - if(singleVersion) { - ASSERT(v == m_lastCommittedVersion); - } Reference snapshot = m_pager->getReadSnapshot(v); - // Snapshot will continue to hold the metakey value memory + // This is a ref because snapshot will continue to hold the metakey value memory KeyRef m = snapshot->getMetaKey(); - return Reference(new Cursor(snapshot, ((MetaKey *)m.begin())->root.get(), recordVersion)); + // Currently all internal records generated in the write path are at version 0 + return Reference(new Cursor(snapshot, ((MetaKey *)m.begin())->root.get(), (Version)0)); } // Must be nondecreasing @@ -3048,13 +3008,6 @@ public: // When starting a new mutation buffer its start version must be greater than the last write version ASSERT(v > m_writeVersion); m_pBuffer = &m_mutationBuffers[v]; - - // Create range representing the entire keyspace. This reduces edge cases to applying mutations - // because now all existing keys are within some range in the mutation map. - (*m_pBuffer)[dbBegin.key] = RangeMutation(); - // Setting the dbEnd key to be cleared prevents having to treat a range clear to dbEnd as a special - // case in order to avoid traversing down the rightmost edge of the tree. - (*m_pBuffer)[dbEnd.key].startKeyMutations[0] = SingleKeyMutation(); } else { // It's OK to set the write version to the same version repeatedly so long as m_pBuffer is not null @@ -3114,10 +3067,6 @@ public: return destroyAndCheckSanity_impl(this); } - bool isSingleVersion() const { - return singleVersion; - } - private: struct VersionAndChildrenRef { VersionAndChildrenRef(Version v, VectorRef children, RedwoodRecordRef upperBound) @@ -3281,47 +3230,102 @@ private: } }; - // Represents mutations on a single key and a possible clear to a range that begins - // immediately after that key - typedef std::map SingleKeyMutationsByVersion; struct RangeMutation { - // Mutations for exactly the start key - SingleKeyMutationsByVersion startKeyMutations; - // A clear range version, if cleared, for the range starting immediately AFTER the start key - Optional rangeClearVersion; - - bool keyCleared() const { - return startKeyMutations.size() == 1 && startKeyMutations.begin()->second.isClear(); + RangeMutation() : boundaryChanged(false), clearAfterBoundary(false) { } - bool keyChanged() const { - return !startKeyMutations.empty(); - } + bool boundaryChanged; + Optional boundaryValue; // Not present means cleared + bool clearAfterBoundary; - bool rangeCleared() const { - return rangeClearVersion.present(); + bool boundaryCleared() const { + return boundaryChanged && !boundaryValue.present(); } // Returns true if this RangeMutation doesn't actually mutate anything bool noChanges() const { - return !rangeClearVersion.present() && startKeyMutations.empty(); + return !boundaryChanged && !clearAfterBoundary; + } + + void clearBoundary() { + boundaryChanged = true; + boundaryValue.reset(); + } + + void clearAll() { + clearBoundary(); + clearAfterBoundary = true; + } + + void setBoundaryValue(ValueRef v) { + boundaryChanged = true; + boundaryValue = v; + } + + bool boundarySet() const { + return boundaryChanged && boundaryValue.present(); } std::string toString() const { - std::string result; - result.append("rangeClearVersion: "); - if(rangeClearVersion.present()) - result.append(format("%" PRId64 "", rangeClearVersion.get())); - else - result.append(""); - result.append(" startKeyMutations: "); - for(SingleKeyMutationsByVersion::value_type const &m : startKeyMutations) - result.append(format("[%" PRId64 " => %s] ", m.first, m.second.toString().c_str())); - return result; + return format("boundaryChanged=%d clearAfterBoundary=%d boundaryValue=%s", boundaryChanged, clearAfterBoundary, ::toString(boundaryValue).c_str()); } }; - typedef std::map MutationBufferT; + struct MutationBuffer { + MutationBuffer() { + // Create range representing the entire keyspace. This reduces edge cases to applying mutations + // because now all existing keys are within some range in the mutation map. + mutations[dbBegin.key]; + // Setting the dbEnd key to be cleared prevents having to treat a range clear to dbEnd as a special + // case in order to avoid traversing down the rightmost edge of the tree. + mutations[dbEnd.key].clearBoundary(); + } + + Arena arena; + typedef std::map MutationsT; + typedef MutationsT::iterator iterator; + typedef MutationsT::const_iterator const_iterator; + MutationsT mutations; + + const_iterator upper_bound(KeyRef k) const { + return mutations.upper_bound(k); + } + + const_iterator lower_bound(KeyRef k) const { + return mutations.lower_bound(k); + } + + // Find or create a mutation buffer boundary for bound and return an iterator to it + iterator insertMutationBoundary(KeyRef boundary) { + // Find the first split point in buffer that is >= key + // Since the initial state of the mutation buffer contains the range '' through + // the maximum possible key, our search had to have found something so we + // can assume the iterator is valid. + iterator ib = mutations.lower_bound(boundary); + + // If we found the boundary we are looking for, return its iterator + if(ib->first == boundary) { + return ib; + } + + // ib is our insert hint. Copy boundary into arena and insert boundary into buffer + boundary = KeyRef(arena, boundary); + ib = mutations.insert(ib, {boundary, RangeMutation()}); + + // ib is certainly > begin() because it is guaranteed that the empty string + // boundary exists and the only way to have found that is to look explicitly + // for it in which case we would have returned above. + iterator iPrevious = ib; + --iPrevious; + // If the range we just divided was being cleared, then the dividing boundary key and range after it must also be cleared + if(iPrevious->second.clearAfterBoundary) { + ib->second.clearAll(); + } + + return ib; + } + + }; /* Mutation Buffer Overview * @@ -3373,8 +3377,8 @@ private: */ IPager2 *m_pager; - MutationBufferT *m_pBuffer; - std::map m_mutationBuffers; + MutationBuffer *m_pBuffer; + std::map m_mutationBuffers; Version m_writeVersion; Version m_lastCommittedVersion; @@ -3382,7 +3386,6 @@ private: Future m_latestCommit; Future m_init; std::string m_name; - bool singleVersion; // MetaKey changes size so allocate space for it to expand into union { @@ -3393,38 +3396,6 @@ private: LazyDeleteQueueT m_lazyDeleteQueue; int m_maxPartSize; - // Find or create a mutation buffer boundary for bound and return an iterator to it - MutationBufferT::iterator insertMutationBoundary(Key boundary) { - ASSERT(m_pBuffer != nullptr); - - // Find the first split point in buffer that is >= key - MutationBufferT::iterator ib = m_pBuffer->lower_bound(boundary); - - // Since the initial state of the mutation buffer contains the range '' through - // the maximum possible key, our search had to have found something. - ASSERT(ib != m_pBuffer->end()); - - // If we found the boundary we are looking for, return its iterator - if(ib->first == boundary) { - return ib; - } - - // ib is our insert hint. Insert the new boundary and set ib to its entry - ib = m_pBuffer->insert(ib, {boundary, RangeMutation()}); - - // ib is certainly > begin() because it is guaranteed that the empty string - // boundary exists and the only way to have found that is to look explicitly - // for it in which case we would have returned above. - MutationBufferT::iterator iPrevious = ib; - --iPrevious; - if(iPrevious->second.rangeClearVersion.present()) { - ib->second.rangeClearVersion = iPrevious->second.rangeClearVersion; - ib->second.startKeyMutations[iPrevious->second.rangeClearVersion.get()] = SingleKeyMutation(); - } - - return ib; - } - // Writes entries to 1 or more pages and return a vector of boundary keys with their IPage(s) ACTOR static Future>> writePages(VersionedBTree *self, bool minimalBoundaries, const RedwoodRecordRef *lowerBound, const RedwoodRecordRef *upperBound, VectorRef entries, int height, Version v, BTreePageID previousID) { ASSERT(entries.size() > 0); @@ -3807,9 +3778,9 @@ private: // iMutationBoundaryEnd is least boundary >= upperBound->key ACTOR static Future> commitSubtree( VersionedBTree *self, - MutationBufferT *mutationBuffer, - //MutationBufferT::const_iterator iMutationBoundary, // = mutationBuffer->upper_bound(lowerBound->key); --iMutationBoundary; - //MutationBufferT::const_iterator iMutationBoundaryEnd, // = mutationBuffer->lower_bound(upperBound->key); + MutationBuffer *mutationBuffer, + //MutationBuffer::const_iterator iMutationBoundary, // = mutationBuffer->upper_bound(lowerBound->key); --iMutationBoundary; + //MutationBuffer::const_iterator iMutationBoundaryEnd, // = mutationBuffer->lower_bound(upperBound->key); Reference snapshot, BTreePageID rootID, bool isLeaf, @@ -3825,6 +3796,7 @@ private: context = format("CommitSubtree(root=%s): ", toString(rootID).c_str()); } + state Version writeVersion = self->getLastCommittedVersion() + 1; state Standalone results; debug_printf("%s lower=%s upper=%s\n", context.c_str(), lowerBound->toString().c_str(), upperBound->toString().c_str()); @@ -3832,9 +3804,9 @@ private: self->counts.commitToPageStart++; // Find the slice of the mutation buffer that is relevant to this subtree - state MutationBufferT::const_iterator iMutationBoundary = mutationBuffer->upper_bound(lowerBound->key); + state MutationBuffer::const_iterator iMutationBoundary = mutationBuffer->upper_bound(lowerBound->key); --iMutationBoundary; - state MutationBufferT::const_iterator iMutationBoundaryEnd = mutationBuffer->lower_bound(upperBound->key); + state MutationBuffer::const_iterator iMutationBoundaryEnd = mutationBuffer->lower_bound(upperBound->key); if(REDWOOD_DEBUG) { debug_printf("%s ---------MUTATION BUFFER SLICE ---------------------\n", context.c_str()); @@ -3857,14 +3829,13 @@ private: // If there are any changes to the one key then the entire subtree should be deleted as the changes for the key // do not go into this subtree. if(iMutationBoundary == iMutationBoundaryEnd) { - if(iMutationBoundary->second.keyChanged()) { + if(iMutationBoundary->second.boundaryChanged) { debug_printf("%s lower and upper bound key/version match and key is modified so deleting page, returning %s\n", context.c_str(), toString(results).c_str()); - Version firstKeyChangeVersion = self->singleVersion ? self->getLastCommittedVersion() + 1 : iMutationBoundary->second.startKeyMutations.begin()->first; if(isLeaf) { - self->freeBtreePage(rootID, firstKeyChangeVersion); + self->freeBtreePage(rootID, writeVersion); } else { - self->m_lazyDeleteQueue.pushBack(LazyDeleteQueueEntry{firstKeyChangeVersion, rootID}); + self->m_lazyDeleteQueue.pushBack(LazyDeleteQueueEntry{writeVersion, rootID}); } return results; } @@ -3877,13 +3848,13 @@ private: // If one mutation range covers the entire subtree, then check if the entire subtree is modified, // unmodified, or possibly/partially modified. - MutationBufferT::const_iterator iMutationBoundaryNext = iMutationBoundary; + MutationBuffer::const_iterator iMutationBoundaryNext = iMutationBoundary; ++iMutationBoundaryNext; if(iMutationBoundaryNext == iMutationBoundaryEnd) { // Cleared means the entire range covering the subtree was cleared. It is assumed true // if the range starting after the lower mutation boundary was cleared, and then proven false // below if possible. - bool cleared = iMutationBoundary->second.rangeCleared(); + bool cleared = iMutationBoundary->second.clearAfterBoundary; // Unchanged means the entire range covering the subtree was unchanged, it is assumed to be the // opposite of cleared() and then proven false below if possible. bool unchanged = !cleared; @@ -3893,12 +3864,12 @@ private: // that key is being changed or cleared affects this subtree. if(iMutationBoundary->first == lowerBound->key) { // If subtree will be cleared (so far) but the lower boundary key is not cleared then the subtree is not cleared - if(cleared && !iMutationBoundary->second.keyCleared()) { + if(cleared && !iMutationBoundary->second.boundaryCleared()) { cleared = false; debug_printf("%s cleared=%d unchanged=%d\n", context.c_str(), cleared, unchanged); } // If the subtree looked unchanged (so far) but the lower boundary is is changed then the subtree is changed - if(unchanged && iMutationBoundary->second.keyChanged()) { + if(unchanged && iMutationBoundary->second.boundaryChanged) { unchanged = false; debug_printf("%s cleared=%d unchanged=%d\n", context.c_str(), cleared, unchanged); } @@ -3909,7 +3880,7 @@ private: if((cleared || unchanged) && iMutationBoundaryEnd->first == upperBound->key) { // If the key is being changed then the records in this subtree with the same key must be removed // so the subtree is definitely not unchanged, though it may be cleared to achieve the same effect. - if(iMutationBoundaryEnd->second.keyChanged()) { + if(iMutationBoundaryEnd->second.boundaryChanged) { unchanged = false; debug_printf("%s cleared=%d unchanged=%d\n", context.c_str(), cleared, unchanged); } @@ -3934,12 +3905,11 @@ private: // If subtree is cleared if(cleared) { debug_printf("%s %s cleared, deleting it, returning %s\n", context.c_str(), isLeaf ? "Page" : "Subtree", toString(results).c_str()); - Version clearVersion = self->singleVersion ? self->getLastCommittedVersion() + 1 : iMutationBoundary->second.rangeClearVersion.get(); if(isLeaf) { - self->freeBtreePage(rootID, clearVersion); + self->freeBtreePage(rootID, writeVersion); } else { - self->m_lazyDeleteQueue.pushBack(LazyDeleteQueueEntry{clearVersion, rootID}); + self->m_lazyDeleteQueue.pushBack(LazyDeleteQueueEntry{writeVersion, rootID}); } return results; } @@ -3952,7 +3922,6 @@ private: debug_printf("%s commitSubtree(): %s\n", context.c_str(), btPage->toString(false, rootID, snapshot->getVersion(), decodeLowerBound, decodeUpperBound).c_str()); state BTreePage::BinaryTree::Cursor cursor; - state Version writeVersion; if(REDWOOD_DEBUG) { debug_printf("%s ---------MUTATION BUFFER SLICE ---------------------\n", context.c_str()); @@ -4011,36 +3980,24 @@ private: while(iMutationBoundary != iMutationBoundaryEnd) { debug_printf("%s New mutation boundary: '%s': %s\n", context.c_str(), printable(iMutationBoundary->first).c_str(), iMutationBoundary->second.toString().c_str()); - SingleKeyMutationsByVersion::const_iterator iMutations; - - // For the first mutation boundary only, if the boundary key is less than the lower bound for the page - // then skip startKeyMutations for this boundary, we're only processing this mutation range here to apply - // a possible clear to existing data. - if(firstMutationBoundary && iMutationBoundary->first < lowerBound->key) { - iMutations = iMutationBoundary->second.startKeyMutations.end(); - } - else { - iMutations = iMutationBoundary->second.startKeyMutations.begin(); - } + // Apply the change to the mutation buffer start boundary key only if + // - there actually is a change (whether a set or a clear, old records are to be removed) + // - either this is not the first boundary or it is but its key matches our lower bound key + bool applyBoundaryChange = iMutationBoundary->second.boundaryChanged && (!firstMutationBoundary || iMutationBoundary->first >= lowerBound->key); firstMutationBoundary = false; - - SingleKeyMutationsByVersion::const_iterator iMutationsEnd = iMutationBoundary->second.startKeyMutations.end(); - - // Iterate over old versions of the mutation boundary key, outputting if necessary - bool boundaryKeyWritten = false; + + // Iterate over records for the mutation boundary key, keep them unless the boundary key was changed or we are not applying it while(cursor.valid() && cursor.get().key == iMutationBoundary->first) { - // If not in single version mode or there were no changes to the key - if(!self->singleVersion || iMutationBoundary->second.noChanges()) { - // If not updating, add to the output set, otherwise do nothing + // If there were no changes to the key or we're not applying it + if(!applyBoundaryChange) { + // If not updating, add to the output set, otherwise skip ahead past the records for the mutation boundary if(!updating) { merged.push_back(merged.arena(), cursor.get()); debug_printf("%s Added %s [existing, boundary start]\n", context.c_str(), cursor.get().toString().c_str()); } - boundaryKeyWritten = true; cursor.moveNext(); } else { - ASSERT(self->singleVersion); changesMade = true; // If updating, erase from the page, otherwise do not add to the output set if(updating) { @@ -4048,7 +4005,7 @@ private: cursor.erase(); } else { - debug_printf("%s Skipped %s [existing, boundary start, singleVersion mode]\n", context.c_str(), cursor.get().toString().c_str()); + debug_printf("%s Skipped %s [existing, boundary start]\n", context.c_str(), cursor.get().toString().c_str()); cursor.moveNext(); } } @@ -4056,54 +4013,40 @@ private: constexpr int maxHeightAllowed = 8; - // TODO: If a mutation set is equal to the previous existing value of the key, maybe don't write it. - // Output mutations for the mutation boundary start key - while(iMutations != iMutationsEnd) { - const SingleKeyMutation &m = iMutations->second; - if(m.isClear() || m.value.size() <= self->m_maxPartSize) { - // If the boundary key was not yet written to the merged list then clears can be skipped. - // Note that in a more complex scenario where there are multiple sibling pages for the same key, with different - // versions and/or part numbers, this is still a valid thing to do. This is because a changing boundary - // key (set or clear) will result in any instances (different versions, split parts) of this key - // on sibling pages to the left of this page to be removed, so an explicit clear need only be stored - // if a record with the mutation boundary key was already written to this page. - if(!boundaryKeyWritten && iMutations->second.isClear()) { - debug_printf("%s Skipped %s [mutation, unnecessary boundary key clear]\n", context.c_str(), m.toRecord(iMutationBoundary->first, iMutations->first).toString().c_str()); - } - else { - RedwoodRecordRef rec = m.toRecord(iMutationBoundary->first, iMutations->first); - // If updating, add to the page, else add to the output set - if(updating) { - if(cursor.mirror->insert(rec, skipLen, maxHeightAllowed)) { - debug_printf("%s Inserted non-split %s [mutation, boundary start]\n", context.c_str(), rec.toString().c_str()); - } - else { - debug_printf("%s Inserted failed for non-split %s [mutation, boundary start]\n", context.c_str(), rec.toString().c_str()); - switchToLinearMerge(); - } - } - if(!updating) { - merged.push_back(merged.arena(), rec); - debug_printf("%s Added non-split %s [mutation, boundary start]\n", context.c_str(), rec.toString().c_str()); - } + // Write the new record(s) for the mutation boundary start key if its value has been set + // Clears of this key will have been processed above by not being erased from the updated page or excluded from the merge output + if(applyBoundaryChange && iMutationBoundary->second.boundarySet()) { + RedwoodRecordRef rec(iMutationBoundary->first, 0, iMutationBoundary->second.boundaryValue.get()); + changesMade = true; - changesMade = true; - boundaryKeyWritten = true; + if(rec.value.get().size() <= self->m_maxPartSize) { + // If updating, add to the page, else add to the output set + if(updating) { + if(cursor.mirror->insert(rec, skipLen, maxHeightAllowed)) { + debug_printf("%s Inserted non-split %s [mutation, boundary start]\n", context.c_str(), rec.toString().c_str()); + } + else { + debug_printf("%s Inserted failed for non-split %s [mutation, boundary start]\n", context.c_str(), rec.toString().c_str()); + switchToLinearMerge(); + } } + if(!updating) { + merged.push_back(merged.arena(), rec); + debug_printf("%s Added non-split %s [mutation, boundary start]\n", context.c_str(), rec.toString().c_str()); + } + } else { - changesMade = true; - int bytesLeft = m.value.size(); + int bytesLeft = rec.value.get().size(); int start = 0; - RedwoodRecordRef whole(iMutationBoundary->first, iMutations->first, m.value); while(bytesLeft > 0) { int partSize = std::min(bytesLeft, self->m_maxPartSize); - // Don't copy the value chunk because this page will stay in memory until after we've built new version(s) of it - RedwoodRecordRef rec = whole.split(start, partSize); + // Don't copy the value chunk because mutation buffer will stay in memory until after the new page is written + RedwoodRecordRef part = rec.split(start, partSize); bytesLeft -= partSize; if(updating) { - if(cursor.mirror->insert(rec, skipLen, maxHeightAllowed)) { + if(cursor.mirror->insert(part, skipLen, maxHeightAllowed)) { debug_printf("%s Inserted split %s [mutation, boundary start] bytesLeft %d\n", context.c_str(), rec.toString().c_str(), bytesLeft); } else { @@ -4113,19 +4056,17 @@ private: } if(!updating) { - merged.push_back(merged.arena(), rec); + merged.push_back(merged.arena(), part); debug_printf("%s Added split %s [mutation, boundary start] bytesLeft %d\n", context.c_str(), rec.toString().c_str(), bytesLeft); } start += partSize; } - boundaryKeyWritten = true; } - ++iMutations; } - // Get the clear version for this range, which is the last thing that we need from it, - Optional clearRangeVersion = iMutationBoundary->second.rangeClearVersion; + // Before advancing the iterator, get whether or not the records in the following range must be removed + bool remove = iMutationBoundary->second.clearAfterBoundary; // Advance to the next boundary because we need to know the end key for the current range. ++iMutationBoundary; if(iMutationBoundary == iMutationBoundaryEnd) { @@ -4135,7 +4076,6 @@ private: debug_printf("%s Mutation range end: '%s'\n", context.c_str(), printable(iMutationBoundary->first).c_str()); // Now handle the records up through but not including the next mutation boundary key - bool remove = self->singleVersion && clearRangeVersion.present(); RedwoodRecordRef end(iMutationBoundary->first); // If the records are being removed and we're not doing an in-place update @@ -4155,7 +4095,7 @@ private: // linear merge than the visit is to add them to the output set. while(cursor.valid() && cursor.get().compare(end, skipLen) < 0) { if(updating) { - debug_printf("%s Erasing %s [existing, boundary start, singleVersion mode]\n", context.c_str(), cursor.get().toString().c_str()); + debug_printf("%s Erasing %s [existing, boundary start]\n", context.c_str(), cursor.get().toString().c_str()); cursor.erase(); changesMade = true; } @@ -4171,7 +4111,7 @@ private: // If there are still more records, they have the same key as the end boundary if(cursor.valid()) { // If the end boundary is changing, we must remove the remaining records in this page - bool remove = iMutationBoundaryEnd->second.keyChanged(); + bool remove = iMutationBoundaryEnd->second.boundaryChanged; if(remove) { changesMade = true; } @@ -4310,9 +4250,6 @@ private: } } - // TODO: Either handle multi-versioned results or change commitSubtree interface to return a single child set. - ASSERT(self->singleVersion); - writeVersion = self->getLastCommittedVersion() + 1; // All of the things added to pageBuilder will exist in the arenas inside futureChildren or will be upperBound BTreePage::BinaryTree::Cursor c = getCursor(page); c.moveFirst(); @@ -4360,7 +4297,7 @@ private: } ACTOR static Future commit_impl(VersionedBTree *self) { - state MutationBufferT *mutations = self->m_pBuffer; + state MutationBuffer *mutations = self->m_pBuffer; // No more mutations are allowed to be written to this mutation buffer we will commit // at m_writeVersion, which we must save locally because it could change during commit. @@ -4561,6 +4498,14 @@ private: return present() && pageCursor->cursor.get().version <= v; } + // This is to enable an optimization for the case where all internal records are at the + // same version and there are no implicit clears + // *this MUST be valid() + bool presentAtExactVersionUnsharded(Version v) const { + auto const &rec = pageCursor->cursor.get(); + return rec.value.present() && rec.version == v && rec.chunk.total == 0; + } + // Returns true if cursor position is present() and has an effective version <= v bool validAtVersion(Version v) { return valid() && pageCursor->cursor.get().version <= v; @@ -4737,8 +4682,8 @@ private: // KeyValueRefs returned become invalid once the cursor is moved class Cursor : public IStoreCursor, public ReferenceCounted, public FastAllocated, NonCopyable { public: - Cursor(Reference pageSource, BTreePageID root, Version recordVersion) - : m_version(recordVersion), + Cursor(Reference pageSource, BTreePageID root, Version internalRecordVersion) + : m_version(internalRecordVersion), m_cur1(pageSource, root), m_cur2(m_cur1) { @@ -4922,9 +4867,10 @@ private: self->m_kv.reset(); while(self->m_cur1.valid()) { - if(self->m_cur1.presentAtVersion(self->m_version) && + if(self->m_cur1.presentAtExactVersionUnsharded(self->m_version) || + (self->m_cur1.presentAtVersion(self->m_version) && (!self->m_cur2.validAtVersion(self->m_version) || - self->m_cur2.get().key != self->m_cur1.get().key) + self->m_cur2.get().key != self->m_cur1.get().key)) ) { wait(readFullKVPair(self)); return Void(); @@ -5004,7 +4950,7 @@ public: KeyValueStoreRedwoodUnversioned(std::string filePrefix, UID logID) : m_filePrefix(filePrefix) { // TODO: This constructor should really just take an IVersionedStore IPager2 *pager = new DWALPager(4096, filePrefix, 0); - m_tree = new VersionedBTree(pager, filePrefix, true); + m_tree = new VersionedBTree(pager, filePrefix); m_init = catchError(init_impl(this)); } @@ -5319,8 +5265,8 @@ ACTOR Future verifyRange(VersionedBTree *btree, Key start, Key end, Version debug_printf("VerifyRangeReverse(@%" PRId64 ", %s, %s): start\n", v, start.toString().c_str(), end.toString().c_str()); - // Randomly use a new cursor for the reverse range read but only if version history is available - if(!btree->isSingleVersion() && deterministicRandom()->coinflip()) { + // Randomly use a new cursor at the same version for the reverse range read, if the version is still available for opening new cursors + if(v >= btree->getOldestVersion() && deterministicRandom()->coinflip()) { cur = btree->readAtVersion(v); } @@ -5365,20 +5311,18 @@ ACTOR Future verifyRange(VersionedBTree *btree, Key start, Key end, Version return errors; } -ACTOR Future verifyAll(VersionedBTree *btree, Version maxCommittedVersion, std::map, Optional> *written, int *pErrorCount) { - // Read back every key at every version set or cleared and verify the result. +// Verify the result of point reads for every set or cleared key at the given version +ACTOR Future seekAll(VersionedBTree *btree, Version v, std::map, Optional> *written, int *pErrorCount) { state std::map, Optional>::const_iterator i = written->cbegin(); state std::map, Optional>::const_iterator iEnd = written->cend(); state int errors = 0; + state Reference cur = btree->readAtVersion(v); while(i != iEnd) { state std::string key = i->first.first; state Version ver = i->first.second; - if(ver <= maxCommittedVersion) { + if(ver == v) { state Optional val = i->second; - - state Reference cur = btree->readAtVersion(ver); - debug_printf("Verifying @%" PRId64 " '%s'\n", ver, key.c_str()); state Arena arena; wait(cur->findEqual(KeyRef(arena, key))); @@ -5408,39 +5352,52 @@ ACTOR Future verifyAll(VersionedBTree *btree, Version maxCommittedVersion, } ACTOR Future verify(VersionedBTree *btree, FutureStream vStream, std::map, Optional> *written, int *pErrorCount, bool serial) { - state Future vall; - state Future vrange; + state Future fRangeAll; + state Future fRangeRandom; + state Future fSeekAll; + + // Queue of committed versions still readable from btree + state std::deque committedVersions; try { loop { state Version v = waitNext(vStream); + committedVersions.push_back(v); - if(btree->isSingleVersion()) { - v = btree->getLastCommittedVersion(); - debug_printf("Verifying at latest committed version %" PRId64 "\n", v); - vall = verifyRange(btree, LiteralStringRef(""), LiteralStringRef("\xff\xff"), v, written, pErrorCount); - if(serial) { - wait(success(vall)); - } - vrange = verifyRange(btree, randomKV().key, randomKV().key, v, written, pErrorCount); - if(serial) { - wait(success(vrange)); - } + // Remove expired versions + while(!committedVersions.empty() && committedVersions.front() < btree->getOldestVersion()) { + committedVersions.pop_front(); } - else { - debug_printf("Verifying through version %" PRId64 "\n", v); - vall = verifyAll(btree, v, written, pErrorCount); - if(serial) { - wait(success(vall)); - } - vrange = verifyRange(btree, randomKV().key, randomKV().key, deterministicRandom()->randomInt(1, v + 1), written, pErrorCount); - if(serial) { - wait(success(vrange)); - } - } - wait(success(vall) && success(vrange)); - debug_printf("Verified through version %" PRId64 ", %d errors\n", v, *pErrorCount); + // Choose a random committed version, or sometimes the latest (which could be ahead of the latest version from vStream) + v = (committedVersions.empty() || deterministicRandom()->coinflip()) ? btree->getLastCommittedVersion() : committedVersions[deterministicRandom()->randomInt(0, committedVersions.size())]; + debug_printf("Using committed version %" PRId64 "\n", v); + // Get a cursor at v so that v doesn't get expired between the possibly serial steps below. + state Reference cur = btree->readAtVersion(v); + + debug_printf("Verifying entire key range at version %" PRId64 "\n", v); + fRangeAll = verifyRange(btree, LiteralStringRef(""), LiteralStringRef("\xff\xff"), v, written, pErrorCount); + if(serial) { + wait(success(fRangeAll)); + } + + Key begin = randomKV().key; + Key end = randomKV().key; + debug_printf("Verifying range (%s, %s) at version %" PRId64 "\n", toString(begin).c_str(), toString(end).c_str(), v); + fRangeRandom = verifyRange(btree, begin, end, v, written, pErrorCount); + if(serial) { + wait(success(fRangeRandom)); + } + + debug_printf("Verifying seeks to each changed key at version %" PRId64 "\n", v); + fSeekAll = seekAll(btree, v, written, pErrorCount); + if(serial) { + wait(success(fSeekAll)); + } + + wait(success(fRangeAll) && success(fRangeRandom) && success(fSeekAll)); + + printf("Verified through version %" PRId64 ", %d errors\n", v, *pErrorCount); if(*pErrorCount != 0) break; @@ -5459,11 +5416,8 @@ ACTOR Future randomReader(VersionedBTree *btree) { state Reference cur; loop { wait(yield()); - if(!cur || deterministicRandom()->random01() > .1) { + if(!cur || deterministicRandom()->random01() > .01) { Version v = btree->getLastCommittedVersion(); - if(!btree->isSingleVersion()) { - v = deterministicRandom()->randomInt(1, v + 1); - } cur = btree->readAtVersion(v); } @@ -6262,7 +6216,6 @@ TEST_CASE("!/redwood/correctness/btree") { state bool serialTest = deterministicRandom()->coinflip(); state bool shortTest = deterministicRandom()->coinflip(); - state bool singleVersion = true; // Multi-version mode is broken / not finished state int pageSize = shortTest ? 200 : (deterministicRandom()->coinflip() ? 4096 : deterministicRandom()->randomInt(200, 400)); @@ -6281,7 +6234,6 @@ TEST_CASE("!/redwood/correctness/btree") { printf("\n"); printf("serialTest: %d\n", serialTest); printf("shortTest: %d\n", shortTest); - printf("singleVersion: %d\n", serialTest); printf("pageSize: %d\n", pageSize); printf("maxKeySize: %d\n", maxKeySize); printf("maxValueSize: %d\n", maxValueSize); @@ -6299,7 +6251,7 @@ TEST_CASE("!/redwood/correctness/btree") { printf("Initializing...\n"); state double startTime = now(); pager = new DWALPager(pageSize, pagerFile, 0); - state VersionedBTree *btree = new VersionedBTree(pager, pagerFile, singleVersion); + state VersionedBTree *btree = new VersionedBTree(pager, pagerFile); wait(btree->init()); state std::map, Optional> written; @@ -6493,7 +6445,7 @@ TEST_CASE("!/redwood/correctness/btree") { printf("Reopening btree from disk.\n"); IPager2 *pager = new DWALPager(pageSize, pagerFile, 0); - btree = new VersionedBTree(pager, pagerFile, singleVersion); + btree = new VersionedBTree(pager, pagerFile); wait(btree->init()); Version v = btree->getLatestVersion(); @@ -6622,22 +6574,21 @@ TEST_CASE("!/redwood/performance/set") { state int pageSize = 4096; state int64_t pageCacheBytes = FLOW_KNOBS->PAGE_CACHE_4K; DWALPager *pager = new DWALPager(pageSize, pagerFile, pageCacheBytes); - state bool singleVersion = true; - state VersionedBTree *btree = new VersionedBTree(pager, pagerFile, singleVersion); + state VersionedBTree *btree = new VersionedBTree(pager, pagerFile); wait(btree->init()); state int nodeCount = 1e9; state int maxChangesPerVersion = 5000; state int64_t kvBytesTarget = 4e9; state int commitTarget = 20e6; - state int minKeyPrefixBytes = 0; + state int minKeyPrefixBytes = 25; state int maxKeyPrefixBytes = 25; - state int minValueSize = 0; - state int maxValueSize = 500; - state int maxConsecutiveRun = 10; + state int minValueSize = 1000; + state int maxValueSize = 2000; state int minConsecutiveRun = 1000; + state int maxConsecutiveRun = 2000; state char firstKeyChar = 'a'; - state char lastKeyChar = 'b'; + state char lastKeyChar = 'm'; printf("pageSize: %d\n", pageSize); printf("pageCacheBytes: %" PRId64 "\n", pageCacheBytes); From 078f85fea744079ea5146e00cba8d03f628de5e6 Mon Sep 17 00:00:00 2001 From: Steve Atherton Date: Sun, 23 Feb 2020 00:13:29 -0800 Subject: [PATCH 0684/1604] Bug fix. Pager could return an an oldest version that is no longer readable because it is being expired in the current commit cycle. --- fdbserver/VersionedBTree.actor.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index f6b6780346..49f96070e6 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -1357,9 +1357,10 @@ public: expireSnapshots(v); }; - // Get the oldest version set as of the last commit. + // Get the oldest *readable* version, which is not the same as the oldest retained version as the version + // returned could have been set as the oldest version in the pending commit Version getOldestVersion() override { - return pLastCommittedHeader->oldestVersion; + return pHeader->oldestVersion; }; // Calculate the *effective* oldest version, which can be older than the one set in the last commit since we @@ -4312,7 +4313,7 @@ private: Future previousCommit = self->m_latestCommit; self->m_latestCommit = committed.getFuture(); - // Wait for the latest commit that started to be finished. + // Wait for the latest commit to be finished. wait(previousCommit); self->m_pager->setOldestVersion(self->m_newOldestVersion); @@ -5370,7 +5371,7 @@ ACTOR Future verify(VersionedBTree *btree, FutureStream vStream, } // Choose a random committed version, or sometimes the latest (which could be ahead of the latest version from vStream) - v = (committedVersions.empty() || deterministicRandom()->coinflip()) ? btree->getLastCommittedVersion() : committedVersions[deterministicRandom()->randomInt(0, committedVersions.size())]; + v = (committedVersions.empty() || deterministicRandom()->random01() < 0.25) ? btree->getLastCommittedVersion() : committedVersions[deterministicRandom()->randomInt(0, committedVersions.size())]; debug_printf("Using committed version %" PRId64 "\n", v); // Get a cursor at v so that v doesn't get expired between the possibly serial steps below. state Reference cur = btree->readAtVersion(v); From 3618dff07b5921d8824397d1b50ef6b135fe9b66 Mon Sep 17 00:00:00 2001 From: Steve Atherton Date: Sun, 23 Feb 2020 00:21:39 -0800 Subject: [PATCH 0685/1604] Corrected statement in test output to reflect new logic. --- fdbserver/VersionedBTree.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index 49f96070e6..010e371bdd 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -5398,7 +5398,7 @@ ACTOR Future verify(VersionedBTree *btree, FutureStream vStream, wait(success(fRangeAll) && success(fRangeRandom) && success(fSeekAll)); - printf("Verified through version %" PRId64 ", %d errors\n", v, *pErrorCount); + printf("Verified version %" PRId64 ", %d errors\n", v, *pErrorCount); if(*pErrorCount != 0) break; From 252b1754b3855e1193e52ca0abecc5a4cf2f76a2 Mon Sep 17 00:00:00 2001 From: Alvin Moore Date: Sun, 23 Feb 2020 15:35:33 -0800 Subject: [PATCH 0686/1604] Updated the docker version Fixed the location of the TLS libraries --- Makefile | 4 ++-- build/Dockerfile | 6 +++--- build/docker-compose.yaml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 90d9c0d28f..1bd6442dbb 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ ifeq ($(PLATFORM),Linux) CXXFLAGS += -std=c++17 BOOST_BASEDIR ?= /opt - TLS_LIBDIR ?= /usr/local/lib + TLS_LIBDIR ?= /usr/lib64 DLEXT := so java_DLEXT := so TARGET_LIBC_VERSION ?= 2.11 @@ -65,7 +65,7 @@ else ifeq ($(PLATFORM),Darwin) .LIBPATTERNS := lib%.dylib lib%.a BOOST_BASEDIR ?= ${HOME} - TLS_LIBDIR ?= /usr/local/lib + TLS_LIBDIR ?= /usr/lib64 DLEXT := dylib java_DLEXT := jnilib else diff --git a/build/Dockerfile b/build/Dockerfile index 674ce54d40..89279ae081 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -46,12 +46,12 @@ RUN cd /tmp && curl -L https://github.com/ninja-build/ninja/archive/v1.9.0.zip - RUN cd /tmp && curl -L https://www.openssl.org/source/openssl-1.1.1d.tar.gz -o openssl.tar.gz &&\ echo "1e3a91bc1f9dfce01af26026f856e064eab4c8ee0a8f457b5ae30b40b8b711f2 openssl.tar.gz" > openssl-sha.txt &&\ sha256sum -c openssl-sha.txt && tar -xzf openssl.tar.gz &&\ - cd openssl-1.1.1d && scl enable devtoolset-8 -- ./config --prefix=/usr/local/stow/openssl CFLAGS="-fPIC -O3" --prefix=/usr/local &&\ + cd openssl-1.1.1d && scl enable devtoolset-8 -- ./config CFLAGS="-fPIC -O3" --prefix=/usr &&\ scl enable devtoolset-8 -- make -j`nproc` && scl enable devtoolset-8 -- make -j1 install &&\ cd /tmp/ && rm -rf /tmp/openssl-1.1.1d /tmp/openssl.tar.gz -LABEL version=0.1.11 -ENV DOCKER_IMAGEVER=0.1.11 +LABEL version=0.1.12 +ENV DOCKER_IMAGEVER=0.1.12 ENV JAVA_HOME=/usr/lib/jvm/java-1.8.0 ENV CC=/opt/rh/devtoolset-8/root/usr/bin/gcc ENV CXX=/opt/rh/devtoolset-8/root/usr/bin/g++ diff --git a/build/docker-compose.yaml b/build/docker-compose.yaml index ea1b21a4e0..2f6fe49b42 100644 --- a/build/docker-compose.yaml +++ b/build/docker-compose.yaml @@ -2,7 +2,7 @@ version: "3" services: common: &common - image: foundationdb/foundationdb-build:0.1.11 + image: foundationdb/foundationdb-build:0.1.12 build-setup: &build-setup <<: *common From ff073f072b76967add069f49f7d322856379b9fb Mon Sep 17 00:00:00 2001 From: Alvin Moore Date: Sun, 23 Feb 2020 18:59:25 -0800 Subject: [PATCH 0687/1604] Removed npm from build docker Set the debug TLS library directory for make to /usr/local/lib64 --- Makefile | 4 ++-- build/Dockerfile | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 1bd6442dbb..6fdd4c28b5 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ ifeq ($(PLATFORM),Linux) CXXFLAGS += -std=c++17 BOOST_BASEDIR ?= /opt - TLS_LIBDIR ?= /usr/lib64 + TLS_LIBDIR ?= /usr/local/lib64 DLEXT := so java_DLEXT := so TARGET_LIBC_VERSION ?= 2.11 @@ -65,7 +65,7 @@ else ifeq ($(PLATFORM),Darwin) .LIBPATTERNS := lib%.dylib lib%.a BOOST_BASEDIR ?= ${HOME} - TLS_LIBDIR ?= /usr/lib64 + TLS_LIBDIR ?= /usr/local/lib64 DLEXT := dylib java_DLEXT := jnilib else diff --git a/build/Dockerfile b/build/Dockerfile index 89279ae081..c8a84818b8 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -9,7 +9,7 @@ RUN yum install -y yum-utils &&\ devtoolset-8-gcc-8.3.1-3.1.el6 devtoolset-8-gcc-c++-8.3.1-3.1.el6 \ rh-python36-python-devel devtoolset-8-valgrind-devel \ mono-core rh-ruby24 golang python27 rpm-build debbuild \ - python-pip npm dos2unix valgrind-devel ccache distcc devtoolset-8-libubsan-devel libubsan-devel &&\ + python-pip dos2unix valgrind-devel ccache distcc devtoolset-8-libubsan-devel libubsan-devel &&\ pip install boto3==1.1.1 USER root @@ -46,8 +46,9 @@ RUN cd /tmp && curl -L https://github.com/ninja-build/ninja/archive/v1.9.0.zip - RUN cd /tmp && curl -L https://www.openssl.org/source/openssl-1.1.1d.tar.gz -o openssl.tar.gz &&\ echo "1e3a91bc1f9dfce01af26026f856e064eab4c8ee0a8f457b5ae30b40b8b711f2 openssl.tar.gz" > openssl-sha.txt &&\ sha256sum -c openssl-sha.txt && tar -xzf openssl.tar.gz &&\ - cd openssl-1.1.1d && scl enable devtoolset-8 -- ./config CFLAGS="-fPIC -O3" --prefix=/usr &&\ + cd openssl-1.1.1d && scl enable devtoolset-8 -- ./config CFLAGS="-fPIC -O3" --prefix=/usr/local &&\ scl enable devtoolset-8 -- make -j`nproc` && scl enable devtoolset-8 -- make -j1 install &&\ + ln -sv /usr/local/lib64/lib*.so.1.1 /usr/lib64/ &&\ cd /tmp/ && rm -rf /tmp/openssl-1.1.1d /tmp/openssl.tar.gz LABEL version=0.1.12 From 710bc3ecb1fd0a9d6236bcf75792472b1222d437 Mon Sep 17 00:00:00 2001 From: Steve Atherton Date: Sun, 23 Feb 2020 22:24:16 -0800 Subject: [PATCH 0688/1604] Added optimization for single key clears to avoid an additional mutation buffer boundary key insertion. Made mutation buffer members private and created additional member functions to avoid needing access to the private members. --- fdbserver/VersionedBTree.actor.cpp | 54 ++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index 010e371bdd..aa5faa5a8a 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -2746,6 +2746,7 @@ public: int64_t extPageWrites; int64_t sets; int64_t clears; + int64_t clearSingleKey; int64_t commits; int64_t gets; int64_t getRanges; @@ -2755,8 +2756,8 @@ public: double startTime; std::string toString(bool clearAfter = false) { - const char *labels[] = {"set", "clear", "get", "getRange", "commit", "pageReads", "extPageRead", "pagePreloads", "extPagePreloads", "pageWrite", "extPageWrite", "commitPage", "commitPageStart", "pageUpdates"}; - const int64_t values[] = {sets, clears, gets, getRanges, commits, pageReads, extPageReads, pagePreloads, extPagePreloads, pageWrites, extPageWrites, commitToPage, commitToPageStart, pageUpdates}; + const char *labels[] = {"set", "clear", "clearSingleKey", "get", "getRange", "commit", "pageReads", "extPageRead", "pagePreloads", "extPagePreloads", "pageWrite", "extPageWrite", "commitPage", "commitPageStart", "pageUpdates"}; + const int64_t values[] = {sets, clears, clearSingleKey, gets, getRanges, commits, pageReads, extPageReads, pagePreloads, extPagePreloads, pageWrites, extPageWrites, commitToPage, commitToPageStart, pageUpdates}; double elapsed = now() - startTime; std::string s; @@ -2813,17 +2814,28 @@ public: // A write shall not become durable until the following call to commit() begins, and shall be durable once the following call to commit() returns void set(KeyValueRef keyValue) { ++counts.sets; - m_pBuffer->insertMutationBoundary(keyValue.key)->second.setBoundaryValue(ValueRef(m_pBuffer->arena, keyValue.value)); + m_pBuffer->insertMutationBoundary(keyValue.key)->second.setBoundaryValue(m_pBuffer->copyToArena(keyValue.value)); } void clear(KeyRangeRef clearedRange) { + // Optimization for single key clears to create just one mutation boundary instead of two + if(clearedRange.begin.size() == clearedRange.end.size() - 1 + && clearedRange.end[clearedRange.end.size() - 1] == 0 + && clearedRange.end.startsWith(clearedRange.begin) + ) { + ++counts.clears; + ++counts.clearSingleKey; + m_pBuffer->insertMutationBoundary(clearedRange.begin)->second.clearBoundary(); + return; + } + ++counts.clears; MutationBuffer::MutationsT::iterator iBegin = m_pBuffer->insertMutationBoundary(clearedRange.begin); MutationBuffer::MutationsT::iterator iEnd = m_pBuffer->insertMutationBoundary(clearedRange.end); iBegin->second.clearAll(); ++iBegin; - m_pBuffer->mutations.erase(iBegin, iEnd); + m_pBuffer->erase(iBegin, iEnd); } void mutate(int op, StringRef param1, StringRef param2) NOT_IMPLEMENTED @@ -3282,20 +3294,31 @@ private: mutations[dbEnd.key].clearBoundary(); } - Arena arena; typedef std::map MutationsT; typedef MutationsT::iterator iterator; typedef MutationsT::const_iterator const_iterator; + + private: + Arena arena; MutationsT mutations; - - const_iterator upper_bound(KeyRef k) const { + + public: + template T copyToArena(const T &object) { + return T(arena, object); + } + + const_iterator upper_bound(const KeyRef &k) const { return mutations.upper_bound(k); } - const_iterator lower_bound(KeyRef k) const { + const_iterator lower_bound(const KeyRef &k) const { return mutations.lower_bound(k); } + void erase(const const_iterator &begin, const const_iterator &end) { + mutations.erase(begin, end); + } + // Find or create a mutation buffer boundary for bound and return an iterator to it iterator insertMutationBoundary(KeyRef boundary) { // Find the first split point in buffer that is >= key @@ -6227,6 +6250,7 @@ TEST_CASE("!/redwood/correctness/btree") { state int maxCommitSize = shortTest ? 1000 : randomSize(std::min((maxKeySize + maxValueSize) * 20000, 10e6)); state int mutationBytesTarget = shortTest ? 5000 : randomSize(std::min(maxCommitSize * 100, 100e6)); state double clearProbability = deterministicRandom()->random01() * .1; + state double clearSingleKeyProbability = deterministicRandom()->random01(); state double clearPostSetProbability = deterministicRandom()->random01() * .1; state double coldStartProbability = deterministicRandom()->random01(); state double advanceOldVersionProbability = deterministicRandom()->random01(); @@ -6241,6 +6265,7 @@ TEST_CASE("!/redwood/correctness/btree") { printf("maxCommitSize: %d\n", maxCommitSize); printf("mutationBytesTarget: %d\n", mutationBytesTarget); printf("clearProbability: %f\n", clearProbability); + printf("clearSingleKeyProbability: %f\n", clearSingleKeyProbability); printf("clearPostSetProbability: %f\n", clearPostSetProbability); printf("coldStartProbability: %f\n", coldStartProbability); printf("advanceOldVersionProbability: %f\n", advanceOldVersionProbability); @@ -6308,12 +6333,15 @@ TEST_CASE("!/redwood/correctness/btree") { end = *i; } - if(end == start) + // Do a single key clear based on probability or end being randomly chosen to be the same as begin (unlikely) + if(deterministicRandom()->random01() < clearSingleKeyProbability || end == start) { end = keyAfter(start); + } else if(end < start) { std::swap(end, start); } + // Apply clear range to verification map ++rangeClears; KeyRangeRef range(start, end); debug_printf(" Mutation: Clear '%s' to '%s' @%" PRId64 "\n", start.toString().c_str(), end.toString().c_str(), version); @@ -6353,14 +6381,6 @@ TEST_CASE("!/redwood/correctness/btree") { btree->set(kv); written[std::make_pair(kv.key.toString(), version)] = kv.value.toString(); } - - // Sometimes set the range end after the clear - if(deterministicRandom()->random01() < clearPostSetProbability) { - KeyValue kv = randomKV(0, maxValueSize); - kv.key = range.end; - btree->set(kv); - written[std::make_pair(kv.key.toString(), version)] = kv.value.toString(); - } } else { // Set a key From eb7016a09a86a7ba41939f8b36c8d1ebcd265a9b Mon Sep 17 00:00:00 2001 From: Steve Atherton Date: Sun, 23 Feb 2020 22:48:43 -0800 Subject: [PATCH 0689/1604] Some more refactoring of MutationBuffer so it will be easier to have multiple implementations. --- fdbserver/VersionedBTree.actor.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index aa5faa5a8a..aa4d065003 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -2814,7 +2814,7 @@ public: // A write shall not become durable until the following call to commit() begins, and shall be durable once the following call to commit() returns void set(KeyValueRef keyValue) { ++counts.sets; - m_pBuffer->insertMutationBoundary(keyValue.key)->second.setBoundaryValue(m_pBuffer->copyToArena(keyValue.value)); + m_pBuffer->insert(keyValue.key)->second.setBoundaryValue(m_pBuffer->copyToArena(keyValue.value)); } void clear(KeyRangeRef clearedRange) { @@ -2825,13 +2825,13 @@ public: ) { ++counts.clears; ++counts.clearSingleKey; - m_pBuffer->insertMutationBoundary(clearedRange.begin)->second.clearBoundary(); + m_pBuffer->insert(clearedRange.begin)->second.clearBoundary(); return; } ++counts.clears; - MutationBuffer::MutationsT::iterator iBegin = m_pBuffer->insertMutationBoundary(clearedRange.begin); - MutationBuffer::MutationsT::iterator iEnd = m_pBuffer->insertMutationBoundary(clearedRange.end); + MutationBuffer::iterator iBegin = m_pBuffer->insert(clearedRange.begin); + MutationBuffer::iterator iEnd = m_pBuffer->insert(clearedRange.end); iBegin->second.clearAll(); ++iBegin; @@ -3294,15 +3294,16 @@ private: mutations[dbEnd.key].clearBoundary(); } - typedef std::map MutationsT; - typedef MutationsT::iterator iterator; - typedef MutationsT::const_iterator const_iterator; - private: + typedef std::map MutationsT; Arena arena; MutationsT mutations; public: + typedef MutationsT::iterator iterator; + typedef MutationsT::const_iterator const_iterator; + + // Return a T constructed in arena template T copyToArena(const T &object) { return T(arena, object); } @@ -3315,12 +3316,13 @@ private: return mutations.lower_bound(k); } + // erase [begin, end) from the mutation map void erase(const const_iterator &begin, const const_iterator &end) { mutations.erase(begin, end); } // Find or create a mutation buffer boundary for bound and return an iterator to it - iterator insertMutationBoundary(KeyRef boundary) { + iterator insert(KeyRef boundary) { // Find the first split point in buffer that is >= key // Since the initial state of the mutation buffer contains the range '' through // the maximum possible key, our search had to have found something so we From 1e45bb2be32e4928188472e536adb8f55ee80112 Mon Sep 17 00:00:00 2001 From: Steve Atherton Date: Sun, 23 Feb 2020 23:44:15 -0800 Subject: [PATCH 0690/1604] Further refactoring of MutationBuffer to make upcoming ART implementation easier to plug in. Key and mutation for an iterator position are now accessed via member functions which gives the implementation an opportunity to decode or reconstitute keys on-demand. --- fdbserver/VersionedBTree.actor.cpp | 83 ++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 27 deletions(-) diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index aa4d065003..3dd01cbaa7 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -2814,7 +2814,7 @@ public: // A write shall not become durable until the following call to commit() begins, and shall be durable once the following call to commit() returns void set(KeyValueRef keyValue) { ++counts.sets; - m_pBuffer->insert(keyValue.key)->second.setBoundaryValue(m_pBuffer->copyToArena(keyValue.value)); + m_pBuffer->insert(keyValue.key).mutation().setBoundaryValue(m_pBuffer->copyToArena(keyValue.value)); } void clear(KeyRangeRef clearedRange) { @@ -2825,7 +2825,7 @@ public: ) { ++counts.clears; ++counts.clearSingleKey; - m_pBuffer->insert(clearedRange.begin)->second.clearBoundary(); + m_pBuffer->insert(clearedRange.begin).mutation().clearBoundary(); return; } @@ -2833,7 +2833,7 @@ public: MutationBuffer::iterator iBegin = m_pBuffer->insert(clearedRange.begin); MutationBuffer::iterator iEnd = m_pBuffer->insert(clearedRange.end); - iBegin->second.clearAll(); + iBegin.mutation().clearAll(); ++iBegin; m_pBuffer->erase(iBegin, iEnd); } @@ -3300,8 +3300,37 @@ private: MutationsT mutations; public: - typedef MutationsT::iterator iterator; - typedef MutationsT::const_iterator const_iterator; + struct iterator : public MutationsT::iterator { + typedef MutationsT::iterator Base; + iterator() = default; + iterator(const MutationsT::iterator &i) : Base(i) { + } + + const KeyRef & key() { + return (*this)->first; + } + + RangeMutation & mutation() { + return (*this)->second; + } + }; + + struct const_iterator : public MutationsT::const_iterator { + typedef MutationsT::const_iterator Base; + const_iterator() = default; + const_iterator(const MutationsT::const_iterator &i) : Base(i) { + } + const_iterator(const MutationsT::iterator &i) : Base(i) { + } + + const KeyRef & key() { + return (*this)->first; + } + + const RangeMutation & mutation() { + return (*this)->second; + } + }; // Return a T constructed in arena template T copyToArena(const T &object) { @@ -3330,7 +3359,7 @@ private: iterator ib = mutations.lower_bound(boundary); // If we found the boundary we are looking for, return its iterator - if(ib->first == boundary) { + if(ib.key() == boundary) { return ib; } @@ -3344,8 +3373,8 @@ private: iterator iPrevious = ib; --iPrevious; // If the range we just divided was being cleared, then the dividing boundary key and range after it must also be cleared - if(iPrevious->second.clearAfterBoundary) { - ib->second.clearAll(); + if(iPrevious.mutation().clearAfterBoundary) { + ib.mutation().clearAll(); } return ib; @@ -3838,7 +3867,7 @@ private: debug_printf("%s ---------MUTATION BUFFER SLICE ---------------------\n", context.c_str()); auto begin = iMutationBoundary; while(1) { - debug_printf("%s Mutation: '%s': %s\n", context.c_str(), printable(begin->first).c_str(), begin->second.toString().c_str()); + debug_printf("%s Mutation: '%s': %s\n", context.c_str(), printable(begin.key()).c_str(), begin.mutation().toString().c_str()); if(begin == iMutationBoundaryEnd) { break; } @@ -3855,7 +3884,7 @@ private: // If there are any changes to the one key then the entire subtree should be deleted as the changes for the key // do not go into this subtree. if(iMutationBoundary == iMutationBoundaryEnd) { - if(iMutationBoundary->second.boundaryChanged) { + if(iMutationBoundary.mutation().boundaryChanged) { debug_printf("%s lower and upper bound key/version match and key is modified so deleting page, returning %s\n", context.c_str(), toString(results).c_str()); if(isLeaf) { self->freeBtreePage(rootID, writeVersion); @@ -3880,7 +3909,7 @@ private: // Cleared means the entire range covering the subtree was cleared. It is assumed true // if the range starting after the lower mutation boundary was cleared, and then proven false // below if possible. - bool cleared = iMutationBoundary->second.clearAfterBoundary; + bool cleared = iMutationBoundary.mutation().clearAfterBoundary; // Unchanged means the entire range covering the subtree was unchanged, it is assumed to be the // opposite of cleared() and then proven false below if possible. bool unchanged = !cleared; @@ -3888,14 +3917,14 @@ private: // If the lower mutation boundary key is the same as the subtree lower bound then whether or not // that key is being changed or cleared affects this subtree. - if(iMutationBoundary->first == lowerBound->key) { + if(iMutationBoundary.key() == lowerBound->key) { // If subtree will be cleared (so far) but the lower boundary key is not cleared then the subtree is not cleared - if(cleared && !iMutationBoundary->second.boundaryCleared()) { + if(cleared && !iMutationBoundary.mutation().boundaryCleared()) { cleared = false; debug_printf("%s cleared=%d unchanged=%d\n", context.c_str(), cleared, unchanged); } // If the subtree looked unchanged (so far) but the lower boundary is is changed then the subtree is changed - if(unchanged && iMutationBoundary->second.boundaryChanged) { + if(unchanged && iMutationBoundary.mutation().boundaryChanged) { unchanged = false; debug_printf("%s cleared=%d unchanged=%d\n", context.c_str(), cleared, unchanged); } @@ -3903,10 +3932,10 @@ private: // If the higher mutation boundary key is the same as the subtree upper bound key then whether // or not it is being changed or cleared affects this subtree. - if((cleared || unchanged) && iMutationBoundaryEnd->first == upperBound->key) { + if((cleared || unchanged) && iMutationBoundaryEnd.key() == upperBound->key) { // If the key is being changed then the records in this subtree with the same key must be removed // so the subtree is definitely not unchanged, though it may be cleared to achieve the same effect. - if(iMutationBoundaryEnd->second.boundaryChanged) { + if(iMutationBoundaryEnd.mutation().boundaryChanged) { unchanged = false; debug_printf("%s cleared=%d unchanged=%d\n", context.c_str(), cleared, unchanged); } @@ -3953,7 +3982,7 @@ private: debug_printf("%s ---------MUTATION BUFFER SLICE ---------------------\n", context.c_str()); auto begin = iMutationBoundary; while(1) { - debug_printf("%s Mutation: '%s': %s\n", context.c_str(), printable(begin->first).c_str(), begin->second.toString().c_str()); + debug_printf("%s Mutation: '%s': %s\n", context.c_str(), printable(begin.key()).c_str(), begin.mutation().toString().c_str()); if(begin == iMutationBoundaryEnd) { break; } @@ -4004,16 +4033,16 @@ private: // Now, process each mutation range and merge changes with existing data. bool firstMutationBoundary = true; while(iMutationBoundary != iMutationBoundaryEnd) { - debug_printf("%s New mutation boundary: '%s': %s\n", context.c_str(), printable(iMutationBoundary->first).c_str(), iMutationBoundary->second.toString().c_str()); + debug_printf("%s New mutation boundary: '%s': %s\n", context.c_str(), printable(iMutationBoundary.key()).c_str(), iMutationBoundary.mutation().toString().c_str()); // Apply the change to the mutation buffer start boundary key only if // - there actually is a change (whether a set or a clear, old records are to be removed) // - either this is not the first boundary or it is but its key matches our lower bound key - bool applyBoundaryChange = iMutationBoundary->second.boundaryChanged && (!firstMutationBoundary || iMutationBoundary->first >= lowerBound->key); + bool applyBoundaryChange = iMutationBoundary.mutation().boundaryChanged && (!firstMutationBoundary || iMutationBoundary.key() >= lowerBound->key); firstMutationBoundary = false; // Iterate over records for the mutation boundary key, keep them unless the boundary key was changed or we are not applying it - while(cursor.valid() && cursor.get().key == iMutationBoundary->first) { + while(cursor.valid() && cursor.get().key == iMutationBoundary.key()) { // If there were no changes to the key or we're not applying it if(!applyBoundaryChange) { // If not updating, add to the output set, otherwise skip ahead past the records for the mutation boundary @@ -4041,8 +4070,8 @@ private: // Write the new record(s) for the mutation boundary start key if its value has been set // Clears of this key will have been processed above by not being erased from the updated page or excluded from the merge output - if(applyBoundaryChange && iMutationBoundary->second.boundarySet()) { - RedwoodRecordRef rec(iMutationBoundary->first, 0, iMutationBoundary->second.boundaryValue.get()); + if(applyBoundaryChange && iMutationBoundary.mutation().boundarySet()) { + RedwoodRecordRef rec(iMutationBoundary.key(), 0, iMutationBoundary.mutation().boundaryValue.get()); changesMade = true; if(rec.value.get().size() <= self->m_maxPartSize) { @@ -4092,17 +4121,17 @@ private: } // Before advancing the iterator, get whether or not the records in the following range must be removed - bool remove = iMutationBoundary->second.clearAfterBoundary; + bool remove = iMutationBoundary.mutation().clearAfterBoundary; // Advance to the next boundary because we need to know the end key for the current range. ++iMutationBoundary; if(iMutationBoundary == iMutationBoundaryEnd) { skipLen = 0; } - debug_printf("%s Mutation range end: '%s'\n", context.c_str(), printable(iMutationBoundary->first).c_str()); + debug_printf("%s Mutation range end: '%s'\n", context.c_str(), printable(iMutationBoundary.key()).c_str()); // Now handle the records up through but not including the next mutation boundary key - RedwoodRecordRef end(iMutationBoundary->first); + RedwoodRecordRef end(iMutationBoundary.key()); // If the records are being removed and we're not doing an in-place update // OR if we ARE doing an update but the records are NOT being removed, then just skip them. @@ -4113,7 +4142,7 @@ private: changesMade = true; } - debug_printf("%s Seeking forward to next boundary (remove=%d updating=%d) %s\n", context.c_str(), remove, updating, iMutationBoundary->first.toString().c_str()); + debug_printf("%s Seeking forward to next boundary (remove=%d updating=%d) %s\n", context.c_str(), remove, updating, iMutationBoundary.key().toString().c_str()); cursor.seekGreaterThanOrEqual(end, skipLen); } else { @@ -4137,7 +4166,7 @@ private: // If there are still more records, they have the same key as the end boundary if(cursor.valid()) { // If the end boundary is changing, we must remove the remaining records in this page - bool remove = iMutationBoundaryEnd->second.boundaryChanged; + bool remove = iMutationBoundaryEnd.mutation().boundaryChanged; if(remove) { changesMade = true; } From 9585cd10f1d9080f9f4959983ac3992314c8ba56 Mon Sep 17 00:00:00 2001 From: Alvin Moore Date: Mon, 24 Feb 2020 00:19:43 -0800 Subject: [PATCH 0691/1604] Removed duplicate CMake link request --- flow/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/CMakeLists.txt b/flow/CMakeLists.txt index b68d758992..cb7585cd3c 100644 --- a/flow/CMakeLists.txt +++ b/flow/CMakeLists.txt @@ -86,6 +86,7 @@ set(FLOW_SRCS configure_file(${CMAKE_CURRENT_SOURCE_DIR}/SourceVersion.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/SourceVersion.h) add_flow_target(STATIC_LIBRARY NAME flow SRCS ${FLOW_SRCS}) +target_include_directories(flow SYSTEM PUBLIC ${CMAKE_THREAD_LIBS_INIT}) target_include_directories(flow PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) if (NOT APPLE AND NOT WIN32) set (FLOW_LIBS ${FLOW_LIBS} rt) @@ -94,7 +95,6 @@ elseif(WIN32) target_link_libraries(flow PUBLIC psapi.lib) endif() target_link_libraries(flow PRIVATE ${FLOW_LIBS}) -target_link_libraries(flow PUBLIC boost_target Threads::Threads ${CMAKE_DL_LIBS}) if(USE_VALGRIND) target_link_libraries(flow PUBLIC Valgrind) endif() From 72a549df14fac16d9843c7266c9e01793f1d9c2a Mon Sep 17 00:00:00 2001 From: Steve Atherton Date: Mon, 24 Feb 2020 00:31:09 -0800 Subject: [PATCH 0692/1604] Added basic mutation buffer insert/lookup test. --- fdbserver/VersionedBTree.actor.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index 3dd01cbaa7..4f1338ff54 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -3284,6 +3284,7 @@ private: } }; +public: struct MutationBuffer { MutationBuffer() { // Create range representing the entire keyspace. This reduces edge cases to applying mutations @@ -3382,6 +3383,7 @@ private: }; +private: /* Mutation Buffer Overview * * This structure's organization is meant to put pending updates for the btree in an order @@ -6265,6 +6267,34 @@ struct SimpleCounter { std::string toString() { return format("%" PRId64 "/%.2f/%.2f", x, rate() / 1e6, avgRate() / 1e6); } }; +TEST_CASE("!/redwood/performance/mutationBuffer") { + // This test uses pregenerated short random keys + int count = 10e6; + + printf("Generating %d strings...\n", count); + Arena arena; + std::vector strings; + while(strings.size() < count) { + strings.push_back(randomString(arena, 5)); + } + + printf("Inserting and then finding each string...\n", count); + double start = timer(); + VersionedBTree::MutationBuffer m; + for(int i = 0; i < count; ++i) { + KeyRef key = strings[i]; + auto a = m.insert(key); + auto b = m.lower_bound(key); + ASSERT(a == b); + m.erase(a, b); + } + + double elapsed = timer() - start; + printf("count=%d elapsed=%f\n", count, elapsed); + + return Void(); +} + TEST_CASE("!/redwood/correctness/btree") { state std::string pagerFile = "unittest_pageFile.redwood"; IPager2 *pager; From 80c2848af66988935fcf2f142b571e28e9bb3643 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Mon, 24 Feb 2020 09:52:31 -0800 Subject: [PATCH 0693/1604] Change the algorithm for the proxy handing out read versions to improve performance and increase responsiveness to changes in workload. --- fdbserver/Knobs.cpp | 3 +- fdbserver/Knobs.h | 3 +- fdbserver/MasterProxyServer.actor.cpp | 113 +++++++++++++++++--------- 3 files changed, 79 insertions(+), 40 deletions(-) diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index 5382f37c51..90adc9cd84 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -301,6 +301,8 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs, bool isSimula init( START_TRANSACTION_BATCH_QUEUE_CHECK_INTERVAL, 0.001 ); init( START_TRANSACTION_MAX_TRANSACTIONS_TO_START, 100000 ); init( START_TRANSACTION_MAX_REQUESTS_TO_START, 10000 ); + init( START_TRANSACTION_RATE_WINDOW, 2.0 ); + init( START_TRANSACTION_MAX_EMPTY_QUEUE_BUDGET, 10.0 ); init( COMMIT_TRANSACTION_BATCH_INTERVAL_FROM_IDLE, 0.0005 ); if( randomize && BUGGIFY ) COMMIT_TRANSACTION_BATCH_INTERVAL_FROM_IDLE = 0.005; init( COMMIT_TRANSACTION_BATCH_INTERVAL_MIN, 0.001 ); if( randomize && BUGGIFY ) COMMIT_TRANSACTION_BATCH_INTERVAL_MIN = 0.1; @@ -318,7 +320,6 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs, bool isSimula init( COMMIT_TRANSACTION_BATCH_BYTES_SCALE_BASE, 100000 ); init( COMMIT_TRANSACTION_BATCH_BYTES_SCALE_POWER, 0.0 ); - init( TRANSACTION_BUDGET_TIME, 0.050 ); if( randomize && BUGGIFY ) TRANSACTION_BUDGET_TIME = 0.0; init( RESOLVER_COALESCE_TIME, 1.0 ); init( BUGGIFIED_ROW_LIMIT, APPLY_MUTATION_BYTES ); if( randomize && BUGGIFY ) BUGGIFIED_ROW_LIMIT = deterministicRandom()->randomInt(3, 30); init( PROXY_SPIN_DELAY, 0.01 ); diff --git a/fdbserver/Knobs.h b/fdbserver/Knobs.h index 144b5eb142..c95b4e0f77 100644 --- a/fdbserver/Knobs.h +++ b/fdbserver/Knobs.h @@ -246,6 +246,8 @@ public: double START_TRANSACTION_BATCH_QUEUE_CHECK_INTERVAL; double START_TRANSACTION_MAX_TRANSACTIONS_TO_START; int START_TRANSACTION_MAX_REQUESTS_TO_START; + double START_TRANSACTION_RATE_WINDOW; + double START_TRANSACTION_MAX_EMPTY_QUEUE_BUDGET; double COMMIT_TRANSACTION_BATCH_INTERVAL_FROM_IDLE; double COMMIT_TRANSACTION_BATCH_INTERVAL_MIN; @@ -261,7 +263,6 @@ public: double COMMIT_BATCHES_MEM_FRACTION_OF_TOTAL; double COMMIT_BATCHES_MEM_TO_TOTAL_MEM_SCALE_FACTOR; - double TRANSACTION_BUDGET_TIME; double RESOLVER_COALESCE_TIME; int BUGGIFIED_ROW_LIMIT; double PROXY_SPIN_DELAY; diff --git a/fdbserver/MasterProxyServer.actor.cpp b/fdbserver/MasterProxyServer.actor.cpp index 76fb0ad7ce..2aad5f149b 100644 --- a/fdbserver/MasterProxyServer.actor.cpp +++ b/fdbserver/MasterProxyServer.actor.cpp @@ -94,8 +94,61 @@ struct ProxyStats { } }; -ACTOR Future getRate(UID myID, Reference> db, int64_t* inTransactionCount, int64_t* inBatchTransactionCount, double* outTransactionRate, - double* outBatchTransactionRate, GetHealthMetricsReply* healthMetricsReply, GetHealthMetricsReply* detailedHealthMetricsReply) { +struct TransactionRateInfo { + double rate; + double limit; + double budget; + + bool disabled; + + Smoother smoothRate; + Smoother smoothReleased; + + TransactionRateInfo(double rate) : rate(rate), limit(0), budget(0), disabled(true), smoothRate(SERVER_KNOBS->START_TRANSACTION_RATE_WINDOW), + smoothReleased(SERVER_KNOBS->START_TRANSACTION_RATE_WINDOW) {} + + void reset(double elapsed) { + double releaseRate = smoothRate.smoothTotal() - smoothReleased.smoothRate(); + limit = SERVER_KNOBS->START_TRANSACTION_RATE_WINDOW * releaseRate; + } + + bool canStart(int64_t numAlreadyStarted, int64_t count) { + return numAlreadyStarted + count <= std::min(limit + budget, SERVER_KNOBS->START_TRANSACTION_MAX_TRANSACTIONS_TO_START); + } + + void updateBudget(int64_t numStartedAtPriority, bool queueEmptyAtPriority, double elapsed) { + budget = std::max(0.0, budget + elapsed * (limit - numStartedAtPriority) / SERVER_KNOBS->START_TRANSACTION_RATE_WINDOW); + + if(queueEmptyAtPriority) { + budget = std::min(budget, SERVER_KNOBS->START_TRANSACTION_MAX_EMPTY_QUEUE_BUDGET); + } + + smoothReleased.addDelta(numStartedAtPriority); + } + + void disable() { + disabled = true; + rate = 0; + smoothRate.reset(0); + } + + void setRate(double rate) { + ASSERT(rate != std::numeric_limits::infinity()); + + this->rate = rate; + if(disabled) { + smoothRate.reset(rate); + disabled = false; + } + else { + smoothRate.setTotal(rate); + } + } +}; + + +ACTOR Future getRate(UID myID, Reference> db, int64_t* inTransactionCount, int64_t* inBatchTransactionCount, TransactionRateInfo *transactionRateInfo, + TransactionRateInfo *batchTransactionRateInfo, GetHealthMetricsReply* healthMetricsReply, GetHealthMetricsReply* detailedHealthMetricsReply) { state Future nextRequestTimer = Never(); state Future leaseTimeout = Never(); state Future reply = Never(); @@ -124,8 +177,9 @@ ACTOR Future getRate(UID myID, Reference> db, int64 } when ( GetRateInfoReply rep = wait(reply) ) { reply = Never(); - *outTransactionRate = rep.transactionRate; - *outBatchTransactionRate = rep.batchTransactionRate; + + transactionRateInfo->setRate(rep.transactionRate); + batchTransactionRateInfo->setRate(rep.batchTransactionRate); //TraceEvent("MasterProxyRate", myID).detail("Rate", rep.transactionRate).detail("BatchRate", rep.batchTransactionRate).detail("Lease", rep.leaseDuration).detail("ReleasedTransactions", *inTransactionCount - lastTC); lastTC = *inTransactionCount; leaseTimeout = delay(rep.leaseDuration); @@ -137,35 +191,15 @@ ACTOR Future getRate(UID myID, Reference> db, int64 } } when ( wait( leaseTimeout ) ) { - *outTransactionRate = 0; - *outBatchTransactionRate = 0; - //TraceEvent("MasterProxyRate", myID).detail("Rate", 0.0).detail("BatchRate", 0.0).detail("Lease", "Expired"); + transactionRateInfo->disable(); + batchTransactionRateInfo->disable(); + TraceEvent(SevWarn, "MasterProxyRateLeaseExpired", myID).suppressFor(5.0); + //TraceEvent("MasterProxyRate", myID).detail("Rate", 0.0).detail("BatchRate", 0.0).detail("Lease", 0); leaseTimeout = Never(); } } } -struct TransactionRateInfo { - double rate; - double limit; - - TransactionRateInfo(double rate) : rate(rate), limit(0) {} - - void reset(double elapsed) { - limit = std::min(0.0, limit) + rate * elapsed; // Adjust the limit based on the full elapsed interval in order to properly erase a deficit - limit = std::min(limit, rate * SERVER_KNOBS->START_TRANSACTION_BATCH_INTERVAL_MAX); // Don't allow the rate to exceed what would be allowed in the maximum batch interval - limit = std::min(limit, SERVER_KNOBS->START_TRANSACTION_MAX_TRANSACTIONS_TO_START); - } - - bool canStart(int64_t numAlreadyStarted) { - return numAlreadyStarted < limit; - } - - void updateBudget(int64_t numStarted) { - limit -= numStarted; - } -}; - ACTOR Future queueTransactionStartRequests( Reference> db, std::priority_queue, @@ -1240,7 +1274,7 @@ ACTOR static Future transactionStarter( state vector otherProxies; state PromiseStream replyTimes; - addActor.send(getRate(proxy.id(), db, &transactionCount, &batchTransactionCount, &normalRateInfo.rate, &batchRateInfo.rate, healthMetricsReply, detailedHealthMetricsReply)); + addActor.send(getRate(proxy.id(), db, &transactionCount, &batchTransactionCount, &normalRateInfo, &batchRateInfo, healthMetricsReply, detailedHealthMetricsReply)); addActor.send(queueTransactionStartRequests(db, &transactionQueue, proxy.getConsistentReadVersion.getFuture(), GRVTimer, &lastGRVTime, &GRVBatchTime, replyTimes.getFuture(), &commitData->stats, &batchRateInfo)); @@ -1282,13 +1316,12 @@ ACTOR static Future transactionStarter( auto& req = transactionQueue.top().first; int tc = req.transactionCount; - if (req.priority() < GetReadVersionRequest::PRIORITY_DEFAULT && - !batchRateInfo.canStart(transactionsStarted[0] + transactionsStarted[1])) { - break; - } else if (req.priority() < GetReadVersionRequest::PRIORITY_SYSTEM_IMMEDIATE && - !normalRateInfo.canStart(transactionsStarted[0] + transactionsStarted[1])) { + if(req.priority() < GetReadVersionRequest::PRIORITY_DEFAULT && !batchRateInfo.canStart(transactionsStarted[0] + transactionsStarted[1], tc)) { break; } + else if(req.priority() < GetReadVersionRequest::PRIORITY_SYSTEM_IMMEDIATE && !normalRateInfo.canStart(transactionsStarted[0] + transactionsStarted[1], tc)) { + break; + } if (req.debugID.present()) { if (!debugID.present()) debugID = nondeterministicRandom()->randomUniqueID(); @@ -1323,11 +1356,15 @@ ACTOR static Future transactionStarter( .detail("TransactionBudget", transactionBudget) .detail("BatchTransactionBudget", batchTransactionBudget);*/ - transactionCount += transactionsStarted[0] + transactionsStarted[1]; - batchTransactionCount += batchPriTransactionsStarted[0] + batchPriTransactionsStarted[1]; + int systemTotalStarted = systemTransactionsStarted[0] + systemTransactionsStarted[1]; + int normalTotalStarted = defaultPriTransactionsStarted[0] + defaultPriTransactionsStarted[1]; + int batchTotalStarted = batchPriTransactionsStarted[0] + batchPriTransactionsStarted[1]; - normalRateInfo.updateBudget(transactionsStarted[0] + transactionsStarted[1]); - batchRateInfo.updateBudget(transactionsStarted[0] + transactionsStarted[1]); + transactionCount += transactionsStarted[0] + transactionsStarted[1]; + batchTransactionCount += batchTotalStarted; + + normalRateInfo.updateBudget(systemTotalStarted + normalTotalStarted, transactionQueue.empty() || transactionQueue.top().first.priority() < GetReadVersionRequest::PRIORITY_DEFAULT, elapsed); + batchRateInfo.updateBudget(systemTotalStarted + normalTotalStarted + batchTotalStarted, transactionQueue.empty(), elapsed); if (debugID.present()) { g_traceBatch.addEvent("TransactionDebug", debugID.get().first(), "MasterProxyServer.masterProxyServerCore.Broadcast"); From e79faae1750f53f58a60d302b9e8d488de8d24f8 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Mon, 24 Feb 2020 11:06:13 -0800 Subject: [PATCH 0694/1604] Add some more tail latencies to the latency trace events in the ReadWrite workload. --- fdbserver/workloads/ReadWrite.actor.cpp | 41 ++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/fdbserver/workloads/ReadWrite.actor.cpp b/fdbserver/workloads/ReadWrite.actor.cpp index 8819a616da..cd7cc918c3 100644 --- a/fdbserver/workloads/ReadWrite.actor.cpp +++ b/fdbserver/workloads/ReadWrite.actor.cpp @@ -327,10 +327,43 @@ struct ReadWriteWorkload : KVWorkload { elapsed += self->periodicLoggingInterval; wait( delayUntil(start + elapsed) ); - TraceEvent((self->description() + "_RowReadLatency").c_str()).detail("Mean", self->readLatencies.mean()).detail("Median", self->readLatencies.median()).detail("Percentile5", self->readLatencies.percentile(.05)).detail("Percentile95", self->readLatencies.percentile(.95)).detail("Count", self->readLatencyCount).detail("Elapsed", elapsed); - TraceEvent((self->description() + "_GRVLatency").c_str()).detail("Mean", self->GRVLatencies.mean()).detail("Median", self->GRVLatencies.median()).detail("Percentile5", self->GRVLatencies.percentile(.05)).detail("Percentile95", self->GRVLatencies.percentile(.95)); - TraceEvent((self->description() + "_CommitLatency").c_str()).detail("Mean", self->commitLatencies.mean()).detail("Median", self->commitLatencies.median()).detail("Percentile5", self->commitLatencies.percentile(.05)).detail("Percentile95", self->commitLatencies.percentile(.95)); - TraceEvent((self->description() + "_TotalLatency").c_str()).detail("Mean", self->latencies.mean()).detail("Median", self->latencies.median()).detail("Percentile5", self->latencies.percentile(.05)).detail("Percentile95", self->latencies.percentile(.95)); + TraceEvent((self->description() + "_RowReadLatency").c_str()) + .detail("Mean", self->readLatencies.mean()) + .detail("Median", self->readLatencies.median()) + .detail("Percentile5", self->readLatencies.percentile(.05)) + .detail("Percentile95", self->readLatencies.percentile(.95)) + .detail("Percentile99", self->readLatencies.percentile(.99)) + .detail("Percentile99_9", self->readLatencies.percentile(.999)) + .detail("Max", self->readLatencies.max()) + .detail("Count", self->readLatencyCount) + .detail("Elapsed", elapsed); + + TraceEvent((self->description() + "_GRVLatency").c_str()) + .detail("Mean", self->GRVLatencies.mean()) + .detail("Median", self->GRVLatencies.median()) + .detail("Percentile5", self->GRVLatencies.percentile(.05)) + .detail("Percentile95", self->GRVLatencies.percentile(.95)) + .detail("Percentile99", self->GRVLatencies.percentile(.99)) + .detail("Percentile99_9", self->GRVLatencies.percentile(.999)) + .detail("Max", self->GRVLatencies.max()); + + TraceEvent((self->description() + "_CommitLatency").c_str()) + .detail("Mean", self->commitLatencies.mean()) + .detail("Median", self->commitLatencies.median()) + .detail("Percentile5", self->commitLatencies.percentile(.05)) + .detail("Percentile95", self->commitLatencies.percentile(.95)) + .detail("Percentile99", self->commitLatencies.percentile(.99)) + .detail("Percentile99_9", self->commitLatencies.percentile(.999)) + .detail("Max", self->commitLatencies.max()); + + TraceEvent((self->description() + "_TotalLatency").c_str()) + .detail("Mean", self->latencies.mean()) + .detail("Median", self->latencies.median()) + .detail("Percentile5", self->latencies.percentile(.05)) + .detail("Percentile95", self->latencies.percentile(.95)) + .detail("Percentile99", self->latencies.percentile(.99)) + .detail("Percentile99_9", self->latencies.percentile(.999)) + .detail("Max", self->latencies.max()); int64_t ops = (self->aTransactions.getValue() * (self->readsPerTransactionA+self->writesPerTransactionA)) + (self->bTransactions.getValue() * (self->readsPerTransactionB+self->writesPerTransactionB)); From c92d3ef3a6ff9a534b8dfe8c119faf11e861dc8a Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Mon, 24 Feb 2020 11:57:53 -0800 Subject: [PATCH 0695/1604] A draft version, which can be refactored --- fdbclient/PrivateKeySpace.actor.cpp | 171 ++++++++++++++++++++++++++-- fdbclient/PrivateKeySpace.h | 29 ++--- 2 files changed, 175 insertions(+), 25 deletions(-) diff --git a/fdbclient/PrivateKeySpace.actor.cpp b/fdbclient/PrivateKeySpace.actor.cpp index 259b7f7ab5..4a33a6f8ea 100644 --- a/fdbclient/PrivateKeySpace.actor.cpp +++ b/fdbclient/PrivateKeySpace.actor.cpp @@ -4,18 +4,166 @@ ACTOR Future> getActor( PrivateKeySpace* pks, ReadYourWritesTransaction* ryw, - KeyRef key, - bool snapshot ) + KeyRef key ) { // use getRange to workaround this Standalone result = wait(pks->getRange(ryw, KeySelector( firstGreaterOrEqual(key) ), - KeySelector( firstGreaterOrEqual(keyAfter(key)) ), GetRangeLimits(1), snapshot)); + KeySelector( firstGreaterOrEqual(keyAfter(key)) ), GetRangeLimits())); + ASSERT(result.size() <= 1); if (result.size()) { return Optional(result[0].value); } else { return Optional(); } } + +ACTOR Future> getRangeAggregationActor( + PrivateKeySpace* pks, + ReadYourWritesTransaction* ryw, + KeySelector begin, + KeySelector end, + GetRangeLimits limits, + bool reverse ) +{ + // This function handles ranges lie over more than one underlying keyrane and aggregates all results + // GetRangeLimits and reverse are also handled here + + // do parameter validation check stuff + if( limits.isReached() ) { + TEST(true); // RYW range read limit 0 + return Standalone(); + } + + // TODO: check the reason here + // if( !limits.isValid() ) + // return range_limits_invalid(); + + // erase equal here + if( begin.orEqual ) + begin.removeOrEqual(begin.arena()); + + if( end.orEqual ) + end.removeOrEqual(end.arena()); + + if( begin.offset >= end.offset && begin.getKey() >= end.getKey() ) { + TEST(true); //range inverted + return Standalone(); + } + + state std::deque resultRef; + state Standalone result; + // state RangeMap::Iterator iter; + state RangeMap::Ranges ranges; + + + // Handle the case where begin offset is zero or negative, which means at least one key before begin.key needs to be read + state RangeMap::Iterator iter = pks->getKeyRangeMap()->rangeContaining(begin.getKey()); + // state RangeMap::Iterator prev = curr; + // --prev; + if (begin.offset <= 0) { + state int remains = 1 - begin.offset; + while (remains > 0) { + if (iter.value() == NULL) { + if (iter == pks->getKeyRangeMap()->ranges().begin()) + break; + else + --iter; + continue; + } + state Standalone temp = wait(iter->value()->getRange( + ryw, + KeySelector(firstGreaterOrEqual(iter.value()->getKeyRange().begin)), + KeySelectorRef(firstGreaterOrEqual(begin.getKey())), + GetRangeLimits() + )); + for (int i = temp.size() - 1; i >= 0 && remains > 0; --i) { + resultRef.push_front(temp[i]); + remains--; + } + if (iter == pks->getKeyRangeMap()->ranges().begin()) + break; + else + --iter; + } + if (remains > 0) { + //throw error here + } + } + // Check limits here + + // (begin.key, range.end) + + // contained range query + + // (range.begin, end.key) + + // end.offset > 0 + + // The interesting range is in (begin.key, end.key) + ranges = pks->getKeyRangeMap()->intersectingRanges(KeyRangeRef(begin.getKey(), end.getKey())); + for (iter = ranges.begin(); iter != ranges.end(); ++iter) { + if (iter->value() == NULL) continue; + KeyRangeRef kr = iter->range(); + Standalone pairs = wait(iter->value()->getRange(ryw, + KeySelector( firstGreaterOrEqual(kr.begin) ), + KeySelector( firstGreaterOrEqual(kr.end)), + GetRangeLimits() + )); + result.append_deep(result.arena(), pairs.begin(), pairs.size()); + } + if(begin.offset - end.offset <= result.size()){ + result.pop_front(begin.offset); + for (int i =0; i<-end.offset; i++) + result.pop_back(); + } else { + return Standalone(); + } + return result; +} + +ACTOR Future> getRangeActor( + const PrivateKeyRangeSimpleImpl* pkrSimpleImpl, + ReadYourWritesTransaction* ryw, + KeySelector begin, + KeySelector end ) +{ + // If the start key or end key lies outside this keyrange, it cannot handle the case. + // Thus, it is forced for the quired range lies the this keyrange + // It assumes the begin of the range is never used as a key + KeyRangeRef range = pkrSimpleImpl->getKeyRange(); + if (begin.orEqual) + begin.removeOrEqual(begin.arena()); + ASSERT(begin.offset > 0 && begin.getKey() >= range.begin); + if (end.orEqual) + end.removeOrEqual(end.arena()); + ASSERT(end.offset <= 0 && end.getKey() <= range.end); + + KeyRangeRef kr(begin.getKey(), end.getKey()); + state Standalone result = wait(pkrSimpleImpl->getRange(ryw, kr)); + if (begin.offset - end.offset >= result.size()) { + // inverted select range + return Standalone(); + } else { + // TODO : may need optimization + // pop from head + result.pop_front(begin.offset); + // pop from end + for (int i = 0; i < -end.offset; ++i) result.pop_back(); + // if (limits.reachedBy(result)) { + // int idx; + // for (idx = 0; idx < result.size(); ++ idx) { + // limits.decrement(result[idx]); + // if (limits.isReached()) break; + // } + // while (idx < result.size()) { + // result.pop_back(); + // ++idx; + // } + // } + return result; + } +} + Future> PrivateKeyRangeSimpleImpl::getRange( ReadYourWritesTransaction* ryw, KeySelector begin, @@ -24,11 +172,9 @@ Future> PrivateKeyRangeSimpleImpl::getRange( bool snapshot, bool reverse ) const { - // do the easiest stuff here, suppose we have no snapshot and reverse - KeyRef startkey = begin.getKey(); - KeyRef endkey = end.getKey(); - KeyRange kr = KeyRangeRef(startkey, endkey); - return getRange(ryw, kr); + // ignore snapshot, which is invalid + // ignore reverse and limits, which is handled by PrivateKeySpace when doing aggregation + return getRangeActor(this, ryw, begin, end); } Future> PrivateKeySpace::getRange( @@ -37,10 +183,10 @@ Future> PrivateKeySpace::getRange( KeySelector end, GetRangeLimits limits, bool snapshot, - bool reverse ) const + bool reverse ) { - // do stuff here - return Standalone(); + // ignore snapshot, which is not used + return getRangeAggregationActor(this, ryw, begin, end, limits, reverse); } Future> PrivateKeySpace::get( @@ -48,5 +194,6 @@ Future> PrivateKeySpace::get( const Key& key, bool snapshot) { - return getActor(this, ryw, key, snapshot); + // ignore snapshot, which is not used + return getActor(this, ryw, key); } \ No newline at end of file diff --git a/fdbclient/PrivateKeySpace.h b/fdbclient/PrivateKeySpace.h index c221f51e1c..2f6c8c71a1 100644 --- a/fdbclient/PrivateKeySpace.h +++ b/fdbclient/PrivateKeySpace.h @@ -11,12 +11,18 @@ class ReadYourWritesTransaction; class PrivateKeyRangeBaseImpl { public: virtual Future> getRange(ReadYourWritesTransaction* ryw, KeySelector begin, KeySelector end, GetRangeLimits limits, bool snapshot = false, bool reverse = false) const = 0; + + KeyRangeRef getKeyRange() const { + return range; + } +protected: + KeyRangeRef range; }; // This class class PrivateKeyRangeSimpleImpl : public PrivateKeyRangeBaseImpl { public: - virtual Future> getRange(ReadYourWritesTransaction* ryw, const KeyRange& keys) const = 0; + virtual Future> getRange(ReadYourWritesTransaction* ryw, KeyRangeRef kr) const = 0; virtual Future> getRange(ReadYourWritesTransaction* ryw, KeySelector begin, KeySelector end, GetRangeLimits limits, bool snapshot = false, bool reverse = false) const; }; @@ -29,24 +35,21 @@ class PrivateKeySpace { public: Future> get(ReadYourWritesTransaction* ryw, const Key& key, bool snapshot = false); - Future> getRange(ReadYourWritesTransaction* ryw, KeySelector begin, KeySelector end, GetRangeLimits limits, bool snapshot = false, bool reverse = false) const; + Future> getRange(ReadYourWritesTransaction* ryw, KeySelector begin, KeySelector end, GetRangeLimits limits, bool snapshot = false, bool reverse = false); void registerKeyRange(const KeyRange& kr, PrivateKeyRangeBaseImpl* impl) { impls.insert(kr, impl); } -private: + RangeMap::Iterator getIteratorForKey(const Key& key) { + return impls.rangeContaining(key); + } - // ACTOR Future> getActor(ReadYourWritesTransaction* ryw, const Key& key, bool snapshot) { - // // use getRange to workaround this - // Standalone result = wait(getRange(ryw, KeySelector( firstGreaterOrEqual(key), key.arena() ), - // KeySelector( firstGreaterOrEqual(keyAfter(key)), key.arena() ), GetRangeLimits(1), snapshot)); - // if (result.size()) { - // return Optional(result[0].value); - // } else { - // return Optional(); - // } - // }; + KeyRangeMap* getKeyRangeMap(){ + return &impls; + } + +private: KeyRangeMap impls; }; From 1c6aef76b53b410c241fdac8fd252b26e0063337 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Mon, 24 Feb 2020 12:39:04 -0800 Subject: [PATCH 0696/1604] When one of the sqlite reader or writer thread pools fail, fail the other with the same error. --- fdbserver/CoroFlow.actor.cpp | 12 +++++++----- fdbserver/KeyValueStoreSQLite.actor.cpp | 6 ++++-- flow/IThreadPool.cpp | 2 +- flow/IThreadPool.h | 2 +- flow/Trace.cpp | 2 +- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/fdbserver/CoroFlow.actor.cpp b/fdbserver/CoroFlow.actor.cpp index 22eaab2b0f..751e89855c 100644 --- a/fdbserver/CoroFlow.actor.cpp +++ b/fdbserver/CoroFlow.actor.cpp @@ -180,10 +180,10 @@ class WorkPool : public IThreadPool, public ReferenceCounted stopOnError( WorkPool* w ) { try { wait( w->getError() ); + ASSERT(false); } catch (Error& e) { - w->error = e; + w->stop(e); } - w->stop(); return Void(); } @@ -230,12 +230,14 @@ public: } else pool->queueLock.leave(); } - virtual Future stop() { - if (error.code() == invalid_error_code) error = success(); + virtual Future stop(Error const& e) { + if (error.code() == invalid_error_code) { + error = e; + } pool->queueLock.enter(); TraceEvent("WorkPool_Stop").detail("Workers", pool->workers.size()).detail("Idle", pool->idle.size()) - .detail("Work", pool->work.size()); + .detail("Work", pool->work.size()).error(e, true); for (uint32_t i=0; iwork.size(); i++) pool->work[i]->cancel(); // What if cancel() does something to this? diff --git a/fdbserver/KeyValueStoreSQLite.actor.cpp b/fdbserver/KeyValueStoreSQLite.actor.cpp index 7f134dc0a0..598bcc0f57 100644 --- a/fdbserver/KeyValueStoreSQLite.actor.cpp +++ b/fdbserver/KeyValueStoreSQLite.actor.cpp @@ -1858,13 +1858,15 @@ private: ACTOR static Future stopOnError( KeyValueStoreSQLite* self ) { try { wait( self->readThreads->getError() || self->writeThread->getError() ); + ASSERT(false); } catch (Error& e) { if (e.code() == error_code_actor_cancelled) throw; + + self->readThreads->stop(e); + self->writeThread->stop(e); } - self->readThreads->stop(); - self->writeThread->stop(); return Void(); } diff --git a/flow/IThreadPool.cpp b/flow/IThreadPool.cpp index 6fece60f59..362eee4598 100644 --- a/flow/IThreadPool.cpp +++ b/flow/IThreadPool.cpp @@ -78,7 +78,7 @@ class ThreadPool : public IThreadPool, public ReferenceCounted { public: ThreadPool() : dontstop(ios), mode(Run) {} ~ThreadPool() {} - Future stop() { + Future stop(Error const& e = success()) { if (mode == Shutdown) return Void(); ReferenceCounted::addref(); ios.stop(); // doesn't work? diff --git a/flow/IThreadPool.h b/flow/IThreadPool.h index 39d5d484a8..af76010895 100644 --- a/flow/IThreadPool.h +++ b/flow/IThreadPool.h @@ -60,7 +60,7 @@ public: virtual Future getError() = 0; // asynchronously throws an error if there is an internal error virtual void addThread( IThreadPoolReceiver* userData ) = 0; virtual void post( PThreadAction action ) = 0; - virtual Future stop() = 0; + virtual Future stop(Error const& e = success()) = 0; virtual bool isCoro() const { return false; } virtual void addref() = 0; virtual void delref() = 0; diff --git a/flow/Trace.cpp b/flow/Trace.cpp index e9a350afed..8e70a47aaf 100644 --- a/flow/Trace.cpp +++ b/flow/Trace.cpp @@ -65,7 +65,7 @@ public: errors.sendError( unknown_error() ); } } - Future stop() { + Future stop(Error const& e) { return Void(); } void addref() { From 84b8f7ce9bc39c9f988f4d66b36a796ee3e91d37 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Mon, 24 Feb 2020 13:22:29 -0800 Subject: [PATCH 0697/1604] Add release notes. --- documentation/sphinx/source/release-notes.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/documentation/sphinx/source/release-notes.rst b/documentation/sphinx/source/release-notes.rst index 4061a9afc8..97f51b9814 100644 --- a/documentation/sphinx/source/release-notes.rst +++ b/documentation/sphinx/source/release-notes.rst @@ -8,6 +8,9 @@ Release Notes Performance ----------- +* Improve GRV tail latencies, particularly as the transaction rate gets nearer the ratekeeper limit. `(PR #2735) `_ +* The proxies are now more responsive to changes in workload when unthrottling lower priority transactions. `(PR #2735) `_ + Fixes ----- From 636c256606fc1748f215d26103796a979b42cca2 Mon Sep 17 00:00:00 2001 From: Steve Atherton Date: Mon, 24 Feb 2020 15:18:34 -0800 Subject: [PATCH 0698/1604] Simplified return value of CommitSubtree to a single ChildLinkSetRef, which doesn't contain a version. Removed unnecessary record copying in several places in CommitSubtree. --- fdbserver/VersionedBTree.actor.cpp | 122 ++++++++++++++--------------- 1 file changed, 60 insertions(+), 62 deletions(-) diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index 4f1338ff54..36f9609092 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -3081,13 +3081,19 @@ public: } private: - struct VersionAndChildrenRef { - VersionAndChildrenRef(Version v, VectorRef children, RedwoodRecordRef upperBound) - : version(v), children(children), upperBound(upperBound) { + struct ChildLinksRef { + ChildLinksRef() = default; + + ChildLinksRef(VectorRef children, RedwoodRecordRef upperBound) + : children(children), upperBound(upperBound) { } - VersionAndChildrenRef(Arena &arena, const VersionAndChildrenRef &toCopy) - : version(toCopy.version), children(arena, toCopy.children), upperBound(arena, toCopy.upperBound) { + ChildLinksRef(const RedwoodRecordRef *child, const RedwoodRecordRef *upperBound) + : children((RedwoodRecordRef *)child, 1), upperBound(*upperBound) { + } + + ChildLinksRef(Arena &arena, const ChildLinksRef &toCopy) + : children(arena, toCopy.children), upperBound(arena, toCopy.upperBound) { } int expectedSize() const { @@ -3095,16 +3101,13 @@ private: } std::string toString() const { - return format("{version=%" PRId64 " children=%s upperbound=%s}", version, ::toString(children).c_str(), upperBound.toString().c_str()); + return format("{children=%s upperbound=%s}", ::toString(children).c_str(), upperBound.toString().c_str()); } - Version version; VectorRef children; RedwoodRecordRef upperBound; }; - typedef VectorRef VersionedChildrenT; - // Utility class for building a vector of internal page entries. // Entries must be added in version order. Modified will be set to true // if any entries differ from the original ones. Additional entries will be @@ -3146,7 +3149,7 @@ private: } public: // Add the child entries from newSet into entries - void addEntries(VersionAndChildrenRef newSet) { + void addEntries(ChildLinksRef newSet) { // If there are already entries, the last one links to a child page, and its upper bound is not the same // as the first lowerBound in newSet (or newSet is empty, as the next newSet is necessarily greater) // then add the upper bound of the previous set as a value-less record so that on future reads @@ -3833,7 +3836,7 @@ private: // Returns list of (version, internal page records, required upper bound) // iMutationBoundary is greatest boundary <= lowerBound->key // iMutationBoundaryEnd is least boundary >= upperBound->key - ACTOR static Future> commitSubtree( + ACTOR static Future> commitSubtree( VersionedBTree *self, MutationBuffer *mutationBuffer, //MutationBuffer::const_iterator iMutationBoundary, // = mutationBuffer->upper_bound(lowerBound->key); --iMutationBoundary; @@ -3854,7 +3857,7 @@ private: } state Version writeVersion = self->getLastCommittedVersion() + 1; - state Standalone results; + state Standalone result; debug_printf("%s lower=%s upper=%s\n", context.c_str(), lowerBound->toString().c_str(), upperBound->toString().c_str()); debug_printf("%s decodeLower=%s decodeUpper=%s\n", context.c_str(), decodeLowerBound->toString().c_str(), decodeUpperBound->toString().c_str()); @@ -3887,20 +3890,20 @@ private: // do not go into this subtree. if(iMutationBoundary == iMutationBoundaryEnd) { if(iMutationBoundary.mutation().boundaryChanged) { - debug_printf("%s lower and upper bound key/version match and key is modified so deleting page, returning %s\n", context.c_str(), toString(results).c_str()); + debug_printf("%s lower and upper bound key/version match and key is modified so deleting page, returning %s\n", context.c_str(), toString(result).c_str()); if(isLeaf) { self->freeBtreePage(rootID, writeVersion); } else { self->m_lazyDeleteQueue.pushBack(LazyDeleteQueueEntry{writeVersion, rootID}); } - return results; + return result; } // Otherwise, no changes to this subtree - results.push_back_deep(results.arena(), VersionAndChildrenRef(0, VectorRef((RedwoodRecordRef *)decodeLowerBound, 1), *decodeUpperBound)); - debug_printf("%s page contains a single key '%s' which is not changing, returning %s\n", context.c_str(), lowerBound->key.toString().c_str(), toString(results).c_str()); - return results; + result.contents() = ChildLinksRef(decodeLowerBound, decodeUpperBound); + debug_printf("%s page contains a single key '%s' which is not changing, returning %s\n", context.c_str(), lowerBound->key.toString().c_str(), toString(result).c_str()); + return result; } // If one mutation range covers the entire subtree, then check if the entire subtree is modified, @@ -3954,21 +3957,21 @@ private: // If no changes in subtree if(unchanged) { - results.push_back_deep(results.arena(), VersionAndChildrenRef(0, VectorRef((RedwoodRecordRef *)decodeLowerBound, 1), *decodeUpperBound)); - debug_printf("%s no changes on this subtree, returning %s\n", context.c_str(), toString(results).c_str()); - return results; + result.contents() = ChildLinksRef(decodeLowerBound, decodeUpperBound); + debug_printf("%s no changes on this subtree, returning %s\n", context.c_str(), toString(result).c_str()); + return result; } // If subtree is cleared if(cleared) { - debug_printf("%s %s cleared, deleting it, returning %s\n", context.c_str(), isLeaf ? "Page" : "Subtree", toString(results).c_str()); + debug_printf("%s %s cleared, deleting it, returning %s\n", context.c_str(), isLeaf ? "Page" : "Subtree", toString(result).c_str()); if(isLeaf) { self->freeBtreePage(rootID, writeVersion); } else { self->m_lazyDeleteQueue.pushBack(LazyDeleteQueueEntry{writeVersion, rootID}); } - return results; + return result; } } @@ -4200,9 +4203,9 @@ private: // No changes were actually made. This could happen if the only mutations are clear ranges which do not match any records. if(!changesMade) { - results.push_back_deep(results.arena(), VersionAndChildrenRef(0, VectorRef((RedwoodRecordRef *)decodeLowerBound, 1), *decodeUpperBound)); - debug_printf("%s No changes were made during mutation merge, returning %s\n", context.c_str(), toString(results).c_str()); - return results; + result.contents() = ChildLinksRef(decodeLowerBound, decodeUpperBound); + debug_printf("%s No changes were made during mutation merge, returning %s\n", context.c_str(), toString(result).c_str()); + return result; } else { debug_printf("%s Changes were made, writing.\n", context.c_str()); @@ -4213,43 +4216,43 @@ private: if(updating) { const BTreePage::BinaryTree &deltaTree = ((const BTreePage *)newPage->begin())->tree(); if(deltaTree.numItems == 0) { - debug_printf("%s Page updates cleared all entries, returning %s\n", context.c_str(), toString(results).c_str()); + debug_printf("%s Page updates cleared all entries, returning %s\n", context.c_str(), toString(result).c_str()); self->freeBtreePage(rootID, writeVersion); - return results; + return result; } else { // Otherwise update it. - BTreePageID newID = wait(self->updateBtreePage(self, rootID, &results.arena(), newPage, writeVersion)); + BTreePageID newID = wait(self->updateBtreePage(self, rootID, &result.arena(), newPage, writeVersion)); - // Set the child page ID, which has already been allocated in results.arena() - RedwoodRecordRef rec = decodeLowerBound->withoutValue(); - rec.setChildPage(newID); + // Set the child page ID, which has already been allocated in result.arena() + RedwoodRecordRef *rec = new (result.arena()) RedwoodRecordRef(decodeLowerBound->withoutValue()); + rec->setChildPage(newID); - results.push_back_deep(results.arena(), VersionAndChildrenRef(writeVersion, VectorRef(&rec, 1), *decodeUpperBound)); - debug_printf("%s Page updated in-place, returning %s\n", context.c_str(), toString(results).c_str()); + result.contents() = ChildLinksRef(rec, decodeUpperBound); + debug_printf("%s Page updated in-place, returning %s\n", context.c_str(), toString(result).c_str()); ++counts.pageUpdates; - return results; + return result; } } // If everything in the page was deleted then this page should be deleted as of the new version // Note that if a single range clear covered the entire page then we should not get this far if(merged.empty()) { - debug_printf("%s All leaf page contents were cleared, returning %s\n", context.c_str(), toString(results).c_str()); + debug_printf("%s All leaf page contents were cleared, returning %s\n", context.c_str(), toString(result).c_str()); self->freeBtreePage(rootID, writeVersion); - return results; + return result; } state Standalone> entries = wait(writePages(self, true, lowerBound, upperBound, merged, btPage->height, writeVersion, rootID)); - results.arena().dependsOn(entries.arena()); - results.push_back(results.arena(), VersionAndChildrenRef(writeVersion, entries, *upperBound)); - debug_printf("%s Merge complete, returning %s\n", context.c_str(), toString(results).c_str()); - return results; + result.arena().dependsOn(entries.arena()); + result.contents() = ChildLinksRef(entries, *upperBound); + debug_printf("%s Merge complete, returning %s\n", context.c_str(), toString(result).c_str()); + return result; } else { // Internal Page ASSERT(!isLeaf); - state std::vector>> futureChildren; + state std::vector>> futureChildren; cursor = getCursor(page); cursor.moveFirst(); @@ -4313,11 +4316,10 @@ private: InternalPageBuilder pageBuilder(c); for(int i = 0; i < futureChildren.size(); ++i) { - VersionedChildrenT versionedChildren = futureChildren[i].get(); - ASSERT(versionedChildren.size() <= 1); + ChildLinksRef c = futureChildren[i].get(); - if(!versionedChildren.empty()) { - pageBuilder.addEntries(versionedChildren.front()); + if(!c.children.empty()) { + pageBuilder.addEntries(c); } } @@ -4327,9 +4329,9 @@ private: if(pageBuilder.modified) { // If the page now has no children if(pageBuilder.childPageCount == 0) { - debug_printf("%s All internal page children were deleted so deleting this page too, returning %s\n", context.c_str(), toString(results).c_str()); + debug_printf("%s All internal page children were deleted so deleting this page too, returning %s\n", context.c_str(), toString(result).c_str()); self->freeBtreePage(rootID, writeVersion); - return results; + return result; } else { debug_printf("%s Internal page modified, creating replacements.\n", context.c_str()); @@ -4339,16 +4341,16 @@ private: Standalone> childEntries = wait(holdWhile(pageBuilder.entries, writePages(self, false, lowerBound, upperBound, pageBuilder.entries, btPage->height, writeVersion, rootID))); - results.arena().dependsOn(childEntries.arena()); - results.push_back(results.arena(), VersionAndChildrenRef(0, childEntries, *upperBound)); - debug_printf("%s Internal modified, returning %s\n", context.c_str(), toString(results).c_str()); - return results; + result.arena().dependsOn(childEntries.arena()); + result.contents() = ChildLinksRef(childEntries, *upperBound); + debug_printf("%s Internal modified, returning %s\n", context.c_str(), toString(result).c_str()); + return result; } } else { - results.push_back_deep(results.arena(), VersionAndChildrenRef(0, VectorRef((RedwoodRecordRef *)decodeLowerBound, 1), *decodeUpperBound)); - debug_printf("%s Page has no changes, returning %s\n", context.c_str(), toString(results).c_str()); - return results; + result.contents() = ChildLinksRef(decodeLowerBound, decodeUpperBound); + debug_printf("%s Page has no changes, returning %s\n", context.c_str(), toString(result).c_str()); + return result; } } } @@ -4384,15 +4386,11 @@ private: state Standalone rootPageID = self->m_header.root.get(); state RedwoodRecordRef lowerBound = dbBegin.withPageID(rootPageID); - Standalone versionedRoots = wait(commitSubtree(self, mutations, self->m_pager->getReadSnapshot(latestVersion), rootPageID, self->m_header.height == 1, &lowerBound, &dbEnd, &lowerBound, &dbEnd)); - debug_printf("CommitSubtree(root %s) returned %s\n", toString(rootPageID).c_str(), toString(versionedRoots).c_str()); - - // CommitSubtree on the root can only return 1 child at most because the pager interface only supports writing - // one meta record (which contains the root page) per commit. - ASSERT(versionedRoots.size() <= 1); + Standalone newRootChildren = wait(commitSubtree(self, mutations, self->m_pager->getReadSnapshot(latestVersion), rootPageID, self->m_header.height == 1, &lowerBound, &dbEnd, &lowerBound, &dbEnd)); + debug_printf("CommitSubtree(root %s) returned %s\n", toString(rootPageID).c_str(), toString(newRootChildren).c_str()); // If the old root was deleted, write a new empty tree root node and free the old roots - if(versionedRoots.empty()) { + if(newRootChildren.children.empty()) { debug_printf("Writing new empty root.\n"); LogicalPageID newRootID = wait(self->m_pager->newPageID()); Reference page = self->m_pager->newPageBuffer(); @@ -4402,7 +4400,7 @@ private: rootPageID = BTreePageID((LogicalPageID *)&newRootID, 1); } else { - Standalone> newRootLevel(versionedRoots.front().children, versionedRoots.arena()); + Standalone> newRootLevel(newRootChildren.children, newRootChildren.arena()); if(newRootLevel.size() == 1) { rootPageID = newRootLevel.front().getChildPage(); } From b76f68d0e2ce97681c4b7a99b0c1eed4beb0ec69 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 24 Feb 2020 16:08:04 -0800 Subject: [PATCH 0699/1604] Add a --no-hints flag to fdbcli to disable linenoise hints --- fdbcli/fdbcli.actor.cpp | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/fdbcli/fdbcli.actor.cpp b/fdbcli/fdbcli.actor.cpp index 67f4a13aca..e76095f737 100644 --- a/fdbcli/fdbcli.actor.cpp +++ b/fdbcli/fdbcli.actor.cpp @@ -66,6 +66,7 @@ enum { OPT_TIMEOUT, OPT_EXEC, OPT_NO_STATUS, + OPT_NO_HINTS, OPT_STATUS_FROM_JSON, OPT_VERSION, OPT_TRACE_FORMAT @@ -79,6 +80,7 @@ CSimpleOpt::SOption g_rgOptions[] = { { OPT_CONNFILE, "-C", SO_REQ_SEP }, { OPT_TIMEOUT, "--timeout", SO_REQ_SEP }, { OPT_EXEC, "--exec", SO_REQ_SEP }, { OPT_NO_STATUS, "--no-status", SO_NONE }, + { OPT_NO_HINTS, "--no-hints", SO_NONE }, { OPT_HELP, "-?", SO_NONE }, { OPT_HELP, "-h", SO_NONE }, { OPT_HELP, "--help", SO_NONE }, @@ -2461,17 +2463,18 @@ void LogCommand(std::string line, UID randomID, std::string errMsg) { struct CLIOptions { std::string program_name; - int exit_code; + int exit_code = -1; std::string commandLine; std::string clusterFile; - bool trace; + bool trace = false; std::string traceDir; std::string traceFormat; - int exit_timeout; + int exit_timeout = 0; Optional exec; - bool initialStatusCheck; + bool initialStatusCheck = true; + bool cliHints = true; std::string tlsCertPath; std::string tlsKeyPath; std::string tlsVerifyPeers; @@ -2479,10 +2482,6 @@ struct CLIOptions { std::string tlsPassword; CLIOptions( int argc, char* argv[] ) - : trace(false), - exit_timeout(0), - initialStatusCheck(true), - exit_code(-1) { program_name = argv[0]; for (int a = 0; a runCli(CLIOptions opt) { [](std::string const& line, std::vector& completions) { fdbcli_comp_cmd(line, completions); }, - [](std::string const& line)->LineNoise::Hint { - int firstWordIdx = line.find(' '); - if (firstWordIdx == std::string::npos) { - firstWordIdx = line.size(); - } - auto iter = helpMap.find(line.substr(0, firstWordIdx)); - if (iter != helpMap.end()) { - return LineNoise::Hint(iter->second.usage.substr(firstWordIdx), 0, false); + [enabled=opt.cliHints](std::string const& line)->LineNoise::Hint { + if (enabled) { + int firstWordIdx = line.find(' '); + if (firstWordIdx == std::string::npos) { + firstWordIdx = line.size(); + } + auto iter = helpMap.find(line.substr(0, firstWordIdx)); + if (iter != helpMap.end()) { + return LineNoise::Hint(iter->second.usage.substr(firstWordIdx), 0, false); + } } return LineNoise::Hint(); }, From 0f7656e52ed849d816a6363f2d04c716ffb3027f Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Tue, 25 Feb 2020 08:45:51 -0800 Subject: [PATCH 0700/1604] Document roughness. Remove an unexplained factor of 2 and handle window edges better. Subtract 1 from roughness to correspond better to variance. --- flow/Stats.actor.cpp | 19 +++++++++++++++---- flow/Stats.h | 20 +++++++++++++++++--- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/flow/Stats.actor.cpp b/flow/Stats.actor.cpp index 751130bc25..65a8df3f9d 100644 --- a/flow/Stats.actor.cpp +++ b/flow/Stats.actor.cpp @@ -22,7 +22,7 @@ #include "flow/actorcompiler.h" // has to be last include Counter::Counter(std::string const& name, CounterCollection& collection) -: name(name), interval_start(0), last_event(0), interval_sq_time(0), interval_start_value(0), interval_delta(0) +: name(name), interval_start(0), last_event(0), interval_sq_time(0), interval_start_value(0), interval_delta(0), roughness_interval_start(0) { metric.init(collection.name + "." + (char)toupper(name.at(0)) + name.substr(1), collection.id); collection.counters.push_back(this); @@ -45,13 +45,21 @@ double Counter::getRate() const { } double Counter::getRoughness() const { - double elapsed = now() - interval_start; + double elapsed = now() - roughness_interval_start; if(elapsed == 0) { return 0; } + // If we have time samples t in T, and let: + // n = size(T) = interval_delta + // m = mean(T) = elapsed / interval_delta + // v = sum(t^2) for t in T = interval_sq_time + // + // The formula below is: (v/(m*n)) / m - 1 + // This is equivalent to (v/n - m^2) / m^2 = Variance(T)/m^2 + // Variance(T)/m^2 is equal to Variance(t/m) for t in T double delay = interval_sq_time / elapsed; - return delay * getRate() * 2; + return delay * interval_delta / elapsed - 1; } void Counter::resetInterval() { @@ -59,7 +67,10 @@ void Counter::resetInterval() { interval_delta = 0; interval_sq_time = 0; interval_start = now(); - last_event = interval_start; // Date: Tue, 25 Feb 2020 11:08:45 -0800 Subject: [PATCH 0701/1604] A refactored draft version, without any tests --- fdbclient/PrivateKeySpace.actor.cpp | 334 ++++++++++++++++------------ fdbclient/PrivateKeySpace.h | 29 ++- 2 files changed, 210 insertions(+), 153 deletions(-) diff --git a/fdbclient/PrivateKeySpace.actor.cpp b/fdbclient/PrivateKeySpace.actor.cpp index 4a33a6f8ea..8a1d92c784 100644 --- a/fdbclient/PrivateKeySpace.actor.cpp +++ b/fdbclient/PrivateKeySpace.actor.cpp @@ -1,6 +1,7 @@ #include "fdbclient/PrivateKeySpace.h" #include "flow/actorcompiler.h" // This must be the last #include. +namespace { ACTOR Future> getActor( PrivateKeySpace* pks, ReadYourWritesTransaction* ryw, @@ -17,6 +18,56 @@ ACTOR Future> getActor( } } +// This function will move the given KeySelector to toward a standard KeySelector: +// orEqual == false && offset == 1 +// If the corresponding key is not in this private key range, it will move as far as possible to adjust the offset to 1 +// It looks like taking more time here since we query all keys twice in the worst case. +// However, moving the KeySelector while handling other parameters like limits makes the code much more complex and hard to maintain +// Seperate each part to make the code easy to understand and more compact +ACTOR Future normalizeKeySelectorActor( + const PrivateKeyRangeBaseImpl* pkrImpl, + ReadYourWritesTransaction* ryw, + KeySelector* ks ) +{ + ASSERT(!ks->orEqual); // should be removed before calling + + state KeyRangeRef range = pkrImpl->getKeyRange(); + state KeyRef startKey = range.begin; + state KeyRef endKey = range.end; + + if (ks->offset < 1) { + // less than the given key + if (range.contains(ks->getKey())) + endKey = keyAfter(ks->getKey()); + } + else { + // greater than the given key + if (range.contains(ks->getKey())) + startKey = ks->getKey(); + } + + Standalone result = wait(pkrImpl->getRange(ryw, KeyRangeRef(startKey, endKey))); + // TODO : KeySelector::setKey has bytes limit according to the knob, customize it if needed + if (ks->offset < 1) { + if (result.size() >= 1 - ks->offset) { + ks->setKey(result[result.size()-(1-ks->offset)].key); + ks->offset = 1; + } else { + ks->setKey(result[0].key); + ks->offset += result.size(); + } + } else { + if (result.size() >= ks->offset - 1) { + ks->setKey(result[ks->offset - 2].key); + ks->offset = 1; + } else { + ks->setKey(result[result.size()-1].key); + ks->offset -= result.size(); + } + } + return Void(); +} + ACTOR Future> getRangeAggregationActor( PrivateKeySpace* pks, ReadYourWritesTransaction* ryw, @@ -25,158 +76,158 @@ ACTOR Future> getRangeAggregationActor( GetRangeLimits limits, bool reverse ) { - // This function handles ranges lie over more than one underlying keyrane and aggregates all results - // GetRangeLimits and reverse are also handled here - - // do parameter validation check stuff - if( limits.isReached() ) { - TEST(true); // RYW range read limit 0 - return Standalone(); - } - - // TODO: check the reason here - // if( !limits.isValid() ) - // return range_limits_invalid(); + // This function handles ranges cover more than one keyrange and aggregates all results + // KeySelector, GetRangeLimits and reverse are all handled here - // erase equal here - if( begin.orEqual ) + // make sure orEqual == false + if(begin.orEqual) begin.removeOrEqual(begin.arena()); + if(end.orEqual) + end.removeOrEqual(end.arena()); - if( end.orEqual ) - end.removeOrEqual(end.arena()); - + // make sure offset == 1 + state RangeMap::Iterator iter = + pks->getKeyRangeMap()->rangeContaining(begin.getKey()); + while (begin.offset != 1 && iter != pks->getKeyRangeMap()->ranges().begin()) { + wait(normalizeKeySelectorActor(iter->value(), ryw, &begin)); + --iter; + } + if (begin.offset != 1) { + // The Key Selector points to key outside the whole private key space + // TODO : Throw error here to indicate the case + TEST(true); + } + iter = pks->getKeyRangeMap()->rangeContaining(end.getKey()); + while (end.offset != 1 && iter != pks->getKeyRangeMap()->ranges().end()) { + wait(normalizeKeySelectorActor(iter->value(), ryw, &end)); + ++iter; + } + if (end.offset != 1) { + // The Key Selector points to key outside the whole private key space + // TODO : Throw error here to indicate the case + TEST(true); + } + // return if range inverted if( begin.offset >= end.offset && begin.getKey() >= end.getKey() ) { - TEST(true); //range inverted + TEST(true); return Standalone(); } - - state std::deque resultRef; - state Standalone result; - // state RangeMap::Iterator iter; - state RangeMap::Ranges ranges; - - - // Handle the case where begin offset is zero or negative, which means at least one key before begin.key needs to be read - state RangeMap::Iterator iter = pks->getKeyRangeMap()->rangeContaining(begin.getKey()); - // state RangeMap::Iterator prev = curr; - // --prev; - if (begin.offset <= 0) { - state int remains = 1 - begin.offset; - while (remains > 0) { - if (iter.value() == NULL) { - if (iter == pks->getKeyRangeMap()->ranges().begin()) - break; - else - --iter; - continue; - } - state Standalone temp = wait(iter->value()->getRange( - ryw, - KeySelector(firstGreaterOrEqual(iter.value()->getKeyRange().begin)), - KeySelectorRef(firstGreaterOrEqual(begin.getKey())), - GetRangeLimits() - )); - for (int i = temp.size() - 1; i >= 0 && remains > 0; --i) { - resultRef.push_front(temp[i]); - remains--; - } - if (iter == pks->getKeyRangeMap()->ranges().begin()) - break; - else - --iter; - } - if (remains > 0) { - //throw error here - } - } - // Check limits here - - // (begin.key, range.end) - - // contained range query - - // (range.begin, end.key) - - // end.offset > 0 - // The interesting range is in (begin.key, end.key) - ranges = pks->getKeyRangeMap()->intersectingRanges(KeyRangeRef(begin.getKey(), end.getKey())); - for (iter = ranges.begin(); iter != ranges.end(); ++iter) { - if (iter->value() == NULL) continue; - KeyRangeRef kr = iter->range(); - Standalone pairs = wait(iter->value()->getRange(ryw, - KeySelector( firstGreaterOrEqual(kr.begin) ), - KeySelector( firstGreaterOrEqual(kr.end)), - GetRangeLimits() - )); - result.append_deep(result.arena(), pairs.begin(), pairs.size()); - } - if(begin.offset - end.offset <= result.size()){ - result.pop_front(begin.offset); - for (int i =0; i<-end.offset; i++) - result.pop_back(); + state Standalone result; + state RangeMap::Ranges ranges = + pks->getKeyRangeMap()->intersectingRanges(KeyRangeRef(begin.getKey(), end.getKey())); + // reverse handler + // TODO : workaround to write this two together to make the code compact + iter = reverse ? ranges.end() : ranges.begin(); + if (reverse) { + while (iter != ranges.begin()) { + --iter; + if (iter->value() == NULL) + continue; + KeyRangeRef kr = iter->range(); + KeyRef keyStart = kr.contains(begin.getKey()) ? begin.getKey() : kr.begin; + KeyRef keyEnd = kr.contains(end.getKey()) ? end.getKey() : kr.end; + Standalone pairs = wait(iter->value()->getRange(ryw, KeyRangeRef(keyStart, keyEnd))); + // limits handler + for (int i = pairs.size() - 1; i >= 0; --i) { + result.push_back_deep(result.arena(), pairs[i]); + limits.decrement(pairs[i]); + if (limits.isReached()) + return result; + } + } } else { - return Standalone(); + for (iter = ranges.begin(); iter != ranges.end(); ++iter) { + if (iter->value() == NULL) + continue; + KeyRangeRef kr = iter->range(); + KeyRef keyStart = kr.contains(begin.getKey()) ? begin.getKey() : kr.begin; + KeyRef keyEnd = kr.contains(end.getKey()) ? end.getKey() : kr.end; + Standalone pairs = wait(iter->value()->getRange(ryw, KeyRangeRef(keyStart, keyEnd))); + // limits handler + for (const KeyValueRef & kv : pairs) { + result.push_back_deep(result.arena(), kv); + limits.decrement(kv); + if (limits.isReached()) + return result; + } + } } return result; } -ACTOR Future> getRangeActor( - const PrivateKeyRangeSimpleImpl* pkrSimpleImpl, - ReadYourWritesTransaction* ryw, - KeySelector begin, - KeySelector end ) -{ - // If the start key or end key lies outside this keyrange, it cannot handle the case. - // Thus, it is forced for the quired range lies the this keyrange - // It assumes the begin of the range is never used as a key - KeyRangeRef range = pkrSimpleImpl->getKeyRange(); - if (begin.orEqual) - begin.removeOrEqual(begin.arena()); - ASSERT(begin.offset > 0 && begin.getKey() >= range.begin); - if (end.orEqual) - end.removeOrEqual(end.arena()); - ASSERT(end.offset <= 0 && end.getKey() <= range.end); +// ACTOR Future moveKeySelectorActor( +// const PrivateKeyRangeBaseImpl* pkrImpl, +// ReadYourWritesTransaction* ryw, +// KeySelector* begin, +// KeySelector* end, +// std::deque& result ) +// { +// ASSERT(!begin->orEqual && !end->orEqual); +// // If the start key or end key lies outside this keyrange, it cannot handle the case. +// // Thus, it is forced for the quired range lies the this keyrange +// // It assumes the begin of the range is never used as a key +// state KeyRangeRef range = pkrImpl->getKeyRange(); +// state KeyRef startKey = range.begin; +// state KeyRef endKey = range.end; +// state int choice; +// if (begin->offset != 1) { +// if (begin->offset < 1) { +// choice = 0; +// if (range.contains(begin->getKey())) +// endKey = keyAfter(begin->getKey()); +// else { +// choice = 1; +// if (range.contains(begin->getKey())) +// startKey = begin->getKey(); +// } +// } else { +// if (end->offset <= 1) { +// choice = 2; +// if (range.contains(begin->getKey())) +// startKey = begin->getKey(); +// if (range.contains(end->getKey())) +// endKey = end->getKey(); +// } else { +// choice = 3; +// if (range.contains(end->getKey())) +// startKey = end->getKey(); +// } +// } - KeyRangeRef kr(begin.getKey(), end.getKey()); - state Standalone result = wait(pkrSimpleImpl->getRange(ryw, kr)); - if (begin.offset - end.offset >= result.size()) { - // inverted select range - return Standalone(); - } else { - // TODO : may need optimization - // pop from head - result.pop_front(begin.offset); - // pop from end - for (int i = 0; i < -end.offset; ++i) result.pop_back(); - // if (limits.reachedBy(result)) { - // int idx; - // for (idx = 0; idx < result.size(); ++ idx) { - // limits.decrement(result[idx]); - // if (limits.isReached()) break; - // } - // while (idx < result.size()) { - // result.pop_back(); - // ++idx; - // } - // } - return result; - } -} - -Future> PrivateKeyRangeSimpleImpl::getRange( - ReadYourWritesTransaction* ryw, - KeySelector begin, - KeySelector end, - GetRangeLimits limits, - bool snapshot, - bool reverse ) const -{ - // ignore snapshot, which is invalid - // ignore reverse and limits, which is handled by PrivateKeySpace when doing aggregation - return getRangeActor(this, ryw, begin, end); -} +// state Standalone temp = wait(pkrImpl->getRange(ryw, KeyRangeRef(startKey, endKey))); +// if (choice == 0) { +// for (int i = temp.size() - 1; i >= 0; --i) { +// result.push_front(temp[i]); +// ++begin->offset; +// if (begin->offset == 1) return false; // TODO : add getLimits check here +// } +// return true; +// } else if (choice == 1) { +// if (begin->offset > 1) { +// if (begin->offset - 1 <= temp.size()) { +// temp.pop_front(begin->offset - 1); +// begin->offset = 1; +// } else { +// begin->offset -= temp.size(); +// return true; +// } +// } +// for (const KeyValueRef & kv : temp) +// result.push_back(kv); + +// if () +// } else { +// for (const KeyValueRef & kv : temp) { +// result.push_back(kv); +// --end->offset; +// if (end->offset == 1) return false; +// } +// return true; +// } +// } +} // namespace end Future> PrivateKeySpace::getRange( ReadYourWritesTransaction* ryw, KeySelector begin, @@ -185,6 +236,13 @@ Future> PrivateKeySpace::getRange( bool snapshot, bool reverse ) { + // validate limits here + if( !limits.isValid() ) + return range_limits_invalid(); + if( limits.isReached() ) { + TEST(true); // read limit 0 + return Standalone(); + } // ignore snapshot, which is not used return getRangeAggregationActor(this, ryw, begin, end, limits, reverse); } diff --git a/fdbclient/PrivateKeySpace.h b/fdbclient/PrivateKeySpace.h index 2f6c8c71a1..6f839413fe 100644 --- a/fdbclient/PrivateKeySpace.h +++ b/fdbclient/PrivateKeySpace.h @@ -10,27 +10,30 @@ class ReadYourWritesTransaction; class PrivateKeyRangeBaseImpl { public: - virtual Future> getRange(ReadYourWritesTransaction* ryw, KeySelector begin, KeySelector end, GetRangeLimits limits, bool snapshot = false, bool reverse = false) const = 0; + // virtual Future> getRange(ReadYourWritesTransaction* ryw, KeySelector begin, KeySelector end, GetRangeLimits limits, bool snapshot = false, bool reverse = false) const = 0; + // TODO : My opinion is that having this interface is enough for underlying keyrange implemention + // A key range doesn't have any knowledge about other key range, parameters like KeySelector, GetRangeLimits should be handled in PrivateKeySpace + // Thus, it is no need to have them here + virtual Future> getRange(ReadYourWritesTransaction* ryw, KeyRangeRef kr) const = 0; + explicit PrivateKeyRangeBaseImpl(KeyRef start, KeyRef end) { + // TODO : checker: make sure it is in valid key range + range = KeyRangeRef(start, end); + } KeyRangeRef getKeyRange() const { return range; } protected: - KeyRangeRef range; + KeyRangeRef range; // underlying key range for this function }; -// This class -class PrivateKeyRangeSimpleImpl : public PrivateKeyRangeBaseImpl { -public: - virtual Future> getRange(ReadYourWritesTransaction* ryw, KeyRangeRef kr) const = 0; - virtual Future> getRange(ReadYourWritesTransaction* ryw, KeySelector begin, KeySelector end, GetRangeLimits limits, bool snapshot = false, bool reverse = false) const; -}; -// class PrivateKeyRangeGetAllImpl : public PrivateKeyRangeSimpleGetRangeImpl { +// class PrivateKeyRangeSimpleImpl : public PrivateKeyRangeBaseImpl { // public: -// virtual Future> getRange(const KeyRange& keys, ReadYourWritesTransaction* ryw) const; -// virtual Future> get(ReadYourWritesTransaction* ryw) const = 0; +// virtual Future> getRange(ReadYourWritesTransaction* ryw, KeyRangeRef kr) const = 0; +// virtual Future> getRange(ReadYourWritesTransaction* ryw, KeySelector begin, KeySelector end, GetRangeLimits limits, bool snapshot = false, bool reverse = false) const; // }; + class PrivateKeySpace { public: Future> get(ReadYourWritesTransaction* ryw, const Key& key, bool snapshot = false); @@ -41,10 +44,6 @@ public: impls.insert(kr, impl); } - RangeMap::Iterator getIteratorForKey(const Key& key) { - return impls.rangeContaining(key); - } - KeyRangeMap* getKeyRangeMap(){ return &impls; } From 13a523a355247a4f7a7e549f69b2f975700a2f5e Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Tue, 25 Feb 2020 12:34:31 -0800 Subject: [PATCH 0702/1604] fix: commit on first proxy did not always commit to the first proxy --- fdbclient/MasterProxyInterface.h | 1 + fdbclient/MonitorLeader.actor.cpp | 1 + fdbclient/NativeAPI.actor.cpp | 8 ++++++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/fdbclient/MasterProxyInterface.h b/fdbclient/MasterProxyInterface.h index b1d12c5a0c..e82070c7bc 100644 --- a/fdbclient/MasterProxyInterface.h +++ b/fdbclient/MasterProxyInterface.h @@ -81,6 +81,7 @@ struct ClientDBInfo { constexpr static FileIdentifier file_identifier = 5355080; UID id; // Changes each time anything else changes vector< MasterProxyInterface > proxies; + Optional firstProxy; //not seralized, used for commitOnFirstProxy when the proxies vector has been shrunk double clientTxnInfoSampleRate; int64_t clientTxnInfoSizeLimit; Optional forward; diff --git a/fdbclient/MonitorLeader.actor.cpp b/fdbclient/MonitorLeader.actor.cpp index 6a5c4195de..a7364a77b0 100644 --- a/fdbclient/MonitorLeader.actor.cpp +++ b/fdbclient/MonitorLeader.actor.cpp @@ -685,6 +685,7 @@ void shrinkProxyList( ClientDBInfo& ni, std::vector& lastProxyUIDs, std::ve TraceEvent("ConnectedProxy").detail("Proxy", lastProxies[i].id()); } } + ni.firstProxy = ni.proxies[0]; ni.proxies = lastProxies; } } diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index 54c2680e4e..e2867e848f 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -2640,8 +2640,12 @@ ACTOR static Future tryCommit( Database cx, Reference req.debugID = commitID; state Future reply; if (options.commitOnFirstProxy) { - const std::vector& proxies = cx->clientInfo->get().proxies; - reply = proxies.size() ? throwErrorOr ( brokenPromiseToMaybeDelivered ( proxies[0].commit.tryGetReply(req) ) ) : Never(); + if(cx->clientInfo->get().firstProxy.present()) { + reply = throwErrorOr ( brokenPromiseToMaybeDelivered ( cx->clientInfo->get().firstProxy.get().commit.tryGetReply(req) ) ); + } else { + const std::vector& proxies = cx->clientInfo->get().proxies; + reply = proxies.size() ? throwErrorOr ( brokenPromiseToMaybeDelivered ( proxies[0].commit.tryGetReply(req) ) ) : Never(); + } } else { reply = loadBalance( cx->getMasterProxies(info.useProvisionalProxies), &MasterProxyInterface::commit, req, TaskPriority::DefaultPromiseEndpoint, true ); } From daee15cbb5b996d36bea515df7f7dd49f21511af Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Tue, 25 Feb 2020 12:35:24 -0800 Subject: [PATCH 0703/1604] fix: starting a DR should do the commit on the first proxy to ensure all mutations from previous backups have been flushed --- fdbclient/DatabaseBackupAgent.actor.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fdbclient/DatabaseBackupAgent.actor.cpp b/fdbclient/DatabaseBackupAgent.actor.cpp index c3fb295622..e1e01672bc 100644 --- a/fdbclient/DatabaseBackupAgent.actor.cpp +++ b/fdbclient/DatabaseBackupAgent.actor.cpp @@ -1840,6 +1840,9 @@ public: tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); tr->setOption(FDBTransactionOptions::LOCK_AWARE); + + //This commit must happen on the first proxy to ensure that the applier has flushed all mutations from previous DRs + tr->setOption(FDBTransactionOptions::COMMIT_ON_FIRST_PROXY); // We will use the global status for now to ensure that multiple backups do not start place with different tags state int status = wait(backupAgent->getStateValue(tr, logUidCurrent)); From f4bf2afffeb525e1c18df584aa3bd0bde0010b23 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Tue, 25 Feb 2020 13:39:36 -0800 Subject: [PATCH 0704/1604] Fix bad link in release notes --- documentation/sphinx/source/release-notes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/sphinx/source/release-notes.rst b/documentation/sphinx/source/release-notes.rst index bcb0b48754..453de62410 100644 --- a/documentation/sphinx/source/release-notes.rst +++ b/documentation/sphinx/source/release-notes.rst @@ -8,7 +8,7 @@ Release Notes Performance ----------- -* Reverse range reads could read too much data from disk, resulting in poor performance relative to forward range reads. `(PR #2650) `_. +* Reverse range reads could read too much data from disk, resulting in poor performance relative to forward range reads. `(PR #2650) `_. Fixes ----- From 71782ff803131b3f968e3c64e7ca63ef6d49aa2a Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Tue, 25 Feb 2020 15:30:19 -0800 Subject: [PATCH 0705/1604] Update fdbclient/MasterProxyInterface.h --- fdbclient/MasterProxyInterface.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbclient/MasterProxyInterface.h b/fdbclient/MasterProxyInterface.h index e82070c7bc..f0ddbe314b 100644 --- a/fdbclient/MasterProxyInterface.h +++ b/fdbclient/MasterProxyInterface.h @@ -81,7 +81,7 @@ struct ClientDBInfo { constexpr static FileIdentifier file_identifier = 5355080; UID id; // Changes each time anything else changes vector< MasterProxyInterface > proxies; - Optional firstProxy; //not seralized, used for commitOnFirstProxy when the proxies vector has been shrunk + Optional firstProxy; //not serialized, used for commitOnFirstProxy when the proxies vector has been shrunk double clientTxnInfoSampleRate; int64_t clientTxnInfoSizeLimit; Optional forward; From 034dfe5e42633066f66d7fd2bbcd4cfaa1491012 Mon Sep 17 00:00:00 2001 From: Xin Dong Date: Sun, 26 Jan 2020 21:11:15 -0800 Subject: [PATCH 0706/1604] Now the inability to flush trace logs will be reported to both 'stderr' and also the status json object. - Since the first flush failure, if the accumulated consecutive failure count exceeds the value defined in knobs, it will trigger the current worker process to report this issue via the 'GetServerDBInfo' interface of the cluster controler - A successful flush will reset the accumulated counter. Notice that the current solution does not take the time into consideration. The assumption is that flush failures tend to only happen in a clustered manner. The intermittent, but short, periods of flush failures are not considered as a problem since the memory pressure built by them should be negligible. --- fdbserver/Knobs.cpp | 2 ++ fdbserver/Knobs.h | 2 ++ fdbserver/WorkerInterface.actor.h | 7 ++++--- fdbserver/tester.actor.cpp | 5 ++++- fdbserver/worker.actor.cpp | 30 ++++++++++++++++++++++++++++-- flow/FileTraceLogWriter.cpp | 7 +++++++ flow/FileTraceLogWriter.h | 2 ++ flow/Trace.cpp | 5 +++++ flow/Trace.h | 2 ++ 9 files changed, 56 insertions(+), 6 deletions(-) diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index 37bdb8bce1..1bc3448e17 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -513,6 +513,8 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs, bool isSimula init( DEGRADED_RESET_INTERVAL, 24*60*60 ); if ( randomize && BUGGIFY ) DEGRADED_RESET_INTERVAL = 10; init( DEGRADED_WARNING_LIMIT, 1 ); init( DEGRADED_WARNING_RESET_DELAY, 7*24*60*60 ); + init( TRACE_LOG_FLUSH_FAILURE_CHECK_INTERVAL_SECONDS, 10 ); + init( TRACE_LOG_FLUSH_FAILURE_REPORT_THRESHOLD, 100 ); // Test harness init( WORKER_POLL_DELAY, 1.0 ); diff --git a/fdbserver/Knobs.h b/fdbserver/Knobs.h index b52f1a4f03..a2cee4d292 100644 --- a/fdbserver/Knobs.h +++ b/fdbserver/Knobs.h @@ -453,6 +453,8 @@ public: double DEGRADED_RESET_INTERVAL; double DEGRADED_WARNING_LIMIT; double DEGRADED_WARNING_RESET_DELAY; + int64_t TRACE_LOG_FLUSH_FAILURE_CHECK_INTERVAL_SECONDS; + int64_t TRACE_LOG_FLUSH_FAILURE_REPORT_THRESHOLD; // Test harness double WORKER_POLL_DELAY; diff --git a/fdbserver/WorkerInterface.actor.h b/fdbserver/WorkerInterface.actor.h index 4fe0f9c5f9..05d87f3c51 100644 --- a/fdbserver/WorkerInterface.actor.h +++ b/fdbserver/WorkerInterface.actor.h @@ -495,9 +495,10 @@ ACTOR Future tLog(IKeyValueStore* persistentData, IDiskQueue* persistentQu bool restoreFromDisk, Promise oldLog, Promise recovered, std::string folder, Reference> degraded, Reference> activeSharedTLog); -ACTOR Future monitorServerDBInfo(Reference>> ccInterface, - Reference ccf, LocalityData locality, - Reference> dbInfo); +ACTOR Future monitorServerDBInfo( + Reference>> ccInterface, Reference ccf, + LocalityData locality, Reference> dbInfo, + Optional>> unsuccessfulFlushCount = Optional>>()); ACTOR Future resolver(ResolverInterface proxy, InitializeResolverRequest initReq, Reference> db); ACTOR Future logRouter(TLogInterface interf, InitializeLogRouterRequest req, diff --git a/fdbserver/tester.actor.cpp b/fdbserver/tester.actor.cpp index cc47f80d96..e5164b33df 100644 --- a/fdbserver/tester.actor.cpp +++ b/fdbserver/tester.actor.cpp @@ -1020,7 +1020,9 @@ vector readTests( ifstream& ifs ) { ACTOR Future runTests( Reference>> cc, Reference>> ci, vector< TesterInterface > testers, vector tests, StringRef startingConfiguration, LocalityData locality ) { state Database cx; state Reference> dbInfo( new AsyncVar ); - state Future ccMonitor = monitorServerDBInfo( cc, Reference(), LocalityData(), dbInfo ); // FIXME: locality + // state Reference> unsuccessfulFlushCount(new AsyncVar(0));; + state Future ccMonitor = + monitorServerDBInfo(cc, Reference(), LocalityData(), dbInfo); // FIXME: locality state bool useDB = false; state bool waitForQuiescenceBegin = false; @@ -1150,6 +1152,7 @@ ACTOR Future runTests( Reference runTests( Reference connFile, test_type_t whatToRun, test_location_t at, int minTestersExpected, std::string fileName, StringRef startingConfiguration, LocalityData locality ) { state vector testSpecs; + // state Reference> unsuccessfulFlushCount(new AsyncVar(0)); Reference>> cc( new AsyncVar> ); Reference>> ci( new AsyncVar> ); vector> actors; diff --git a/fdbserver/worker.actor.cpp b/fdbserver/worker.actor.cpp index f1a3129893..82ee7299f8 100644 --- a/fdbserver/worker.actor.cpp +++ b/fdbserver/worker.actor.cpp @@ -746,7 +746,25 @@ ACTOR Future workerSnapCreate(WorkerSnapRequest snapReq, StringRef snapFol return Void(); } -ACTOR Future monitorServerDBInfo( Reference>> ccInterface, Reference connFile, LocalityData locality, Reference> dbInfo ) { +ACTOR Future monitorTraceLogFlushFailure(Optional>> unsuccessfulFlushCount) { + if (unsuccessfulFlushCount.present()) { + loop { + wait(delay(SERVER_KNOBS->TRACE_LOG_FLUSH_FAILURE_CHECK_INTERVAL_SECONDS)); + auto _unsuccessfulFlushCount = getUnsuccessfulFlushCount(); + if (_unsuccessfulFlushCount > SERVER_KNOBS->TRACE_LOG_FLUSH_FAILURE_REPORT_THRESHOLD) { + unsuccessfulFlushCount.get()->set(_unsuccessfulFlushCount); + } else { + unsuccessfulFlushCount.get()->set(0); + } + } + } + return Void(); +} + +ACTOR Future monitorServerDBInfo(Reference>> ccInterface, + Reference connFile, LocalityData locality, + Reference> dbInfo, + Optional>> unsuccessfulFlushCount) { // Initially most of the serverDBInfo is not known, but we know our locality right away ServerDBInfo localInfo; localInfo.myLocality = locality; @@ -757,6 +775,10 @@ ACTOR Future monitorServerDBInfo( Referenceget().id; + if (unsuccessfulFlushCount.present() && unsuccessfulFlushCount.get()->get() > 0) { + req.issues.push_back_deep(req.issues.arena(), LiteralStringRef("too_many_trace_log_flush_failures")); + } + ClusterConnectionString fileConnectionString; if (connFile && !connFile->fileContentsUpToDate(fileConnectionString)) { req.issues.push_back_deep(req.issues.arena(), LiteralStringRef("incorrect_cluster_file_contents")); @@ -800,6 +822,7 @@ ACTOR Future monitorServerDBInfo( Referenceget().present()) TraceEvent("GotCCInterfaceChange").detail("CCID", ccInterface->get().get().id()).detail("CCMachine", ccInterface->get().get().getWorkers.getEndpoint().getPrimaryAddress()); } + when(wait(unsuccessfulFlushCount.present() ? unsuccessfulFlushCount.get()->onChange() : Never())) {} } } } @@ -868,6 +891,8 @@ ACTOR Future workerServer( state WorkerInterface interf( locality ); interf.initEndpoints(); + state Reference> unsuccessfulFlushCount(new AsyncVar(0)); + folder = abspath(folder); if(metricsPrefix.size() > 0) { @@ -887,7 +912,8 @@ ACTOR Future workerServer( errorForwarders.add( resetAfter(degraded, SERVER_KNOBS->DEGRADED_RESET_INTERVAL, false, SERVER_KNOBS->DEGRADED_WARNING_LIMIT, SERVER_KNOBS->DEGRADED_WARNING_RESET_DELAY, "DegradedReset")); errorForwarders.add( loadedPonger( interf.debugPing.getFuture() ) ); errorForwarders.add( waitFailureServer( interf.waitFailure.getFuture() ) ); - errorForwarders.add( monitorServerDBInfo( ccInterface, connFile, locality, dbInfo ) ); + errorForwarders.add(monitorTraceLogFlushFailure(unsuccessfulFlushCount)); + errorForwarders.add(monitorServerDBInfo(ccInterface, connFile, locality, dbInfo, unsuccessfulFlushCount)); errorForwarders.add( testerServerCore( interf.testerInterface, connFile, dbInfo, locality ) ); errorForwarders.add(monitorHighMemory(memoryProfileThreshold)); diff --git a/flow/FileTraceLogWriter.cpp b/flow/FileTraceLogWriter.cpp index 5b2fdecbf2..1994eac32a 100644 --- a/flow/FileTraceLogWriter.cpp +++ b/flow/FileTraceLogWriter.cpp @@ -78,13 +78,20 @@ void FileTraceLogWriter::write(const std::string& str) { lastError(0); remaining -= ret; ptr += ret; + unsuccessfulFlushCount = 0; } else { + unsuccessfulFlushCount++; + fprintf(stderr, "Unexpected error [%d] when flushing trace log.\n", errno); lastError(errno); threadSleep(0.1); } } } +uint64_t FileTraceLogWriter::getUnsuccessfulFlushCount() { + return unsuccessfulFlushCount; +} + void FileTraceLogWriter::open() { cleanupTraceFiles(); diff --git a/flow/FileTraceLogWriter.h b/flow/FileTraceLogWriter.h index 865d91bb01..ee2b042ed3 100644 --- a/flow/FileTraceLogWriter.h +++ b/flow/FileTraceLogWriter.h @@ -38,6 +38,7 @@ private: uint64_t maxLogsSize; int traceFileFD; uint32_t index; + uint64_t unsuccessfulFlushCount; std::function onError; @@ -56,6 +57,7 @@ public: void sync(); void cleanupTraceFiles(); + uint64_t getUnsuccessfulFlushCount() override; }; #endif diff --git a/flow/Trace.cpp b/flow/Trace.cpp index e9a350afed..29f3a2fd98 100644 --- a/flow/Trace.cpp +++ b/flow/Trace.cpp @@ -493,6 +493,8 @@ public: } } + uint64_t getUnsuccessfulFlushCount() { return logWriter->getUnsuccessfulFlushCount(); } + ~TraceLog() { close(); if (writer) writer->addref(); // FIXME: We are not shutting down the writer thread at all, because the ThreadPool shutdown mechanism is blocking (necessarily waits for current work items to finish) and we might not be able to finish everything. @@ -727,6 +729,9 @@ TraceEvent& TraceEvent::operator=(TraceEvent &&ev) { return *this; } +uint64_t getUnsuccessfulFlushCount() { + return g_traceLog.getUnsuccessfulFlushCount(); +} TraceEvent::TraceEvent( const char* type, UID id ) : id(id), type(type), severity(SevInfo), initialized(false), enabled(true), logged(false) { g_trace_depth++; diff --git a/flow/Trace.h b/flow/Trace.h index 385cd81bee..8f2e9796ce 100644 --- a/flow/Trace.h +++ b/flow/Trace.h @@ -516,6 +516,7 @@ struct ITraceLogWriter { virtual void addref() = 0; virtual void delref() = 0; + virtual uint64_t getUnsuccessfulFlushCount() = 0; }; struct ITraceLogFormatter { @@ -586,6 +587,7 @@ bool validateTraceClockSource(std::string source); void addTraceRole(std::string role); void removeTraceRole(std::string role); +uint64_t getUnsuccessfulFlushCount(); enum trace_clock_t { TRACE_CLOCK_NOW, TRACE_CLOCK_REALTIME }; extern std::atomic g_trace_clock; From 0b0414fb94e9132c0532a142b8958d942b6a2f8d Mon Sep 17 00:00:00 2001 From: Xin Dong Date: Mon, 27 Jan 2020 15:38:26 -0800 Subject: [PATCH 0707/1604] Addressded review comments. Change the issue reporting from 'ITraceLogWriter' to be a more generic way. --- fdbserver/WorkerInterface.actor.h | 9 +++++---- fdbserver/tester.actor.cpp | 2 -- fdbserver/worker.actor.cpp | 30 ++++++++++++++++-------------- flow/FileTraceLogWriter.cpp | 7 +++---- flow/FileTraceLogWriter.h | 5 +++-- flow/Trace.cpp | 6 +++++- flow/Trace.h | 4 ++-- 7 files changed, 34 insertions(+), 29 deletions(-) diff --git a/fdbserver/WorkerInterface.actor.h b/fdbserver/WorkerInterface.actor.h index 05d87f3c51..d629f122b6 100644 --- a/fdbserver/WorkerInterface.actor.h +++ b/fdbserver/WorkerInterface.actor.h @@ -495,10 +495,11 @@ ACTOR Future tLog(IKeyValueStore* persistentData, IDiskQueue* persistentQu bool restoreFromDisk, Promise oldLog, Promise recovered, std::string folder, Reference> degraded, Reference> activeSharedTLog); -ACTOR Future monitorServerDBInfo( - Reference>> ccInterface, Reference ccf, - LocalityData locality, Reference> dbInfo, - Optional>> unsuccessfulFlushCount = Optional>>()); +ACTOR Future monitorServerDBInfo(Reference>> ccInterface, + Reference ccf, LocalityData locality, + Reference> dbInfo, + Optional>>> unsuccessfulFlushCount = + Optional>>>()); ACTOR Future resolver(ResolverInterface proxy, InitializeResolverRequest initReq, Reference> db); ACTOR Future logRouter(TLogInterface interf, InitializeLogRouterRequest req, diff --git a/fdbserver/tester.actor.cpp b/fdbserver/tester.actor.cpp index e5164b33df..bac8f6c22a 100644 --- a/fdbserver/tester.actor.cpp +++ b/fdbserver/tester.actor.cpp @@ -1020,7 +1020,6 @@ vector readTests( ifstream& ifs ) { ACTOR Future runTests( Reference>> cc, Reference>> ci, vector< TesterInterface > testers, vector tests, StringRef startingConfiguration, LocalityData locality ) { state Database cx; state Reference> dbInfo( new AsyncVar ); - // state Reference> unsuccessfulFlushCount(new AsyncVar(0));; state Future ccMonitor = monitorServerDBInfo(cc, Reference(), LocalityData(), dbInfo); // FIXME: locality @@ -1152,7 +1151,6 @@ ACTOR Future runTests( Reference runTests( Reference connFile, test_type_t whatToRun, test_location_t at, int minTestersExpected, std::string fileName, StringRef startingConfiguration, LocalityData locality ) { state vector testSpecs; - // state Reference> unsuccessfulFlushCount(new AsyncVar(0)); Reference>> cc( new AsyncVar> ); Reference>> ci( new AsyncVar> ); vector> actors; diff --git a/fdbserver/worker.actor.cpp b/fdbserver/worker.actor.cpp index 82ee7299f8..b0d7b828a3 100644 --- a/fdbserver/worker.actor.cpp +++ b/fdbserver/worker.actor.cpp @@ -746,15 +746,13 @@ ACTOR Future workerSnapCreate(WorkerSnapRequest snapReq, StringRef snapFol return Void(); } -ACTOR Future monitorTraceLogFlushFailure(Optional>> unsuccessfulFlushCount) { - if (unsuccessfulFlushCount.present()) { +ACTOR Future monitorTraceLogIssues(Optional>>> issues) { + if (issues.present()) { loop { wait(delay(SERVER_KNOBS->TRACE_LOG_FLUSH_FAILURE_CHECK_INTERVAL_SECONDS)); - auto _unsuccessfulFlushCount = getUnsuccessfulFlushCount(); - if (_unsuccessfulFlushCount > SERVER_KNOBS->TRACE_LOG_FLUSH_FAILURE_REPORT_THRESHOLD) { - unsuccessfulFlushCount.get()->set(_unsuccessfulFlushCount); - } else { - unsuccessfulFlushCount.get()->set(0); + std::set _issues = getTraceLogIssues(); + if (_issues.size() > 0) { + issues.get()->set(_issues); } } } @@ -764,19 +762,23 @@ ACTOR Future monitorTraceLogFlushFailure(Optional monitorServerDBInfo(Reference>> ccInterface, Reference connFile, LocalityData locality, Reference> dbInfo, - Optional>> unsuccessfulFlushCount) { + Optional>>> issues) { // Initially most of the serverDBInfo is not known, but we know our locality right away ServerDBInfo localInfo; localInfo.myLocality = locality; dbInfo->set(localInfo); state Optional incorrectTime; + state bool checkIssues = false; loop { GetServerDBInfoRequest req; req.knownServerInfoID = dbInfo->get().id; - if (unsuccessfulFlushCount.present() && unsuccessfulFlushCount.get()->get() > 0) { - req.issues.push_back_deep(req.issues.arena(), LiteralStringRef("too_many_trace_log_flush_failures")); + if (issues.present() && checkIssues) { + for (auto const& i : issues.get()->get()) { + req.issues.push_back_deep(req.issues.arena(), i); + } + checkIssues = false; } ClusterConnectionString fileConnectionString; @@ -822,7 +824,7 @@ ACTOR Future monitorServerDBInfo(Referenceget().present()) TraceEvent("GotCCInterfaceChange").detail("CCID", ccInterface->get().get().id()).detail("CCMachine", ccInterface->get().get().getWorkers.getEndpoint().getPrimaryAddress()); } - when(wait(unsuccessfulFlushCount.present() ? unsuccessfulFlushCount.get()->onChange() : Never())) {} + when(wait(issues.present() ? issues.get()->onChange() : Never())) { checkIssues = true; } } } } @@ -891,7 +893,7 @@ ACTOR Future workerServer( state WorkerInterface interf( locality ); interf.initEndpoints(); - state Reference> unsuccessfulFlushCount(new AsyncVar(0)); + state Reference>> issues(new AsyncVar>()); folder = abspath(folder); @@ -912,8 +914,8 @@ ACTOR Future workerServer( errorForwarders.add( resetAfter(degraded, SERVER_KNOBS->DEGRADED_RESET_INTERVAL, false, SERVER_KNOBS->DEGRADED_WARNING_LIMIT, SERVER_KNOBS->DEGRADED_WARNING_RESET_DELAY, "DegradedReset")); errorForwarders.add( loadedPonger( interf.debugPing.getFuture() ) ); errorForwarders.add( waitFailureServer( interf.waitFailure.getFuture() ) ); - errorForwarders.add(monitorTraceLogFlushFailure(unsuccessfulFlushCount)); - errorForwarders.add(monitorServerDBInfo(ccInterface, connFile, locality, dbInfo, unsuccessfulFlushCount)); + errorForwarders.add(monitorTraceLogIssues(issues)); + errorForwarders.add(monitorServerDBInfo(ccInterface, connFile, locality, dbInfo, issues)); errorForwarders.add( testerServerCore( interf.testerInterface, connFile, dbInfo, locality ) ); errorForwarders.add(monitorHighMemory(memoryProfileThreshold)); diff --git a/flow/FileTraceLogWriter.cpp b/flow/FileTraceLogWriter.cpp index 1994eac32a..d521cb039b 100644 --- a/flow/FileTraceLogWriter.cpp +++ b/flow/FileTraceLogWriter.cpp @@ -78,9 +78,8 @@ void FileTraceLogWriter::write(const std::string& str) { lastError(0); remaining -= ret; ptr += ret; - unsuccessfulFlushCount = 0; } else { - unsuccessfulFlushCount++; + issues.insert(LiteralStringRef("trace_log_flush_failure")); fprintf(stderr, "Unexpected error [%d] when flushing trace log.\n", errno); lastError(errno); threadSleep(0.1); @@ -88,8 +87,8 @@ void FileTraceLogWriter::write(const std::string& str) { } } -uint64_t FileTraceLogWriter::getUnsuccessfulFlushCount() { - return unsuccessfulFlushCount; +std::set FileTraceLogWriter::getTraceLogIssues() { + return std::move(issues); } void FileTraceLogWriter::open() { diff --git a/flow/FileTraceLogWriter.h b/flow/FileTraceLogWriter.h index ee2b042ed3..37c329be21 100644 --- a/flow/FileTraceLogWriter.h +++ b/flow/FileTraceLogWriter.h @@ -26,6 +26,7 @@ #include "flow/FastRef.h" #include "flow/Trace.h" +#include #include class FileTraceLogWriter : public ITraceLogWriter, ReferenceCounted { @@ -38,7 +39,7 @@ private: uint64_t maxLogsSize; int traceFileFD; uint32_t index; - uint64_t unsuccessfulFlushCount; + std::set issues; std::function onError; @@ -57,7 +58,7 @@ public: void sync(); void cleanupTraceFiles(); - uint64_t getUnsuccessfulFlushCount() override; + std::set getTraceLogIssues() override; }; #endif diff --git a/flow/Trace.cpp b/flow/Trace.cpp index 29f3a2fd98..5269cac2fc 100644 --- a/flow/Trace.cpp +++ b/flow/Trace.cpp @@ -493,7 +493,7 @@ public: } } - uint64_t getUnsuccessfulFlushCount() { return logWriter->getUnsuccessfulFlushCount(); } + std::set getTraceLogIssues() { return logWriter->getTraceLogIssues(); } ~TraceLog() { close(); @@ -686,6 +686,7 @@ void removeTraceRole(std::string role) { g_traceLog.removeRole(role); } +<<<<<<< HEAD TraceEvent::TraceEvent() : initialized(true), enabled(false), logged(true) {} TraceEvent::TraceEvent(TraceEvent &&ev) { @@ -732,6 +733,9 @@ TraceEvent& TraceEvent::operator=(TraceEvent &&ev) { uint64_t getUnsuccessfulFlushCount() { return g_traceLog.getUnsuccessfulFlushCount(); } +std::set getTraceLogIssues() { + return std::move(g_traceLog.getTraceLogIssues()); +} TraceEvent::TraceEvent( const char* type, UID id ) : id(id), type(type), severity(SevInfo), initialized(false), enabled(true), logged(false) { g_trace_depth++; diff --git a/flow/Trace.h b/flow/Trace.h index 8f2e9796ce..79319ec47b 100644 --- a/flow/Trace.h +++ b/flow/Trace.h @@ -516,7 +516,7 @@ struct ITraceLogWriter { virtual void addref() = 0; virtual void delref() = 0; - virtual uint64_t getUnsuccessfulFlushCount() = 0; + virtual std::set getTraceLogIssues() = 0; }; struct ITraceLogFormatter { @@ -587,7 +587,7 @@ bool validateTraceClockSource(std::string source); void addTraceRole(std::string role); void removeTraceRole(std::string role); -uint64_t getUnsuccessfulFlushCount(); +std::set getTraceLogIssues(); enum trace_clock_t { TRACE_CLOCK_NOW, TRACE_CLOCK_REALTIME }; extern std::atomic g_trace_clock; From a6580dc15f0b594649b98a44a101e59d1dece78b Mon Sep 17 00:00:00 2001 From: Xin Dong Date: Tue, 28 Jan 2020 10:47:33 -0800 Subject: [PATCH 0708/1604] Added the ability to ping a trace log writer thread and the monitoring in worker.actor.cpp. The current solution is simple a loose check. We can change this to be accurate check by using 'pthread_kill(writer_thread, 0)' --- fdbserver/Knobs.cpp | 1 + fdbserver/Knobs.h | 1 + fdbserver/worker.actor.cpp | 24 ++++++++++++++++++++---- flow/FileTraceLogWriter.cpp | 2 +- flow/Trace.cpp | 17 +++++++++++++++++ flow/Trace.h | 3 +++ 6 files changed, 43 insertions(+), 5 deletions(-) diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index 1bc3448e17..3334f7f245 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -515,6 +515,7 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs, bool isSimula init( DEGRADED_WARNING_RESET_DELAY, 7*24*60*60 ); init( TRACE_LOG_FLUSH_FAILURE_CHECK_INTERVAL_SECONDS, 10 ); init( TRACE_LOG_FLUSH_FAILURE_REPORT_THRESHOLD, 100 ); + init( TRACE_LOG_PING_TIMEOUT_SECONDS, 5.0 ); // Test harness init( WORKER_POLL_DELAY, 1.0 ); diff --git a/fdbserver/Knobs.h b/fdbserver/Knobs.h index a2cee4d292..0ea7f76668 100644 --- a/fdbserver/Knobs.h +++ b/fdbserver/Knobs.h @@ -455,6 +455,7 @@ public: double DEGRADED_WARNING_RESET_DELAY; int64_t TRACE_LOG_FLUSH_FAILURE_CHECK_INTERVAL_SECONDS; int64_t TRACE_LOG_FLUSH_FAILURE_REPORT_THRESHOLD; + double TRACE_LOG_PING_TIMEOUT_SECONDS; // Test harness double WORKER_POLL_DELAY; diff --git a/fdbserver/worker.actor.cpp b/fdbserver/worker.actor.cpp index b0d7b828a3..0e76312157 100644 --- a/fdbserver/worker.actor.cpp +++ b/fdbserver/worker.actor.cpp @@ -747,16 +747,32 @@ ACTOR Future workerSnapCreate(WorkerSnapRequest snapReq, StringRef snapFol } ACTOR Future monitorTraceLogIssues(Optional>>> issues) { - if (issues.present()) { - loop { - wait(delay(SERVER_KNOBS->TRACE_LOG_FLUSH_FAILURE_CHECK_INTERVAL_SECONDS)); + state bool pingTimeout = false; + loop { + wait(delay(SERVER_KNOBS->TRACE_LOG_FLUSH_FAILURE_CHECK_INTERVAL_SECONDS)); + Promise p; + pingTraceLogWriterThread(p); + try { + wait(timeoutError(p.getFuture(), SERVER_KNOBS->TRACE_LOG_PING_TIMEOUT_SECONDS)); + } catch (Error& e) { + if (e.code() == error_code_timed_out) { + pingTimeout = true; + } else { + throw; + } + } + if (issues.present()) { std::set _issues = getTraceLogIssues(); + if (pingTimeout) { + // Ping trace log writer thread timeout. + _issues.insert(LiteralStringRef("trace_log_writer_thread_likely_died")); + pingTimeout = false; + } if (_issues.size() > 0) { issues.get()->set(_issues); } } } - return Void(); } ACTOR Future monitorServerDBInfo(Reference>> ccInterface, diff --git a/flow/FileTraceLogWriter.cpp b/flow/FileTraceLogWriter.cpp index d521cb039b..ffb41208c3 100644 --- a/flow/FileTraceLogWriter.cpp +++ b/flow/FileTraceLogWriter.cpp @@ -79,7 +79,7 @@ void FileTraceLogWriter::write(const std::string& str) { remaining -= ret; ptr += ret; } else { - issues.insert(LiteralStringRef("trace_log_flush_failure")); + issues.insert(LiteralStringRef("trace_log_writer_flush_failure")); fprintf(stderr, "Unexpected error [%d] when flushing trace log.\n", errno); lastError(errno); threadSleep(0.1); diff --git a/flow/Trace.cpp b/flow/Trace.cpp index 5269cac2fc..e6359bb043 100644 --- a/flow/Trace.cpp +++ b/flow/Trace.cpp @@ -280,6 +280,14 @@ public: logWriter->sync(); } } + + struct Ping : TypedAction { + Promise p; + + explicit Ping(Promise p) : p(p){}; + virtual double getTimeEstimate() { return 0; } + }; + void action(Ping& a) { a.p.send(Void()); } }; TraceLog() : bufferLength(0), loggedLength(0), opened(false), preopenOverflowCount(0), barriers(new BarrierList), logTraceEventMetrics(false), formatter(new XmlTraceLogFormatter()) {} @@ -493,6 +501,11 @@ public: } } + void pingWriterThread(Promise& p) { + auto a = new WriterThread::Ping(p); + writer->post(a); + } + std::set getTraceLogIssues() { return logWriter->getTraceLogIssues(); } ~TraceLog() { @@ -737,6 +750,10 @@ std::set getTraceLogIssues() { return std::move(g_traceLog.getTraceLogIssues()); } +void pingTraceLogWriterThread(Promise& p) { + return g_traceLog.pingWriterThread(p); +} + TraceEvent::TraceEvent( const char* type, UID id ) : id(id), type(type), severity(SevInfo), initialized(false), enabled(true), logged(false) { g_trace_depth++; setMaxFieldLength(0); diff --git a/flow/Trace.h b/flow/Trace.h index 79319ec47b..4be7e2d3b9 100644 --- a/flow/Trace.h +++ b/flow/Trace.h @@ -588,6 +588,9 @@ bool validateTraceClockSource(std::string source); void addTraceRole(std::string role); void removeTraceRole(std::string role); std::set getTraceLogIssues(); +template +struct Promise; +void pingTraceLogWriterThread(Promise& p); enum trace_clock_t { TRACE_CLOCK_NOW, TRACE_CLOCK_REALTIME }; extern std::atomic g_trace_clock; From f4f860bfa813251accee0ebc93a4707f2ef03916 Mon Sep 17 00:00:00 2001 From: Xin Dong Date: Wed, 29 Jan 2020 13:21:50 -0800 Subject: [PATCH 0709/1604] Changed issue reporting to be thread safe. Also changed the liveness ping to be thread safe. --- fdbserver/WorkerInterface.actor.h | 4 +-- fdbserver/worker.actor.cpp | 21 ++++++++++------ flow/FileTraceLogWriter.cpp | 15 +++++------ flow/FileTraceLogWriter.h | 6 ++--- flow/Trace.cpp | 42 +++++++++++++++++++++++++------ flow/Trace.h | 15 ++++++++--- 6 files changed, 71 insertions(+), 32 deletions(-) diff --git a/fdbserver/WorkerInterface.actor.h b/fdbserver/WorkerInterface.actor.h index d629f122b6..e8bdffd04e 100644 --- a/fdbserver/WorkerInterface.actor.h +++ b/fdbserver/WorkerInterface.actor.h @@ -498,8 +498,8 @@ ACTOR Future tLog(IKeyValueStore* persistentData, IDiskQueue* persistentQu ACTOR Future monitorServerDBInfo(Reference>> ccInterface, Reference ccf, LocalityData locality, Reference> dbInfo, - Optional>>> unsuccessfulFlushCount = - Optional>>>()); + Optional>>> unsuccessfulFlushCount = + Optional>>>()); ACTOR Future resolver(ResolverInterface proxy, InitializeResolverRequest initReq, Reference> db); ACTOR Future logRouter(TLogInterface interf, InitializeLogRouterRequest req, diff --git a/fdbserver/worker.actor.cpp b/fdbserver/worker.actor.cpp index 0e76312157..2cc6a43abb 100644 --- a/fdbserver/worker.actor.cpp +++ b/fdbserver/worker.actor.cpp @@ -41,6 +41,7 @@ #include "fdbclient/MonitorLeader.h" #include "fdbclient/ClientWorkerInterface.h" #include "flow/Profiler.h" +#include "flow/ThreadHelper.actor.h" #ifdef __linux__ #include @@ -746,14 +747,18 @@ ACTOR Future workerSnapCreate(WorkerSnapRequest snapReq, StringRef snapFol return Void(); } -ACTOR Future monitorTraceLogIssues(Optional>>> issues) { +ACTOR Future monitorTraceLogIssues(Optional>>> issues) { state bool pingTimeout = false; loop { wait(delay(SERVER_KNOBS->TRACE_LOG_FLUSH_FAILURE_CHECK_INTERVAL_SECONDS)); - Promise p; - pingTraceLogWriterThread(p); + ThreadFuture f(new ThreadSingleAssignmentVar); + Reference> callback = + Reference>(new CompletionCallback(f)); + callback->self = callback; + f.callOrSetAsCallback(callback.getPtr(), callback->userParam, 0); + pingTraceLogWriterThread(f); try { - wait(timeoutError(p.getFuture(), SERVER_KNOBS->TRACE_LOG_PING_TIMEOUT_SECONDS)); + wait(timeoutError(callback->promise.getFuture(), SERVER_KNOBS->TRACE_LOG_PING_TIMEOUT_SECONDS)); } catch (Error& e) { if (e.code() == error_code_timed_out) { pingTimeout = true; @@ -762,10 +767,10 @@ ACTOR Future monitorTraceLogIssues(Optional _issues = getTraceLogIssues(); + std::set _issues = getTraceLogIssues(); if (pingTimeout) { // Ping trace log writer thread timeout. - _issues.insert(LiteralStringRef("trace_log_writer_thread_likely_died")); + _issues.insert("trace_log_writer_thread_likely_died"); pingTimeout = false; } if (_issues.size() > 0) { @@ -778,7 +783,7 @@ ACTOR Future monitorTraceLogIssues(Optional monitorServerDBInfo(Reference>> ccInterface, Reference connFile, LocalityData locality, Reference> dbInfo, - Optional>>> issues) { + Optional>>> issues) { // Initially most of the serverDBInfo is not known, but we know our locality right away ServerDBInfo localInfo; localInfo.myLocality = locality; @@ -909,7 +914,7 @@ ACTOR Future workerServer( state WorkerInterface interf( locality ); interf.initEndpoints(); - state Reference>> issues(new AsyncVar>()); + state Reference>> issues(new AsyncVar>()); folder = abspath(folder); diff --git a/flow/FileTraceLogWriter.cpp b/flow/FileTraceLogWriter.cpp index ffb41208c3..902edce36d 100644 --- a/flow/FileTraceLogWriter.cpp +++ b/flow/FileTraceLogWriter.cpp @@ -48,8 +48,11 @@ #include #include -FileTraceLogWriter::FileTraceLogWriter(std::string directory, std::string processName, std::string basename, std::string extension, uint64_t maxLogsSize, std::function onError) - : directory(directory), processName(processName), basename(basename), extension(extension), maxLogsSize(maxLogsSize), traceFileFD(-1), index(0), onError(onError) {} +FileTraceLogWriter::FileTraceLogWriter(std::string directory, std::string processName, std::string basename, + std::string extension, uint64_t maxLogsSize, std::function onError, + Reference issues) + : directory(directory), processName(processName), basename(basename), extension(extension), maxLogsSize(maxLogsSize), + traceFileFD(-1), index(0), onError(onError), issues(issues) {} void FileTraceLogWriter::addref() { ReferenceCounted::addref(); @@ -64,6 +67,7 @@ void FileTraceLogWriter::lastError(int err) { // the error and the occurrence of the error are unblocked, even though we haven't actually succeeded in flushing. // Otherwise a permanent write error would make the program block forever. if (err != 0 && err != EINTR) { + issues->addIssue("trace_log_writer_flush_error_" + std::to_string(err)); onError(); } } @@ -79,7 +83,7 @@ void FileTraceLogWriter::write(const std::string& str) { remaining -= ret; ptr += ret; } else { - issues.insert(LiteralStringRef("trace_log_writer_flush_failure")); + issues->addIssue("trace_log_writer_flush_failure"); fprintf(stderr, "Unexpected error [%d] when flushing trace log.\n", errno); lastError(errno); threadSleep(0.1); @@ -87,10 +91,6 @@ void FileTraceLogWriter::write(const std::string& str) { } } -std::set FileTraceLogWriter::getTraceLogIssues() { - return std::move(issues); -} - void FileTraceLogWriter::open() { cleanupTraceFiles(); @@ -117,6 +117,7 @@ void FileTraceLogWriter::open() { } else { fprintf(stderr, "ERROR: could not create trace log file `%s' (%d: %s)\n", finalname.c_str(), errno, strerror(errno)); + issues->addIssue("trace_log_writer_could_not_create_trace_log_file"); int errorNum = errno; onMainThreadVoid([finalname, errorNum]{ diff --git a/flow/FileTraceLogWriter.h b/flow/FileTraceLogWriter.h index 37c329be21..7a74004087 100644 --- a/flow/FileTraceLogWriter.h +++ b/flow/FileTraceLogWriter.h @@ -39,12 +39,13 @@ private: uint64_t maxLogsSize; int traceFileFD; uint32_t index; - std::set issues; + Reference issues; std::function onError; public: - FileTraceLogWriter(std::string directory, std::string processName, std::string basename, std::string extension, uint64_t maxLogsSize, std::function onError); + FileTraceLogWriter(std::string directory, std::string processName, std::string basename, std::string extension, + uint64_t maxLogsSize, std::function onError, Reference issues); void addref(); void delref(); @@ -58,7 +59,6 @@ public: void sync(); void cleanupTraceFiles(); - std::set getTraceLogIssues() override; }; #endif diff --git a/flow/Trace.cpp b/flow/Trace.cpp index e6359bb043..e3ff9c70bd 100644 --- a/flow/Trace.cpp +++ b/flow/Trace.cpp @@ -220,6 +220,28 @@ public: } }; + struct IssuesList : ITraceLogIssuesReporter, ThreadSafeReferenceCounted { + IssuesList(){}; + void insertIssue(std::string& issue) { + MutexHolder h(mutex); + issues.insert(issue); + } + + std::set getAndFlushIssues() { + MutexHolder h(mutex); + return std::move(issues); + } + + void addref() { ThreadSafeReferenceCounted::addref(); } + void delref() { ThreadSafeReferenceCounted::delref(); } + + private: + Mutex mutex; + std::set issues; + }; + + Reference issues; + Reference barriers; struct WriterThread : IThreadPoolReceiver { @@ -282,12 +304,12 @@ public: } struct Ping : TypedAction { - Promise p; + ThreadFuture p; - explicit Ping(Promise p) : p(p){}; + explicit Ping(ThreadFuture p) : p(p){}; virtual double getTimeEstimate() { return 0; } }; - void action(Ping& a) { a.p.send(Void()); } + void action(Ping& a) { ((ThreadSingleAssignmentVar*)a.p.getPtr())->send(Void()); } }; TraceLog() : bufferLength(0), loggedLength(0), opened(false), preopenOverflowCount(0), barriers(new BarrierList), logTraceEventMetrics(false), formatter(new XmlTraceLogFormatter()) {} @@ -303,7 +325,9 @@ public: this->localAddress = na; basename = format("%s/%s.%s.%s", directory.c_str(), processName.c_str(), timestamp.c_str(), deterministicRandom()->randomAlphaNumeric(6).c_str()); - logWriter = Reference(new FileTraceLogWriter(directory, processName, basename, formatter->getExtension(), maxLogsSize, [this](){ barriers->triggerAll(); })); + logWriter = Reference(new FileTraceLogWriter(directory, processName, basename, + formatter->getExtension(), maxLogsSize, + [this]() { barriers->triggerAll(); }, issues)); if ( g_network->isSimulated() ) writer = Reference(new DummyThreadPool()); @@ -501,12 +525,12 @@ public: } } - void pingWriterThread(Promise& p) { + void pingWriterThread(ThreadFuture& p) { auto a = new WriterThread::Ping(p); writer->post(a); } - std::set getTraceLogIssues() { return logWriter->getTraceLogIssues(); } + std::set getTraceLogIssues() { return issues->getAndFlushIssues(); } ~TraceLog() { close(); @@ -699,7 +723,6 @@ void removeTraceRole(std::string role) { g_traceLog.removeRole(role); } -<<<<<<< HEAD TraceEvent::TraceEvent() : initialized(true), enabled(false), logged(true) {} TraceEvent::TraceEvent(TraceEvent &&ev) { @@ -747,10 +770,13 @@ uint64_t getUnsuccessfulFlushCount() { return g_traceLog.getUnsuccessfulFlushCount(); } std::set getTraceLogIssues() { +======= +std::set getTraceLogIssues() { +>>>>>>> Changed issue reporting to be thread safe. Also changed the liveness ping to be thread safe. return std::move(g_traceLog.getTraceLogIssues()); } -void pingTraceLogWriterThread(Promise& p) { +void pingTraceLogWriterThread(ThreadFuture& p) { return g_traceLog.pingWriterThread(p); } diff --git a/flow/Trace.h b/flow/Trace.h index 4be7e2d3b9..cce44a0773 100644 --- a/flow/Trace.h +++ b/flow/Trace.h @@ -516,7 +516,6 @@ struct ITraceLogWriter { virtual void addref() = 0; virtual void delref() = 0; - virtual std::set getTraceLogIssues() = 0; }; struct ITraceLogFormatter { @@ -529,6 +528,14 @@ struct ITraceLogFormatter { virtual void delref() = 0; }; +struct ITraceLogIssuesReporter { + virtual void addIssue(std::string issue) = 0; + virtual std::set getAndFlushIssues() = 0; + + virtual void addref() = 0; + virtual void delref() = 0; +}; + struct TraceInterval { TraceInterval( const char* type ) : count(-1), type(type), severity(SevInfo) {} @@ -587,10 +594,10 @@ bool validateTraceClockSource(std::string source); void addTraceRole(std::string role); void removeTraceRole(std::string role); -std::set getTraceLogIssues(); +std::set getTraceLogIssues(); template -struct Promise; -void pingTraceLogWriterThread(Promise& p); +struct ThreadFuture; +void pingTraceLogWriterThread(ThreadFuture& p); enum trace_clock_t { TRACE_CLOCK_NOW, TRACE_CLOCK_REALTIME }; extern std::atomic g_trace_clock; From 39c92c9cce4412db09c43e0fed7a195a410ee177 Mon Sep 17 00:00:00 2001 From: Xin Dong Date: Wed, 29 Jan 2020 13:58:21 -0800 Subject: [PATCH 0710/1604] Update flow/FileTraceLogWriter.cpp Co-Authored-By: A.J. Beamon --- flow/FileTraceLogWriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/FileTraceLogWriter.cpp b/flow/FileTraceLogWriter.cpp index 902edce36d..43531d6816 100644 --- a/flow/FileTraceLogWriter.cpp +++ b/flow/FileTraceLogWriter.cpp @@ -117,7 +117,7 @@ void FileTraceLogWriter::open() { } else { fprintf(stderr, "ERROR: could not create trace log file `%s' (%d: %s)\n", finalname.c_str(), errno, strerror(errno)); - issues->addIssue("trace_log_writer_could_not_create_trace_log_file"); + issues->addIssue("trace_log_could_not_create_file"); int errorNum = errno; onMainThreadVoid([finalname, errorNum]{ From 1c346fcfb0f1fa5c31c8f6d3ca201f1a172a369a Mon Sep 17 00:00:00 2001 From: Xin Dong Date: Wed, 29 Jan 2020 15:10:04 -0800 Subject: [PATCH 0711/1604] Added the new issues into Status Schema. Remove the issue reporting in lastError since: - If the issue string contains the error number, status schema needs to be super verbose to include all possible issue strings - If the issue string does not contain the error number, the generic issue string can be pretty useless. Thus now specific issues are being reported before calling lastError --- fdbclient/Schemas.cpp | 8 ++++++-- flow/FileTraceLogWriter.cpp | 3 +-- flow/Trace.cpp | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/fdbclient/Schemas.cpp b/fdbclient/Schemas.cpp index c4253bd0af..e6269ca9bb 100644 --- a/fdbclient/Schemas.cpp +++ b/fdbclient/Schemas.cpp @@ -162,6 +162,8 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema( "$enum":[ "file_open_error", "incorrect_cluster_file_contents", + "trace_log_file_write_error", + "trace_log_could_not_create_file", "process_error", "io_error", "io_timeout", @@ -399,7 +401,9 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema( { "name":{ "$enum":[ - "incorrect_cluster_file_contents" + "incorrect_cluster_file_contents", + "trace_log_file_write_error", + "trace_log_could_not_create_file" ] }, "description":"Cluster file contents do not match current cluster connection string. Verify cluster file is writable and has not been overwritten externally." @@ -409,7 +413,7 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema( } ], )statusSchema" - R"statusSchema( + R"statusSchema( "recovery_state":{ "required_resolvers":1, "required_proxies":1, diff --git a/flow/FileTraceLogWriter.cpp b/flow/FileTraceLogWriter.cpp index 43531d6816..29d852eee1 100644 --- a/flow/FileTraceLogWriter.cpp +++ b/flow/FileTraceLogWriter.cpp @@ -67,7 +67,6 @@ void FileTraceLogWriter::lastError(int err) { // the error and the occurrence of the error are unblocked, even though we haven't actually succeeded in flushing. // Otherwise a permanent write error would make the program block forever. if (err != 0 && err != EINTR) { - issues->addIssue("trace_log_writer_flush_error_" + std::to_string(err)); onError(); } } @@ -83,7 +82,7 @@ void FileTraceLogWriter::write(const std::string& str) { remaining -= ret; ptr += ret; } else { - issues->addIssue("trace_log_writer_flush_failure"); + issues->addIssue("trace_log_file_write_error"); fprintf(stderr, "Unexpected error [%d] when flushing trace log.\n", errno); lastError(errno); threadSleep(0.1); diff --git a/flow/Trace.cpp b/flow/Trace.cpp index e3ff9c70bd..6eace47570 100644 --- a/flow/Trace.cpp +++ b/flow/Trace.cpp @@ -222,12 +222,12 @@ public: struct IssuesList : ITraceLogIssuesReporter, ThreadSafeReferenceCounted { IssuesList(){}; - void insertIssue(std::string& issue) { + void addIssue(std::string& issue) override { MutexHolder h(mutex); issues.insert(issue); } - std::set getAndFlushIssues() { + std::set getAndFlushIssues() override { MutexHolder h(mutex); return std::move(issues); } From 288e95c7e1663166cbe3881266af11bf00b62729 Mon Sep 17 00:00:00 2001 From: Xin Dong Date: Wed, 29 Jan 2020 21:42:30 -0800 Subject: [PATCH 0712/1604] Reallocate the issues set after each get. Changed an issues name to be accurate --- fdbserver/worker.actor.cpp | 2 +- flow/Trace.cpp | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/fdbserver/worker.actor.cpp b/fdbserver/worker.actor.cpp index 2cc6a43abb..edcead498c 100644 --- a/fdbserver/worker.actor.cpp +++ b/fdbserver/worker.actor.cpp @@ -770,7 +770,7 @@ ACTOR Future monitorTraceLogIssues(Optional _issues = getTraceLogIssues(); if (pingTimeout) { // Ping trace log writer thread timeout. - _issues.insert("trace_log_writer_thread_likely_died"); + _issues.insert("trace_log_writer_thread_unresponsive"); pingTimeout = false; } if (_issues.size() > 0) { diff --git a/flow/Trace.cpp b/flow/Trace.cpp index 6eace47570..ade6e4d7ca 100644 --- a/flow/Trace.cpp +++ b/flow/Trace.cpp @@ -221,14 +221,19 @@ public: }; struct IssuesList : ITraceLogIssuesReporter, ThreadSafeReferenceCounted { - IssuesList(){}; - void addIssue(std::string& issue) override { + IssuesList() : moved(false){}; + void addIssue(std::string issue) override { MutexHolder h(mutex); + if (moved) { + issues = std::set(); + moved = false; + } issues.insert(issue); } std::set getAndFlushIssues() override { MutexHolder h(mutex); + moved = true; return std::move(issues); } @@ -237,6 +242,7 @@ public: private: Mutex mutex; + bool moved; std::set issues; }; @@ -312,7 +318,9 @@ public: void action(Ping& a) { ((ThreadSingleAssignmentVar*)a.p.getPtr())->send(Void()); } }; - TraceLog() : bufferLength(0), loggedLength(0), opened(false), preopenOverflowCount(0), barriers(new BarrierList), logTraceEventMetrics(false), formatter(new XmlTraceLogFormatter()) {} + TraceLog() + : bufferLength(0), loggedLength(0), opened(false), preopenOverflowCount(0), barriers(new BarrierList), + logTraceEventMetrics(false), formatter(new XmlTraceLogFormatter()), issues(new IssuesList) {} bool isOpen() const { return opened; } @@ -769,11 +777,10 @@ TraceEvent& TraceEvent::operator=(TraceEvent &&ev) { uint64_t getUnsuccessfulFlushCount() { return g_traceLog.getUnsuccessfulFlushCount(); } -std::set getTraceLogIssues() { -======= + std::set getTraceLogIssues() { ->>>>>>> Changed issue reporting to be thread safe. Also changed the liveness ping to be thread safe. return std::move(g_traceLog.getTraceLogIssues()); + return g_traceLog.getTraceLogIssues(); } void pingTraceLogWriterThread(ThreadFuture& p) { From aaa63331b692c4e144bb168402b3373f6fd94d41 Mon Sep 17 00:00:00 2001 From: Xin Dong Date: Thu, 30 Jan 2020 09:46:02 -0800 Subject: [PATCH 0713/1604] Fix windows build --- flow/Trace.h | 1 + 1 file changed, 1 insertion(+) diff --git a/flow/Trace.h b/flow/Trace.h index cce44a0773..70f2146d0e 100644 --- a/flow/Trace.h +++ b/flow/Trace.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "flow/IRandom.h" #include "flow/Error.h" From 6325c403366a0e8593ffd4ba7785c9ddf3ca52af Mon Sep 17 00:00:00 2001 From: Xin Dong Date: Tue, 4 Feb 2020 13:13:46 -0800 Subject: [PATCH 0714/1604] Apply suggestions from code review Co-Authored-By: A.J. Beamon --- fdbserver/WorkerInterface.actor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/WorkerInterface.actor.h b/fdbserver/WorkerInterface.actor.h index e8bdffd04e..c09cc5899a 100644 --- a/fdbserver/WorkerInterface.actor.h +++ b/fdbserver/WorkerInterface.actor.h @@ -498,7 +498,7 @@ ACTOR Future tLog(IKeyValueStore* persistentData, IDiskQueue* persistentQu ACTOR Future monitorServerDBInfo(Reference>> ccInterface, Reference ccf, LocalityData locality, Reference> dbInfo, - Optional>>> unsuccessfulFlushCount = + Optional>>> issues = Optional>>>()); ACTOR Future resolver(ResolverInterface proxy, InitializeResolverRequest initReq, Reference> db); From 090c89e90aa4125754bd3a3b71bdc29e6a146ace Mon Sep 17 00:00:00 2001 From: Xin Dong Date: Wed, 5 Feb 2020 10:05:48 -0800 Subject: [PATCH 0715/1604] Addressed review comments. Fix the bug where issues on a worker may be wrongly cleared by subsequent GetDBinfo request. --- fdbclient/Schemas.cpp | 6 +++-- fdbserver/Knobs.cpp | 1 - fdbserver/Knobs.h | 1 - fdbserver/WorkerInterface.actor.h | 4 +-- fdbserver/worker.actor.cpp | 21 +++++++--------- flow/FileTraceLogWriter.cpp | 4 +-- flow/Knobs.cpp | 1 + flow/Knobs.h | 1 + flow/Trace.cpp | 42 +++++++++++++++++++------------ flow/Trace.h | 12 ++++++--- 10 files changed, 53 insertions(+), 40 deletions(-) diff --git a/fdbclient/Schemas.cpp b/fdbclient/Schemas.cpp index e6269ca9bb..f4cb486960 100644 --- a/fdbclient/Schemas.cpp +++ b/fdbclient/Schemas.cpp @@ -164,6 +164,7 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema( "incorrect_cluster_file_contents", "trace_log_file_write_error", "trace_log_could_not_create_file", + "trace_log_writer_thread_unresponsive", "process_error", "io_error", "io_timeout", @@ -403,7 +404,8 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema( "$enum":[ "incorrect_cluster_file_contents", "trace_log_file_write_error", - "trace_log_could_not_create_file" + "trace_log_could_not_create_file", + "trace_log_writer_thread_unresponsive" ] }, "description":"Cluster file contents do not match current cluster connection string. Verify cluster file is writable and has not been overwritten externally." @@ -413,7 +415,7 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema( } ], )statusSchema" - R"statusSchema( + R"statusSchema( "recovery_state":{ "required_resolvers":1, "required_proxies":1, diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index 3334f7f245..3c7f989e0b 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -514,7 +514,6 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs, bool isSimula init( DEGRADED_WARNING_LIMIT, 1 ); init( DEGRADED_WARNING_RESET_DELAY, 7*24*60*60 ); init( TRACE_LOG_FLUSH_FAILURE_CHECK_INTERVAL_SECONDS, 10 ); - init( TRACE_LOG_FLUSH_FAILURE_REPORT_THRESHOLD, 100 ); init( TRACE_LOG_PING_TIMEOUT_SECONDS, 5.0 ); // Test harness diff --git a/fdbserver/Knobs.h b/fdbserver/Knobs.h index 0ea7f76668..a8266c5656 100644 --- a/fdbserver/Knobs.h +++ b/fdbserver/Knobs.h @@ -454,7 +454,6 @@ public: double DEGRADED_WARNING_LIMIT; double DEGRADED_WARNING_RESET_DELAY; int64_t TRACE_LOG_FLUSH_FAILURE_CHECK_INTERVAL_SECONDS; - int64_t TRACE_LOG_FLUSH_FAILURE_REPORT_THRESHOLD; double TRACE_LOG_PING_TIMEOUT_SECONDS; // Test harness diff --git a/fdbserver/WorkerInterface.actor.h b/fdbserver/WorkerInterface.actor.h index c09cc5899a..b63310e59e 100644 --- a/fdbserver/WorkerInterface.actor.h +++ b/fdbserver/WorkerInterface.actor.h @@ -498,8 +498,8 @@ ACTOR Future tLog(IKeyValueStore* persistentData, IDiskQueue* persistentQu ACTOR Future monitorServerDBInfo(Reference>> ccInterface, Reference ccf, LocalityData locality, Reference> dbInfo, - Optional>>> issues = - Optional>>>()); + Optional>>> issues = + Optional>>>()); ACTOR Future resolver(ResolverInterface proxy, InitializeResolverRequest initReq, Reference> db); ACTOR Future logRouter(TLogInterface interf, InitializeLogRouterRequest req, diff --git a/fdbserver/worker.actor.cpp b/fdbserver/worker.actor.cpp index edcead498c..0f9bf08756 100644 --- a/fdbserver/worker.actor.cpp +++ b/fdbserver/worker.actor.cpp @@ -747,7 +747,7 @@ ACTOR Future workerSnapCreate(WorkerSnapRequest snapReq, StringRef snapFol return Void(); } -ACTOR Future monitorTraceLogIssues(Optional>>> issues) { +ACTOR Future monitorTraceLogIssues(Optional>>> issues) { state bool pingTimeout = false; loop { wait(delay(SERVER_KNOBS->TRACE_LOG_FLUSH_FAILURE_CHECK_INTERVAL_SECONDS)); @@ -767,15 +767,14 @@ ACTOR Future monitorTraceLogIssues(Optional _issues = getTraceLogIssues(); + std::vector _issues; + retriveTraceLogIssues(_issues); if (pingTimeout) { // Ping trace log writer thread timeout. - _issues.insert("trace_log_writer_thread_unresponsive"); + _issues.push_back("trace_log_writer_thread_unresponsive"); pingTimeout = false; } - if (_issues.size() > 0) { - issues.get()->set(_issues); - } + issues.get()->set(_issues); } } } @@ -783,23 +782,21 @@ ACTOR Future monitorTraceLogIssues(Optional monitorServerDBInfo(Reference>> ccInterface, Reference connFile, LocalityData locality, Reference> dbInfo, - Optional>>> issues) { + Optional>>> issues) { // Initially most of the serverDBInfo is not known, but we know our locality right away ServerDBInfo localInfo; localInfo.myLocality = locality; dbInfo->set(localInfo); state Optional incorrectTime; - state bool checkIssues = false; loop { GetServerDBInfoRequest req; req.knownServerInfoID = dbInfo->get().id; - if (issues.present() && checkIssues) { + if (issues.present()) { for (auto const& i : issues.get()->get()) { req.issues.push_back_deep(req.issues.arena(), i); } - checkIssues = false; } ClusterConnectionString fileConnectionString; @@ -845,7 +842,7 @@ ACTOR Future monitorServerDBInfo(Referenceget().present()) TraceEvent("GotCCInterfaceChange").detail("CCID", ccInterface->get().get().id()).detail("CCMachine", ccInterface->get().get().getWorkers.getEndpoint().getPrimaryAddress()); } - when(wait(issues.present() ? issues.get()->onChange() : Never())) { checkIssues = true; } + when(wait(issues.present() ? issues.get()->onChange() : Never())) {} } } } @@ -914,7 +911,7 @@ ACTOR Future workerServer( state WorkerInterface interf( locality ); interf.initEndpoints(); - state Reference>> issues(new AsyncVar>()); + state Reference>> issues(new AsyncVar>()); folder = abspath(folder); diff --git a/flow/FileTraceLogWriter.cpp b/flow/FileTraceLogWriter.cpp index 29d852eee1..937eb91735 100644 --- a/flow/FileTraceLogWriter.cpp +++ b/flow/FileTraceLogWriter.cpp @@ -82,7 +82,7 @@ void FileTraceLogWriter::write(const std::string& str) { remaining -= ret; ptr += ret; } else { - issues->addIssue("trace_log_file_write_error"); + issues->addAndExpire("trace_log_file_write_error"); fprintf(stderr, "Unexpected error [%d] when flushing trace log.\n", errno); lastError(errno); threadSleep(0.1); @@ -116,7 +116,7 @@ void FileTraceLogWriter::open() { } else { fprintf(stderr, "ERROR: could not create trace log file `%s' (%d: %s)\n", finalname.c_str(), errno, strerror(errno)); - issues->addIssue("trace_log_could_not_create_file"); + issues->addAndExpire("trace_log_could_not_create_file"); int errorNum = errno; onMainThreadVoid([finalname, errorNum]{ diff --git a/flow/Knobs.cpp b/flow/Knobs.cpp index 9eaee74826..7c540f6dff 100644 --- a/flow/Knobs.cpp +++ b/flow/Knobs.cpp @@ -161,6 +161,7 @@ FlowKnobs::FlowKnobs(bool randomize, bool isSimulated) { init( TRACE_EVENT_THROTTLER_MSG_LIMIT, 20000 ); init( MAX_TRACE_FIELD_LENGTH, 495 ); // If the value of this is changed, the corresponding default in Trace.cpp should be changed as well init( MAX_TRACE_EVENT_LENGTH, 4000 ); // If the value of this is changed, the corresponding default in Trace.cpp should be changed as well + init( TRACE_LOG_ISSUE_EXPIRATION_INTERVAL, 5.0); //TDMetrics init( MAX_METRICS, 600 ); diff --git a/flow/Knobs.h b/flow/Knobs.h index 5a50c47eea..cd092dfa35 100644 --- a/flow/Knobs.h +++ b/flow/Knobs.h @@ -182,6 +182,7 @@ public: int TRACE_EVENT_THROTTLER_MSG_LIMIT; int MAX_TRACE_FIELD_LENGTH; int MAX_TRACE_EVENT_LENGTH; + double TRACE_LOG_ISSUE_EXPIRATION_INTERVAL; //TDMetrics int64_t MAX_METRIC_SIZE; diff --git a/flow/Trace.cpp b/flow/Trace.cpp index ade6e4d7ca..9c1a4cdb61 100644 --- a/flow/Trace.cpp +++ b/flow/Trace.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include "flow/IThreadPool.h" #include "flow/ThreadHelper.actor.h" @@ -222,19 +223,31 @@ public: struct IssuesList : ITraceLogIssuesReporter, ThreadSafeReferenceCounted { IssuesList() : moved(false){}; - void addIssue(std::string issue) override { + void addAndExpire(std::string issue, double expirationInterval) override { MutexHolder h(mutex); - if (moved) { - issues = std::set(); - moved = false; + auto now = ::now(); + if (issues.find(issue) != issues.end()) { + issues[issue]++; + } else { + issues[issue] = 1; } - issues.insert(issue); + queue.emplace_back(now + expirationInterval, issue); } - std::set getAndFlushIssues() override { + void retrieveIssues(std::vector& out) override { MutexHolder h(mutex); - moved = true; - return std::move(issues); + // clean up any expired events first + auto now = ::now(); + while (queue.size() > 0 && queue.front().first <= now) { + ASSERT(issues.find(queue.front().second) != issues.end()); + if (--issues[queue.front().second] == 0) { + issues.erase(queue.front().second); + } + queue.pop_front(); + } + for (auto const& i : issues) { + out.push_back(i.first); + } } void addref() { ThreadSafeReferenceCounted::addref(); } @@ -243,7 +256,8 @@ public: private: Mutex mutex; bool moved; - std::set issues; + std::unordered_map issues; + Deque> queue; }; Reference issues; @@ -538,7 +552,7 @@ public: writer->post(a); } - std::set getTraceLogIssues() { return issues->getAndFlushIssues(); } + void retriveTraceLogIssues(std::vector& out) { return issues->retrieveIssues(out); } ~TraceLog() { close(); @@ -774,13 +788,9 @@ TraceEvent& TraceEvent::operator=(TraceEvent &&ev) { return *this; } -uint64_t getUnsuccessfulFlushCount() { - return g_traceLog.getUnsuccessfulFlushCount(); -} -std::set getTraceLogIssues() { - return std::move(g_traceLog.getTraceLogIssues()); - return g_traceLog.getTraceLogIssues(); +void retriveTraceLogIssues(std::vector& out) { + return g_traceLog.retriveTraceLogIssues(out); } void pingTraceLogWriterThread(ThreadFuture& p) { diff --git a/flow/Trace.h b/flow/Trace.h index 70f2146d0e..11d274c5c3 100644 --- a/flow/Trace.h +++ b/flow/Trace.h @@ -27,7 +27,6 @@ #include #include #include -#include #include #include "flow/IRandom.h" #include "flow/Error.h" @@ -530,8 +529,13 @@ struct ITraceLogFormatter { }; struct ITraceLogIssuesReporter { - virtual void addIssue(std::string issue) = 0; - virtual std::set getAndFlushIssues() = 0; + // The issue will expire after (now + expirationInterval) seconds + virtual void addAndExpire(std::string issue, + double expirationInterval = FLOW_KNOBS->TRACE_LOG_ISSUE_EXPIRATION_INTERVAL) = 0; + + // When called, this function will first clean up expired issues. + // If it's never called somehow and the trace log thread is struggling, the memory usage may build up. + virtual void retrieveIssues(std::vector& out) = 0; virtual void addref() = 0; virtual void delref() = 0; @@ -595,7 +599,7 @@ bool validateTraceClockSource(std::string source); void addTraceRole(std::string role); void removeTraceRole(std::string role); -std::set getTraceLogIssues(); +void retriveTraceLogIssues(std::vector& out); template struct ThreadFuture; void pingTraceLogWriterThread(ThreadFuture& p); From fce71e4516c62268744fdb78c76d046196e1c08a Mon Sep 17 00:00:00 2001 From: Xin Dong Date: Wed, 5 Feb 2020 12:00:14 -0800 Subject: [PATCH 0716/1604] Added a TODO for the usage of 'issues' in 'monitorServerDBInfo' --- fdbserver/worker.actor.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fdbserver/worker.actor.cpp b/fdbserver/worker.actor.cpp index 0f9bf08756..298fa3655d 100644 --- a/fdbserver/worker.actor.cpp +++ b/fdbserver/worker.actor.cpp @@ -779,6 +779,9 @@ ACTOR Future monitorTraceLogIssues(Optional monitorServerDBInfo(Reference>> ccInterface, Reference connFile, LocalityData locality, Reference> dbInfo, From 3f24ae93f2c89f3505ff3d5688e06f3e0a9a23aa Mon Sep 17 00:00:00 2001 From: Xin Dong Date: Sun, 9 Feb 2020 22:12:53 -0800 Subject: [PATCH 0717/1604] Remove the unused variable --- flow/Trace.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/flow/Trace.cpp b/flow/Trace.cpp index 9c1a4cdb61..e17202e53a 100644 --- a/flow/Trace.cpp +++ b/flow/Trace.cpp @@ -222,7 +222,7 @@ public: }; struct IssuesList : ITraceLogIssuesReporter, ThreadSafeReferenceCounted { - IssuesList() : moved(false){}; + IssuesList(){}; void addAndExpire(std::string issue, double expirationInterval) override { MutexHolder h(mutex); auto now = ::now(); @@ -255,7 +255,6 @@ public: private: Mutex mutex; - bool moved; std::unordered_map issues; Deque> queue; }; From f20619c9fbfdb46972bcf259fcd86245235f839d Mon Sep 17 00:00:00 2001 From: Xin Dong Date: Tue, 25 Feb 2020 15:34:08 -0800 Subject: [PATCH 0718/1604] Resolve review comments. Changed how issues got cleared --- fdbserver/WorkerInterface.actor.h | 4 ++-- fdbserver/worker.actor.cpp | 10 ++++----- flow/FileTraceLogWriter.cpp | 15 +++++++++++-- flow/Trace.cpp | 35 +++++++++++-------------------- flow/Trace.h | 9 ++++---- 5 files changed, 36 insertions(+), 37 deletions(-) diff --git a/fdbserver/WorkerInterface.actor.h b/fdbserver/WorkerInterface.actor.h index b63310e59e..c09cc5899a 100644 --- a/fdbserver/WorkerInterface.actor.h +++ b/fdbserver/WorkerInterface.actor.h @@ -498,8 +498,8 @@ ACTOR Future tLog(IKeyValueStore* persistentData, IDiskQueue* persistentQu ACTOR Future monitorServerDBInfo(Reference>> ccInterface, Reference ccf, LocalityData locality, Reference> dbInfo, - Optional>>> issues = - Optional>>>()); + Optional>>> issues = + Optional>>>()); ACTOR Future resolver(ResolverInterface proxy, InitializeResolverRequest initReq, Reference> db); ACTOR Future logRouter(TLogInterface interf, InitializeLogRouterRequest req, diff --git a/fdbserver/worker.actor.cpp b/fdbserver/worker.actor.cpp index 298fa3655d..6460959ac9 100644 --- a/fdbserver/worker.actor.cpp +++ b/fdbserver/worker.actor.cpp @@ -747,7 +747,7 @@ ACTOR Future workerSnapCreate(WorkerSnapRequest snapReq, StringRef snapFol return Void(); } -ACTOR Future monitorTraceLogIssues(Optional>>> issues) { +ACTOR Future monitorTraceLogIssues(Optional>>> issues) { state bool pingTimeout = false; loop { wait(delay(SERVER_KNOBS->TRACE_LOG_FLUSH_FAILURE_CHECK_INTERVAL_SECONDS)); @@ -767,11 +767,11 @@ ACTOR Future monitorTraceLogIssues(Optional _issues; + std::set _issues; retriveTraceLogIssues(_issues); if (pingTimeout) { // Ping trace log writer thread timeout. - _issues.push_back("trace_log_writer_thread_unresponsive"); + _issues.insert("trace_log_writer_thread_unresponsive"); pingTimeout = false; } issues.get()->set(_issues); @@ -785,7 +785,7 @@ ACTOR Future monitorTraceLogIssues(Optional monitorServerDBInfo(Reference>> ccInterface, Reference connFile, LocalityData locality, Reference> dbInfo, - Optional>>> issues) { + Optional>>> issues) { // Initially most of the serverDBInfo is not known, but we know our locality right away ServerDBInfo localInfo; localInfo.myLocality = locality; @@ -914,7 +914,7 @@ ACTOR Future workerServer( state WorkerInterface interf( locality ); interf.initEndpoints(); - state Reference>> issues(new AsyncVar>()); + state Reference>> issues(new AsyncVar>()); folder = abspath(folder); diff --git a/flow/FileTraceLogWriter.cpp b/flow/FileTraceLogWriter.cpp index 937eb91735..3e4d0bdcd4 100644 --- a/flow/FileTraceLogWriter.cpp +++ b/flow/FileTraceLogWriter.cpp @@ -74,6 +74,7 @@ void FileTraceLogWriter::lastError(int err) { void FileTraceLogWriter::write(const std::string& str) { auto ptr = str.c_str(); int remaining = str.size(); + bool needsResolve = false; while ( remaining ) { int ret = __write( traceFileFD, ptr, remaining ); @@ -81,8 +82,13 @@ void FileTraceLogWriter::write(const std::string& str) { lastError(0); remaining -= ret; ptr += ret; + if (needsResolve) { + issues->resolveIssue("trace_log_file_write_error"); + needsResolve = false; + } } else { - issues->addAndExpire("trace_log_file_write_error"); + issues->addIssue("trace_log_file_write_error"); + needsResolve = true; fprintf(stderr, "Unexpected error [%d] when flushing trace log.\n", errno); lastError(errno); threadSleep(0.1); @@ -92,6 +98,7 @@ void FileTraceLogWriter::write(const std::string& str) { void FileTraceLogWriter::open() { cleanupTraceFiles(); + bool needsResolve = false; ++index; @@ -116,7 +123,8 @@ void FileTraceLogWriter::open() { } else { fprintf(stderr, "ERROR: could not create trace log file `%s' (%d: %s)\n", finalname.c_str(), errno, strerror(errno)); - issues->addAndExpire("trace_log_could_not_create_file"); + issues->addIssue("trace_log_could_not_create_file"); + needsResolve = true; int errorNum = errno; onMainThreadVoid([finalname, errorNum]{ @@ -129,6 +137,9 @@ void FileTraceLogWriter::open() { } } onMainThreadVoid([]{ latestEventCache.clear("TraceFileOpenError"); }, NULL); + if (needsResolve) { + issues->resolveIssue("trace_log_could_not_create_file"); + } lastError(0); } diff --git a/flow/Trace.cpp b/flow/Trace.cpp index e17202e53a..fdeab2e2a6 100644 --- a/flow/Trace.cpp +++ b/flow/Trace.cpp @@ -223,30 +223,19 @@ public: struct IssuesList : ITraceLogIssuesReporter, ThreadSafeReferenceCounted { IssuesList(){}; - void addAndExpire(std::string issue, double expirationInterval) override { + void addIssue(std::string issue) override { issues.insert(issue); } + + void retrieveIssues(std::set& out) override { MutexHolder h(mutex); - auto now = ::now(); - if (issues.find(issue) != issues.end()) { - issues[issue]++; - } else { - issues[issue] = 1; + for (auto const& i : issues) { + out.insert(i); } - queue.emplace_back(now + expirationInterval, issue); } - void retrieveIssues(std::vector& out) override { + void resolveIssue(std::string issue) override { MutexHolder h(mutex); - // clean up any expired events first - auto now = ::now(); - while (queue.size() > 0 && queue.front().first <= now) { - ASSERT(issues.find(queue.front().second) != issues.end()); - if (--issues[queue.front().second] == 0) { - issues.erase(queue.front().second); - } - queue.pop_front(); - } - for (auto const& i : issues) { - out.push_back(i.first); + if (issues.find(issue) != issues.end()) { + issues.erase(issue); } } @@ -255,8 +244,7 @@ public: private: Mutex mutex; - std::unordered_map issues; - Deque> queue; + std::set issues; }; Reference issues; @@ -551,7 +539,7 @@ public: writer->post(a); } - void retriveTraceLogIssues(std::vector& out) { return issues->retrieveIssues(out); } + void retriveTraceLogIssues(std::set& out) { return issues->retrieveIssues(out); } ~TraceLog() { close(); @@ -744,6 +732,7 @@ void removeTraceRole(std::string role) { g_traceLog.removeRole(role); } +<<<<<<< HEAD TraceEvent::TraceEvent() : initialized(true), enabled(false), logged(true) {} TraceEvent::TraceEvent(TraceEvent &&ev) { @@ -788,7 +777,7 @@ TraceEvent& TraceEvent::operator=(TraceEvent &&ev) { return *this; } -void retriveTraceLogIssues(std::vector& out) { +void retriveTraceLogIssues(std::set& out) { return g_traceLog.retriveTraceLogIssues(out); } diff --git a/flow/Trace.h b/flow/Trace.h index 11d274c5c3..7d91eccc57 100644 --- a/flow/Trace.h +++ b/flow/Trace.h @@ -529,13 +529,12 @@ struct ITraceLogFormatter { }; struct ITraceLogIssuesReporter { - // The issue will expire after (now + expirationInterval) seconds - virtual void addAndExpire(std::string issue, - double expirationInterval = FLOW_KNOBS->TRACE_LOG_ISSUE_EXPIRATION_INTERVAL) = 0; + virtual void addIssue(std::string issue) = 0; + virtual void resolveIssue(std::string issue) = 0; // When called, this function will first clean up expired issues. // If it's never called somehow and the trace log thread is struggling, the memory usage may build up. - virtual void retrieveIssues(std::vector& out) = 0; + virtual void retrieveIssues(std::set& out) = 0; virtual void addref() = 0; virtual void delref() = 0; @@ -599,7 +598,7 @@ bool validateTraceClockSource(std::string source); void addTraceRole(std::string role); void removeTraceRole(std::string role); -void retriveTraceLogIssues(std::vector& out); +void retriveTraceLogIssues(std::set& out); template struct ThreadFuture; void pingTraceLogWriterThread(ThreadFuture& p); From 7b51ab6b632ab85e129e3414eb6d3e66b3a6d710 Mon Sep 17 00:00:00 2001 From: Xin Dong Date: Tue, 25 Feb 2020 15:43:33 -0800 Subject: [PATCH 0719/1604] Rebased with master --- flow/Trace.cpp | 1 - flow/Trace.h | 2 -- 2 files changed, 3 deletions(-) diff --git a/flow/Trace.cpp b/flow/Trace.cpp index fdeab2e2a6..4a3cae6122 100644 --- a/flow/Trace.cpp +++ b/flow/Trace.cpp @@ -732,7 +732,6 @@ void removeTraceRole(std::string role) { g_traceLog.removeRole(role); } -<<<<<<< HEAD TraceEvent::TraceEvent() : initialized(true), enabled(false), logged(true) {} TraceEvent::TraceEvent(TraceEvent &&ev) { diff --git a/flow/Trace.h b/flow/Trace.h index 7d91eccc57..18613c07da 100644 --- a/flow/Trace.h +++ b/flow/Trace.h @@ -532,8 +532,6 @@ struct ITraceLogIssuesReporter { virtual void addIssue(std::string issue) = 0; virtual void resolveIssue(std::string issue) = 0; - // When called, this function will first clean up expired issues. - // If it's never called somehow and the trace log thread is struggling, the memory usage may build up. virtual void retrieveIssues(std::set& out) = 0; virtual void addref() = 0; From 6e7d2ff7dd682472da03520a0793ff78e8b3d169 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Tue, 25 Feb 2020 15:46:13 -0800 Subject: [PATCH 0720/1604] prevent the proxy from delaying too long based on an incorrect estimate of the compute time --- fdbrpc/sim2.actor.cpp | 2 +- fdbserver/Knobs.cpp | 1 + fdbserver/Knobs.h | 1 + fdbserver/MasterProxyServer.actor.cpp | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/fdbrpc/sim2.actor.cpp b/fdbrpc/sim2.actor.cpp index f9e8b482a3..74e06f8310 100644 --- a/fdbrpc/sim2.actor.cpp +++ b/fdbrpc/sim2.actor.cpp @@ -756,7 +756,7 @@ public: // timer() can be up to one second ahead of now() virtual double timer() { - timerTime += deterministicRandom()->random01()*(time+1.0-timerTime)/2.0; + timerTime += deterministicRandom()->random01()*(time+0.1-timerTime)/2.0; return timerTime; } diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index 992f84625c..7b8e25247b 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -318,6 +318,7 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs, bool isSimula init( ALWAYS_CAUSAL_READ_RISKY, false ); init( MAX_COMMIT_UPDATES, 2000 ); if( randomize && BUGGIFY ) MAX_COMMIT_UPDATES = 1; init( MIN_PROXY_COMPUTE, 0.001 ); + init( MAX_PROXY_COMPUTE, 2.0 ); init( PROXY_COMPUTE_BUCKETS, 20000 ); init( PROXY_COMPUTE_GROWTH_RATE, 0.01 ); diff --git a/fdbserver/Knobs.h b/fdbserver/Knobs.h index 42aee44ef5..70ab0be45f 100644 --- a/fdbserver/Knobs.h +++ b/fdbserver/Knobs.h @@ -264,6 +264,7 @@ public: bool ALWAYS_CAUSAL_READ_RISKY; int MAX_COMMIT_UPDATES; double MIN_PROXY_COMPUTE; + double MAX_PROXY_COMPUTE; int PROXY_COMPUTE_BUCKETS; double PROXY_COMPUTE_GROWTH_RATE; diff --git a/fdbserver/MasterProxyServer.actor.cpp b/fdbserver/MasterProxyServer.actor.cpp index 3ef38de80a..7859b4a4e5 100644 --- a/fdbserver/MasterProxyServer.actor.cpp +++ b/fdbserver/MasterProxyServer.actor.cpp @@ -534,7 +534,7 @@ ACTOR Future commitBatch( /////// Phase 1: Pre-resolution processing (CPU bound except waiting for a version # which is separately pipelined and *should* be available by now (unless empty commit); ordered; currently atomic but could yield) TEST(self->latestLocalCommitBatchResolving.get() < localBatchNumber-1); // Queuing pre-resolution commit processing wait(self->latestLocalCommitBatchResolving.whenAtLeast(localBatchNumber-1)); - state Future releaseDelay = delay(batchOperations*self->commitComputePerOperation[latencyBucket], TaskPriority::ProxyMasterVersionReply); + state Future releaseDelay = delay(std::min(SERVER_KNOBS->MAX_PROXY_COMPUTE, batchOperations*self->commitComputePerOperation[latencyBucket]), TaskPriority::ProxyMasterVersionReply); if (debugID.present()) g_traceBatch.addEvent("CommitDebug", debugID.get().first(), "MasterProxyServer.commitBatch.GettingCommitVersion"); From 12b5064041901b7accabda55334177c2034c1eb4 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Tue, 25 Feb 2020 15:47:10 -0800 Subject: [PATCH 0721/1604] a high free_space_ratio_cutoff is not needed anymore because avoid teams with low disk space is no longer the responsibility of getLoadBytes() --- fdbserver/Knobs.cpp | 2 +- fdbserver/Knobs.h | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index 7b8e25247b..2ce0aac021 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -184,7 +184,7 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs, bool isSimula init( DD_MERGE_COALESCE_DELAY, isSimulated ? 30.0 : 300.0 ); if( randomize && BUGGIFY ) DD_MERGE_COALESCE_DELAY = 0.001; init( STORAGE_METRICS_POLLING_DELAY, 2.0 ); if( randomize && BUGGIFY ) STORAGE_METRICS_POLLING_DELAY = 15.0; init( STORAGE_METRICS_RANDOM_DELAY, 0.2 ); - init( FREE_SPACE_RATIO_CUTOFF, 0.35 ); + init( AVAILABLE_SPACE_RATIO_CUTOFF, 0.05 ); init( DESIRED_TEAMS_PER_SERVER, 5 ); if( randomize && BUGGIFY ) DESIRED_TEAMS_PER_SERVER = 1; init( MAX_TEAMS_PER_SERVER, 5*DESIRED_TEAMS_PER_SERVER ); init( DD_SHARD_SIZE_GRANULARITY, 5000000 ); diff --git a/fdbserver/Knobs.h b/fdbserver/Knobs.h index 70ab0be45f..c5c41fc58f 100644 --- a/fdbserver/Knobs.h +++ b/fdbserver/Knobs.h @@ -149,8 +149,7 @@ public: double DD_MERGE_COALESCE_DELAY; double STORAGE_METRICS_POLLING_DELAY; double STORAGE_METRICS_RANDOM_DELAY; - double FREE_SPACE_RATIO_CUTOFF; - double FREE_SPACE_CUTOFF_PENALTY; + double AVAILABLE_SPACE_RATIO_CUTOFF; int DESIRED_TEAMS_PER_SERVER; int MAX_TEAMS_PER_SERVER; int64_t DD_SHARD_SIZE_GRANULARITY; From c05c95cbe8fcd85c0f3657ff39ce6490bab31c7e Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Tue, 25 Feb 2020 15:47:39 -0800 Subject: [PATCH 0722/1604] forgot to rename the knob --- fdbserver/DataDistribution.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/DataDistribution.actor.cpp b/fdbserver/DataDistribution.actor.cpp index 9ed9f3d159..3d1e8ecdfc 100644 --- a/fdbserver/DataDistribution.actor.cpp +++ b/fdbserver/DataDistribution.actor.cpp @@ -240,7 +240,7 @@ public: int64_t physicalBytes = getLoadAverage(); double minAvailableSpaceRatio = getMinAvailableSpaceRatio(includeInFlight); int64_t inFlightBytes = includeInFlight ? getDataInFlightToTeam() / servers.size() : 0; - double availableSpaceMultiplier = SERVER_KNOBS->FREE_SPACE_RATIO_CUTOFF / ( std::max( std::min( SERVER_KNOBS->FREE_SPACE_RATIO_CUTOFF, minAvailableSpaceRatio ), 0.000001 ) ); + double availableSpaceMultiplier = SERVER_KNOBS->AVAILABLE_SPACE_RATIO_CUTOFF / ( std::max( std::min( SERVER_KNOBS->AVAILABLE_SPACE_RATIO_CUTOFF, minAvailableSpaceRatio ), 0.000001 ) ); if(servers.size()>2) { //make sure in triple replication the penalty is high enough that you will always avoid a team with a member at 20% free space availableSpaceMultiplier = availableSpaceMultiplier * availableSpaceMultiplier; From a486ec2de09954b5433f186f01407c287c14fdd7 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Tue, 25 Feb 2020 15:48:00 -0800 Subject: [PATCH 0723/1604] pipelined fdbdr status --- fdbclient/DatabaseBackupAgent.actor.cpp | 31 +++++++++++++++++-------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/fdbclient/DatabaseBackupAgent.actor.cpp b/fdbclient/DatabaseBackupAgent.actor.cpp index e1e01672bc..8d2fe7ea4e 100644 --- a/fdbclient/DatabaseBackupAgent.actor.cpp +++ b/fdbclient/DatabaseBackupAgent.actor.cpp @@ -2277,6 +2277,7 @@ public: state Reference tr(new ReadYourWritesTransaction(cx)); tr->setOption(FDBTransactionOptions::LOCK_AWARE); state std::string statusText; + state int retries = 0; loop{ try { @@ -2294,27 +2295,33 @@ public: tr->setOption(FDBTransactionOptions::LOCK_AWARE); state Future> fPaused = tr->get(backupAgent->taskBucket->getPauseKey()); + state Future> fErrorValues = errorLimit > 0 ? tr->getRange(backupAgent->errors.get(BinaryWriter::toValue(logUid, Unversioned())).range(), errorLimit, false, true) : Future>(); + state Future> fBackupUid = tr->get(backupAgent->states.get(BinaryWriter::toValue(logUid, Unversioned())).pack(DatabaseBackupAgent::keyFolderId)); + state Future> fBackupVerison = tr->get(BinaryWriter::toValue(logUid, Unversioned()).withPrefix(applyMutationsBeginRange.begin)); + state Future> fTagName = tr->get(backupAgent->states.get(BinaryWriter::toValue(logUid, Unversioned())).pack(BackupAgentBase::keyConfigBackupTag)); + state Future> fStopVersionKey = tr->get(backupAgent->states.get(BinaryWriter::toValue(logUid, Unversioned())).pack(BackupAgentBase::keyStateStop)); + state Future> fBackupKeysPacked = tr->get(backupAgent->config.get(BinaryWriter::toValue(logUid, Unversioned())).pack(BackupAgentBase::keyConfigBackupRanges)); + int backupStateInt = wait(backupAgent->getStateValue(tr, logUid)); state BackupAgentBase::enumState backupState = (BackupAgentBase::enumState)backupStateInt; - + if (backupState == DatabaseBackupAgent::STATE_NEVERRAN) { statusText += "No previous backups found.\n"; } else { state std::string tagNameDisplay; - Optional tagName = wait(tr->get(backupAgent->states.get(BinaryWriter::toValue(logUid, Unversioned())).pack(BackupAgentBase::keyConfigBackupTag))); + Optional tagName = wait(fTagName); // Define the display tag name if (tagName.present()) { tagNameDisplay = tagName.get().toString(); } - state Optional uid = wait(tr->get(backupAgent->config.get(BinaryWriter::toValue(logUid, Unversioned())).pack(BackupAgentBase::keyFolderId))); - state Optional stopVersionKey = wait(tr->get(backupAgent->states.get(BinaryWriter::toValue(logUid, Unversioned())).pack(BackupAgentBase::keyStateStop))); + state Optional stopVersionKey = wait(fStopVersionKey); + + Optional backupKeysPacked = wait(fBackupKeysPacked); state Standalone> backupRanges; - Optional backupKeysPacked = wait(tr->get(backupAgent->config.get(BinaryWriter::toValue(logUid, Unversioned())).pack(BackupAgentBase::keyConfigBackupRanges))); - if (backupKeysPacked.present()) { BinaryReader br(backupKeysPacked.get(), IncludeVersion()); br >> backupRanges; @@ -2350,7 +2357,7 @@ public: // Append the errors, if requested if (errorLimit > 0) { - Standalone values = wait(tr->getRange(backupAgent->errors.get(BinaryWriter::toValue(logUid, Unversioned())).range(), errorLimit, false, true)); + Standalone values = wait( fErrorValues ); // Display the errors, if any if (values.size() > 0) { @@ -2367,10 +2374,9 @@ public: //calculate time differential - state Optional backupUid = wait(tr->get(backupAgent->states.get(BinaryWriter::toValue(logUid, Unversioned())).pack(DatabaseBackupAgent::keyFolderId))); + Optional backupUid = wait(fBackupUid); if(backupUid.present()) { - Optional v = wait(tr->get(BinaryWriter::toValue(logUid, Unversioned()).withPrefix(applyMutationsBeginRange.begin))); - + Optional v = wait(fBackupVerison); if (v.present()) { state Version destApplyBegin = BinaryReader::fromStringRef(v.get(), Unversioned()); Version sourceVersion = wait(srcReadVersion); @@ -2387,6 +2393,11 @@ public: break; } catch (Error &e) { + retries++; + if(retries > 5) { + statusText += format("\nWARNING: Could not fetch full DR status: %s\n", e.name()); + return statusText; + } wait(tr->onError(e)); } } From d3bca19960fa4d21e0c9392fc059d27128db4312 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Tue, 25 Feb 2020 15:57:32 -0800 Subject: [PATCH 0724/1604] backup should also submit on the first proxy for similar reasons to DR --- fdbclient/FileBackupAgent.actor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/fdbclient/FileBackupAgent.actor.cpp b/fdbclient/FileBackupAgent.actor.cpp index fa21bc710e..d1282174b6 100644 --- a/fdbclient/FileBackupAgent.actor.cpp +++ b/fdbclient/FileBackupAgent.actor.cpp @@ -3552,6 +3552,7 @@ public: ACTOR static Future submitBackup(FileBackupAgent* backupAgent, Reference tr, Key outContainer, int snapshotIntervalSeconds, std::string tagName, Standalone> backupRanges, bool stopWhenDone) { tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); tr->setOption(FDBTransactionOptions::LOCK_AWARE); + tr->setOption(FDBTransactionOptions::COMMIT_ON_FIRST_PROXY); TraceEvent(SevInfo, "FBA_SubmitBackup") .detail("TagName", tagName.c_str()) From d60268123b8cf5b3a866aaa4b007f1cea8aea54b Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Tue, 25 Feb 2020 16:00:46 -0800 Subject: [PATCH 0725/1604] updated comment --- fdbrpc/sim2.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbrpc/sim2.actor.cpp b/fdbrpc/sim2.actor.cpp index 74e06f8310..9d0e516899 100644 --- a/fdbrpc/sim2.actor.cpp +++ b/fdbrpc/sim2.actor.cpp @@ -754,7 +754,7 @@ public: // Everything actually network related is delegated to the Sim2Net class; Sim2 is only concerned with simulating machines and time virtual double now() { return time; } - // timer() can be up to one second ahead of now() + // timer() can be up to 0.1 seconds ahead of now() virtual double timer() { timerTime += deterministicRandom()->random01()*(time+0.1-timerTime)/2.0; return timerTime; From 6b78342f64da7ffd55ba0d81f8dcc7c7e99f02f8 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Tue, 25 Feb 2020 16:55:51 -0800 Subject: [PATCH 0726/1604] updated release notes for 6.2.16 --- documentation/sphinx/source/downloads.rst | 24 +++++++++---------- documentation/sphinx/source/release-notes.rst | 5 ++++ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/documentation/sphinx/source/downloads.rst b/documentation/sphinx/source/downloads.rst index c206708dc3..8d92ea03ad 100644 --- a/documentation/sphinx/source/downloads.rst +++ b/documentation/sphinx/source/downloads.rst @@ -10,38 +10,38 @@ macOS The macOS installation package is supported on macOS 10.7+. It includes the client and (optionally) the server. -* `FoundationDB-6.2.15.pkg `_ +* `FoundationDB-6.2.16.pkg `_ Ubuntu ------ The Ubuntu packages are supported on 64-bit Ubuntu 12.04+, but beware of the Linux kernel bug in Ubuntu 12.x. -* `foundationdb-clients-6.2.15-1_amd64.deb `_ -* `foundationdb-server-6.2.15-1_amd64.deb `_ (depends on the clients package) +* `foundationdb-clients-6.2.16-1_amd64.deb `_ +* `foundationdb-server-6.2.16-1_amd64.deb `_ (depends on the clients package) RHEL/CentOS EL6 --------------- The RHEL/CentOS EL6 packages are supported on 64-bit RHEL/CentOS 6.x. -* `foundationdb-clients-6.2.15-1.el6.x86_64.rpm `_ -* `foundationdb-server-6.2.15-1.el6.x86_64.rpm `_ (depends on the clients package) +* `foundationdb-clients-6.2.16-1.el6.x86_64.rpm `_ +* `foundationdb-server-6.2.16-1.el6.x86_64.rpm `_ (depends on the clients package) RHEL/CentOS EL7 --------------- The RHEL/CentOS EL7 packages are supported on 64-bit RHEL/CentOS 7.x. -* `foundationdb-clients-6.2.15-1.el7.x86_64.rpm `_ -* `foundationdb-server-6.2.15-1.el7.x86_64.rpm `_ (depends on the clients package) +* `foundationdb-clients-6.2.16-1.el7.x86_64.rpm `_ +* `foundationdb-server-6.2.16-1.el7.x86_64.rpm `_ (depends on the clients package) Windows ------- The Windows installer is supported on 64-bit Windows XP and later. It includes the client and (optionally) the server. -* `foundationdb-6.2.15-x64.msi `_ +* `foundationdb-6.2.16-x64.msi `_ API Language Bindings ===================== @@ -58,18 +58,18 @@ On macOS and Windows, the FoundationDB Python API bindings are installed as part If you need to use the FoundationDB Python API from other Python installations or paths, download the Python package: -* `foundationdb-6.2.15.tar.gz `_ +* `foundationdb-6.2.16.tar.gz `_ Ruby 1.9.3/2.0.0+ ----------------- -* `fdb-6.2.15.gem `_ +* `fdb-6.2.16.gem `_ Java 8+ ------- -* `fdb-java-6.2.15.jar `_ -* `fdb-java-6.2.15-javadoc.jar `_ +* `fdb-java-6.2.16.jar `_ +* `fdb-java-6.2.16-javadoc.jar `_ Go 1.11+ -------- diff --git a/documentation/sphinx/source/release-notes.rst b/documentation/sphinx/source/release-notes.rst index bcb0b48754..db7ed80c19 100644 --- a/documentation/sphinx/source/release-notes.rst +++ b/documentation/sphinx/source/release-notes.rst @@ -8,7 +8,11 @@ Release Notes Performance ----------- +* Reduced tail commit latencies by improving commit pipelining on the proxies. `(PR #2589) `_. +* Data distribution does a better job balancing data when disks are more than 70% full. `(PR #2722) `_. * Reverse range reads could read too much data from disk, resulting in poor performance relative to forward range reads. `(PR #2650) `_. +* Switched from LibreSSL to OpenSSL to improve the speed of establishing connections. `(PR #2650) `_. +* The cluster controller does a better job avoiding multiple recoveries when first recruited. `(PR #2698) `_. Fixes ----- @@ -19,6 +23,7 @@ Fixes * Backup container filename parsing was unnecessarily consulting the local filesystem which will error when permission is denied. `(PR #2693) `_. * Rebalancing data movement could stop doing work even though the data in the cluster was not well balanced. `(PR #2703) `_. * Data movement uses available space rather than free space when deciding how full a process is. `(PR #2708) `_. +* Fetching status attempts to reuse its connection with the cluster controller. `(PR #2583) `_. 6.2.15 ====== From 26d6e0799096e40ecd88b62ec39db3b4dc6e5675 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Tue, 25 Feb 2020 17:21:59 -0800 Subject: [PATCH 0727/1604] update installer WIX GUID following release --- packaging/msi/FDBInstaller.wxs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/msi/FDBInstaller.wxs b/packaging/msi/FDBInstaller.wxs index 0a5c2d9a1c..58b6ee7dac 100644 --- a/packaging/msi/FDBInstaller.wxs +++ b/packaging/msi/FDBInstaller.wxs @@ -32,7 +32,7 @@ Date: Tue, 25 Feb 2020 20:50:48 -0800 Subject: [PATCH 0728/1604] update version to 6.2.17 --- CMakeLists.txt | 2 +- bindings/python/LICENSE | 207 ++++++++++++++++++++++++++++++++++++++++ versions.target | 2 +- 3 files changed, 209 insertions(+), 2 deletions(-) create mode 100644 bindings/python/LICENSE diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b5356c432..a3ce2424e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ # limitations under the License. cmake_minimum_required(VERSION 3.12) project(foundationdb - VERSION 6.2.16 + VERSION 6.2.17 DESCRIPTION "FoundationDB is a scalable, fault-tolerant, ordered key-value store with full ACID transactions." HOMEPAGE_URL "http://www.foundationdb.org/" LANGUAGES C CXX ASM) diff --git a/bindings/python/LICENSE b/bindings/python/LICENSE new file mode 100644 index 0000000000..19586598a8 --- /dev/null +++ b/bindings/python/LICENSE @@ -0,0 +1,207 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + 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. + +------------------------------------------------------------------------------- +SOFTWARE DISTRIBUTED WITH FOUNDATIONDB: + +The FoundationDB software includes a number of subcomponents with separate +copyright notices and license terms - please see the file ACKNOWLEDGEMENTS. +------------------------------------------------------------------------------- diff --git a/versions.target b/versions.target index dca0d19801..e04723df48 100644 --- a/versions.target +++ b/versions.target @@ -1,7 +1,7 @@ - 6.2.16 + 6.2.17 6.2 From 30e628e79a466de69c2c212909bbe5b25b426d7d Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Tue, 25 Feb 2020 20:50:48 -0800 Subject: [PATCH 0729/1604] update installer WIX GUID following release --- packaging/msi/FDBInstaller.wxs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/msi/FDBInstaller.wxs b/packaging/msi/FDBInstaller.wxs index 58b6ee7dac..50cb2932ec 100644 --- a/packaging/msi/FDBInstaller.wxs +++ b/packaging/msi/FDBInstaller.wxs @@ -32,7 +32,7 @@ Date: Tue, 25 Feb 2020 20:55:55 -0800 Subject: [PATCH 0730/1604] remove extra license file --- bindings/python/LICENSE | 207 ---------------------------------------- 1 file changed, 207 deletions(-) delete mode 100644 bindings/python/LICENSE diff --git a/bindings/python/LICENSE b/bindings/python/LICENSE deleted file mode 100644 index 19586598a8..0000000000 --- a/bindings/python/LICENSE +++ /dev/null @@ -1,207 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - 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. - -------------------------------------------------------------------------------- -SOFTWARE DISTRIBUTED WITH FOUNDATIONDB: - -The FoundationDB software includes a number of subcomponents with separate -copyright notices and license terms - please see the file ACKNOWLEDGEMENTS. -------------------------------------------------------------------------------- From 74c929d98da2220ec93a69ec9f43c5eaa1a4496d Mon Sep 17 00:00:00 2001 From: Xin Dong Date: Wed, 26 Feb 2020 10:01:08 -0800 Subject: [PATCH 0731/1604] Fix windows build, again --- flow/Trace.h | 1 + 1 file changed, 1 insertion(+) diff --git a/flow/Trace.h b/flow/Trace.h index 18613c07da..fed251077e 100644 --- a/flow/Trace.h +++ b/flow/Trace.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "flow/IRandom.h" #include "flow/Error.h" From fbf5020af9f2f8fa013d576bfe092b639da5bd7d Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Wed, 26 Feb 2020 11:27:30 -0800 Subject: [PATCH 0732/1604] FastRestore:Applier:Add fetchKeys counter --- fdbserver/RestoreApplier.actor.cpp | 1 + fdbserver/RestoreApplier.actor.h | 3 ++- fdbserver/RestoreCommon.actor.h | 4 +--- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fdbserver/RestoreApplier.actor.cpp b/fdbserver/RestoreApplier.actor.cpp index e398e0ae8c..a628025883 100644 --- a/fdbserver/RestoreApplier.actor.cpp +++ b/fdbserver/RestoreApplier.actor.cpp @@ -306,6 +306,7 @@ ACTOR static Future precomputeMutationsResult(Reference for (; stagingKeyIter != batchData->stagingKeys.end(); stagingKeyIter++) { if (!stagingKeyIter->second.hasBaseValue()) { imcompleteStagingKeys.emplace(stagingKeyIter->first, stagingKeyIter); + batchData->counters.fetchKeys++; } } diff --git a/fdbserver/RestoreApplier.actor.h b/fdbserver/RestoreApplier.actor.h index 6782186650..99b7a08a66 100644 --- a/fdbserver/RestoreApplier.actor.h +++ b/fdbserver/RestoreApplier.actor.h @@ -218,13 +218,14 @@ struct ApplierBatchData : public ReferenceCounted { Counter receivedBytes, receivedWeightedBytes, receivedMutations, receivedAtomicOps; Counter appliedWeightedBytes, appliedMutations, appliedAtomicOps; Counter appliedTxns; + Counter fetchKeys; // number of keys to fetch from dest. FDB cluster. Counters(ApplierBatchData* self, UID applierInterfID, int batchIndex) : cc("ApplierBatch", applierInterfID.toString() + ":" + std::to_string(batchIndex)), receivedBytes("ReceivedBytes", cc), receivedMutations("ReceivedMutations", cc), receivedAtomicOps("ReceivedAtomicOps", cc), receivedWeightedBytes("ReceivedWeightedMutations", cc), appliedWeightedBytes("AppliedWeightedBytes", cc), appliedMutations("AppliedMutations", cc), - appliedAtomicOps("AppliedAtomicOps", cc), appliedTxns("AppliedTxns", cc) {} + appliedAtomicOps("AppliedAtomicOps", cc), appliedTxns("AppliedTxns", cc), fetchKeys("FetchKeys", cc) {} } counters; void addref() { return ReferenceCounted::addref(); } diff --git a/fdbserver/RestoreCommon.actor.h b/fdbserver/RestoreCommon.actor.h index 6c2f618349..1baaea1c37 100644 --- a/fdbserver/RestoreCommon.actor.h +++ b/fdbserver/RestoreCommon.actor.h @@ -45,8 +45,6 @@ // TODO: Merge this RestoreConfig with the original RestoreConfig in FileBackupAgent.actor.cpp // For convenience typedef FileBackupAgent::ERestoreState ERestoreState; -// template <> Tuple Codec::pack(ERestoreState const& val); -// template <> ERestoreState Codec::unpack(Tuple const& val); template<> inline Tuple Codec::pack(ERestoreState const &val) { return Tuple().append(val); } template<> inline ERestoreState Codec::unpack(Tuple const &val) { return (ERestoreState)val.getInt(0); } @@ -365,4 +363,4 @@ Future sendBatchRequests(RequestStream Interface::*channel, std:: } #include "flow/unactorcompiler.h" -#endif // FDBCLIENT_Restore_H \ No newline at end of file +#endif // FDBSERVER_RESTORECOMMON_ACTOR_H \ No newline at end of file From 0f5c999d4b433e30f70021fb335a471b256bedd3 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Wed, 26 Feb 2020 12:26:43 -0800 Subject: [PATCH 0733/1604] Better containment of boost errors related to TLS. --- bindings/c/fdb_c.cpp | 7 +--- fdbcli/fdbcli.actor.cpp | 3 -- fdbserver/fdbserver.actor.cpp | 1 + flow/Net2.actor.cpp | 62 +++++++++++++++++++---------------- 4 files changed, 36 insertions(+), 37 deletions(-) diff --git a/bindings/c/fdb_c.cpp b/bindings/c/fdb_c.cpp index 1c787f060a..e0aacf01d3 100644 --- a/bindings/c/fdb_c.cpp +++ b/bindings/c/fdb_c.cpp @@ -108,12 +108,7 @@ fdb_error_t fdb_network_set_option( FDBNetworkOption option, } fdb_error_t fdb_setup_network_impl() { - CATCH_AND_RETURN( - try { - API->setupNetwork(); - } catch (boost::system::system_error& e) { - return error_code_tls_error; - } ); + CATCH_AND_RETURN( API->setupNetwork() ); } fdb_error_t fdb_setup_network_v13( const char* localAddress ) { diff --git a/fdbcli/fdbcli.actor.cpp b/fdbcli/fdbcli.actor.cpp index 223a624a75..cf76fe7ee4 100644 --- a/fdbcli/fdbcli.actor.cpp +++ b/fdbcli/fdbcli.actor.cpp @@ -3757,8 +3757,5 @@ int main(int argc, char **argv) { } catch (Error& e) { printf("ERROR: %s (%d)\n", e.what(), e.code()); return 1; - } catch (boost::system::system_error& e) { - printf("ERROR: %s (%d)\n", e.what(), e.code().value()); - return 1; } } diff --git a/fdbserver/fdbserver.actor.cpp b/fdbserver/fdbserver.actor.cpp index 9df3ea4cf5..08c8695af7 100644 --- a/fdbserver/fdbserver.actor.cpp +++ b/fdbserver/fdbserver.actor.cpp @@ -1964,6 +1964,7 @@ int main(int argc, char* argv[]) { //printf("\n%d tests passed; %d tests failed\n", passCount, failCount); flushAndExit(FDB_EXIT_MAIN_ERROR); } catch (boost::system::system_error& e) { + ASSERT_WE_THINK(false); // boost errors shouldn't leak fprintf(stderr, "boost::system::system_error: %s (%d)", e.what(), e.code().value()); TraceEvent(SevError, "MainError").error(unknown_error()).detail("RootException", e.what()); //printf("\n%d tests passed; %d tests failed\n", passCount, failCount); diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index 22a58b181e..8c234796d7 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -863,36 +863,42 @@ Net2::Net2(bool useThreadPool, bool useMetrics, Reference policy, con TraceEvent("Net2Starting"); #ifndef TLS_DISABLED - sslContext.set_options(boost::asio::ssl::context::default_workarounds); - sslContext.set_verify_mode(boost::asio::ssl::context::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert); - if (policy) { - sslContext.set_verify_callback([policy](bool preverified, boost::asio::ssl::verify_context& ctx) { - return policy->verify_peer(preverified, ctx.native_handle()); - }); - } else { - sslContext.set_verify_callback(boost::bind(&insecurely_always_accept, _1, _2)); - } + try { + sslContext.set_options(boost::asio::ssl::context::default_workarounds); + sslContext.set_verify_mode(boost::asio::ssl::context::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert); + if (policy) { + sslContext.set_verify_callback([policy](bool preverified, boost::asio::ssl::verify_context& ctx) { + return policy->verify_peer(preverified, ctx.native_handle()); + }); + } else { + sslContext.set_verify_callback(boost::bind(&insecurely_always_accept, _1, _2)); + } - sslContext.set_password_callback(std::bind(&Net2::get_password, this)); + sslContext.set_password_callback(std::bind(&Net2::get_password, this)); - if (tlsParams.tlsCertPath.size() ) { - sslContext.use_certificate_chain_file(tlsParams.tlsCertPath); + if (tlsParams.tlsCertPath.size() ) { + sslContext.use_certificate_chain_file(tlsParams.tlsCertPath); + } + if (tlsParams.tlsCertBytes.size() ) { + sslContext.use_certificate(boost::asio::buffer(tlsParams.tlsCertBytes.data(), tlsParams.tlsCertBytes.size()), boost::asio::ssl::context::pem); + } + if (tlsParams.tlsCAPath.size()) { + std::string cert = readFileBytes(tlsParams.tlsCAPath, FLOW_KNOBS->CERT_FILE_MAX_SIZE); + sslContext.add_certificate_authority(boost::asio::buffer(cert.data(), cert.size())); + } + if (tlsParams.tlsCABytes.size()) { + sslContext.add_certificate_authority(boost::asio::buffer(tlsParams.tlsCABytes.data(), tlsParams.tlsCABytes.size())); + } + if (tlsParams.tlsKeyPath.size()) { + sslContext.use_private_key_file(tlsParams.tlsKeyPath, boost::asio::ssl::context::pem); + } + if (tlsParams.tlsKeyBytes.size()) { + sslContext.use_private_key(boost::asio::buffer(tlsParams.tlsKeyBytes.data(), tlsParams.tlsKeyBytes.size()), boost::asio::ssl::context::pem); + } } - if (tlsParams.tlsCertBytes.size() ) { - sslContext.use_certificate(boost::asio::buffer(tlsParams.tlsCertBytes.data(), tlsParams.tlsCertBytes.size()), boost::asio::ssl::context::pem); - } - if (tlsParams.tlsCAPath.size()) { - std::string cert = readFileBytes(tlsParams.tlsCAPath, FLOW_KNOBS->CERT_FILE_MAX_SIZE); - sslContext.add_certificate_authority(boost::asio::buffer(cert.data(), cert.size())); - } - if (tlsParams.tlsCABytes.size()) { - sslContext.add_certificate_authority(boost::asio::buffer(tlsParams.tlsCABytes.data(), tlsParams.tlsCABytes.size())); - } - if (tlsParams.tlsKeyPath.size()) { - sslContext.use_private_key_file(tlsParams.tlsKeyPath, boost::asio::ssl::context::pem); - } - if (tlsParams.tlsKeyBytes.size()) { - sslContext.use_private_key(boost::asio::buffer(tlsParams.tlsKeyBytes.data(), tlsParams.tlsKeyBytes.size()), boost::asio::ssl::context::pem); + catch(boost::system::system_error e) { + TraceEvent("Net2TLSInitError").detail("Message", e.what()); + throw tls_error(); } #endif @@ -1456,7 +1462,7 @@ INetwork* newNet2(bool useThreadPool, bool useMetrics, Reference poli } catch(boost::system::system_error e) { TraceEvent("Net2InitError").detail("Message", e.what()); - throw; + throw unknown_error(); } catch(std::exception const& e) { TraceEvent("Net2InitError").detail("Message", e.what()); From ca726fc68e6b7e38cde0a62a47e9157fd31ffbe2 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Wed, 26 Feb 2020 13:43:30 -0800 Subject: [PATCH 0734/1604] FastRestore:Introduce OOM protection An actor is schedulable to run if the current worker has enough resourc, i.e., the worker's memory usage is below the threshold; Exception: If the actor is working on the current version batch, we have to schedule the actor to run to avoid dead-lock. Future: When we release the actors that are blocked by memory usage, we should release them in increasing order of their version batch. --- fdbclient/RestoreWorkerInterface.actor.h | 4 +++- fdbserver/Knobs.cpp | 2 ++ fdbserver/Knobs.h | 2 ++ fdbserver/RestoreApplier.actor.cpp | 2 +- fdbserver/RestoreApplier.actor.h | 1 - fdbserver/RestoreLoader.actor.cpp | 20 ++++++++++++++++++ fdbserver/RestoreMaster.actor.cpp | 16 +++++++++++++++ fdbserver/RestoreMaster.actor.h | 1 - fdbserver/RestoreRoleCommon.actor.cpp | 26 ++++++++++++++++++++++++ fdbserver/RestoreRoleCommon.actor.h | 8 ++++++-- 10 files changed, 76 insertions(+), 6 deletions(-) diff --git a/fdbclient/RestoreWorkerInterface.actor.h b/fdbclient/RestoreWorkerInterface.actor.h index 82c6e9b25d..684a12c44e 100644 --- a/fdbclient/RestoreWorkerInterface.actor.h +++ b/fdbclient/RestoreWorkerInterface.actor.h @@ -130,6 +130,7 @@ struct RestoreLoaderInterface : RestoreRoleInterface { RequestStream loadFile; RequestStream sendMutations; RequestStream initVersionBatch; + RequestStream finishVersionBatch; RequestStream collectRestoreRoleInterfaces; RequestStream finishRestore; @@ -149,6 +150,7 @@ struct RestoreLoaderInterface : RestoreRoleInterface { loadFile.getEndpoint(TaskPriority::LoadBalancedEndpoint); sendMutations.getEndpoint(TaskPriority::LoadBalancedEndpoint); initVersionBatch.getEndpoint(TaskPriority::LoadBalancedEndpoint); + finishVersionBatch.getEndpoint(TaskPriority::LoadBalancedEndpoint); collectRestoreRoleInterfaces.getEndpoint(TaskPriority::LoadBalancedEndpoint); finishRestore.getEndpoint(TaskPriority::LoadBalancedEndpoint); } @@ -156,7 +158,7 @@ struct RestoreLoaderInterface : RestoreRoleInterface { template void serialize(Ar& ar) { serializer(ar, *(RestoreRoleInterface*)this, heartbeat, updateRestoreSysInfo, loadFile, sendMutations, - initVersionBatch, collectRestoreRoleInterfaces, finishRestore); + initVersionBatch, finishVersionBatch, collectRestoreRoleInterfaces, finishRestore); } }; diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index a0b479e3a1..d20e7b7e6d 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -558,6 +558,8 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs, bool isSimula init( FASTRESTORE_STRAGGLER_THRESHOLD, 60 ); if( randomize && BUGGIFY ) { FASTRESTORE_STRAGGLER_THRESHOLD = deterministicRandom()->random01() * 240 + 10; } init( FASTRESTORE_TRACK_REQUEST_LATENCY, true ); if( randomize && BUGGIFY ) { FASTRESTORE_TRACK_REQUEST_LATENCY = false; } init( FASTRESTORE_TRACK_LOADER_SEND_REQUESTS, false ); if( randomize && BUGGIFY ) { FASTRESTORE_TRACK_LOADER_SEND_REQUESTS = true; } + init( FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT, 6144 ); if( randomize && BUGGIFY ) { FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT = 1; } + init( FASTRESTORE_WAIT_FOR_MEMORY_LATENCY, 10 ); if( randomize && BUGGIFY ) { FASTRESTORE_WAIT_FOR_MEMORY_LATENCY = 60; } // clang-format on diff --git a/fdbserver/Knobs.h b/fdbserver/Knobs.h index 19a1d529df..0cf2f048d8 100644 --- a/fdbserver/Knobs.h +++ b/fdbserver/Knobs.h @@ -500,6 +500,8 @@ public: int64_t FASTRESTORE_STRAGGLER_THRESHOLD; bool FASTRESTORE_TRACK_REQUEST_LATENCY; bool FASTRESTORE_TRACK_LOADER_SEND_REQUESTS; // track requests of load send mutations to appliers? + int64_t FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT; // threshold when pipelined actors should be delayed + int64_t FASTRESTORE_WAIT_FOR_MEMORY_LATENCY; ServerKnobs(bool randomize = false, ClientKnobs* clientKnobs = NULL, bool isSimulated = false); }; diff --git a/fdbserver/RestoreApplier.actor.cpp b/fdbserver/RestoreApplier.actor.cpp index a628025883..4b587db402 100644 --- a/fdbserver/RestoreApplier.actor.cpp +++ b/fdbserver/RestoreApplier.actor.cpp @@ -306,7 +306,7 @@ ACTOR static Future precomputeMutationsResult(Reference for (; stagingKeyIter != batchData->stagingKeys.end(); stagingKeyIter++) { if (!stagingKeyIter->second.hasBaseValue()) { imcompleteStagingKeys.emplace(stagingKeyIter->first, stagingKeyIter); - batchData->counters.fetchKeys++; + batchData->counters.fetchKeys += 1; } } diff --git a/fdbserver/RestoreApplier.actor.h b/fdbserver/RestoreApplier.actor.h index 99b7a08a66..96f268eef6 100644 --- a/fdbserver/RestoreApplier.actor.h +++ b/fdbserver/RestoreApplier.actor.h @@ -329,7 +329,6 @@ struct ApplierBatchData : public ReferenceCounted { struct RestoreApplierData : RestoreRoleData, public ReferenceCounted { // Buffer for uncommitted data at ongoing version batches std::map> batch; - NotifiedVersion finishedBatch; // The version batch that has been applied to DB void addref() { return ReferenceCounted::addref(); } void delref() { return ReferenceCounted::delref(); } diff --git a/fdbserver/RestoreLoader.actor.cpp b/fdbserver/RestoreLoader.actor.cpp index 42cef592ad..8f956bd426 100644 --- a/fdbserver/RestoreLoader.actor.cpp +++ b/fdbserver/RestoreLoader.actor.cpp @@ -56,6 +56,7 @@ ACTOR static Future _parseRangeFileToMutationsOnLoader( std::map::iterator kvOpsIter, std::map::iterator samplesIter, LoaderCounters* cc, Reference bc, Version version, RestoreAsset asset); +ACTOR Future handleFinishVersionBatchRequest(RestoreVersionBatchRequest req, Reference self); ACTOR Future restoreLoaderCore(RestoreLoaderInterface loaderInterf, int nodeIndex, Database cx) { state Reference self = @@ -92,6 +93,10 @@ ACTOR Future restoreLoaderCore(RestoreLoaderInterface loaderInterf, int no requestTypeStr = "initVersionBatch"; actors.add(handleInitVersionBatchRequest(req, self)); } + when(RestoreVersionBatchRequest req = waitNext(loaderInterf.finishVersionBatch.getFuture())) { + requestTypeStr = "finishVersionBatch"; + actors.add(handleFinishVersionBatchRequest(req, self)); + } when(RestoreFinishRequest req = waitNext(loaderInterf.finishRestore.getFuture())) { requestTypeStr = "finishRestore"; handleFinishRestoreRequest(req, self); @@ -727,4 +732,19 @@ std::vector getApplierIDs(std::map& rangeToApplier) { ASSERT(!applierIDs.empty()); return applierIDs; +} + +// Notify loaders that the version batch (index) has been applied. +// This affects which version batch each loader can release actors even when the worker has low memory +ACTOR Future handleFinishVersionBatchRequest(RestoreVersionBatchRequest req, Reference self) { + // Ensure batch (i-1) is applied before batch i + TraceEvent("FastRestoreLoaderHandleFinishVersionBatch", self->id()) + .detail("FinishedBatchIndex", self->finishedBatch.get()) + .detail("RequestedBatchIndex", req.batchIndex); + wait(self->finishedBatch.whenAtLeast(req.batchIndex - 1)); + if (self->finishedBatch.get() == req.batchIndex - 1) { + self->finishedBatch.set(req.batchIndex); + } + req.reply.send(RestoreCommonReply(self->id(), false)); + return Void(); } \ No newline at end of file diff --git a/fdbserver/RestoreMaster.actor.cpp b/fdbserver/RestoreMaster.actor.cpp index eccbc6bc21..197eef9d73 100644 --- a/fdbserver/RestoreMaster.actor.cpp +++ b/fdbserver/RestoreMaster.actor.cpp @@ -57,6 +57,8 @@ ACTOR static Future notifyApplierToApplyMutations(Reference batchStatus, std::map appliersInterf, int batchIndex, NotifiedVersion* finishedBatch); +ACTOR static Future notifyLoadersVersionBatchFinished(std::map loadersInterf, + int batchIndex); ACTOR static Future notifyRestoreCompleted(Reference self, bool terminate); ACTOR static Future signalRestoreCompleted(Reference self, Database cx); @@ -542,6 +544,8 @@ ACTOR static Future distributeWorkloadPerVersionBatch(ReferenceappliersInterf, batchIndex, &self->finishedBatch)); + wait(notifyLoadersVersionBatchFinished(self->loadersInterf, batchIndex)); + self->runningVersionBatches.set(self->runningVersionBatches.get() - 1); return Void(); } @@ -806,6 +810,18 @@ ACTOR static Future notifyApplierToApplyMutations(Reference notifyLoadersVersionBatchFinished(std::map loadersInterf, + int batchIndex) { + std::vector> requestsToLoaders; + for (auto& loader : loadersInterf) { + requestsToLoaders.emplace_back(loader.first, RestoreVersionBatchRequest(batchIndex)); + } + wait(sendBatchRequests(&RestoreLoaderInterface::finishVersionBatch, loadersInterf, requestsToLoaders)); + + return Void(); +} + // Ask all loaders and appliers to perform housecleaning at the end of a restore request // Terminate those roles if terminate = true ACTOR static Future notifyRestoreCompleted(Reference self, bool terminate = false) { diff --git a/fdbserver/RestoreMaster.actor.h b/fdbserver/RestoreMaster.actor.h index 3ab6ff8932..e67987bad9 100644 --- a/fdbserver/RestoreMaster.actor.h +++ b/fdbserver/RestoreMaster.actor.h @@ -141,7 +141,6 @@ struct RestoreMasterData : RestoreRoleData, public ReferenceCounted> batch; std::map> batchStatus; - NotifiedVersion finishedBatch; // The highest batch index all appliers have applied mutations AsyncVar runningVersionBatches; // Currently running version batches diff --git a/fdbserver/RestoreRoleCommon.actor.cpp b/fdbserver/RestoreRoleCommon.actor.cpp index 46792a163e..f668c7450b 100644 --- a/fdbserver/RestoreRoleCommon.actor.cpp +++ b/fdbserver/RestoreRoleCommon.actor.cpp @@ -91,6 +91,32 @@ void updateProcessStats(Reference self) { } } +// An actor is schedulable to run if the current worker has enough resourc, i.e., +// the worker's memory usage is below the threshold; +// Exception: If the actor is working on the current version batch, we have to schedule +// the actor to run to avoid dead-lock. +// Future: When we release the actors that are blocked by memory usage, we should release them +// in increasing order of their version batch. +ACTOR Future isSchedulable(Reference self, int actorBatchIndex, std::string name) { + self->delayedActors++; + loop { + double memory = getSystemStatistics().processMemory; + if (memory < SERVER_KNOBS->FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT || + self->finishedBatch.get() + 1 == actorBatchIndex) { + if (memory >= SERVER_KNOBS->FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT) { + TraceEvent(SevWarn, "FastRestoreMemoryUsageAboveThreshold") + .detail("BatchIndex", actorBatchIndex) + .detail("Actor", name); + } + self->delayedActors--; + break; + } else { + wait(delay(SERVER_KNOBS->FASTRESTORE_WAIT_FOR_MEMORY_LATENCY) || self->checkMemory.onTrigger()); + } + } + return Void(); +} + ACTOR Future traceProcessMetrics(Reference self, std::string role) { loop { TraceEvent("FastRestoreTraceProcessMetrics") diff --git a/fdbserver/RestoreRoleCommon.actor.h b/fdbserver/RestoreRoleCommon.actor.h index fc41e8c2a5..1f54e632dd 100644 --- a/fdbserver/RestoreRoleCommon.actor.h +++ b/fdbserver/RestoreRoleCommon.actor.h @@ -114,14 +114,18 @@ public: double memory; double residentMemory; + AsyncTrigger checkMemory; + int delayedActors; // actors that are delayed to release because of low memory + std::map loadersInterf; // UID: loaderInterf's id std::map appliersInterf; // UID: applierInterf's id - NotifiedVersion versionBatchId; // Continuously increase for each versionBatch + NotifiedVersion versionBatchId; // The index of the version batch that has been initialized and put into pipeline + NotifiedVersion finishedBatch; // The highest batch index all appliers have applied mutations bool versionBatchStart = false; - RestoreRoleData() : role(RestoreRole::Invalid), cpuUsage(0.0), memory(0.0), residentMemory(0.0){}; + RestoreRoleData() : role(RestoreRole::Invalid), cpuUsage(0.0), memory(0.0), residentMemory(0.0), delayedActors(0){}; virtual ~RestoreRoleData() {} From a354f6ffa28c132cbe225103b57e518649231d44 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Wed, 26 Feb 2020 14:12:56 -0800 Subject: [PATCH 0735/1604] FastRestore:Applier:Use isSchedulable to guard OOM --- fdbserver/RestoreApplier.actor.cpp | 5 ++++- fdbserver/RestoreRoleCommon.actor.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/fdbserver/RestoreApplier.actor.cpp b/fdbserver/RestoreApplier.actor.cpp index 4b587db402..b4d936411c 100644 --- a/fdbserver/RestoreApplier.actor.cpp +++ b/fdbserver/RestoreApplier.actor.cpp @@ -112,7 +112,10 @@ ACTOR static Future handleSendMutationVectorRequest(RestoreSendVersionedMu .detail("BatchIndex", req.batchIndex) .detail("RestoreAsset", req.asset.toString()) .detail("ProcessedFileVersion", curFilePos.get()) - .detail("Request", req.toString()); + .detail("Request", req.toString()) + .detail("CurrentMemory", getSystemStatistics().processMemory); + + wait(isSchedulable(self, req.batchIndex, __FUNCTION__); wait(curFilePos.whenAtLeast(req.prevVersion)); diff --git a/fdbserver/RestoreRoleCommon.actor.h b/fdbserver/RestoreRoleCommon.actor.h index 1f54e632dd..a94fb58805 100644 --- a/fdbserver/RestoreRoleCommon.actor.h +++ b/fdbserver/RestoreRoleCommon.actor.h @@ -53,6 +53,7 @@ struct RestoreSimpleRequest; using VersionedMutationsMap = std::map; +ACTOR Future isSchedulable(Reference self, int actorBatchIndex, std::string name); ACTOR Future handleHeartbeat(RestoreSimpleRequest req, UID id); ACTOR Future handleInitVersionBatchRequest(RestoreVersionBatchRequest req, Reference self); void handleFinishRestoreRequest(const RestoreFinishRequest& req, Reference self); From 06495b90ae873e01fcac8d8eb2a0ce320d908aa9 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Wed, 26 Feb 2020 14:35:03 -0800 Subject: [PATCH 0736/1604] FastRestore:Loader:Use isSchedulable to guard OOM And trigger delayed actors that are blocked on memory to recheck memory. --- fdbserver/RestoreApplier.actor.cpp | 4 ++++ fdbserver/RestoreLoader.actor.cpp | 11 ++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/fdbserver/RestoreApplier.actor.cpp b/fdbserver/RestoreApplier.actor.cpp index b4d936411c..891409c98c 100644 --- a/fdbserver/RestoreApplier.actor.cpp +++ b/fdbserver/RestoreApplier.actor.cpp @@ -457,6 +457,10 @@ ACTOR static Future handleApplyToDBRequest(RestoreVersionBatchRequest req, self->finishedBatch.set(req.batchIndex); } } + + if (self->delayedActors > 0) { + self->checkMemory.trigger(); + } req.reply.send(RestoreCommonReply(self->id(), isDuplicated)); return Void(); diff --git a/fdbserver/RestoreLoader.actor.cpp b/fdbserver/RestoreLoader.actor.cpp index 8f956bd426..7191f5496d 100644 --- a/fdbserver/RestoreLoader.actor.cpp +++ b/fdbserver/RestoreLoader.actor.cpp @@ -201,7 +201,11 @@ ACTOR Future handleLoadFileRequest(RestoreLoadFileRequest req, ReferenceprocessedFileParams.find(req.param) == batchData->processedFileParams.end()) { TraceEvent("FastRestoreLoadFile", self->id()) .detail("BatchIndex", req.batchIndex) @@ -226,6 +230,8 @@ ACTOR Future handleLoadFileRequest(RestoreLoadFileRequest req, Reference handleSendMutationsRequest(RestoreSendMutationsToAppliersRequest req, Reference self) { state Reference batchData = self->batch[req.batchIndex]; @@ -745,6 +751,9 @@ ACTOR Future handleFinishVersionBatchRequest(RestoreVersionBatchRequest re if (self->finishedBatch.get() == req.batchIndex - 1) { self->finishedBatch.set(req.batchIndex); } + if (self->delayedActors > 0) { + self->checkMemory.trigger(); + } req.reply.send(RestoreCommonReply(self->id(), false)); return Void(); } \ No newline at end of file From fbb6e8f39dc29ad66e2ca5b5a26d5a48e124802c Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Wed, 26 Feb 2020 14:40:01 -0800 Subject: [PATCH 0737/1604] FastRestore:Create low memory situation in simulation on purpose --- fdbserver/RestoreApplier.actor.cpp | 2 +- fdbserver/RestoreLoader.actor.cpp | 2 +- fdbserver/RestoreMaster.actor.cpp | 7 +++++++ fdbserver/RestoreRoleCommon.actor.cpp | 5 +++++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/fdbserver/RestoreApplier.actor.cpp b/fdbserver/RestoreApplier.actor.cpp index 891409c98c..9bee66c55c 100644 --- a/fdbserver/RestoreApplier.actor.cpp +++ b/fdbserver/RestoreApplier.actor.cpp @@ -115,7 +115,7 @@ ACTOR static Future handleSendMutationVectorRequest(RestoreSendVersionedMu .detail("Request", req.toString()) .detail("CurrentMemory", getSystemStatistics().processMemory); - wait(isSchedulable(self, req.batchIndex, __FUNCTION__); + wait(isSchedulable(self, req.batchIndex, __FUNCTION__)); wait(curFilePos.whenAtLeast(req.prevVersion)); diff --git a/fdbserver/RestoreLoader.actor.cpp b/fdbserver/RestoreLoader.actor.cpp index 7191f5496d..e9d5a598f4 100644 --- a/fdbserver/RestoreLoader.actor.cpp +++ b/fdbserver/RestoreLoader.actor.cpp @@ -204,7 +204,7 @@ ACTOR Future handleLoadFileRequest(RestoreLoadFileRequest req, ReferenceprocessedFileParams.find(req.param) == batchData->processedFileParams.end()) { TraceEvent("FastRestoreLoadFile", self->id()) diff --git a/fdbserver/RestoreMaster.actor.cpp b/fdbserver/RestoreMaster.actor.cpp index 197eef9d73..b7d69c56d0 100644 --- a/fdbserver/RestoreMaster.actor.cpp +++ b/fdbserver/RestoreMaster.actor.cpp @@ -516,6 +516,9 @@ ACTOR static Future distributeWorkloadPerVersionBatch(ReferencerunningVersionBatches.set(self->runningVersionBatches.get() + 1); + // In case sampling data takes too much memory on master + wait(isSchedulable(self, batchIndex, __FUNCTION__)); + wait(initializeVersionBatch(self->appliersInterf, self->loadersInterf, batchIndex)); ASSERT(!versionBatch.isEmpty()); @@ -547,6 +550,10 @@ ACTOR static Future distributeWorkloadPerVersionBatch(ReferenceloadersInterf, batchIndex)); self->runningVersionBatches.set(self->runningVersionBatches.get() - 1); + + if (self->delayedActors > 0) { + self->checkMemory.trigger(); + } return Void(); } diff --git a/fdbserver/RestoreRoleCommon.actor.cpp b/fdbserver/RestoreRoleCommon.actor.cpp index f668c7450b..18ca7ea93d 100644 --- a/fdbserver/RestoreRoleCommon.actor.cpp +++ b/fdbserver/RestoreRoleCommon.actor.cpp @@ -101,6 +101,11 @@ ACTOR Future isSchedulable(Reference self, int actorBatch self->delayedActors++; loop { double memory = getSystemStatistics().processMemory; + if (g_network->isSimulated() && BUGGIFY) { + // Intentionally randomly block actors for low memory reason. + // memory will be larger than threshold when deterministicRandom()->random01() > 1/2 + memory = SERVER_KNOBS->FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT * 2 * deterministicRandom()->random01(); + } if (memory < SERVER_KNOBS->FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT || self->finishedBatch.get() + 1 == actorBatchIndex) { if (memory >= SERVER_KNOBS->FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT) { From 2586bade68dbdfd2e088dff5a920d73784a769c3 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 26 Feb 2020 15:33:48 -0800 Subject: [PATCH 0738/1604] re-added support for configuration TLS options with environment variables --- fdbclient/NativeAPI.actor.cpp | 8 ++++- fdbrpc/Platform.cpp | 21 ------------ fdbrpc/Platform.h | 6 ---- fdbserver/fdbserver.actor.cpp | 6 +++- flow/Net2.actor.cpp | 60 +++++++++++++++++++++++++++-------- flow/Platform.cpp | 24 +++++++------- flow/Platform.h | 7 ++-- 7 files changed, 77 insertions(+), 55 deletions(-) diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index e2867e848f..1b023dce92 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -890,20 +890,24 @@ void setNetworkOption(FDBNetworkOptions::Option option, Optional valu break; case FDBNetworkOptions::TLS_CERT_PATH: validateOptionValue(value, true); + tlsParams.tlsCertBytes = ""; tlsParams.tlsCertPath = value.get().toString(); break; case FDBNetworkOptions::TLS_CERT_BYTES: { validateOptionValue(value, true); + tlsParams.tlsCertPath = ""; tlsParams.tlsCertBytes = value.get().toString(); break; } case FDBNetworkOptions::TLS_CA_PATH: { validateOptionValue(value, true); + tlsParams.tlsCABytes = ""; tlsParams.tlsCAPath = value.get().toString(); break; } case FDBNetworkOptions::TLS_CA_BYTES: { validateOptionValue(value, true); + tlsParams.tlsCAPath = ""; tlsParams.tlsCABytes = value.get().toString(); break; } @@ -912,11 +916,13 @@ void setNetworkOption(FDBNetworkOptions::Option option, Optional valu tlsParams.tlsPassword = value.get().toString(); break; case FDBNetworkOptions::TLS_KEY_PATH: - validateOptionValue(value, true); + validateOptionValue(value, true); + tlsParams.tlsKeyBytes = ""; tlsParams.tlsKeyPath = value.get().toString(); break; case FDBNetworkOptions::TLS_KEY_BYTES: { validateOptionValue(value, true); + tlsParams.tlsKeyPath = ""; tlsParams.tlsKeyBytes = value.get().toString(); break; } diff --git a/fdbrpc/Platform.cpp b/fdbrpc/Platform.cpp index a680540d53..12af7491d0 100644 --- a/fdbrpc/Platform.cpp +++ b/fdbrpc/Platform.cpp @@ -112,24 +112,6 @@ int eraseDirectoryRecursive(std::string const& dir) { return __eraseDirectoryRecurseiveCount; } -std::string getDefaultConfigPath() { -#ifdef _WIN32 - TCHAR szPath[MAX_PATH]; - if( SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, 0, szPath) != S_OK ) { - TraceEvent(SevError, "WindowsAppDataError").GetLastError(); - throw platform_error(); - } - std::string _filepath(szPath); - return _filepath + "\\foundationdb"; -#elif defined(__linux__) - return "/etc/foundationdb"; -#elif defined(__APPLE__) - return "/usr/local/etc/foundationdb"; -#else - #error Port me! -#endif -} - bool isSse42Supported() { #if defined(_WIN32) @@ -145,7 +127,4 @@ bool isSse42Supported() #endif } -std::string getDefaultClusterFilePath() { - return joinPath(platform::getDefaultConfigPath(), "fdb.cluster"); -} } // namespace platform diff --git a/fdbrpc/Platform.h b/fdbrpc/Platform.h index fe6eb69542..8051057fd4 100644 --- a/fdbrpc/Platform.h +++ b/fdbrpc/Platform.h @@ -30,12 +30,6 @@ namespace platform { // Avoid in production code: not atomic, not fast, not reliable in all environments int eraseDirectoryRecursive(std::string const& directory); -// Returns the absolute platform-dependant path for the default fdb.cluster file -std::string getDefaultClusterFilePath(); - -// Returns the absolute platform-dependant path for server-based files -std::string getDefaultConfigPath(); - bool isSse42Supported(); } // namespace platform diff --git a/fdbserver/fdbserver.actor.cpp b/fdbserver/fdbserver.actor.cpp index 9df3ea4cf5..918dc7bd30 100644 --- a/fdbserver/fdbserver.actor.cpp +++ b/fdbserver/fdbserver.actor.cpp @@ -1553,7 +1553,11 @@ int main(int argc, char* argv[]) { } else { #ifndef TLS_DISABLED if ( tlsVerifyPeers.size() ) { - tlsPolicy->set_verify_peers( tlsVerifyPeers ); + if (!tlsPolicy->set_verify_peers( tlsVerifyPeers )) { + fprintf(stderr, "ERROR: The format of the --tls_verify_peers option is incorrect.\n"); + printHelpTeaser(argv[0]); + flushAndExit(FDB_EXIT_ERROR); + } } #endif g_network = newNet2(useThreadPool, true, tlsPolicy, tlsParams); diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index 22a58b181e..dede7b92f0 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -111,7 +111,7 @@ thread_local INetwork* thread_network = 0; class Net2 sealed : public INetwork, public INetworkConnections { public: - Net2(bool useThreadPool, bool useMetrics, Reference policy, const TLSParams& tlsParams); + Net2(bool useThreadPool, bool useMetrics, Reference policy, TLSParams tlsParams); void run(); void initMetrics(); @@ -844,7 +844,7 @@ bool insecurely_always_accept(bool _1, boost::asio::ssl::verify_context& _2) { } #endif -Net2::Net2(bool useThreadPool, bool useMetrics, Reference policy, const TLSParams& tlsParams) +Net2::Net2(bool useThreadPool, bool useMetrics, Reference policy, TLSParams tlsParams) : useThreadPool(useThreadPool), network(this), reactor(this), @@ -863,6 +863,20 @@ Net2::Net2(bool useThreadPool, bool useMetrics, Reference policy, con TraceEvent("Net2Starting"); #ifndef TLS_DISABLED + const char *defaultCertFileName = "fdb.pem"; + + if( policy && !policy->rules.size() ) { + std::string verify_peers; + if (platform::getEnvironmentVar("FDB_TLS_VERIFY_PEERS", verify_peers)) { + if(!policy->set_verify_peers({ verify_peers })) { + TraceEvent(SevWarnAlways, "TLSVerifySetError").detail("Input", verify_peers ); + throw tls_error(); + } + } else { + policy->set_verify_peers({ std::string("Check.Valid=1")}); + } + } + sslContext.set_options(boost::asio::ssl::context::default_workarounds); sslContext.set_verify_mode(boost::asio::ssl::context::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert); if (policy) { @@ -873,27 +887,47 @@ Net2::Net2(bool useThreadPool, bool useMetrics, Reference policy, con sslContext.set_verify_callback(boost::bind(&insecurely_always_accept, _1, _2)); } + if ( !tlsPassword.size() ) { + platform::getEnvironmentVar( "FDB_TLS_PASSWORD", tlsPassword ); + } sslContext.set_password_callback(std::bind(&Net2::get_password, this)); - if (tlsParams.tlsCertPath.size() ) { - sslContext.use_certificate_chain_file(tlsParams.tlsCertPath); + if ( tlsParams.tlsCertBytes.size() ) { + sslContext.use_certificate_chain(boost::asio::buffer(tlsParams.tlsCertBytes.data(), tlsParams.tlsCertBytes.size())); } - if (tlsParams.tlsCertBytes.size() ) { - sslContext.use_certificate(boost::asio::buffer(tlsParams.tlsCertBytes.data(), tlsParams.tlsCertBytes.size()), boost::asio::ssl::context::pem); + else { + if ( !tlsParams.tlsCertPath.size() ) { + if ( !platform::getEnvironmentVar( "FDB_TLS_CERTIFICATE_FILE", tlsParams.tlsCertPath ) ) { + tlsParams.tlsCertPath = fileExists(defaultCertFileName) ? defaultCertFileName : joinPath(platform::getDefaultConfigPath(), defaultCertFileName); + } + sslContext.use_certificate_chain_file(tlsParams.tlsCertPath); + } } - if (tlsParams.tlsCAPath.size()) { - std::string cert = readFileBytes(tlsParams.tlsCAPath, FLOW_KNOBS->CERT_FILE_MAX_SIZE); - sslContext.add_certificate_authority(boost::asio::buffer(cert.data(), cert.size())); - } - if (tlsParams.tlsCABytes.size()) { + + if ( tlsParams.tlsCABytes.size() ) { sslContext.add_certificate_authority(boost::asio::buffer(tlsParams.tlsCABytes.data(), tlsParams.tlsCABytes.size())); } - if (tlsParams.tlsKeyPath.size()) { - sslContext.use_private_key_file(tlsParams.tlsKeyPath, boost::asio::ssl::context::pem); + else { + if ( !tlsParams.tlsCAPath.size() ) { + platform::getEnvironmentVar("FDB_TLS_CA_FILE", tlsParams.tlsCAPath); + } + if ( tlsParams.tlsCAPath.size() ) { + std::string cert = readFileBytes(tlsParams.tlsCAPath, FLOW_KNOBS->CERT_FILE_MAX_SIZE); + sslContext.add_certificate_authority(boost::asio::buffer(cert.data(), cert.size())); + } } + if (tlsParams.tlsKeyBytes.size()) { sslContext.use_private_key(boost::asio::buffer(tlsParams.tlsKeyBytes.data(), tlsParams.tlsKeyBytes.size()), boost::asio::ssl::context::pem); + } else { + if (!tlsParams.tlsKeyPath.size()) { + if(!platform::getEnvironmentVar( "FDB_TLS_KEY_FILE", tlsParams.tlsKeyPath)) { + tlsParams.tlsKeyPath = fileExists(defaultCertFileName) ? defaultCertFileName : joinPath(platform::getDefaultConfigPath(), defaultCertFileName); + } + } + sslContext.use_private_key_file(tlsParams.tlsKeyPath, boost::asio::ssl::context::pem); } + #endif // Set the global members diff --git a/flow/Platform.cpp b/flow/Platform.cpp index 962fe1e8e0..b1f70c6bc4 100644 --- a/flow/Platform.cpp +++ b/flow/Platform.cpp @@ -2369,26 +2369,28 @@ std::string getWorkingDirectory() { extern std::string format( const char *form, ... ); - namespace platform { - -std::string getDefaultPluginPath( const char* plugin_name ) { +std::string getDefaultConfigPath() { #ifdef _WIN32 - std::string installPath; - if(!platform::getEnvironmentVar("FOUNDATIONDB_INSTALL_PATH", installPath)) { - // This is relying of the DLL search order to load the plugin, - // starting in the same directory as the executable. - return plugin_name; + TCHAR szPath[MAX_PATH]; + if( SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, 0, szPath) != S_OK ) { + TraceEvent(SevError, "WindowsAppDataError").GetLastError(); + throw platform_error(); } - return format( "%splugins\\%s.dll", installPath.c_str(), plugin_name ); + std::string _filepath(szPath); + return _filepath + "\\foundationdb"; #elif defined(__linux__) - return format( "/usr/lib/foundationdb/plugins/%s.so", plugin_name ); + return "/etc/foundationdb"; #elif defined(__APPLE__) - return format( "/usr/local/foundationdb/plugins/%s.dylib", plugin_name ); + return "/usr/local/etc/foundationdb"; #else #error Port me! #endif } + +std::string getDefaultClusterFilePath() { + return joinPath(getDefaultConfigPath(), "fdb.cluster"); +} } // namespace platform #ifdef ALLOC_INSTRUMENTATION diff --git a/flow/Platform.h b/flow/Platform.h index 6e1300ac3e..28788b0a47 100644 --- a/flow/Platform.h +++ b/flow/Platform.h @@ -375,8 +375,11 @@ int setEnvironmentVar(const char *name, const char *value, int overwrite); std::string getWorkingDirectory(); -// Returns the ... something something figure out plugin locations -std::string getDefaultPluginPath( const char* plugin_name ); +// Returns the absolute platform-dependant path for server-based files +std::string getDefaultConfigPath(); + +// Returns the absolute platform-dependant path for the default fdb.cluster file +std::string getDefaultClusterFilePath(); void *getImageOffset(); From d1598e7c99d415f0e932bad613c2352ad861feaf Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 26 Feb 2020 16:06:16 -0800 Subject: [PATCH 0739/1604] set_verify_peers throws an error instead of returning a value --- fdbclient/NativeAPI.actor.cpp | 6 +----- fdbserver/fdbserver.actor.cpp | 4 +++- flow/TLSPolicy.cpp | 5 ++--- flow/TLSPolicy.h | 2 +- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index 1b023dce92..5f9d9878e2 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -930,11 +930,7 @@ void setNetworkOption(FDBNetworkOptions::Option option, Optional valu validateOptionValue(value, true); initTLSPolicy(); #ifndef TLS_DISABLED - if (!tlsPolicy->set_verify_peers({ value.get().toString() })) { - TraceEvent(SevWarnAlways, "TLSValidationSetError") - .detail("Input", value.get().toString() ); - throw invalid_option_value(); - } + tlsPolicy->set_verify_peers({ value.get().toString() }); #endif break; case FDBNetworkOptions::CLIENT_BUGGIFY_ENABLE: diff --git a/fdbserver/fdbserver.actor.cpp b/fdbserver/fdbserver.actor.cpp index 918dc7bd30..022d4191ff 100644 --- a/fdbserver/fdbserver.actor.cpp +++ b/fdbserver/fdbserver.actor.cpp @@ -1553,7 +1553,9 @@ int main(int argc, char* argv[]) { } else { #ifndef TLS_DISABLED if ( tlsVerifyPeers.size() ) { - if (!tlsPolicy->set_verify_peers( tlsVerifyPeers )) { + try { + tlsPolicy->set_verify_peers( tlsVerifyPeers ); + } catch( Error &e ) { fprintf(stderr, "ERROR: The format of the --tls_verify_peers option is incorrect.\n"); printHelpTeaser(argv[0]); flushAndExit(FDB_EXIT_ERROR); diff --git a/flow/TLSPolicy.cpp b/flow/TLSPolicy.cpp index c3a71abe1e..cc83a24629 100644 --- a/flow/TLSPolicy.cpp +++ b/flow/TLSPolicy.cpp @@ -215,7 +215,7 @@ static X509Location locationForNID(NID nid) { } } -bool TLSPolicy::set_verify_peers(std::vector verify_peers) { +void TLSPolicy::set_verify_peers(std::vector verify_peers) { for (int i = 0; i < verify_peers.size(); i++) { try { std::string& verifyString = verify_peers[i]; @@ -235,10 +235,9 @@ bool TLSPolicy::set_verify_peers(std::vector verify_peers) { rules.clear(); std::string& verifyString = verify_peers[i]; TraceEvent(SevError, "FDBLibTLSVerifyPeersParseError").detail("Config", verifyString); - return false; + throw tls_error(); } } - return true; } TLSPolicy::Rule::Rule(std::string input) { diff --git a/flow/TLSPolicy.h b/flow/TLSPolicy.h index 1af5abfb73..9a0ddfcfa9 100644 --- a/flow/TLSPolicy.h +++ b/flow/TLSPolicy.h @@ -89,7 +89,7 @@ public: #ifndef TLS_DISABLED static std::string ErrorString(boost::system::error_code e); - bool set_verify_peers(std::vector verify_peers); + void set_verify_peers(std::vector verify_peers); bool verify_peer(bool preverified, X509_STORE_CTX* store_ctx); std::string toString() const; From f85af10a187fc06e6c0b176a112fd0259bb7e90c Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 26 Feb 2020 16:06:45 -0800 Subject: [PATCH 0740/1604] fixed a few problems with tls setup --- flow/Net2.actor.cpp | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index dede7b92f0..7e2c214ea0 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -868,10 +868,7 @@ Net2::Net2(bool useThreadPool, bool useMetrics, Reference policy, TLS if( policy && !policy->rules.size() ) { std::string verify_peers; if (platform::getEnvironmentVar("FDB_TLS_VERIFY_PEERS", verify_peers)) { - if(!policy->set_verify_peers({ verify_peers })) { - TraceEvent(SevWarnAlways, "TLSVerifySetError").detail("Input", verify_peers ); - throw tls_error(); - } + policy->set_verify_peers({ verify_peers }); } else { policy->set_verify_peers({ std::string("Check.Valid=1")}); } @@ -898,8 +895,14 @@ Net2::Net2(bool useThreadPool, bool useMetrics, Reference policy, TLS else { if ( !tlsParams.tlsCertPath.size() ) { if ( !platform::getEnvironmentVar( "FDB_TLS_CERTIFICATE_FILE", tlsParams.tlsCertPath ) ) { - tlsParams.tlsCertPath = fileExists(defaultCertFileName) ? defaultCertFileName : joinPath(platform::getDefaultConfigPath(), defaultCertFileName); + if( fileExists(defaultCertFileName) ) { + tlsParams.tlsCertPath = fileExists(defaultCertFileName); + } else if( fileExists( joinPath(platform::getDefaultConfigPath(), defaultCertFileName) ) ) { + tlsParams.tlsCertPath = joinPath(platform::getDefaultConfigPath(), defaultCertFileName); + } } + } + if ( tlsParams.tlsCertPath.size() ) { sslContext.use_certificate_chain_file(tlsParams.tlsCertPath); } } @@ -912,8 +915,14 @@ Net2::Net2(bool useThreadPool, bool useMetrics, Reference policy, TLS platform::getEnvironmentVar("FDB_TLS_CA_FILE", tlsParams.tlsCAPath); } if ( tlsParams.tlsCAPath.size() ) { - std::string cert = readFileBytes(tlsParams.tlsCAPath, FLOW_KNOBS->CERT_FILE_MAX_SIZE); - sslContext.add_certificate_authority(boost::asio::buffer(cert.data(), cert.size())); + try { + std::string cert = readFileBytes(tlsParams.tlsCAPath, FLOW_KNOBS->CERT_FILE_MAX_SIZE); + sslContext.add_certificate_authority(boost::asio::buffer(cert.data(), cert.size())); + } + catch (Error& e) { + fprintf(stderr, "Error reading CA file %s: %s\n", tlsParams.tlsCAPath.c_str(), e.name()); + throw; + } } } @@ -922,10 +931,16 @@ Net2::Net2(bool useThreadPool, bool useMetrics, Reference policy, TLS } else { if (!tlsParams.tlsKeyPath.size()) { if(!platform::getEnvironmentVar( "FDB_TLS_KEY_FILE", tlsParams.tlsKeyPath)) { - tlsParams.tlsKeyPath = fileExists(defaultCertFileName) ? defaultCertFileName : joinPath(platform::getDefaultConfigPath(), defaultCertFileName); + if( fileExists(defaultCertFileName) ) { + tlsParams.tlsKeyPath = fileExists(defaultCertFileName); + } else if( fileExists( joinPath(platform::getDefaultConfigPath(), defaultCertFileName) ) ) { + tlsParams.tlsKeyPath = joinPath(platform::getDefaultConfigPath(), defaultCertFileName); + } } } - sslContext.use_private_key_file(tlsParams.tlsKeyPath, boost::asio::ssl::context::pem); + if (tlsParams.tlsKeyPath.size()) { + sslContext.use_private_key_file(tlsParams.tlsKeyPath, boost::asio::ssl::context::pem); + } } #endif From 97d7eb49b52d33fa3041b95e73f58b0ae68ef0b4 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Wed, 26 Feb 2020 15:45:21 -0800 Subject: [PATCH 0741/1604] FastRestore:Master:Report unavailable role periodically Ping all restore roles and report unavailable ones. --- fdbserver/Knobs.cpp | 2 ++ fdbserver/Knobs.h | 2 ++ fdbserver/RestoreMaster.actor.cpp | 53 +++++++++++++++++++++++++++++++ fdbserver/RestoreMaster.actor.h | 2 ++ 4 files changed, 59 insertions(+) diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index d20e7b7e6d..a50760febe 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -560,6 +560,8 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs, bool isSimula init( FASTRESTORE_TRACK_LOADER_SEND_REQUESTS, false ); if( randomize && BUGGIFY ) { FASTRESTORE_TRACK_LOADER_SEND_REQUESTS = true; } init( FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT, 6144 ); if( randomize && BUGGIFY ) { FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT = 1; } init( FASTRESTORE_WAIT_FOR_MEMORY_LATENCY, 10 ); if( randomize && BUGGIFY ) { FASTRESTORE_WAIT_FOR_MEMORY_LATENCY = 60; } + init( FASTRESTORE_HEARTBEAT_DELAY, 10 ); if( randomize && BUGGIFY ) { FASTRESTORE_HEARTBEAT_DELAY = deterministicRandom()->random01() * 120; } + init( FASTRESTORE_HEARTBEAT_MAX_DELAY, 10 ); if( randomize && BUGGIFY ) { FASTRESTORE_HEARTBEAT_MAX_DELAY = FASTRESTORE_HEARTBEAT_DELAY * 10; } // clang-format on diff --git a/fdbserver/Knobs.h b/fdbserver/Knobs.h index 0cf2f048d8..6cab39ac5b 100644 --- a/fdbserver/Knobs.h +++ b/fdbserver/Knobs.h @@ -502,6 +502,8 @@ public: bool FASTRESTORE_TRACK_LOADER_SEND_REQUESTS; // track requests of load send mutations to appliers? int64_t FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT; // threshold when pipelined actors should be delayed int64_t FASTRESTORE_WAIT_FOR_MEMORY_LATENCY; + int64_t FASTRESTORE_HEARTBEAT_DELAY; // interval for master to ping loaders and appliers + int64_t FASTRESTORE_HEARTBEAT_MAX_DELAY; // master claim a node is down if no heart beat from the node for this delay ServerKnobs(bool randomize = false, ClientKnobs* clientKnobs = NULL, bool isSimulated = false); }; diff --git a/fdbserver/RestoreMaster.actor.cpp b/fdbserver/RestoreMaster.actor.cpp index b7d69c56d0..5e22c2ff9e 100644 --- a/fdbserver/RestoreMaster.actor.cpp +++ b/fdbserver/RestoreMaster.actor.cpp @@ -61,17 +61,23 @@ ACTOR static Future notifyLoadersVersionBatchFinished(std::map notifyRestoreCompleted(Reference self, bool terminate); ACTOR static Future signalRestoreCompleted(Reference self, Database cx); +ACTOR static Future updateHeartbeatTime(Reference self); +ACTOR static Future checkRolesLiveness(Reference self); void splitKeyRangeForAppliers(Reference batchData, std::map appliersInterf, int batchIndex); ACTOR Future startRestoreMaster(Reference masterWorker, Database cx) { state Reference self = Reference(new RestoreMasterData()); + state ActorCollectionNoErrors actors; try { // recruitRestoreRoles must come after masterWorker has finished collectWorkerInterface wait(recruitRestoreRoles(masterWorker, self)); + actors.add(updateHeartbeatTime(self)); + actors.add(checkRolesLiveness(self)); + wait(distributeRestoreSysInfo(masterWorker, self)); wait(startProcessRestoreRequests(self, cx)); @@ -885,4 +891,51 @@ ACTOR static Future signalRestoreCompleted(Reference se TraceEvent("FastRestore").detail("RestoreMaster", "AllRestoreCompleted"); return Void(); +} + +// Update the most recent time when master receives hearbeat from each loader and applier +ACTOR static Future updateHeartbeatTime(Reference self) { + state std::map::iterator loader = self->loadersInterf.begin(); + state std::map::iterator applier = self->appliersInterf.begin(); + state std::vector> fReplies; + state std::vector nodes; + loop { + loader = self->loadersInterf.begin(); + applier = self->appliersInterf.begin(); + fReplies.clear(); + nodes.clear(); + // ping loaders and appliers + while(loader != self->loadersInterf.end()) { + fReplies.push_back(loader->second.heartbeat.getReply(RestoreSimpleRequest())); + nodes.push_back(loader->first); + loader++; + } + while(applier != self->appliersInterf.end()) { + fReplies.push_back(applier->second.heartbeat.getReply(RestoreSimpleRequest())); + nodes.push_back(applier->first); + applier++; + } + + wait(waitForAll(fReplies) || delay(SERVER_KNOBS->FASTRESTORE_HEARTBEAT_DELAY)); + // Update the most recent heart beat time for each role + for (int i = 0; i < fReplies.size(); ++i) { + if (fReplies[i].isReady()) { + double currentTime = now(); + auto item = self->rolesHeartBeatTime.emplace(nodes[i], currentTime); + item.first->second = currentTime; + } + } + } +} + +// Check if a restore role dies or disconnected +ACTOR static Future checkRolesLiveness(Reference self) { + loop { + wait(delay(SERVER_KNOBS->FASTRESTORE_HEARTBEAT_MAX_DELAY)); + for (auto& role : self->rolesHeartBeatTime) { + if (now() - role.second > SERVER_KNOBS->FASTRESTORE_HEARTBEAT_MAX_DELAY) { + TraceEvent(SevWarnAlways, "FastRestoreUnavailableRole", role.first).detail("Delta", now() - role.second).detail("LastAliveTime", role.second); + } + } + } } \ No newline at end of file diff --git a/fdbserver/RestoreMaster.actor.h b/fdbserver/RestoreMaster.actor.h index e67987bad9..19b4ff4f4a 100644 --- a/fdbserver/RestoreMaster.actor.h +++ b/fdbserver/RestoreMaster.actor.h @@ -144,6 +144,8 @@ struct RestoreMasterData : RestoreRoleData, public ReferenceCounted runningVersionBatches; // Currently running version batches + std::map rolesHeartBeatTime; // Key: role id; Value: most recent time master receives heart beat + void addref() { return ReferenceCounted::addref(); } void delref() { return ReferenceCounted::delref(); } From 93655c92e882f1a4bcc7b0c5a1831682b519cd6e Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Wed, 26 Feb 2020 16:36:22 -0800 Subject: [PATCH 0742/1604] Add missing semicolon --- bindings/c/fdb_c.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/c/fdb_c.cpp b/bindings/c/fdb_c.cpp index e0aacf01d3..356c3225d5 100644 --- a/bindings/c/fdb_c.cpp +++ b/bindings/c/fdb_c.cpp @@ -108,7 +108,7 @@ fdb_error_t fdb_network_set_option( FDBNetworkOption option, } fdb_error_t fdb_setup_network_impl() { - CATCH_AND_RETURN( API->setupNetwork() ); + CATCH_AND_RETURN( API->setupNetwork(); ); } fdb_error_t fdb_setup_network_v13( const char* localAddress ) { From 4bbac9d996b0ef80604e6bfce3f94b55ca91301d Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Wed, 26 Feb 2020 16:39:13 -0800 Subject: [PATCH 0743/1604] Change a special case return to -1. Update comments to clarify and correct some things. --- flow/Stats.actor.cpp | 4 ++-- flow/Stats.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/flow/Stats.actor.cpp b/flow/Stats.actor.cpp index 65a8df3f9d..d7e8881c23 100644 --- a/flow/Stats.actor.cpp +++ b/flow/Stats.actor.cpp @@ -47,10 +47,10 @@ double Counter::getRate() const { double Counter::getRoughness() const { double elapsed = now() - roughness_interval_start; if(elapsed == 0) { - return 0; + return -1; } - // If we have time samples t in T, and let: + // If we have time interval samples t in T, and let: // n = size(T) = interval_delta // m = mean(T) = elapsed / interval_delta // v = sum(t^2) for t in T = interval_sq_time diff --git a/flow/Stats.h b/flow/Stats.h index f09cf3eef3..0120b5c208 100644 --- a/flow/Stats.h +++ b/flow/Stats.h @@ -100,11 +100,11 @@ public: // A delta of N is treated as N distinct increments, with N-1 increments having time span 0. // Normalization is performed by dividing each time sample by the mean time before taking variance. // - // roughness = Variance(t/mean(T)) for time samples t in T + // roughness = Variance(t/mean(T)) for time interval samples t in T // // A uniformly periodic counter will have roughness of 0 // A uniformly periodic counter that increases in clumps of N will have roughness of N-1 - // A poisson distributed counter will have roughness of 1 + // A counter with exponentially distributed incrementations will have roughness of 1 double getRoughness() const; bool hasRate() const { return true; } From 87495bf1893b9f9a8e0a156a6b1f3ddc6f8098cd Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Wed, 26 Feb 2020 16:45:19 -0800 Subject: [PATCH 0744/1604] A simple Unit test, passing with bugs fixed --- fdbclient/PrivateKeySpace.actor.cpp | 95 +++++------------------------ fdbclient/PrivateKeySpace.h | 11 +++- fdbrpc/FlowTests.actor.cpp | 55 +++++++++++++++++ 3 files changed, 77 insertions(+), 84 deletions(-) diff --git a/fdbclient/PrivateKeySpace.actor.cpp b/fdbclient/PrivateKeySpace.actor.cpp index 8a1d92c784..b10691d930 100644 --- a/fdbclient/PrivateKeySpace.actor.cpp +++ b/fdbclient/PrivateKeySpace.actor.cpp @@ -30,6 +30,7 @@ ACTOR Future normalizeKeySelectorActor( KeySelector* ks ) { ASSERT(!ks->orEqual); // should be removed before calling + ASSERT(ks->offset != 1); // The function is never called when KeySelector is already normalized state KeyRangeRef range = pkrImpl->getKeyRange(); state KeyRef startKey = range.begin; @@ -50,18 +51,18 @@ ACTOR Future normalizeKeySelectorActor( // TODO : KeySelector::setKey has bytes limit according to the knob, customize it if needed if (ks->offset < 1) { if (result.size() >= 1 - ks->offset) { - ks->setKey(result[result.size()-(1-ks->offset)].key); + ks->setKey(KeyRef(ks->arena(), result[result.size()-(1-ks->offset)].key)); ks->offset = 1; } else { - ks->setKey(result[0].key); + ks->setKey(KeyRef(ks->arena(), result[0].key)); ks->offset += result.size(); } } else { - if (result.size() >= ks->offset - 1) { - ks->setKey(result[ks->offset - 2].key); + if (result.size() >= ks->offset) { + ks->setKey(KeyRef(ks->arena(), result[ks->offset - 1].key)); ks->offset = 1; } else { - ks->setKey(result[result.size()-1].key); + ks->setKey(KeyRef(ks->arena(), result[result.size()-1].key)); ks->offset -= result.size(); } } @@ -89,18 +90,21 @@ ACTOR Future> getRangeAggregationActor( state RangeMap::Iterator iter = pks->getKeyRangeMap()->rangeContaining(begin.getKey()); while (begin.offset != 1 && iter != pks->getKeyRangeMap()->ranges().begin()) { - wait(normalizeKeySelectorActor(iter->value(), ryw, &begin)); - --iter; + if (iter->value() != NULL) + wait(normalizeKeySelectorActor(iter->value(), ryw, &begin)); + begin.offset < 1 ? --iter : ++iter; } if (begin.offset != 1) { // The Key Selector points to key outside the whole private key space // TODO : Throw error here to indicate the case TEST(true); } + // state Key keyStartCopy(begin.getKey()); iter = pks->getKeyRangeMap()->rangeContaining(end.getKey()); while (end.offset != 1 && iter != pks->getKeyRangeMap()->ranges().end()) { - wait(normalizeKeySelectorActor(iter->value(), ryw, &end)); - ++iter; + if (iter->value() != NULL) + wait(normalizeKeySelectorActor(iter->value(), ryw, &end)); + end.offset < 1 ? --iter : ++iter; } if (end.offset != 1) { // The Key Selector points to key outside the whole private key space @@ -112,7 +116,7 @@ ACTOR Future> getRangeAggregationActor( TEST(true); return Standalone(); } - + // state Key keyEndCopy(end.getKey()); state Standalone result; state RangeMap::Ranges ranges = pks->getKeyRangeMap()->intersectingRanges(KeyRangeRef(begin.getKey(), end.getKey())); @@ -156,77 +160,6 @@ ACTOR Future> getRangeAggregationActor( return result; } -// ACTOR Future moveKeySelectorActor( -// const PrivateKeyRangeBaseImpl* pkrImpl, -// ReadYourWritesTransaction* ryw, -// KeySelector* begin, -// KeySelector* end, -// std::deque& result ) -// { -// ASSERT(!begin->orEqual && !end->orEqual); -// // If the start key or end key lies outside this keyrange, it cannot handle the case. -// // Thus, it is forced for the quired range lies the this keyrange -// // It assumes the begin of the range is never used as a key -// state KeyRangeRef range = pkrImpl->getKeyRange(); -// state KeyRef startKey = range.begin; -// state KeyRef endKey = range.end; -// state int choice; -// if (begin->offset != 1) { -// if (begin->offset < 1) { -// choice = 0; -// if (range.contains(begin->getKey())) -// endKey = keyAfter(begin->getKey()); -// else { -// choice = 1; -// if (range.contains(begin->getKey())) -// startKey = begin->getKey(); -// } -// } else { -// if (end->offset <= 1) { -// choice = 2; -// if (range.contains(begin->getKey())) -// startKey = begin->getKey(); -// if (range.contains(end->getKey())) -// endKey = end->getKey(); -// } else { -// choice = 3; -// if (range.contains(end->getKey())) -// startKey = end->getKey(); -// } -// } - -// state Standalone temp = wait(pkrImpl->getRange(ryw, KeyRangeRef(startKey, endKey))); - -// if (choice == 0) { -// for (int i = temp.size() - 1; i >= 0; --i) { -// result.push_front(temp[i]); -// ++begin->offset; -// if (begin->offset == 1) return false; // TODO : add getLimits check here -// } -// return true; -// } else if (choice == 1) { -// if (begin->offset > 1) { -// if (begin->offset - 1 <= temp.size()) { -// temp.pop_front(begin->offset - 1); -// begin->offset = 1; -// } else { -// begin->offset -= temp.size(); -// return true; -// } -// } -// for (const KeyValueRef & kv : temp) -// result.push_back(kv); - -// if () -// } else { -// for (const KeyValueRef & kv : temp) { -// result.push_back(kv); -// --end->offset; -// if (end->offset == 1) return false; -// } -// return true; -// } -// } } // namespace end Future> PrivateKeySpace::getRange( ReadYourWritesTransaction* ryw, diff --git a/fdbclient/PrivateKeySpace.h b/fdbclient/PrivateKeySpace.h index 6f839413fe..79d9701016 100644 --- a/fdbclient/PrivateKeySpace.h +++ b/fdbclient/PrivateKeySpace.h @@ -3,6 +3,7 @@ #pragma once #include "flow/flow.h" +#include "flow/Arena.h" #include "fdbclient/FDBTypes.h" #include "fdbclient/KeyRangeMap.h" @@ -21,10 +22,10 @@ public: range = KeyRangeRef(start, end); } KeyRangeRef getKeyRange() const { - return range; + return KeyRangeRef(range.begin, range.end); } protected: - KeyRangeRef range; // underlying key range for this function + KeyRange range; // underlying key range for this function }; @@ -40,7 +41,11 @@ public: Future> getRange(ReadYourWritesTransaction* ryw, KeySelector begin, KeySelector end, GetRangeLimits limits, bool snapshot = false, bool reverse = false); - void registerKeyRange(const KeyRange& kr, PrivateKeyRangeBaseImpl* impl) { + PrivateKeySpace() { + impls = KeyRangeMap(NULL, LiteralStringRef("\xff\xff\xff")); + } + void registerKeyRange(const KeyRangeRef& kr, PrivateKeyRangeBaseImpl* impl) { + // TODO : range checker impls.insert(kr, impl); } diff --git a/fdbrpc/FlowTests.actor.cpp b/fdbrpc/FlowTests.actor.cpp index 0cdbf23ea8..d1ab479458 100644 --- a/fdbrpc/FlowTests.actor.cpp +++ b/fdbrpc/FlowTests.actor.cpp @@ -25,6 +25,7 @@ #include "flow/IThreadPool.h" #include "fdbrpc/fdbrpc.h" #include "fdbrpc/IAsyncFile.h" +#include "fdbclient/PrivateKeySpace.h" #include "flow/actorcompiler.h" // This must be the last #include. void forceLinkFlowTests() {} @@ -1271,3 +1272,57 @@ TEST_CASE("/flow/DeterministicRandom/SignedOverflow") { std::numeric_limits::max() - 1); return Void(); } + +class PrivateKeyRangeTestImpl : public PrivateKeyRangeBaseImpl { +public: + explicit PrivateKeyRangeTestImpl(KeyRef start, KeyRef end, KeyRef prefix, int size) : PrivateKeyRangeBaseImpl(start, end) { + this->prefix = prefix; + this->size = size; + ASSERT(size > 0); + for (int i = 0; i < size; ++i) { + kvs.push_back_deep(kvs.arena(), KeyValueRef(getKeyForIndex(i), Value(std::to_string(i)))); + } + } + + Key getKeyForIndex( int idx ) { + return Key( format( "%010d", idx ) ).withPrefix(prefix).withPrefix(range.begin); + } + + virtual Future> getRange(ReadYourWritesTransaction* ryw, KeyRangeRef kr) const override { + int startIndex=0, endIndex= size; + while (startIndex < size && kvs[startIndex].key < kr.begin) + ++startIndex; + while (endIndex > startIndex && kvs[endIndex-1].key >= kr.end) + --endIndex; + if (startIndex == endIndex) + return Standalone(); + else { + Standalone result; + for (int i = startIndex; i < endIndex; ++i) + result.push_back_deep(result.arena(), kvs[i]); + return result; + } + } +private: + Standalone> kvs; + Key prefix; + int size; +}; + +TEST_CASE("/fdbclient/PrivateKeySpace/Aggregation") { + PrivateKeySpace pks; + PrivateKeyRangeTestImpl pkr1(LiteralStringRef("\xff\xff/cat/"), LiteralStringRef("\xff\xff/cat/\xff"), LiteralStringRef("small"), 10); + PrivateKeyRangeTestImpl pkr2(LiteralStringRef("\xff\xff/dog/"), LiteralStringRef("\xff\xff/dog/\xff"), LiteralStringRef("medium"), 100); + PrivateKeyRangeTestImpl pkr3(LiteralStringRef("\xff\xff/pig/"), LiteralStringRef("\xff\xff/pig/\xff"), LiteralStringRef("large"), 1000); + pks.registerKeyRange(pkr1.getKeyRange(), &pkr1); + pks.registerKeyRange(pkr2.getKeyRange(), &pkr2); + pks.registerKeyRange(pkr3.getKeyRange(), &pkr3); + KeySelector start = KeySelectorRef(LiteralStringRef("\xff\xff/elepant"), false, -10); + KeySelector end = KeySelectorRef(LiteralStringRef("\xff\xff/frog"), false, +10); + auto resultFuture = pks.getRange(NULL, start, end, GetRangeLimits()); + ASSERT(resultFuture.isReady()); + auto result = resultFuture.getValue(); + ASSERT(result.size() == 20); + ASSERT(result[0].key.endsWith(Key( format( "%010d", 89) ).withPrefix(LiteralStringRef("medium")))); + return Void(); +} \ No newline at end of file From f035bed870783d6ac870f1273424cbbcdf6ad0bc Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 26 Feb 2020 17:50:07 -0800 Subject: [PATCH 0745/1604] defer initializing TLS to avoid throwing errors from a constructor and so that errors can be logged to the trace file --- flow/Net2.actor.cpp | 84 ++++++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 36 deletions(-) diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index 7e2c214ea0..4c0790e2a2 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -111,7 +111,8 @@ thread_local INetwork* thread_network = 0; class Net2 sealed : public INetwork, public INetworkConnections { public: - Net2(bool useThreadPool, bool useMetrics, Reference policy, TLSParams tlsParams); + Net2(bool useThreadPool, bool useMetrics, Reference tlsPolicy, const TLSParams& tlsParams); + void initTLS(); void run(); void initMetrics(); @@ -158,10 +159,12 @@ public: #ifndef TLS_DISABLED boost::asio::ssl::context sslContext; #endif - std::string tlsPassword; + Reference tlsPolicy; + TLSParams tlsParams; + bool tlsInitialized; std::string get_password() const { - return tlsPassword; + return tlsParams.tlsPassword; } INetworkConnections *network; // initially this, but can be changed @@ -844,7 +847,7 @@ bool insecurely_always_accept(bool _1, boost::asio::ssl::verify_context& _2) { } #endif -Net2::Net2(bool useThreadPool, bool useMetrics, Reference policy, TLSParams tlsParams) +Net2::Net2(bool useThreadPool, bool useMetrics, Reference tlsPolicy, const TLSParams& tlsParams) : useThreadPool(useThreadPool), network(this), reactor(this), @@ -854,7 +857,9 @@ Net2::Net2(bool useThreadPool, bool useMetrics, Reference policy, TLS tsc_begin(0), tsc_end(0), taskBegin(0), currentTaskID(TaskPriority::DefaultYield), lastMinTaskID(TaskPriority::Zero), numYields(0), - tlsPassword(tlsParams.tlsPassword) + tlsInitialized(false), + tlsPolicy(tlsPolicy), + tlsParams(tlsParams) #ifndef TLS_DISABLED ,sslContext(boost::asio::ssl::context(boost::asio::ssl::context::tlsv12)) #endif @@ -862,30 +867,55 @@ Net2::Net2(bool useThreadPool, bool useMetrics, Reference policy, TLS { TraceEvent("Net2Starting"); + // Set the global members + if(useMetrics) { + setGlobal(INetwork::enTDMetrics, (flowGlobalType) &tdmetrics); + } + setGlobal(INetwork::enNetworkConnections, (flowGlobalType) network); + setGlobal(INetwork::enASIOService, (flowGlobalType) &reactor.ios); + setGlobal(INetwork::enBlobCredentialFiles, &blobCredentialFiles); + +#ifdef __linux__ + setGlobal(INetwork::enEventFD, (flowGlobalType) N2::ASIOReactor::newEventFD(reactor)); +#endif + + + int priBins[] = { 1, 2050, 3050, 4050, 4950, 5050, 7050, 8050, 10050 }; + static_assert( sizeof(priBins) == sizeof(int)*NetworkMetrics::PRIORITY_BINS, "Fix priority bins"); + for(int i=0; i(priBins[i]); + updateNow(); + +} + +void Net2::initTLS() { + if(tlsInitialized) { + return; + } #ifndef TLS_DISABLED const char *defaultCertFileName = "fdb.pem"; - if( policy && !policy->rules.size() ) { + if( tlsPolicy && !tlsPolicy->rules.size() ) { std::string verify_peers; if (platform::getEnvironmentVar("FDB_TLS_VERIFY_PEERS", verify_peers)) { - policy->set_verify_peers({ verify_peers }); + tlsPolicy->set_verify_peers({ verify_peers }); } else { - policy->set_verify_peers({ std::string("Check.Valid=1")}); + tlsPolicy->set_verify_peers({ std::string("Check.Valid=1")}); } } sslContext.set_options(boost::asio::ssl::context::default_workarounds); sslContext.set_verify_mode(boost::asio::ssl::context::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert); - if (policy) { - sslContext.set_verify_callback([policy](bool preverified, boost::asio::ssl::verify_context& ctx) { - return policy->verify_peer(preverified, ctx.native_handle()); + if (tlsPolicy) { + sslContext.set_verify_callback([this](bool preverified, boost::asio::ssl::verify_context& ctx) { + return tlsPolicy->verify_peer(preverified, ctx.native_handle()); }); } else { sslContext.set_verify_callback(boost::bind(&insecurely_always_accept, _1, _2)); } - if ( !tlsPassword.size() ) { - platform::getEnvironmentVar( "FDB_TLS_PASSWORD", tlsPassword ); + if ( !tlsParams.tlsPassword.size() ) { + platform::getEnvironmentVar( "FDB_TLS_PASSWORD", tlsParams.tlsPassword ); } sslContext.set_password_callback(std::bind(&Net2::get_password, this)); @@ -896,7 +926,7 @@ Net2::Net2(bool useThreadPool, bool useMetrics, Reference policy, TLS if ( !tlsParams.tlsCertPath.size() ) { if ( !platform::getEnvironmentVar( "FDB_TLS_CERTIFICATE_FILE", tlsParams.tlsCertPath ) ) { if( fileExists(defaultCertFileName) ) { - tlsParams.tlsCertPath = fileExists(defaultCertFileName); + tlsParams.tlsCertPath = defaultCertFileName; } else if( fileExists( joinPath(platform::getDefaultConfigPath(), defaultCertFileName) ) ) { tlsParams.tlsCertPath = joinPath(platform::getDefaultConfigPath(), defaultCertFileName); } @@ -932,7 +962,7 @@ Net2::Net2(bool useThreadPool, bool useMetrics, Reference policy, TLS if (!tlsParams.tlsKeyPath.size()) { if(!platform::getEnvironmentVar( "FDB_TLS_KEY_FILE", tlsParams.tlsKeyPath)) { if( fileExists(defaultCertFileName) ) { - tlsParams.tlsKeyPath = fileExists(defaultCertFileName); + tlsParams.tlsKeyPath = defaultCertFileName; } else if( fileExists( joinPath(platform::getDefaultConfigPath(), defaultCertFileName) ) ) { tlsParams.tlsKeyPath = joinPath(platform::getDefaultConfigPath(), defaultCertFileName); } @@ -942,28 +972,8 @@ Net2::Net2(bool useThreadPool, bool useMetrics, Reference policy, TLS sslContext.use_private_key_file(tlsParams.tlsKeyPath, boost::asio::ssl::context::pem); } } - #endif - - // Set the global members - if(useMetrics) { - setGlobal(INetwork::enTDMetrics, (flowGlobalType) &tdmetrics); - } - setGlobal(INetwork::enNetworkConnections, (flowGlobalType) network); - setGlobal(INetwork::enASIOService, (flowGlobalType) &reactor.ios); - setGlobal(INetwork::enBlobCredentialFiles, &blobCredentialFiles); - -#ifdef __linux__ - setGlobal(INetwork::enEventFD, (flowGlobalType) N2::ASIOReactor::newEventFD(reactor)); -#endif - - - int priBins[] = { 1, 2050, 3050, 4050, 4950, 5050, 7050, 8050, 10050 }; - static_assert( sizeof(priBins) == sizeof(int)*NetworkMetrics::PRIORITY_BINS, "Fix priority bins"); - for(int i=0; i(priBins[i]); - updateNow(); - + tlsInitialized = true; } ACTOR Future Net2::logTimeOffset() { @@ -1322,6 +1332,7 @@ THREAD_HANDLE Net2::startThread( THREAD_FUNC_RETURN (*func) (void*), void *arg ) Future< Reference > Net2::connect( NetworkAddress toAddr, std::string host ) { #ifndef TLS_DISABLED + initTLS(); if ( toAddr.isTLS() ) { return SSLConnection::connect(&this->reactor.ios, &this->sslContext, toAddr); } @@ -1401,6 +1412,7 @@ bool Net2::isAddressOnThisHost( NetworkAddress const& addr ) { Reference Net2::listen( NetworkAddress localAddr ) { try { #ifndef TLS_DISABLED + initTLS(); if ( localAddr.isTLS() ) { return Reference(new SSLListener( reactor.ios, &this->sslContext, localAddr )); } From c3299b8ebed8ddf23bea80e21c05ffd2dc2e8fd1 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 26 Feb 2020 18:53:06 -0800 Subject: [PATCH 0746/1604] if tls cannot be initialized, throw an error from createDatabase --- fdbclient/NativeAPI.actor.cpp | 2 ++ fdbserver/fdbserver.actor.cpp | 2 +- flow/Net2.actor.cpp | 2 +- flow/network.h | 3 +++ 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index 5f9d9878e2..e30390cf22 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -811,6 +811,8 @@ Database Database::createDatabase( Reference connFile, in } } + g_network->initTLS(); + Reference> clientInfo(new AsyncVar()); Reference>> connectionFile(new AsyncVar>()); connectionFile->set(connFile); diff --git a/fdbserver/fdbserver.actor.cpp b/fdbserver/fdbserver.actor.cpp index 85cd709a3b..9f43cd2bde 100644 --- a/fdbserver/fdbserver.actor.cpp +++ b/fdbserver/fdbserver.actor.cpp @@ -1575,7 +1575,7 @@ int main(int argc, char* argv[]) { } openTraceFile(publicAddresses.address, rollsize, maxLogsSize, logFolder, "trace", logGroup); - + g_network->initTLS(); if (expectsPublicAddress) { for (int ii = 0; ii < (publicAddresses.secondaryAddress.present() ? 2 : 1); ++ii) { diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index e0dcdd6d13..695c446b3f 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -953,7 +953,7 @@ void Net2::initTLS() { catch (Error& e) { fprintf(stderr, "Error reading CA file %s: %s\n", tlsParams.tlsCAPath.c_str(), e.what()); TraceEvent("Net2TLSReadCAError").error(e); - throw; + throw tls_error(); } } } diff --git a/flow/network.h b/flow/network.h index 02898797dd..127d765bba 100644 --- a/flow/network.h +++ b/flow/network.h @@ -481,6 +481,9 @@ public: virtual void initMetrics() {} // Metrics must be initialized after FlowTransport::createInstance has been called + virtual void initTLS() {} + // TLS must be initialized before using the network + virtual void getDiskBytes( std::string const& directory, int64_t& free, int64_t& total) = 0; //Gets the number of free and total bytes available on the disk which contains directory From b96e9c939c191e44497ab9e2fda785f8b5b09fc9 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 26 Feb 2020 18:53:19 -0800 Subject: [PATCH 0747/1604] updated release notes --- documentation/sphinx/source/release-notes.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/documentation/sphinx/source/release-notes.rst b/documentation/sphinx/source/release-notes.rst index 7b0eb7072a..4bb82d8dcd 100644 --- a/documentation/sphinx/source/release-notes.rst +++ b/documentation/sphinx/source/release-notes.rst @@ -2,6 +2,14 @@ Release Notes ############# +6.2.17 +====== + +Fixes +----- + +* Restored the ability to set TLS configuration using environment variables. `(PR #2755) `_. + 6.2.16 ====== From 707fc1ddea94c412415086d74021c475ea2ffffb Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 26 Feb 2020 19:04:49 -0800 Subject: [PATCH 0748/1604] only capture the policy to match prior code --- flow/Net2.actor.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index 695c446b3f..be6aecedd5 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -908,8 +908,9 @@ void Net2::initTLS() { sslContext.set_options(boost::asio::ssl::context::default_workarounds); sslContext.set_verify_mode(boost::asio::ssl::context::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert); if (tlsPolicy) { - sslContext.set_verify_callback([this](bool preverified, boost::asio::ssl::verify_context& ctx) { - return tlsPolicy->verify_peer(preverified, ctx.native_handle()); + Reference policy = tlsPolicy; + sslContext.set_verify_callback([policy](bool preverified, boost::asio::ssl::verify_context& ctx) { + return policy->verify_peer(preverified, ctx.native_handle()); }); } else { sslContext.set_verify_callback(boost::bind(&insecurely_always_accept, _1, _2)); From 1708f46f670ff2b5697f775b8074c4c091e5eb9a Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 26 Feb 2020 19:08:06 -0800 Subject: [PATCH 0749/1604] update docs for 6.2.17 --- documentation/sphinx/source/downloads.rst | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/documentation/sphinx/source/downloads.rst b/documentation/sphinx/source/downloads.rst index 8d92ea03ad..6659b6a27f 100644 --- a/documentation/sphinx/source/downloads.rst +++ b/documentation/sphinx/source/downloads.rst @@ -10,38 +10,38 @@ macOS The macOS installation package is supported on macOS 10.7+. It includes the client and (optionally) the server. -* `FoundationDB-6.2.16.pkg `_ +* `FoundationDB-6.2.17.pkg `_ Ubuntu ------ The Ubuntu packages are supported on 64-bit Ubuntu 12.04+, but beware of the Linux kernel bug in Ubuntu 12.x. -* `foundationdb-clients-6.2.16-1_amd64.deb `_ -* `foundationdb-server-6.2.16-1_amd64.deb `_ (depends on the clients package) +* `foundationdb-clients-6.2.17-1_amd64.deb `_ +* `foundationdb-server-6.2.17-1_amd64.deb `_ (depends on the clients package) RHEL/CentOS EL6 --------------- The RHEL/CentOS EL6 packages are supported on 64-bit RHEL/CentOS 6.x. -* `foundationdb-clients-6.2.16-1.el6.x86_64.rpm `_ -* `foundationdb-server-6.2.16-1.el6.x86_64.rpm `_ (depends on the clients package) +* `foundationdb-clients-6.2.17-1.el6.x86_64.rpm `_ +* `foundationdb-server-6.2.17-1.el6.x86_64.rpm `_ (depends on the clients package) RHEL/CentOS EL7 --------------- The RHEL/CentOS EL7 packages are supported on 64-bit RHEL/CentOS 7.x. -* `foundationdb-clients-6.2.16-1.el7.x86_64.rpm `_ -* `foundationdb-server-6.2.16-1.el7.x86_64.rpm `_ (depends on the clients package) +* `foundationdb-clients-6.2.17-1.el7.x86_64.rpm `_ +* `foundationdb-server-6.2.17-1.el7.x86_64.rpm `_ (depends on the clients package) Windows ------- The Windows installer is supported on 64-bit Windows XP and later. It includes the client and (optionally) the server. -* `foundationdb-6.2.16-x64.msi `_ +* `foundationdb-6.2.17-x64.msi `_ API Language Bindings ===================== @@ -58,18 +58,18 @@ On macOS and Windows, the FoundationDB Python API bindings are installed as part If you need to use the FoundationDB Python API from other Python installations or paths, download the Python package: -* `foundationdb-6.2.16.tar.gz `_ +* `foundationdb-6.2.17.tar.gz `_ Ruby 1.9.3/2.0.0+ ----------------- -* `fdb-6.2.16.gem `_ +* `fdb-6.2.17.gem `_ Java 8+ ------- -* `fdb-java-6.2.16.jar `_ -* `fdb-java-6.2.16-javadoc.jar `_ +* `fdb-java-6.2.17.jar `_ +* `fdb-java-6.2.17-javadoc.jar `_ Go 1.11+ -------- From d8a21bc02f05411e081f4f308bc638f327d6dbae Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 26 Feb 2020 22:24:48 -0800 Subject: [PATCH 0750/1604] update version to 6.2.18 --- CMakeLists.txt | 2 +- versions.target | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a3ce2424e0..61a626b185 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ # limitations under the License. cmake_minimum_required(VERSION 3.12) project(foundationdb - VERSION 6.2.17 + VERSION 6.2.18 DESCRIPTION "FoundationDB is a scalable, fault-tolerant, ordered key-value store with full ACID transactions." HOMEPAGE_URL "http://www.foundationdb.org/" LANGUAGES C CXX ASM) diff --git a/versions.target b/versions.target index e04723df48..6932c61ae2 100644 --- a/versions.target +++ b/versions.target @@ -1,7 +1,7 @@ - 6.2.17 + 6.2.18 6.2 From e113b84e254caf0fa668d7ac4c12163ed80acf47 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 26 Feb 2020 22:24:48 -0800 Subject: [PATCH 0751/1604] update installer WIX GUID following release --- packaging/msi/FDBInstaller.wxs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/msi/FDBInstaller.wxs b/packaging/msi/FDBInstaller.wxs index 50cb2932ec..286dd84f19 100644 --- a/packaging/msi/FDBInstaller.wxs +++ b/packaging/msi/FDBInstaller.wxs @@ -32,7 +32,7 @@ Date: Thu, 27 Feb 2020 10:19:17 -0800 Subject: [PATCH 0752/1604] Added back the mutex holder that was removed accidentally --- flow/Trace.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/flow/Trace.cpp b/flow/Trace.cpp index 4a3cae6122..44c8c38407 100644 --- a/flow/Trace.cpp +++ b/flow/Trace.cpp @@ -223,7 +223,10 @@ public: struct IssuesList : ITraceLogIssuesReporter, ThreadSafeReferenceCounted { IssuesList(){}; - void addIssue(std::string issue) override { issues.insert(issue); } + void addIssue(std::string issue) override { + MutexHolder h(mutex); + issues.insert(issue); + } void retrieveIssues(std::set& out) override { MutexHolder h(mutex); From 16575ae94d6c8025f8f6ef61b6aaa2a29283522d Mon Sep 17 00:00:00 2001 From: Xin Dong Date: Thu, 27 Feb 2020 11:54:15 -0800 Subject: [PATCH 0753/1604] Address review comments --- flow/FileTraceLogWriter.h | 1 - flow/Knobs.cpp | 1 - flow/Knobs.h | 1 - flow/Trace.cpp | 4 +--- 4 files changed, 1 insertion(+), 6 deletions(-) diff --git a/flow/FileTraceLogWriter.h b/flow/FileTraceLogWriter.h index 7a74004087..1a7d86a840 100644 --- a/flow/FileTraceLogWriter.h +++ b/flow/FileTraceLogWriter.h @@ -26,7 +26,6 @@ #include "flow/FastRef.h" #include "flow/Trace.h" -#include #include class FileTraceLogWriter : public ITraceLogWriter, ReferenceCounted { diff --git a/flow/Knobs.cpp b/flow/Knobs.cpp index 7c540f6dff..9eaee74826 100644 --- a/flow/Knobs.cpp +++ b/flow/Knobs.cpp @@ -161,7 +161,6 @@ FlowKnobs::FlowKnobs(bool randomize, bool isSimulated) { init( TRACE_EVENT_THROTTLER_MSG_LIMIT, 20000 ); init( MAX_TRACE_FIELD_LENGTH, 495 ); // If the value of this is changed, the corresponding default in Trace.cpp should be changed as well init( MAX_TRACE_EVENT_LENGTH, 4000 ); // If the value of this is changed, the corresponding default in Trace.cpp should be changed as well - init( TRACE_LOG_ISSUE_EXPIRATION_INTERVAL, 5.0); //TDMetrics init( MAX_METRICS, 600 ); diff --git a/flow/Knobs.h b/flow/Knobs.h index cd092dfa35..5a50c47eea 100644 --- a/flow/Knobs.h +++ b/flow/Knobs.h @@ -182,7 +182,6 @@ public: int TRACE_EVENT_THROTTLER_MSG_LIMIT; int MAX_TRACE_FIELD_LENGTH; int MAX_TRACE_EVENT_LENGTH; - double TRACE_LOG_ISSUE_EXPIRATION_INTERVAL; //TDMetrics int64_t MAX_METRIC_SIZE; diff --git a/flow/Trace.cpp b/flow/Trace.cpp index 44c8c38407..9a1a525621 100644 --- a/flow/Trace.cpp +++ b/flow/Trace.cpp @@ -237,9 +237,7 @@ public: void resolveIssue(std::string issue) override { MutexHolder h(mutex); - if (issues.find(issue) != issues.end()) { - issues.erase(issue); - } + issues.erase(issue); } void addref() { ThreadSafeReferenceCounted::addref(); } From e79caf27d0c3d066eb09222cf9fd07a96485f718 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Thu, 27 Feb 2020 11:58:28 -0800 Subject: [PATCH 0754/1604] One more test, bug fixed --- fdbclient/PrivateKeySpace.actor.cpp | 5 +-- fdbclient/PrivateKeySpace.h | 4 +-- fdbrpc/FlowTests.actor.cpp | 51 ++++++++++++++++++----------- 3 files changed, 36 insertions(+), 24 deletions(-) diff --git a/fdbclient/PrivateKeySpace.actor.cpp b/fdbclient/PrivateKeySpace.actor.cpp index b10691d930..d27ed47c41 100644 --- a/fdbclient/PrivateKeySpace.actor.cpp +++ b/fdbclient/PrivateKeySpace.actor.cpp @@ -33,8 +33,8 @@ ACTOR Future normalizeKeySelectorActor( ASSERT(ks->offset != 1); // The function is never called when KeySelector is already normalized state KeyRangeRef range = pkrImpl->getKeyRange(); - state KeyRef startKey = range.begin; - state KeyRef endKey = range.end; + state Key startKey(range.begin); + state Key endKey(range.end); if (ks->offset < 1) { // less than the given key @@ -122,6 +122,7 @@ ACTOR Future> getRangeAggregationActor( pks->getKeyRangeMap()->intersectingRanges(KeyRangeRef(begin.getKey(), end.getKey())); // reverse handler // 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(); if (reverse) { while (iter != ranges.begin()) { diff --git a/fdbclient/PrivateKeySpace.h b/fdbclient/PrivateKeySpace.h index 79d9701016..489c0ce4c2 100644 --- a/fdbclient/PrivateKeySpace.h +++ b/fdbclient/PrivateKeySpace.h @@ -19,10 +19,10 @@ public: explicit PrivateKeyRangeBaseImpl(KeyRef start, KeyRef end) { // TODO : checker: make sure it is in valid key range - range = KeyRangeRef(start, end); + range = KeyRangeRef(range.arena(), KeyRangeRef(start, end)); } KeyRangeRef getKeyRange() const { - return KeyRangeRef(range.begin, range.end); + return range; } protected: KeyRange range; // underlying key range for this function diff --git a/fdbrpc/FlowTests.actor.cpp b/fdbrpc/FlowTests.actor.cpp index d1ab479458..3b83ab062c 100644 --- a/fdbrpc/FlowTests.actor.cpp +++ b/fdbrpc/FlowTests.actor.cpp @@ -1275,17 +1275,17 @@ TEST_CASE("/flow/DeterministicRandom/SignedOverflow") { class PrivateKeyRangeTestImpl : public PrivateKeyRangeBaseImpl { public: - explicit PrivateKeyRangeTestImpl(KeyRef start, KeyRef end, KeyRef prefix, int size) : PrivateKeyRangeBaseImpl(start, end) { + explicit PrivateKeyRangeTestImpl(KeyRef start, KeyRef end, std::string prefix, int size) : PrivateKeyRangeBaseImpl(start, end) { this->prefix = prefix; this->size = size; ASSERT(size > 0); for (int i = 0; i < size; ++i) { - kvs.push_back_deep(kvs.arena(), KeyValueRef(getKeyForIndex(i), Value(std::to_string(i)))); + kvs.push_back_deep(kvs.arena(), KeyValueRef(getKeyForIndex(i), deterministicRandom()->randomAlphaNumeric(16))); } } Key getKeyForIndex( int idx ) { - return Key( format( "%010d", idx ) ).withPrefix(prefix).withPrefix(range.begin); + return Key( prefix + format("%010d", idx)).withPrefix(range.begin); } virtual Future> getRange(ReadYourWritesTransaction* ryw, KeyRangeRef kr) const override { @@ -1296,33 +1296,44 @@ public: --endIndex; if (startIndex == endIndex) return Standalone(); - else { - Standalone result; - for (int i = startIndex; i < endIndex; ++i) - result.push_back_deep(result.arena(), kvs[i]); - return result; - } + else + return Standalone(RangeResultRef(kvs.slice(startIndex, endIndex), false)); } private: Standalone> kvs; - Key prefix; + std::string prefix; int size; }; TEST_CASE("/fdbclient/PrivateKeySpace/Aggregation") { PrivateKeySpace pks; - PrivateKeyRangeTestImpl pkr1(LiteralStringRef("\xff\xff/cat/"), LiteralStringRef("\xff\xff/cat/\xff"), LiteralStringRef("small"), 10); - PrivateKeyRangeTestImpl pkr2(LiteralStringRef("\xff\xff/dog/"), LiteralStringRef("\xff\xff/dog/\xff"), LiteralStringRef("medium"), 100); - PrivateKeyRangeTestImpl pkr3(LiteralStringRef("\xff\xff/pig/"), LiteralStringRef("\xff\xff/pig/\xff"), LiteralStringRef("large"), 1000); + PrivateKeyRangeTestImpl pkr1(LiteralStringRef("\xff\xff/cat/"), LiteralStringRef("\xff\xff/cat/\xff"), "small", 10); + PrivateKeyRangeTestImpl pkr2(LiteralStringRef("\xff\xff/dog/"), LiteralStringRef("\xff\xff/dog/\xff"), "medium", 100); + PrivateKeyRangeTestImpl pkr3(LiteralStringRef("\xff\xff/pig/"), LiteralStringRef("\xff\xff/pig/\xff"), "large", 1000); pks.registerKeyRange(pkr1.getKeyRange(), &pkr1); pks.registerKeyRange(pkr2.getKeyRange(), &pkr2); pks.registerKeyRange(pkr3.getKeyRange(), &pkr3); - KeySelector start = KeySelectorRef(LiteralStringRef("\xff\xff/elepant"), false, -10); - KeySelector end = KeySelectorRef(LiteralStringRef("\xff\xff/frog"), false, +10); - auto resultFuture = pks.getRange(NULL, start, end, GetRangeLimits()); - ASSERT(resultFuture.isReady()); - auto result = resultFuture.getValue(); - ASSERT(result.size() == 20); - ASSERT(result[0].key.endsWith(Key( format( "%010d", 89) ).withPrefix(LiteralStringRef("medium")))); + // test case 1 + { + KeySelector start = KeySelectorRef(LiteralStringRef("\xff\xff/elepant"), false, -10); + KeySelector end = KeySelectorRef(LiteralStringRef("\xff\xff/frog"), false, +10); + auto resultFuture = pks.getRange(NULL, start, end, GetRangeLimits()); + ASSERT(resultFuture.isReady()); + auto result = resultFuture.getValue(); + ASSERT(result.size() == 20); + ASSERT(result[0].key == pkr2.getKeyForIndex(89)) ; + ASSERT(result[result.size()-1].key == pkr3.getKeyForIndex(8)); + } + // test case 2 + { + KeySelector start = KeySelectorRef(pkr3.getKeyForIndex(999), true, -1109); + KeySelector end = KeySelectorRef(pkr1.getKeyForIndex(0), false, +1110); + auto resultFuture = pks.getRange(NULL, start, end, GetRangeLimits()); + ASSERT(resultFuture.isReady()); + auto result = resultFuture.getValue(); + ASSERT(result.size() == 1109); + ASSERT(result[0].key == pkr1.getKeyForIndex(0)) ; + ASSERT(result[result.size()-1].key == pkr3.getKeyForIndex(998)); + } return Void(); } \ No newline at end of file From 97ed846618153279edf8f8454461290a700c90ef Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Thu, 27 Feb 2020 15:21:05 -0800 Subject: [PATCH 0755/1604] More tests, bugs fixed --- fdbclient/PrivateKeySpace.actor.cpp | 40 +++++++++++------ fdbclient/PrivateKeySpace.h | 8 ++-- fdbrpc/FlowTests.actor.cpp | 69 ++++++++++++++++++++++++----- 3 files changed, 89 insertions(+), 28 deletions(-) diff --git a/fdbclient/PrivateKeySpace.actor.cpp b/fdbclient/PrivateKeySpace.actor.cpp index d27ed47c41..abc2c261a1 100644 --- a/fdbclient/PrivateKeySpace.actor.cpp +++ b/fdbclient/PrivateKeySpace.actor.cpp @@ -18,10 +18,10 @@ ACTOR Future> getActor( } } -// This function will move the given KeySelector to toward a standard KeySelector: -// orEqual == false && offset == 1 +// This function will normalize the given KeySelector to a standard KeySelector: +// orEqual == false && offset == 1 (Standard form) // If the corresponding key is not in this private key range, it will move as far as possible to adjust the offset to 1 -// It looks like taking more time here since we query all keys twice in the worst case. +// It does have overhead here since we query all keys twice in the worst case. // However, moving the KeySelector while handling other parameters like limits makes the code much more complex and hard to maintain // Seperate each part to make the code easy to understand and more compact ACTOR Future normalizeKeySelectorActor( @@ -47,8 +47,14 @@ ACTOR Future normalizeKeySelectorActor( startKey = ks->getKey(); } + TraceEvent("NormalizeKeySelector"). + detail("OriginalKey", ks->getKey()). + detail("OriginalOffset", ks->offset). + detail("PrivateKeyRangeStart", range.begin). + detail("PrivateKeyRangeEnd", range.end); + Standalone result = wait(pkrImpl->getRange(ryw, KeyRangeRef(startKey, endKey))); - // TODO : KeySelector::setKey has bytes limit according to the knob, customize it if needed + // TODO : KeySelector::setKey has byte limit according to the knobs, customize it if needed if (ks->offset < 1) { if (result.size() >= 1 - ks->offset) { ks->setKey(KeyRef(ks->arena(), result[result.size()-(1-ks->offset)].key)); @@ -62,10 +68,15 @@ ACTOR Future normalizeKeySelectorActor( ks->setKey(KeyRef(ks->arena(), result[ks->offset - 1].key)); ks->offset = 1; } else { - ks->setKey(KeyRef(ks->arena(), result[result.size()-1].key)); + ks->setKey(KeyRef(ks->arena(), keyAfter(result[result.size()-1].key))); ks->offset -= result.size(); } } + TraceEvent("NormalizeKeySelector"). + detail("NormalizedKey", ks->getKey()). + detail("NormalizedOffset", ks->offset). + detail("PrivateKeyRangeStart", range.begin). + detail("PrivateKeyRangeEnd", range.end); return Void(); } @@ -96,10 +107,10 @@ ACTOR Future> getRangeAggregationActor( } if (begin.offset != 1) { // The Key Selector points to key outside the whole private key space - // TODO : Throw error here to indicate the case - TEST(true); + TraceEvent(SevError, "IllegalBeginKeySelector"). + detail("TerminateKey", begin.getKey()). + detail("TerminateOffset", begin.offset); } - // state Key keyStartCopy(begin.getKey()); iter = pks->getKeyRangeMap()->rangeContaining(end.getKey()); while (end.offset != 1 && iter != pks->getKeyRangeMap()->ranges().end()) { if (iter->value() != NULL) @@ -108,21 +119,20 @@ ACTOR Future> getRangeAggregationActor( } if (end.offset != 1) { // The Key Selector points to key outside the whole private key space - // TODO : Throw error here to indicate the case - TEST(true); + TraceEvent(SevError, "IllegalEndKeySelector"). + detail("TerminateKey", end.getKey()). + detail("TerminateOffset", end.offset); } // return if range inverted if( begin.offset >= end.offset && begin.getKey() >= end.getKey() ) { TEST(true); return Standalone(); } - // state Key keyEndCopy(end.getKey()); state Standalone result; state RangeMap::Ranges ranges = pks->getKeyRangeMap()->intersectingRanges(KeyRangeRef(begin.getKey(), end.getKey())); - // reverse handler // TODO : workaround to write this two together to make the code compact - // The issue here is boost::iterator_range<> doest not provide rbegin, rend + // The issue here is boost::iterator_range<> doest not provide rbegin(), rend() iter = reverse ? ranges.end() : ranges.begin(); if (reverse) { while (iter != ranges.begin()) { @@ -136,6 +146,8 @@ ACTOR Future> getRangeAggregationActor( // limits handler for (int i = pairs.size() - 1; i >= 0; --i) { result.push_back_deep(result.arena(), pairs[i]); + // TODO : the behavior here is even the last kv makes bytes larger than specified, + // it is still returned and set limits.bytes to zero limits.decrement(pairs[i]); if (limits.isReached()) return result; @@ -152,6 +164,8 @@ ACTOR Future> getRangeAggregationActor( // limits handler for (const KeyValueRef & kv : pairs) { result.push_back_deep(result.arena(), kv); + // TODO : behavior here is even the last kv makes bytes larger than specified, + // it is still returned and set limits.bytes to zero limits.decrement(kv); if (limits.isReached()) return result; diff --git a/fdbclient/PrivateKeySpace.h b/fdbclient/PrivateKeySpace.h index 489c0ce4c2..3b5dbfa108 100644 --- a/fdbclient/PrivateKeySpace.h +++ b/fdbclient/PrivateKeySpace.h @@ -18,7 +18,6 @@ public: virtual Future> getRange(ReadYourWritesTransaction* ryw, KeyRangeRef kr) const = 0; explicit PrivateKeyRangeBaseImpl(KeyRef start, KeyRef end) { - // TODO : checker: make sure it is in valid key range range = KeyRangeRef(range.arena(), KeyRangeRef(start, end)); } KeyRangeRef getKeyRange() const { @@ -41,11 +40,12 @@ public: Future> getRange(ReadYourWritesTransaction* ryw, KeySelector begin, KeySelector end, GetRangeLimits limits, bool snapshot = false, bool reverse = false); - PrivateKeySpace() { - impls = KeyRangeMap(NULL, LiteralStringRef("\xff\xff\xff")); + PrivateKeySpace(KeyRef rangeEndKey = allKeys.end) { + // Default value is NULL + impls = KeyRangeMap(NULL, rangeEndKey); } void registerKeyRange(const KeyRangeRef& kr, PrivateKeyRangeBaseImpl* impl) { - // TODO : range checker + // TODO : range check impls.insert(kr, impl); } diff --git a/fdbrpc/FlowTests.actor.cpp b/fdbrpc/FlowTests.actor.cpp index 3b83ab062c..725c8e771e 100644 --- a/fdbrpc/FlowTests.actor.cpp +++ b/fdbrpc/FlowTests.actor.cpp @@ -1284,6 +1284,8 @@ public: } } + KeyValueRef getKeyValueForIndex(int idx) {return kvs[idx];} + Key getKeyForIndex( int idx ) { return Key( prefix + format("%010d", idx)).withPrefix(range.begin); } @@ -1306,34 +1308,79 @@ private: }; TEST_CASE("/fdbclient/PrivateKeySpace/Aggregation") { - PrivateKeySpace pks; + PrivateKeySpace pks(LiteralStringRef("\xff\xff\xff")); PrivateKeyRangeTestImpl pkr1(LiteralStringRef("\xff\xff/cat/"), LiteralStringRef("\xff\xff/cat/\xff"), "small", 10); PrivateKeyRangeTestImpl pkr2(LiteralStringRef("\xff\xff/dog/"), LiteralStringRef("\xff\xff/dog/\xff"), "medium", 100); PrivateKeyRangeTestImpl pkr3(LiteralStringRef("\xff\xff/pig/"), LiteralStringRef("\xff\xff/pig/\xff"), "large", 1000); pks.registerKeyRange(pkr1.getKeyRange(), &pkr1); pks.registerKeyRange(pkr2.getKeyRange(), &pkr2); pks.registerKeyRange(pkr3.getKeyRange(), &pkr3); - // test case 1 + // get { - KeySelector start = KeySelectorRef(LiteralStringRef("\xff\xff/elepant"), false, -10); - KeySelector end = KeySelectorRef(LiteralStringRef("\xff\xff/frog"), false, +10); + auto resultFuture = pks.get(NULL, LiteralStringRef("\xff\xff/cat/small0000000009")); + ASSERT(resultFuture.isReady()); + auto result = resultFuture.getValue().get(); + ASSERT(result == pkr1.getKeyValueForIndex(9).value); + auto emptyFuture = pks.get(NULL, LiteralStringRef("\xff\xff/cat/small0000000010")); + ASSERT(emptyFuture.isReady()); + auto emptyResult = emptyFuture.getValue(); + ASSERT(!emptyResult.present()); + } + // general getRange + { + KeySelector start = KeySelectorRef(LiteralStringRef("\xff\xff/elepant"), false, -9); + KeySelector end = KeySelectorRef(LiteralStringRef("\xff\xff/frog"), false, +11); auto resultFuture = pks.getRange(NULL, start, end, GetRangeLimits()); ASSERT(resultFuture.isReady()); auto result = resultFuture.getValue(); ASSERT(result.size() == 20); - ASSERT(result[0].key == pkr2.getKeyForIndex(89)) ; - ASSERT(result[result.size()-1].key == pkr3.getKeyForIndex(8)); + ASSERT(result[0].key == pkr2.getKeyForIndex(90)) ; + ASSERT(result[result.size()-1].key == pkr3.getKeyForIndex(9)); } - // test case 2 + // KeySelector points outside { - KeySelector start = KeySelectorRef(pkr3.getKeyForIndex(999), true, -1109); - KeySelector end = KeySelectorRef(pkr1.getKeyForIndex(0), false, +1110); + KeySelector start = KeySelectorRef(pkr3.getKeyForIndex(999), true, -1110); + KeySelector end = KeySelectorRef(pkr1.getKeyForIndex(0), false, +1112); auto resultFuture = pks.getRange(NULL, start, end, GetRangeLimits()); ASSERT(resultFuture.isReady()); auto result = resultFuture.getValue(); - ASSERT(result.size() == 1109); + ASSERT(result.size() == 1110); ASSERT(result[0].key == pkr1.getKeyForIndex(0)) ; - ASSERT(result[result.size()-1].key == pkr3.getKeyForIndex(998)); + ASSERT(result[result.size()-1].key == pkr3.getKeyForIndex(999)); + } + // GetRangeLimits with row limit + { + KeySelector start = KeySelectorRef(pkr2.getKeyForIndex(0), true, 0); + KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(0), false, 0); + auto resultFuture = pks.getRange(NULL, start, end, GetRangeLimits(2)); + ASSERT(resultFuture.isReady()); + auto result = resultFuture.getValue(); + ASSERT(result.size() == 2); + ASSERT(result[0].key == pkr2.getKeyForIndex(0)); + ASSERT(result[1].key == pkr2.getKeyForIndex(1)); + } + // GetRangeLimits with byte limit + { + KeySelector start = KeySelectorRef(pkr2.getKeyForIndex(0), true, 0); + KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(0), false, 0); + auto resultFuture = pks.getRange(NULL, start, end, GetRangeLimits(10, 100)); + ASSERT(resultFuture.isReady()); + auto result = resultFuture.getValue(); + int bytes = 0; + for (int i = 0; i < result.size()-1; ++i) + bytes += 8 + pkr2.getKeyValueForIndex(i).expectedSize(); + ASSERT(bytes < 100); + ASSERT(bytes + 8 + pkr2.getKeyValueForIndex(result.size()).expectedSize() >= 100); + } + // reverse test + { + KeySelector start = KeySelectorRef(pkr2.getKeyForIndex(0), true, 0); + KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(0), false, 0); + auto resultFuture = pks.getRange(NULL, start, end, GetRangeLimits(100), false, true); + ASSERT(resultFuture.isReady()); + auto result = resultFuture.getValue(); + for (int i = 0; i < result.size(); ++i) + ASSERT(result[i] == pkr2.getKeyValueForIndex(result.size() - 1 - i)); } return Void(); } \ No newline at end of file From 9f945a2a5cd8673af380f71811bef43e7d7b0835 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Thu, 27 Feb 2020 15:38:46 -0800 Subject: [PATCH 0756/1604] Update comments --- fdbclient/PrivateKeySpace.actor.cpp | 4 ++-- fdbclient/PrivateKeySpace.h | 9 ++++++--- fdbrpc/FlowTests.actor.cpp | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/fdbclient/PrivateKeySpace.actor.cpp b/fdbclient/PrivateKeySpace.actor.cpp index abc2c261a1..3e30de65de 100644 --- a/fdbclient/PrivateKeySpace.actor.cpp +++ b/fdbclient/PrivateKeySpace.actor.cpp @@ -88,7 +88,7 @@ ACTOR Future> getRangeAggregationActor( GetRangeLimits limits, bool reverse ) { - // This function handles ranges cover more than one keyrange and aggregates all results + // This function handles ranges which cover more than one keyrange and aggregates all results // KeySelector, GetRangeLimits and reverse are all handled here // make sure orEqual == false @@ -202,4 +202,4 @@ Future> PrivateKeySpace::get( { // ignore snapshot, which is not used return getActor(this, ryw, key); -} \ No newline at end of file +} diff --git a/fdbclient/PrivateKeySpace.h b/fdbclient/PrivateKeySpace.h index 3b5dbfa108..fdc60e4438 100644 --- a/fdbclient/PrivateKeySpace.h +++ b/fdbclient/PrivateKeySpace.h @@ -11,10 +11,13 @@ class ReadYourWritesTransaction; class PrivateKeyRangeBaseImpl { public: + // TO DISCUSS : do we need this general getRange interface here? + // Since a keyRange doesn't have any knowledge about other keyRanges, parameters like KeySelector, + // GetRangeLimits should be handled together in PrivateKeySpace + // Thus, having this general interface looks unnessary. // virtual Future> getRange(ReadYourWritesTransaction* ryw, KeySelector begin, KeySelector end, GetRangeLimits limits, bool snapshot = false, bool reverse = false) const = 0; - // TODO : My opinion is that having this interface is enough for underlying keyrange implemention - // A key range doesn't have any knowledge about other key range, parameters like KeySelector, GetRangeLimits should be handled in PrivateKeySpace - // Thus, it is no need to have them here + + // Each derived class only needs to implement this simple version of getRange virtual Future> getRange(ReadYourWritesTransaction* ryw, KeyRangeRef kr) const = 0; explicit PrivateKeyRangeBaseImpl(KeyRef start, KeyRef end) { diff --git a/fdbrpc/FlowTests.actor.cpp b/fdbrpc/FlowTests.actor.cpp index 725c8e771e..561e80f884 100644 --- a/fdbrpc/FlowTests.actor.cpp +++ b/fdbrpc/FlowTests.actor.cpp @@ -1383,4 +1383,4 @@ TEST_CASE("/fdbclient/PrivateKeySpace/Aggregation") { ASSERT(result[i] == pkr2.getKeyValueForIndex(result.size() - 1 - i)); } return Void(); -} \ No newline at end of file +} From 90ad0662e344e5690a1fe13fb0fd6499372c48cf Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Thu, 27 Feb 2020 15:39:51 -0800 Subject: [PATCH 0757/1604] Update comments --- fdbclient/PrivateKeySpace.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbclient/PrivateKeySpace.h b/fdbclient/PrivateKeySpace.h index fdc60e4438..9aa345ff79 100644 --- a/fdbclient/PrivateKeySpace.h +++ b/fdbclient/PrivateKeySpace.h @@ -60,4 +60,4 @@ private: KeyRangeMap impls; }; -#endif \ No newline at end of file +#endif From 047412fd6306d7b3d05deaf61cbd7d7d90349f79 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Thu, 27 Feb 2020 17:33:47 -0800 Subject: [PATCH 0758/1604] update build file --- fdbclient/fdbclient.vcxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fdbclient/fdbclient.vcxproj b/fdbclient/fdbclient.vcxproj index 555f257509..7a0551146f 100644 --- a/fdbclient/fdbclient.vcxproj +++ b/fdbclient/fdbclient.vcxproj @@ -77,6 +77,7 @@ false + @@ -123,6 +124,7 @@ + From 2657d41bb251a357b7a313be9ba0fd624249c504 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Thu, 27 Feb 2020 18:32:02 -0800 Subject: [PATCH 0759/1604] FastRestore:Add debug msg when memory is over threshold --- fdbserver/DataDistributionQueue.actor.cpp | 2 +- fdbserver/RestoreRoleCommon.actor.cpp | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/fdbserver/DataDistributionQueue.actor.cpp b/fdbserver/DataDistributionQueue.actor.cpp index 88ffd0f008..45777218a7 100644 --- a/fdbserver/DataDistributionQueue.actor.cpp +++ b/fdbserver/DataDistributionQueue.actor.cpp @@ -1035,7 +1035,7 @@ ACTOR Future dataDistributionRelocator( DDQueueData *self, RelocateData rd state Error error = success(); state Promise dataMovementComplete; - // Move keys from source to destination by chaning the serverKeyList and keyServerList system keys + // Move keys from source to destination by changing the serverKeyList and keyServerList system keys state Future doMoveKeys = moveKeys(self->cx, rd.keys, destIds, healthyIds, self->lock, dataMovementComplete, &self->startMoveKeysParallelismLock, &self->finishMoveKeysParallelismLock, self->teamCollections.size() > 1, relocateShardInterval.pairID ); state Future pollHealth = signalledTransferComplete ? Never() : delay( SERVER_KNOBS->HEALTH_POLL_TIME, TaskPriority::DataDistributionLaunch ); try { diff --git a/fdbserver/RestoreRoleCommon.actor.cpp b/fdbserver/RestoreRoleCommon.actor.cpp index 18ca7ea93d..345b82f071 100644 --- a/fdbserver/RestoreRoleCommon.actor.cpp +++ b/fdbserver/RestoreRoleCommon.actor.cpp @@ -116,6 +116,10 @@ ACTOR Future isSchedulable(Reference self, int actorBatch self->delayedActors--; break; } else { + TraceEvent(SevDebug, "FastRestoreMemoryUsageAboveThresholdWait") + .detail("BatchIndex", actorBatchIndex) + .detail("Actor", name) + .detail("CurrentMemory", memory); wait(delay(SERVER_KNOBS->FASTRESTORE_WAIT_FOR_MEMORY_LATENCY) || self->checkMemory.onTrigger()); } } From 97199257a5fb80049381eb3f7d6dc0019735df1e Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Thu, 27 Feb 2020 18:35:09 -0800 Subject: [PATCH 0760/1604] Change from pointer to reference --- fdbclient/PrivateKeySpace.actor.cpp | 10 +++++----- fdbclient/PrivateKeySpace.h | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/fdbclient/PrivateKeySpace.actor.cpp b/fdbclient/PrivateKeySpace.actor.cpp index 3e30de65de..aa88a28345 100644 --- a/fdbclient/PrivateKeySpace.actor.cpp +++ b/fdbclient/PrivateKeySpace.actor.cpp @@ -99,8 +99,8 @@ ACTOR Future> getRangeAggregationActor( // make sure offset == 1 state RangeMap::Iterator iter = - pks->getKeyRangeMap()->rangeContaining(begin.getKey()); - while (begin.offset != 1 && iter != pks->getKeyRangeMap()->ranges().begin()) { + pks->getKeyRangeMap().rangeContaining(begin.getKey()); + while (begin.offset != 1 && iter != pks->getKeyRangeMap().ranges().begin()) { if (iter->value() != NULL) wait(normalizeKeySelectorActor(iter->value(), ryw, &begin)); begin.offset < 1 ? --iter : ++iter; @@ -111,8 +111,8 @@ ACTOR Future> getRangeAggregationActor( detail("TerminateKey", begin.getKey()). detail("TerminateOffset", begin.offset); } - iter = pks->getKeyRangeMap()->rangeContaining(end.getKey()); - while (end.offset != 1 && iter != pks->getKeyRangeMap()->ranges().end()) { + iter = pks->getKeyRangeMap().rangeContaining(end.getKey()); + while (end.offset != 1 && iter != pks->getKeyRangeMap().ranges().end()) { if (iter->value() != NULL) wait(normalizeKeySelectorActor(iter->value(), ryw, &end)); end.offset < 1 ? --iter : ++iter; @@ -130,7 +130,7 @@ ACTOR Future> getRangeAggregationActor( } state Standalone result; state RangeMap::Ranges ranges = - pks->getKeyRangeMap()->intersectingRanges(KeyRangeRef(begin.getKey(), end.getKey())); + pks->getKeyRangeMap().intersectingRanges(KeyRangeRef(begin.getKey(), end.getKey())); // 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(); diff --git a/fdbclient/PrivateKeySpace.h b/fdbclient/PrivateKeySpace.h index 9aa345ff79..f729ee9420 100644 --- a/fdbclient/PrivateKeySpace.h +++ b/fdbclient/PrivateKeySpace.h @@ -52,8 +52,8 @@ public: impls.insert(kr, impl); } - KeyRangeMap* getKeyRangeMap(){ - return &impls; + KeyRangeMap& getKeyRangeMap(){ + return impls; } private: From b8a9d49b2e2b1fe74bd54ee7029a2374fd9b3e11 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Thu, 27 Feb 2020 18:46:31 -0800 Subject: [PATCH 0761/1604] FastRestore:Ensure minimum delay between heart beats --- fdbserver/RestoreMaster.actor.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fdbserver/RestoreMaster.actor.cpp b/fdbserver/RestoreMaster.actor.cpp index 5e22c2ff9e..2a9d729387 100644 --- a/fdbserver/RestoreMaster.actor.cpp +++ b/fdbserver/RestoreMaster.actor.cpp @@ -897,9 +897,11 @@ ACTOR static Future signalRestoreCompleted(Reference se ACTOR static Future updateHeartbeatTime(Reference self) { state std::map::iterator loader = self->loadersInterf.begin(); state std::map::iterator applier = self->appliersInterf.begin(); - state std::vector> fReplies; + state std::vector> fReplies; // TODO: Reserve memory for this vector state std::vector nodes; + loop { + wait(delay(SERVER_KNOBS->FASTRESTORE_HEARTBEAT_DELAY)); loader = self->loadersInterf.begin(); applier = self->appliersInterf.begin(); fReplies.clear(); From 6d06fcc13be5ad9cf274aa8370323a051368f212 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Thu, 27 Feb 2020 18:53:13 -0800 Subject: [PATCH 0762/1604] Add range check in PrivateKeySpace --- fdbclient/PrivateKeySpace.h | 11 +++++++---- fdbrpc/FlowTests.actor.cpp | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/fdbclient/PrivateKeySpace.h b/fdbclient/PrivateKeySpace.h index f729ee9420..64715d0811 100644 --- a/fdbclient/PrivateKeySpace.h +++ b/fdbclient/PrivateKeySpace.h @@ -43,12 +43,14 @@ public: Future> getRange(ReadYourWritesTransaction* ryw, KeySelector begin, KeySelector end, GetRangeLimits limits, bool snapshot = false, bool reverse = false); - PrivateKeySpace(KeyRef rangeEndKey = allKeys.end) { - // Default value is NULL - impls = KeyRangeMap(NULL, rangeEndKey); + PrivateKeySpace(KeyRef spaceStartKey = Key(), KeyRef spaceEndKey = allKeys.end) { + // Default value is NULL, begin of KeyRangeMap is Key() + impls = KeyRangeMap(NULL, spaceEndKey); + range = KeyRangeRef(spaceStartKey, spaceEndKey); } void registerKeyRange(const KeyRangeRef& kr, PrivateKeyRangeBaseImpl* impl) { - // TODO : range check + // range check + ASSERT(kr.begin >= range.begin && kr.end <= range.end); impls.insert(kr, impl); } @@ -58,6 +60,7 @@ public: private: KeyRangeMap impls; + KeyRange range; }; #endif diff --git a/fdbrpc/FlowTests.actor.cpp b/fdbrpc/FlowTests.actor.cpp index 561e80f884..eb99b3fdf9 100644 --- a/fdbrpc/FlowTests.actor.cpp +++ b/fdbrpc/FlowTests.actor.cpp @@ -1308,7 +1308,7 @@ private: }; TEST_CASE("/fdbclient/PrivateKeySpace/Aggregation") { - PrivateKeySpace pks(LiteralStringRef("\xff\xff\xff")); + PrivateKeySpace pks(LiteralStringRef("\xff\xff"), LiteralStringRef("\xff\xff\xff")); PrivateKeyRangeTestImpl pkr1(LiteralStringRef("\xff\xff/cat/"), LiteralStringRef("\xff\xff/cat/\xff"), "small", 10); PrivateKeyRangeTestImpl pkr2(LiteralStringRef("\xff\xff/dog/"), LiteralStringRef("\xff\xff/dog/\xff"), "medium", 100); PrivateKeyRangeTestImpl pkr3(LiteralStringRef("\xff\xff/pig/"), LiteralStringRef("\xff\xff/pig/\xff"), "large", 1000); From d77177367c2df8ccab4b9ba62c6fc5d48b2f469a Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Thu, 27 Feb 2020 19:23:29 -0800 Subject: [PATCH 0763/1604] FastRestore:Track each ongoing version batch progress state for applier and loader roles --- fdbserver/RestoreApplier.actor.cpp | 11 ++++++++--- fdbserver/RestoreApplier.actor.h | 22 +++++++++++++++++++++- fdbserver/RestoreLoader.actor.h | 26 +++++++++++++++++++++++++- fdbserver/RestoreMaster.actor.h | 6 ++++++ fdbserver/RestoreRoleCommon.actor.cpp | 22 +++++++++++++++++++++- fdbserver/RestoreRoleCommon.actor.h | 6 +++++- 6 files changed, 86 insertions(+), 7 deletions(-) diff --git a/fdbserver/RestoreApplier.actor.cpp b/fdbserver/RestoreApplier.actor.cpp index 9bee66c55c..28f843161e 100644 --- a/fdbserver/RestoreApplier.actor.cpp +++ b/fdbserver/RestoreApplier.actor.cpp @@ -47,6 +47,7 @@ ACTOR Future restoreApplierCore(RestoreApplierInterface applierInterf, int state Future updateProcessStatsTimer = delay(SERVER_KNOBS->FASTRESTORE_UPDATE_PROCESS_STATS_INTERVAL); actors.add(traceProcessMetrics(self, "Applier")); + actors.add(traceRoleVersionBatchProgress(self, "Applier")); loop { state std::string requestTypeStr = "[Init]"; @@ -113,11 +114,13 @@ ACTOR static Future handleSendMutationVectorRequest(RestoreSendVersionedMu .detail("RestoreAsset", req.asset.toString()) .detail("ProcessedFileVersion", curFilePos.get()) .detail("Request", req.toString()) - .detail("CurrentMemory", getSystemStatistics().processMemory); + .detail("CurrentMemory", getSystemStatistics().processMemory) + .detail("PreviousVersionBatchState", batchData->vbState); wait(isSchedulable(self, req.batchIndex, __FUNCTION__)); wait(curFilePos.whenAtLeast(req.prevVersion)); + batchData->vbState = ApplierVersionBatchState::RECEIVE_MUTATIONS; state bool isDuplicated = true; if (curFilePos.get() == req.prevVersion) { @@ -438,7 +441,9 @@ ACTOR static Future handleApplyToDBRequest(RestoreVersionBatchRequest req, TraceEvent("FastRestoreApplierPhaseHandleApplyToDB", self->id()) .detail("BatchIndex", req.batchIndex) .detail("FinishedBatch", self->finishedBatch.get()) - .detail("HasStarted", batchData->dbApplier.present()); + .detail("HasStarted", batchData->dbApplier.present()) + .detail("PreviousVersionBatchState", batchData->vbState); + batchData->vbState = ApplierVersionBatchState::WRITE_TO_DB; if (self->finishedBatch.get() == req.batchIndex - 1) { ASSERT(batchData.isValid()); if (!batchData->dbApplier.present()) { @@ -495,4 +500,4 @@ Value applyAtomicOp(Optional existingValue, Value value, MutationRef: ASSERT(false); } return Value(); -} +} \ No newline at end of file diff --git a/fdbserver/RestoreApplier.actor.h b/fdbserver/RestoreApplier.actor.h index 96f268eef6..5491e492c9 100644 --- a/fdbserver/RestoreApplier.actor.h +++ b/fdbserver/RestoreApplier.actor.h @@ -201,6 +201,9 @@ struct StagingKeyRange { } }; +// Applier state in each verion batch +enum class ApplierVersionBatchState : RoleVersionBatchState { NOT_INIT = 0, INIT = 1, RECEIVE_MUTATIONS = 2, WRITE_TO_DB = 3, INVALID = 4 }; + struct ApplierBatchData : public ReferenceCounted { // processedFileState: key: RestoreAsset; value: largest version of mutation received on the applier std::map processedFileState; @@ -212,6 +215,8 @@ struct ApplierBatchData : public ReferenceCounted { Future pollMetrics; + RoleVersionBatchState vbState; + // Status counters struct Counters { CounterCollection cc; @@ -232,7 +237,8 @@ struct ApplierBatchData : public ReferenceCounted { void delref() { return ReferenceCounted::delref(); } explicit ApplierBatchData(UID nodeID, int batchIndex) - : counters(this, nodeID, batchIndex), applyStagingKeysBatchLock(SERVER_KNOBS->FASTRESTORE_APPLYING_PARALLELISM) { + : counters(this, nodeID, batchIndex), applyStagingKeysBatchLock(SERVER_KNOBS->FASTRESTORE_APPLYING_PARALLELISM), + vbState(ApplierVersionBatchState::NOT_INIT) { pollMetrics = traceCounters("FastRestoreApplierMetrics", nodeID, SERVER_KNOBS->FASTRESTORE_ROLE_LOGGING_DELAY, &counters.cc, nodeID.toString() + "/RestoreApplierMetrics/" + std::to_string(batchIndex)); @@ -345,6 +351,20 @@ struct RestoreApplierData : RestoreRoleData, public ReferenceCounted>::iterator item = batch.find(batchIndex); + if ( item == batch.end()) { + return ApplierVersionBatchState::INVALID; + } else { + return item->second->vbState; + } + } + void setVersionBatchState(int batchIndex, RoleVersionBatchState vbState) { + std::map>::iterator item = batch.find(batchIndex); + ASSERT(item != batch.end()); + item->second->vbState = (ApplierVersionBatchState) vbState; + } + void initVersionBatch(int batchIndex) { TraceEvent("FastRestoreApplierInitVersionBatch", id()).detail("BatchIndex", batchIndex); batch[batchIndex] = Reference(new ApplierBatchData(nodeID, batchIndex)); diff --git a/fdbserver/RestoreLoader.actor.h b/fdbserver/RestoreLoader.actor.h index d53bbf4412..610bc11dcc 100644 --- a/fdbserver/RestoreLoader.actor.h +++ b/fdbserver/RestoreLoader.actor.h @@ -42,6 +42,14 @@ #include "flow/actorcompiler.h" // has to be last include +enum class LoaderVersionBatchState : RoleVersionBatchState { + NOT_INIT, + INIT, + LOAD_FILE, + SEND_MUTATIONS, + INVALID +}; + struct LoaderBatchData : public ReferenceCounted { std::map> processedFileParams; std::map kvOpsPerLP; // Buffered kvOps for each loading param @@ -56,6 +64,8 @@ struct LoaderBatchData : public ReferenceCounted { Future pollMetrics; + LoaderVersionBatchState vbState; + // Status counters struct Counters { CounterCollection cc; @@ -68,7 +78,7 @@ struct LoaderBatchData : public ReferenceCounted { sampledRangeBytes("SampledRangeBytes", cc), sampledLogBytes("SampledLogBytes", cc) {} } counters; - explicit LoaderBatchData(UID nodeID, int batchIndex) : counters(this, nodeID, batchIndex) { + explicit LoaderBatchData(UID nodeID, int batchIndex) : counters(this, nodeID, batchIndex), vbState(LoaderVersionBatchState::NOT_INIT) { pollMetrics = traceCounters("FastRestoreLoaderMetrics", nodeID, SERVER_KNOBS->FASTRESTORE_ROLE_LOGGING_DELAY, &counters.cc, nodeID.toString() + "/RestoreLoaderMetrics/" + std::to_string(batchIndex)); @@ -129,6 +139,20 @@ struct RestoreLoaderData : RestoreRoleData, public ReferenceCounted>::iterator item = batch.find(batchIndex); + if ( item == batch.end()) { + return LoaderVersionBatchState::INVALID; + } else { + return item->second->vbState; + } + } + void setVersionBatchState(int batchIndex, RoleVersionBatchState vbState) { + std::map>::iterator item = batch.find(batchIndex); + ASSERT(item != batch.end()); + item->second->vbState = (LoaderVersionBatchState) vbState; + } + void initVersionBatch(int batchIndex) { TraceEvent("FastRestore").detail("InitVersionBatchOnLoader", nodeID); batch[batchIndex] = Reference(new LoaderBatchData(nodeID, batchIndex)); diff --git a/fdbserver/RestoreMaster.actor.h b/fdbserver/RestoreMaster.actor.h index 19b4ff4f4a..b70f7e2033 100644 --- a/fdbserver/RestoreMaster.actor.h +++ b/fdbserver/RestoreMaster.actor.h @@ -157,6 +157,12 @@ struct RestoreMasterData : RestoreRoleData, public ReferenceCounted handleInitVersionBatchRequest(RestoreVersionBatchRequest req, TraceEvent("FastRestoreRolePhaseInitVersionBatch", self->id()) .detail("BatchIndex", req.batchIndex) .detail("Role", getRoleStr(self->role)) - .detail("VersionBatchNotifiedVersion", self->versionBatchId.get()); + .detail("VersionBatchNotifiedVersion", self->versionBatchId.get()) + .detail("PreviousVersionBatchState", batchData->vbState); // batchId is continuous. (req.batchIndex-1) is the id of the just finished batch. wait(self->versionBatchId.whenAtLeast(req.batchIndex - 1)); + batchData->vbState = ApplierVersionBatchState::INIT; if (self->versionBatchId.get() == req.batchIndex - 1) { self->initVersionBatch(req.batchIndex); TraceEvent("FastRestoreInitVersionBatch") @@ -138,6 +140,24 @@ ACTOR Future traceProcessMetrics(Reference self, std::str } } +ACTOR Future traceRoleVersionBatchProgress(Reference self, std::string role) { + loop { + int batchIndex = self->finishedBatch.get(); + int maxBatchIndex = self->versionBatchId.get(); + + TraceEvent ev("FastRestoreVersionBatchProgress", self->nodeID); + ev.detail("Role", role) + ev.detail("Node", self->nodeID); + while (batchIndex <= maxBatchIndex) { + ev.detail("BatchIndex", batchIndex); + ev.detail("VersionBatchState", self->batch[batchIndex]->vbState); + batchIndex++; + } + + wait(delay(SERVER_KNOBS->FASTRESTORE_ROLE_LOGGING_DELAY)); + } +} + //-------Helper functions std::string getHexString(StringRef input) { std::stringstream ss; diff --git a/fdbserver/RestoreRoleCommon.actor.h b/fdbserver/RestoreRoleCommon.actor.h index a94fb58805..8930e8b5cf 100644 --- a/fdbserver/RestoreRoleCommon.actor.h +++ b/fdbserver/RestoreRoleCommon.actor.h @@ -105,6 +105,8 @@ struct BackupStringRefReader { Error failure_error; }; +enum class RoleVersionBatchState {INVALID}; + struct RestoreRoleData : NonCopyable, public ReferenceCounted { public: RestoreRole role; @@ -133,8 +135,9 @@ public: UID id() const { return nodeID; } virtual void initVersionBatch(int batchIndex) = 0; - virtual void resetPerRestoreRequest() = 0; + virtual RoleVersionBatchState getVersionBatchState(int batchIndex); + virtual void setVersionBatchState(int batchIndex, RoleVersionBatchState vbState); void clearInterfaces() { loadersInterf.clear(); @@ -146,6 +149,7 @@ public: void updateProcessStats(Reference self); ACTOR Future traceProcessMetrics(Reference self, std::string role); +ACTOR Future traceRoleVersionBatchProgress(Reference self, std::string role); #include "flow/unactorcompiler.h" #endif From fe8b8bbbffe1469fea4af2f3e9925863e0d896d4 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Thu, 27 Feb 2020 20:13:20 -0800 Subject: [PATCH 0764/1604] FastRestore:Change vb state to class from enum --- fdbserver/RestoreApplier.actor.cpp | 4 ++-- fdbserver/RestoreApplier.actor.h | 27 +++++++++++++---------- fdbserver/RestoreLoader.actor.h | 34 +++++++++++++++++++---------- fdbserver/RestoreMaster.actor.h | 4 ++-- fdbserver/RestoreRoleCommon.actor.h | 21 +++++++++++++++--- 5 files changed, 60 insertions(+), 30 deletions(-) diff --git a/fdbserver/RestoreApplier.actor.cpp b/fdbserver/RestoreApplier.actor.cpp index 28f843161e..e8075f76a0 100644 --- a/fdbserver/RestoreApplier.actor.cpp +++ b/fdbserver/RestoreApplier.actor.cpp @@ -115,7 +115,7 @@ ACTOR static Future handleSendMutationVectorRequest(RestoreSendVersionedMu .detail("ProcessedFileVersion", curFilePos.get()) .detail("Request", req.toString()) .detail("CurrentMemory", getSystemStatistics().processMemory) - .detail("PreviousVersionBatchState", batchData->vbState); + .detail("PreviousVersionBatchState", batchData->vbState.get()); wait(isSchedulable(self, req.batchIndex, __FUNCTION__)); @@ -442,7 +442,7 @@ ACTOR static Future handleApplyToDBRequest(RestoreVersionBatchRequest req, .detail("BatchIndex", req.batchIndex) .detail("FinishedBatch", self->finishedBatch.get()) .detail("HasStarted", batchData->dbApplier.present()) - .detail("PreviousVersionBatchState", batchData->vbState); + .detail("PreviousVersionBatchState", batchData->vbState.get()); batchData->vbState = ApplierVersionBatchState::WRITE_TO_DB; if (self->finishedBatch.get() == req.batchIndex - 1) { ASSERT(batchData.isValid()); diff --git a/fdbserver/RestoreApplier.actor.h b/fdbserver/RestoreApplier.actor.h index 5491e492c9..a4f07a782a 100644 --- a/fdbserver/RestoreApplier.actor.h +++ b/fdbserver/RestoreApplier.actor.h @@ -202,7 +202,15 @@ struct StagingKeyRange { }; // Applier state in each verion batch -enum class ApplierVersionBatchState : RoleVersionBatchState { NOT_INIT = 0, INIT = 1, RECEIVE_MUTATIONS = 2, WRITE_TO_DB = 3, INVALID = 4 }; +class ApplierVersionBatchState : RoleVersionBatchState { + static const int NOT_INIT = 0; + static const int INIT = 1; + static const int RECEIVE_MUTATIONS = 2; + static const int WRITE_TO_DB = 3; + static const int INVALID = 4; + + explicit ApplierVersionBatchState(int newState) : vbState(newState) {} +}; struct ApplierBatchData : public ReferenceCounted { // processedFileState: key: RestoreAsset; value: largest version of mutation received on the applier @@ -351,18 +359,15 @@ struct RestoreApplierData : RestoreRoleData, public ReferenceCounted>::iterator item = batch.find(batchIndex); - if ( item == batch.end()) { - return ApplierVersionBatchState::INVALID; - } else { - return item->second->vbState; - } - } - void setVersionBatchState(int batchIndex, RoleVersionBatchState vbState) { + int getVersionBatchState(int batchIndex) { std::map>::iterator item = batch.find(batchIndex); ASSERT(item != batch.end()); - item->second->vbState = (ApplierVersionBatchState) vbState; + return item->second->vbState.get(); + } + void setVersionBatchState(int batchIndex, int vbState) { + std::map>::iterator item = batch.find(batchIndex); + ASSERT(item != batch.end()); + item->second->vbState = vbState; } void initVersionBatch(int batchIndex) { diff --git a/fdbserver/RestoreLoader.actor.h b/fdbserver/RestoreLoader.actor.h index 610bc11dcc..8a8bdeb376 100644 --- a/fdbserver/RestoreLoader.actor.h +++ b/fdbserver/RestoreLoader.actor.h @@ -42,12 +42,25 @@ #include "flow/actorcompiler.h" // has to be last include -enum class LoaderVersionBatchState : RoleVersionBatchState { - NOT_INIT, - INIT, - LOAD_FILE, - SEND_MUTATIONS, - INVALID +class LoaderVersionBatchState : RoleVersionBatchState { + static const int NOT_INIT = 0; + static const int INIT = 1; + static const int LOAD_FILE = 2; + static const int SEND_MUTATIONS = 3; + static const int INVALID = 4; + + explicit LoaderVersionBatchState(int newState) : vbState(newState) {} + + // static std::string getVersionBatchState(int vbState) { + // switch(vbSTate) { + // case NOT_INIT: return "NOT_INIT"; + // case INIT: return "INIT"; + // case LOAD_FILE: return "LOAD_FILE"; + // case SEND_MUTATIONS: return "SEND_MUTATIONS"; + // case INVALID: return "INVALID"; + // default: return "UNKNOWN"; + // } + // } }; struct LoaderBatchData : public ReferenceCounted { @@ -139,13 +152,10 @@ struct RestoreLoaderData : RestoreRoleData, public ReferenceCounted>::iterator item = batch.find(batchIndex); - if ( item == batch.end()) { - return LoaderVersionBatchState::INVALID; - } else { - return item->second->vbState; - } + ASSERT(item != batch.end()); + return item->second->vbState; } void setVersionBatchState(int batchIndex, RoleVersionBatchState vbState) { std::map>::iterator item = batch.find(batchIndex); diff --git a/fdbserver/RestoreMaster.actor.h b/fdbserver/RestoreMaster.actor.h index b70f7e2033..92ecd83f32 100644 --- a/fdbserver/RestoreMaster.actor.h +++ b/fdbserver/RestoreMaster.actor.h @@ -157,10 +157,10 @@ struct RestoreMasterData : RestoreRoleData, public ReferenceCounted { public: @@ -136,8 +151,8 @@ public: virtual void initVersionBatch(int batchIndex) = 0; virtual void resetPerRestoreRequest() = 0; - virtual RoleVersionBatchState getVersionBatchState(int batchIndex); - virtual void setVersionBatchState(int batchIndex, RoleVersionBatchState vbState); + virtual int getVersionBatchState(int batchIndex); + virtual void setVersionBatchState(int batchIndex, int vbState); void clearInterfaces() { loadersInterf.clear(); From a6e66da29fb8d40e7443dc1f601d32349b4b11bb Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Thu, 27 Feb 2020 20:59:34 -0800 Subject: [PATCH 0765/1604] FastRestore:fix compilation error for version batch state --- fdbserver/RestoreApplier.actor.h | 5 ++++- fdbserver/RestoreLoader.actor.h | 13 ++++++++----- fdbserver/RestoreRoleCommon.actor.h | 3 ++- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/fdbserver/RestoreApplier.actor.h b/fdbserver/RestoreApplier.actor.h index a4f07a782a..3b6585d1e0 100644 --- a/fdbserver/RestoreApplier.actor.h +++ b/fdbserver/RestoreApplier.actor.h @@ -203,13 +203,16 @@ struct StagingKeyRange { // Applier state in each verion batch class ApplierVersionBatchState : RoleVersionBatchState { +public: static const int NOT_INIT = 0; static const int INIT = 1; static const int RECEIVE_MUTATIONS = 2; static const int WRITE_TO_DB = 3; static const int INVALID = 4; - explicit ApplierVersionBatchState(int newState) : vbState(newState) {} + explicit ApplierVersionBatchState(int newState) { + vbState = newState; + } }; struct ApplierBatchData : public ReferenceCounted { diff --git a/fdbserver/RestoreLoader.actor.h b/fdbserver/RestoreLoader.actor.h index 8a8bdeb376..a1337299d6 100644 --- a/fdbserver/RestoreLoader.actor.h +++ b/fdbserver/RestoreLoader.actor.h @@ -43,13 +43,16 @@ #include "flow/actorcompiler.h" // has to be last include class LoaderVersionBatchState : RoleVersionBatchState { +public: static const int NOT_INIT = 0; static const int INIT = 1; static const int LOAD_FILE = 2; static const int SEND_MUTATIONS = 3; static const int INVALID = 4; - explicit LoaderVersionBatchState(int newState) : vbState(newState) {} + explicit LoaderVersionBatchState(int newState) { + vbState = newState; + } // static std::string getVersionBatchState(int vbState) { // switch(vbSTate) { @@ -152,15 +155,15 @@ struct RestoreLoaderData : RestoreRoleData, public ReferenceCounted>::iterator item = batch.find(batchIndex); ASSERT(item != batch.end()); - return item->second->vbState; + return item->second->vbState.get(); } - void setVersionBatchState(int batchIndex, RoleVersionBatchState vbState) { + void setVersionBatchState(int batchIndex, int vbState) { std::map>::iterator item = batch.find(batchIndex); ASSERT(item != batch.end()); - item->second->vbState = (LoaderVersionBatchState) vbState; + item->second->vbState = vbState; } void initVersionBatch(int batchIndex) { diff --git a/fdbserver/RestoreRoleCommon.actor.h b/fdbserver/RestoreRoleCommon.actor.h index 0cbb5d7bc3..405ea7a596 100644 --- a/fdbserver/RestoreRoleCommon.actor.h +++ b/fdbserver/RestoreRoleCommon.actor.h @@ -113,10 +113,11 @@ public: return vbState; } - operator = (int newState) { + void operator = (int newState) { vbState = newState; } + explicit RoleVersionBatchState() : vbState(INVALID) {} explicit RoleVersionBatchState(int newState) : vbState(newState) {} int vbState; From f4cd0ef74f6d383a05175dfaa1092a8e59deb7c3 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Thu, 27 Feb 2020 20:59:56 -0800 Subject: [PATCH 0766/1604] FastRestore:Apply clang-format --- fdbserver/RestoreApplier.actor.h | 10 ++++++++++ fdbserver/RestoreLoader.actor.h | 10 ++++++++++ fdbserver/RestoreRoleCommon.actor.cpp | 8 ++++---- fdbserver/RestoreRoleCommon.actor.h | 8 +++++--- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/fdbserver/RestoreApplier.actor.h b/fdbserver/RestoreApplier.actor.h index 3b6585d1e0..07fb7a46f2 100644 --- a/fdbserver/RestoreApplier.actor.h +++ b/fdbserver/RestoreApplier.actor.h @@ -213,6 +213,16 @@ public: explicit ApplierVersionBatchState(int newState) { vbState = newState; } + + ~ApplierVersionBatchState() = default; + + void operator = (int newState) { + vbState = newState; + } + + int get() { + return RoleVersionBatchState::get(); + } }; struct ApplierBatchData : public ReferenceCounted { diff --git a/fdbserver/RestoreLoader.actor.h b/fdbserver/RestoreLoader.actor.h index a1337299d6..506d3174c5 100644 --- a/fdbserver/RestoreLoader.actor.h +++ b/fdbserver/RestoreLoader.actor.h @@ -54,6 +54,16 @@ public: vbState = newState; } + ~LoaderVersionBatchState() = default; + + void operator = (int newState) { + vbState = newState; + } + + int get() { + return RoleVersionBatchState::get(); + } + // static std::string getVersionBatchState(int vbState) { // switch(vbSTate) { // case NOT_INIT: return "NOT_INIT"; diff --git a/fdbserver/RestoreRoleCommon.actor.cpp b/fdbserver/RestoreRoleCommon.actor.cpp index 0e75ba6c31..a1506b7ff0 100644 --- a/fdbserver/RestoreRoleCommon.actor.cpp +++ b/fdbserver/RestoreRoleCommon.actor.cpp @@ -57,11 +57,11 @@ ACTOR Future handleInitVersionBatchRequest(RestoreVersionBatchRequest req, .detail("BatchIndex", req.batchIndex) .detail("Role", getRoleStr(self->role)) .detail("VersionBatchNotifiedVersion", self->versionBatchId.get()) - .detail("PreviousVersionBatchState", batchData->vbState); + .detail("PreviousVersionBatchState", self->getVersionBatchState(req.batchIndex)); // batchId is continuous. (req.batchIndex-1) is the id of the just finished batch. wait(self->versionBatchId.whenAtLeast(req.batchIndex - 1)); - batchData->vbState = ApplierVersionBatchState::INIT; + self->setVersionBatchState(req.batchIndex, ApplierVersionBatchState::INIT); if (self->versionBatchId.get() == req.batchIndex - 1) { self->initVersionBatch(req.batchIndex); TraceEvent("FastRestoreInitVersionBatch") @@ -146,11 +146,11 @@ ACTOR Future traceRoleVersionBatchProgress(Reference self int maxBatchIndex = self->versionBatchId.get(); TraceEvent ev("FastRestoreVersionBatchProgress", self->nodeID); - ev.detail("Role", role) + ev.detail("Role", role); ev.detail("Node", self->nodeID); while (batchIndex <= maxBatchIndex) { ev.detail("BatchIndex", batchIndex); - ev.detail("VersionBatchState", self->batch[batchIndex]->vbState); + ev.detail("VersionBatchState", self->getVersionBatchState(batchIndex)); batchIndex++; } diff --git a/fdbserver/RestoreRoleCommon.actor.h b/fdbserver/RestoreRoleCommon.actor.h index 405ea7a596..9efb308fd2 100644 --- a/fdbserver/RestoreRoleCommon.actor.h +++ b/fdbserver/RestoreRoleCommon.actor.h @@ -109,17 +109,19 @@ class RoleVersionBatchState { public: static const int INVALID = -1; - int get() { + virtual int get() { return vbState; } - void operator = (int newState) { + virtual void operator = (int newState) { vbState = newState; } explicit RoleVersionBatchState() : vbState(INVALID) {} explicit RoleVersionBatchState(int newState) : vbState(newState) {} + virtual ~RoleVersionBatchState() = default; + int vbState; }; @@ -146,7 +148,7 @@ public: RestoreRoleData() : role(RestoreRole::Invalid), cpuUsage(0.0), memory(0.0), residentMemory(0.0), delayedActors(0){}; - virtual ~RestoreRoleData() {} + virtual ~RestoreRoleData() = default; UID id() const { return nodeID; } From 6018b64d73f3e3c179680011a862e9e469c53b69 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Thu, 27 Feb 2020 21:18:01 -0800 Subject: [PATCH 0767/1604] FastRestore:Fix undefined ref to vtable error --- fdbserver/RestoreApplier.actor.h | 10 +++------- fdbserver/RestoreLoader.actor.h | 10 +++------- fdbserver/RestoreRoleCommon.actor.h | 4 ++-- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/fdbserver/RestoreApplier.actor.h b/fdbserver/RestoreApplier.actor.h index 07fb7a46f2..fd5f07951f 100644 --- a/fdbserver/RestoreApplier.actor.h +++ b/fdbserver/RestoreApplier.actor.h @@ -214,15 +214,11 @@ public: vbState = newState; } - ~ApplierVersionBatchState() = default; + virtual ~ApplierVersionBatchState() = default; - void operator = (int newState) { - vbState = newState; - } + virtual void operator=(int newState) { vbState = newState; } - int get() { - return RoleVersionBatchState::get(); - } + virtual int get() { return vbState; } }; struct ApplierBatchData : public ReferenceCounted { diff --git a/fdbserver/RestoreLoader.actor.h b/fdbserver/RestoreLoader.actor.h index 506d3174c5..d84c2e1b8a 100644 --- a/fdbserver/RestoreLoader.actor.h +++ b/fdbserver/RestoreLoader.actor.h @@ -54,15 +54,11 @@ public: vbState = newState; } - ~LoaderVersionBatchState() = default; + virtual ~LoaderVersionBatchState() = default; - void operator = (int newState) { - vbState = newState; - } + virtual void operator=(int newState) { vbState = newState; } - int get() { - return RoleVersionBatchState::get(); - } + virtual int get() { return vbState; } // static std::string getVersionBatchState(int vbState) { // switch(vbSTate) { diff --git a/fdbserver/RestoreRoleCommon.actor.h b/fdbserver/RestoreRoleCommon.actor.h index 9efb308fd2..531e652a7e 100644 --- a/fdbserver/RestoreRoleCommon.actor.h +++ b/fdbserver/RestoreRoleCommon.actor.h @@ -154,8 +154,8 @@ public: virtual void initVersionBatch(int batchIndex) = 0; virtual void resetPerRestoreRequest() = 0; - virtual int getVersionBatchState(int batchIndex); - virtual void setVersionBatchState(int batchIndex, int vbState); + virtual int getVersionBatchState(int batchIndex) = 0; + virtual void setVersionBatchState(int batchIndex, int vbState) = 0; void clearInterfaces() { loadersInterf.clear(); From 22b34bc609522c2133b46c05bef040d1908cd276 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Thu, 27 Feb 2020 23:45:48 -0800 Subject: [PATCH 0768/1604] FastRestore:getVersionBatchState can be called before version batch is initialized --- fdbserver/RestoreApplier.actor.h | 7 +++++-- fdbserver/RestoreLoader.actor.h | 18 +++++------------- fdbserver/RestoreRoleCommon.actor.cpp | 3 +-- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/fdbserver/RestoreApplier.actor.h b/fdbserver/RestoreApplier.actor.h index fd5f07951f..cacbb882bb 100644 --- a/fdbserver/RestoreApplier.actor.h +++ b/fdbserver/RestoreApplier.actor.h @@ -370,8 +370,11 @@ struct RestoreApplierData : RestoreRoleData, public ReferenceCounted>::iterator item = batch.find(batchIndex); - ASSERT(item != batch.end()); - return item->second->vbState.get(); + if (item == batch.end()) { + return ApplierVersionBatchState::INVALID; + } else { + return item->second->vbState.get(); + } } void setVersionBatchState(int batchIndex, int vbState) { std::map>::iterator item = batch.find(batchIndex); diff --git a/fdbserver/RestoreLoader.actor.h b/fdbserver/RestoreLoader.actor.h index d84c2e1b8a..9d7ac6881c 100644 --- a/fdbserver/RestoreLoader.actor.h +++ b/fdbserver/RestoreLoader.actor.h @@ -59,17 +59,6 @@ public: virtual void operator=(int newState) { vbState = newState; } virtual int get() { return vbState; } - - // static std::string getVersionBatchState(int vbState) { - // switch(vbSTate) { - // case NOT_INIT: return "NOT_INIT"; - // case INIT: return "INIT"; - // case LOAD_FILE: return "LOAD_FILE"; - // case SEND_MUTATIONS: return "SEND_MUTATIONS"; - // case INVALID: return "INVALID"; - // default: return "UNKNOWN"; - // } - // } }; struct LoaderBatchData : public ReferenceCounted { @@ -163,8 +152,11 @@ struct RestoreLoaderData : RestoreRoleData, public ReferenceCounted>::iterator item = batch.find(batchIndex); - ASSERT(item != batch.end()); - return item->second->vbState.get(); + if (item != batch.end()) { + return LoaderVersionBatchState::INVALID; + } else { + return item->second->vbState.get(); + } } void setVersionBatchState(int batchIndex, int vbState) { std::map>::iterator item = batch.find(batchIndex); diff --git a/fdbserver/RestoreRoleCommon.actor.cpp b/fdbserver/RestoreRoleCommon.actor.cpp index a1506b7ff0..35474f480d 100644 --- a/fdbserver/RestoreRoleCommon.actor.cpp +++ b/fdbserver/RestoreRoleCommon.actor.cpp @@ -56,8 +56,7 @@ ACTOR Future handleInitVersionBatchRequest(RestoreVersionBatchRequest req, TraceEvent("FastRestoreRolePhaseInitVersionBatch", self->id()) .detail("BatchIndex", req.batchIndex) .detail("Role", getRoleStr(self->role)) - .detail("VersionBatchNotifiedVersion", self->versionBatchId.get()) - .detail("PreviousVersionBatchState", self->getVersionBatchState(req.batchIndex)); + .detail("VersionBatchNotifiedVersion", self->versionBatchId.get()); // batchId is continuous. (req.batchIndex-1) is the id of the just finished batch. wait(self->versionBatchId.whenAtLeast(req.batchIndex - 1)); From eaf340652f546fb4d11356559dc94b9c55d8ff3f Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Thu, 27 Feb 2020 23:50:09 -0800 Subject: [PATCH 0769/1604] FastRestore:ensure setVersionBatchState after version batch is initialized --- fdbserver/RestoreRoleCommon.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/RestoreRoleCommon.actor.cpp b/fdbserver/RestoreRoleCommon.actor.cpp index 35474f480d..724bb0d7d5 100644 --- a/fdbserver/RestoreRoleCommon.actor.cpp +++ b/fdbserver/RestoreRoleCommon.actor.cpp @@ -60,9 +60,9 @@ ACTOR Future handleInitVersionBatchRequest(RestoreVersionBatchRequest req, // batchId is continuous. (req.batchIndex-1) is the id of the just finished batch. wait(self->versionBatchId.whenAtLeast(req.batchIndex - 1)); - self->setVersionBatchState(req.batchIndex, ApplierVersionBatchState::INIT); if (self->versionBatchId.get() == req.batchIndex - 1) { self->initVersionBatch(req.batchIndex); + self->setVersionBatchState(req.batchIndex, ApplierVersionBatchState::INIT); TraceEvent("FastRestoreInitVersionBatch") .detail("BatchIndex", req.batchIndex) .detail("Role", getRoleStr(self->role)) From 89b121ae25474984ed9a55730ad8452f4b7a3e4e Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Fri, 28 Feb 2020 00:08:01 -0800 Subject: [PATCH 0770/1604] FastRestore:Fix duplicate type in traceRoleVersionBatchProgress --- fdbserver/RestoreRoleCommon.actor.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fdbserver/RestoreRoleCommon.actor.cpp b/fdbserver/RestoreRoleCommon.actor.cpp index 724bb0d7d5..fa2f159abf 100644 --- a/fdbserver/RestoreRoleCommon.actor.cpp +++ b/fdbserver/RestoreRoleCommon.actor.cpp @@ -144,12 +144,11 @@ ACTOR Future traceRoleVersionBatchProgress(Reference self int batchIndex = self->finishedBatch.get(); int maxBatchIndex = self->versionBatchId.get(); - TraceEvent ev("FastRestoreVersionBatchProgress", self->nodeID); + TraceEvent ev("FastRestoreVersionBatchProgressState", self->nodeID); ev.detail("Role", role); ev.detail("Node", self->nodeID); while (batchIndex <= maxBatchIndex) { - ev.detail("BatchIndex", batchIndex); - ev.detail("VersionBatchState", self->getVersionBatchState(batchIndex)); + ev.detail("VersionBatch" + batchIndex, self->getVersionBatchState(batchIndex)); batchIndex++; } From 1be1145bc6f456b49e49d6a8b7073aa604aec0b3 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Fri, 28 Feb 2020 00:23:58 -0800 Subject: [PATCH 0771/1604] FastRestore:Fix corrupted trace event in FastRestoreVersionBatchProgressState --- fdbserver/RestoreRoleCommon.actor.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fdbserver/RestoreRoleCommon.actor.cpp b/fdbserver/RestoreRoleCommon.actor.cpp index fa2f159abf..6292da70ee 100644 --- a/fdbserver/RestoreRoleCommon.actor.cpp +++ b/fdbserver/RestoreRoleCommon.actor.cpp @@ -145,10 +145,11 @@ ACTOR Future traceRoleVersionBatchProgress(Reference self int maxBatchIndex = self->versionBatchId.get(); TraceEvent ev("FastRestoreVersionBatchProgressState", self->nodeID); - ev.detail("Role", role); - ev.detail("Node", self->nodeID); + ev.detail("Role", role).detail("Node", self->nodeID).detail("FinishedBatch", batchIndex).detail("InitializedBatch", maxBatchIndex); while (batchIndex <= maxBatchIndex) { - ev.detail("VersionBatch" + batchIndex, self->getVersionBatchState(batchIndex)); + std::stringstream typeName; + typeName << "VersionBatch" << batchIndex; + ev.detail(typeName.str(), self->getVersionBatchState(batchIndex)); batchIndex++; } From d1e1fea42dee01aefda8410d3a4030f3c1acfbdb Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Fri, 28 Feb 2020 09:35:21 -0800 Subject: [PATCH 0772/1604] Our binaries that act like clients (fdbcli, backup and DR binaries) were reporting an unknown client version. Clients did not react if the list of supported versions changed. --- fdbclient/ClusterInterface.h | 1 + fdbclient/MonitorLeader.actor.cpp | 6 +++--- fdbclient/MonitorLeader.h | 2 +- fdbclient/NativeAPI.actor.cpp | 22 +++++++++++++++++++--- fdbclient/NativeAPI.actor.h | 9 ++------- flow/genericactors.actor.h | 30 ++++++++++++++++++++++++++++++ 6 files changed, 56 insertions(+), 14 deletions(-) diff --git a/fdbclient/ClusterInterface.h b/fdbclient/ClusterInterface.h index b0724e2b57..8e2839cfbb 100644 --- a/fdbclient/ClusterInterface.h +++ b/fdbclient/ClusterInterface.h @@ -93,6 +93,7 @@ struct ClientVersionRef { } ClientVersionRef(Arena &arena, ClientVersionRef const& cv) : clientVersion(arena, cv.clientVersion), sourceVersion(arena, cv.sourceVersion), protocolVersion(arena, cv.protocolVersion) {} + ClientVersionRef(StringRef clientVersion, StringRef sourceVersion, StringRef protocolVersion) : clientVersion(clientVersion), sourceVersion(sourceVersion), protocolVersion(protocolVersion) {} ClientVersionRef(std::string versionString) { size_t index = versionString.find(","); if(index == versionString.npos) { diff --git a/fdbclient/MonitorLeader.actor.cpp b/fdbclient/MonitorLeader.actor.cpp index a7364a77b0..6c0e5a0c65 100644 --- a/fdbclient/MonitorLeader.actor.cpp +++ b/fdbclient/MonitorLeader.actor.cpp @@ -691,7 +691,7 @@ void shrinkProxyList( ClientDBInfo& ni, std::vector& lastProxyUIDs, std::ve } // Leader is the process that will be elected by coordinators as the cluster controller -ACTOR Future monitorProxiesOneGeneration( Reference connFile, Reference> clientInfo, MonitorLeaderInfo info, Standalone> supportedVersions, Key traceLogGroup) { +ACTOR Future monitorProxiesOneGeneration( Reference connFile, Reference> clientInfo, MonitorLeaderInfo info, Reference>>> supportedVersions, Key traceLogGroup) { state ClusterConnectionString cs = info.intermediateConnFile->getConnectionString(); state vector addrs = cs.coordinators(); state int idx = 0; @@ -707,7 +707,7 @@ ACTOR Future monitorProxiesOneGeneration( Referenceget().id; - req.supportedVersions = supportedVersions; + req.supportedVersions = supportedVersions->get(); req.traceLogGroup = traceLogGroup; ClusterConnectionString fileConnectionString; @@ -760,7 +760,7 @@ ACTOR Future monitorProxiesOneGeneration( Reference monitorProxies( Reference>> connFile, Reference> clientInfo, Standalone> supportedVersions, Key traceLogGroup ) { +ACTOR Future monitorProxies( Reference>> connFile, Reference> clientInfo, Reference>>> supportedVersions, Key traceLogGroup ) { state MonitorLeaderInfo info(connFile->get()); loop { choose { diff --git a/fdbclient/MonitorLeader.h b/fdbclient/MonitorLeader.h index 0eae5151f7..3843847de7 100644 --- a/fdbclient/MonitorLeader.h +++ b/fdbclient/MonitorLeader.h @@ -57,7 +57,7 @@ Future monitorLeader( Reference const& connFile, Re Future monitorLeaderForProxies( Value const& key, vector const& coordinators, ClientData* const& clientData ); -Future monitorProxies( Reference>> const& connFile, Reference> const& clientInfo, Standalone> const& supportedVersions, Key const& traceLogGroup ); +Future monitorProxies( Reference>> const& connFile, Reference> const& clientInfo, Reference>>> const& supportedVersions, Key const& traceLogGroup ); void shrinkProxyList( ClientDBInfo& ni, std::vector& lastProxyUIDs, std::vector& lastProxies ); diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index e30390cf22..b1a51ae009 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -78,6 +78,21 @@ static void initTLSPolicy() { #endif } +// The default values, TRACE_DEFAULT_ROLL_SIZE and TRACE_DEFAULT_MAX_LOGS_SIZE are located in Trace.h. +NetworkOptions::NetworkOptions() + : localAddress(""), clusterFile(""), traceDirectory(Optional()), + traceRollSize(TRACE_DEFAULT_ROLL_SIZE), traceMaxLogsSize(TRACE_DEFAULT_MAX_LOGS_SIZE), traceLogGroup("default"), + traceFormat("xml"), slowTaskProfilingEnabled(false) { + + Standalone> defaultSupportedVersions; + + StringRef sourceVersion = StringRef((const uint8_t*)getHGVersion(), strlen(getHGVersion())); + std::string protocolVersionString = format("%llx", currentProtocolVersion.version()); + defaultSupportedVersions.push_back_deep(defaultSupportedVersions.arena(), ClientVersionRef(LiteralStringRef(FDB_VT_VERSION), sourceVersion, protocolVersionString)); + + supportedVersions = ReferencedObject>>::from(defaultSupportedVersions); +} + static const Key CLIENT_LATENCY_INFO_PREFIX = LiteralStringRef("client_latency/"); static const Key CLIENT_LATENCY_INFO_CTR_PREFIX = LiteralStringRef("client_latency_counter/"); @@ -960,18 +975,19 @@ void setNetworkOption(FDBNetworkOptions::Option option, Optional valu ASSERT(g_network); ASSERT(value.present()); - networkOptions.supportedVersions.resize(networkOptions.supportedVersions.arena(), 0); + Standalone> supportedVersions; std::string versionString = value.get().toString(); size_t index = 0; size_t nextIndex = 0; while(nextIndex != versionString.npos) { nextIndex = versionString.find(';', index); - networkOptions.supportedVersions.push_back_deep(networkOptions.supportedVersions.arena(), ClientVersionRef(versionString.substr(index, nextIndex-index))); + supportedVersions.push_back_deep(supportedVersions.arena(), ClientVersionRef(versionString.substr(index, nextIndex-index))); index = nextIndex + 1; } - ASSERT(networkOptions.supportedVersions.size() > 0); + ASSERT(supportedVersions.size() > 0); + networkOptions.supportedVersions->set(supportedVersions); break; } diff --git a/fdbclient/NativeAPI.actor.h b/fdbclient/NativeAPI.actor.h index f02be1b5b3..50f6591386 100644 --- a/fdbclient/NativeAPI.actor.h +++ b/fdbclient/NativeAPI.actor.h @@ -25,7 +25,6 @@ #elif !defined(FDBCLIENT_NATIVEAPI_ACTOR_H) #define FDBCLIENT_NATIVEAPI_ACTOR_H - #include "flow/flow.h" #include "flow/TDMetric.actor.h" #include "fdbclient/FDBTypes.h" @@ -59,14 +58,10 @@ struct NetworkOptions { std::string traceLogGroup; std::string traceFormat; Optional logClientInfo; - Standalone> supportedVersions; + Reference>>> supportedVersions; bool slowTaskProfilingEnabled; - // The default values, TRACE_DEFAULT_ROLL_SIZE and TRACE_DEFAULT_MAX_LOGS_SIZE are located in Trace.h. - NetworkOptions() - : localAddress(""), clusterFile(""), traceDirectory(Optional()), - traceRollSize(TRACE_DEFAULT_ROLL_SIZE), traceMaxLogsSize(TRACE_DEFAULT_MAX_LOGS_SIZE), traceLogGroup("default"), - traceFormat("xml"), slowTaskProfilingEnabled(false) {} + NetworkOptions(); }; class Database { diff --git a/flow/genericactors.actor.h b/flow/genericactors.actor.h index 7365220e97..e2f9eda32f 100644 --- a/flow/genericactors.actor.h +++ b/flow/genericactors.actor.h @@ -647,6 +647,36 @@ protected: } }; +template +class ReferencedObject : NonCopyable, public ReferenceCounted> { + public: + ReferencedObject() : value() {} + ReferencedObject(V const& v) : value(v) {} + ReferencedObject(ReferencedObject&& r) : value(std::move(r.value)) {} + void operator=(ReferencedObject&& r) { + value = std::move(r.value); + } + + V const& get() const { + return value; + } + + V& mutate() const { + return value; + } + + void set(V const& v) { + value = v; + } + + static Reference> from(V v) { + return Reference>(new ReferencedObject(v)); + } + + private: + V value; +}; + template class AsyncVar : NonCopyable, public ReferenceCounted> { public: From fd180e242ded25a3c0094f425df7c18ac50a2870 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Fri, 28 Feb 2020 11:10:42 -0800 Subject: [PATCH 0773/1604] Fix heap buffer overflow in mako --- bindings/c/test/mako/mako.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/c/test/mako/mako.c b/bindings/c/test/mako/mako.c index 9b49e57fc3..83821ceffd 100755 --- a/bindings/c/test/mako/mako.c +++ b/bindings/c/test/mako/mako.c @@ -1711,7 +1711,7 @@ int main(int argc, char *argv[]) { shm->readycount = 0; /* fork worker processes */ - worker_pids = (pid_t *)calloc(sizeof(pid_t), args.num_processes); + worker_pids = (pid_t*)calloc(sizeof(pid_t), args.num_processes + 1); if (!worker_pids) { fprintf(stderr, "ERROR: cannot allocate worker_pids (%d processes)\n", args.num_processes); From f29d6c3f674caa1b02c0ea4835e3a6826834a70a Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Fri, 28 Feb 2020 12:33:57 -0800 Subject: [PATCH 0774/1604] Move implementation of ArenaBlock members to Arena.cpp --- flow/Arena.cpp | 274 ++++++++++++++++++++++++++++++++++++++ flow/Arena.h | 196 +++------------------------ flow/CMakeLists.txt | 1 + flow/flow.vcxproj | 1 + flow/flow.vcxproj.filters | 1 + 5 files changed, 294 insertions(+), 179 deletions(-) create mode 100644 flow/Arena.cpp diff --git a/flow/Arena.cpp b/flow/Arena.cpp new file mode 100644 index 0000000000..7701c13e91 --- /dev/null +++ b/flow/Arena.cpp @@ -0,0 +1,274 @@ +/* + * Arena.cpp + * + * This source file is part of the FoundationDB open source project + * + * Copyright 2013-2020 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 "Arena.h" + +void ArenaBlock::delref() { + if (delref_no_destroy()) destroy(); +} + +bool ArenaBlock::isTiny() const { + return tinySize != NOT_TINY; +} +int ArenaBlock::size() const { + if (isTiny()) + return tinySize; + else + return bigSize; +} +int ArenaBlock::used() const { + if (isTiny()) + return tinyUsed; + else + return bigUsed; +} +int ArenaBlock::unused() const { + if (isTiny()) + return tinySize - tinyUsed; + else + return bigSize - bigUsed; +} +const void* ArenaBlock::getData() const { + return this; +} +const void* ArenaBlock::getNextData() const { + return (const uint8_t*)getData() + used(); +} +size_t ArenaBlock::totalSize() { + if (isTiny()) return size(); + + size_t s = size(); + int o = nextBlockOffset; + while (o) { + ArenaBlockRef* r = (ArenaBlockRef*)((char*)getData() + o); + s += r->next->totalSize(); + o = r->nextBlockOffset; + } + return s; +} +// just for debugging: +void ArenaBlock::getUniqueBlocks(std::set& a) { + a.insert(this); + if (isTiny()) return; + + int o = nextBlockOffset; + while (o) { + ArenaBlockRef* r = (ArenaBlockRef*)((char*)getData() + o); + r->next->getUniqueBlocks(a); + o = r->nextBlockOffset; + } + return; +} + +int ArenaBlock::addUsed(int bytes) { + if (isTiny()) { + int t = tinyUsed; + tinyUsed += bytes; + return t; + } else { + int t = bigUsed; + bigUsed += bytes; + return t; + } +} + +void ArenaBlock::makeReference(ArenaBlock* next) { + ArenaBlockRef* r = (ArenaBlockRef*)((char*)getData() + bigUsed); + r->next = next; + r->nextBlockOffset = nextBlockOffset; + nextBlockOffset = bigUsed; + bigUsed += sizeof(ArenaBlockRef); +} + +void ArenaBlock::dependOn(Reference& self, ArenaBlock* other) { + other->addref(); + if (!self || self->isTiny() || self->unused() < sizeof(ArenaBlockRef)) + create(SMALL, self)->makeReference(other); + else + self->makeReference(other); +} + +void* ArenaBlock::allocate(Reference& self, int bytes) { + ArenaBlock* b = self.getPtr(); + if (!self || self->unused() < bytes) b = create(bytes, self); + + return (char*)b->getData() + b->addUsed(bytes); +} + +// Return an appropriately-sized ArenaBlock to store the given data +ArenaBlock* ArenaBlock::create(int dataSize, Reference& next) { + ArenaBlock* b; + if (dataSize <= SMALL - TINY_HEADER && !next) { + if (dataSize <= 16 - TINY_HEADER) { + b = (ArenaBlock*)FastAllocator<16>::allocate(); + b->tinySize = 16; + INSTRUMENT_ALLOCATE("Arena16"); + } else if (dataSize <= 32 - TINY_HEADER) { + b = (ArenaBlock*)FastAllocator<32>::allocate(); + b->tinySize = 32; + INSTRUMENT_ALLOCATE("Arena32"); + } else { + b = (ArenaBlock*)FastAllocator<64>::allocate(); + b->tinySize = 64; + INSTRUMENT_ALLOCATE("Arena64"); + } + b->tinyUsed = TINY_HEADER; + + } else { + int reqSize = dataSize + sizeof(ArenaBlock); + if (next) reqSize += sizeof(ArenaBlockRef); + + if (reqSize < LARGE) { + // Each block should be larger than the previous block, up to a limit, to minimize allocations + // Worst-case allocation pattern: 1 +10 +17 +42 +67 +170 +323 +681 +1348 +2728 +2210 +2211 (+1K +3K+1 +4K)* + // Overhead: 4X for small arenas, 3X intermediate, 1.33X for large arenas + int prevSize = next ? next->size() : 0; + reqSize = std::max(reqSize, std::min(prevSize * 2, std::max(LARGE - 1, reqSize * 4))); + } + + if (reqSize < LARGE) { + if (reqSize <= 128) { + b = (ArenaBlock*)FastAllocator<128>::allocate(); + b->bigSize = 128; + INSTRUMENT_ALLOCATE("Arena128"); + } else if (reqSize <= 256) { + b = (ArenaBlock*)FastAllocator<256>::allocate(); + b->bigSize = 256; + INSTRUMENT_ALLOCATE("Arena256"); + } else if (reqSize <= 512) { + b = (ArenaBlock*)FastAllocator<512>::allocate(); + b->bigSize = 512; + INSTRUMENT_ALLOCATE("Arena512"); + } else if (reqSize <= 1024) { + b = (ArenaBlock*)FastAllocator<1024>::allocate(); + b->bigSize = 1024; + INSTRUMENT_ALLOCATE("Arena1024"); + } else if (reqSize <= 2048) { + b = (ArenaBlock*)FastAllocator<2048>::allocate(); + b->bigSize = 2048; + INSTRUMENT_ALLOCATE("Arena2048"); + } else if (reqSize <= 4096) { + b = (ArenaBlock*)FastAllocator<4096>::allocate(); + b->bigSize = 4096; + INSTRUMENT_ALLOCATE("Arena4096"); + } else { + b = (ArenaBlock*)FastAllocator<8192>::allocate(); + b->bigSize = 8192; + INSTRUMENT_ALLOCATE("Arena8192"); + } + b->tinySize = b->tinyUsed = NOT_TINY; + b->bigUsed = sizeof(ArenaBlock); + } else { +#ifdef ALLOC_INSTRUMENTATION + allocInstr["ArenaHugeKB"].alloc((reqSize + 1023) >> 10); +#endif + b = (ArenaBlock*)new uint8_t[reqSize]; + b->tinySize = b->tinyUsed = NOT_TINY; + b->bigSize = reqSize; + b->bigUsed = sizeof(ArenaBlock); + + if (FLOW_KNOBS && g_trace_depth == 0 && + nondeterministicRandom()->random01() < (reqSize / FLOW_KNOBS->HUGE_ARENA_LOGGING_BYTES)) { + hugeArenaSample(reqSize); + } + g_hugeArenaMemory.fetch_add(reqSize); + + // If the new block has less free space than the old block, make the old block depend on it + if (next && !next->isTiny() && next->unused() >= reqSize - dataSize) { + b->nextBlockOffset = 0; + b->setrefCountUnsafe(1); + next->makeReference(b); + return b; + } + } + b->nextBlockOffset = 0; + if (next) b->makeReference(next.getPtr()); + } + b->setrefCountUnsafe(1); + next.setPtrUnsafe(b); + return b; +} + +void ArenaBlock::destroy() { + // If the stack never contains more than one item, nothing will be allocated from stackArena. + // If stackArena is used, it will always be a linked list, so destroying *it* will not create another arena + ArenaBlock* tinyStack = this; + Arena stackArena; + VectorRef stack(&tinyStack, 1); + + while (stack.size()) { + ArenaBlock* b = stack.end()[-1]; + stack.pop_back(); + + if (!b->isTiny()) { + int o = b->nextBlockOffset; + while (o) { + ArenaBlockRef* br = (ArenaBlockRef*)((char*)b->getData() + o); + if (br->next->delref_no_destroy()) stack.push_back(stackArena, br->next); + o = br->nextBlockOffset; + } + } + b->destroyLeaf(); + } +} + +void ArenaBlock::destroyLeaf() { + if (isTiny()) { + if (tinySize <= 16) { + FastAllocator<16>::release(this); + INSTRUMENT_RELEASE("Arena16"); + } else if (tinySize <= 32) { + FastAllocator<32>::release(this); + INSTRUMENT_RELEASE("Arena32"); + } else { + FastAllocator<64>::release(this); + INSTRUMENT_RELEASE("Arena64"); + } + } else { + if (bigSize <= 128) { + FastAllocator<128>::release(this); + INSTRUMENT_RELEASE("Arena128"); + } else if (bigSize <= 256) { + FastAllocator<256>::release(this); + INSTRUMENT_RELEASE("Arena256"); + } else if (bigSize <= 512) { + FastAllocator<512>::release(this); + INSTRUMENT_RELEASE("Arena512"); + } else if (bigSize <= 1024) { + FastAllocator<1024>::release(this); + INSTRUMENT_RELEASE("Arena1024"); + } else if (bigSize <= 2048) { + FastAllocator<2048>::release(this); + INSTRUMENT_RELEASE("Arena2048"); + } else if (bigSize <= 4096) { + FastAllocator<4096>::release(this); + INSTRUMENT_RELEASE("Arena4096"); + } else if (bigSize <= 8192) { + FastAllocator<8192>::release(this); + INSTRUMENT_RELEASE("Arena8192"); + } else { +#ifdef ALLOC_INSTRUMENTATION + allocInstr["ArenaHugeKB"].dealloc((bigSize + 1023) >> 10); +#endif + g_hugeArenaMemory.fetch_sub(bigSize); + delete[](uint8_t*) this; + } + } +} diff --git a/flow/Arena.h b/flow/Arena.h index 7181bdef79..8cb2f25f96 100644 --- a/flow/Arena.h +++ b/flow/Arena.h @@ -143,166 +143,27 @@ struct ArenaBlock : NonCopyable, ThreadSafeReferenceCounted uint32_t bigSize, bigUsed; // include block header uint32_t nextBlockOffset; - void delref() { - if (delref_no_destroy()) - destroy(); - } - - bool isTiny() const { return tinySize != NOT_TINY; } - int size() const { if (isTiny()) return tinySize; else return bigSize; } - int used() const { if (isTiny()) return tinyUsed; else return bigUsed; } - inline int unused() const { if (isTiny()) return tinySize-tinyUsed; else return bigSize-bigUsed; } - const void* getData() const { return this; } - const void* getNextData() const { return (const uint8_t*)getData() + used(); } - size_t totalSize() { - if (isTiny()) return size(); - - size_t s = size(); - int o = nextBlockOffset; - while (o) { - ArenaBlockRef* r = (ArenaBlockRef*)((char*)getData() + o); - s += r->next->totalSize(); - o = r->nextBlockOffset; - } - return s; - } + void delref(); + bool isTiny() const; + int size() const; + int used() const; + int unused() const; + const void* getData() const; + const void* getNextData() const; + size_t totalSize(); // just for debugging: - void getUniqueBlocks(std::set& a) { - a.insert(this); - if (isTiny()) return; - - int o = nextBlockOffset; - while (o) { - ArenaBlockRef* r = (ArenaBlockRef*)((char*)getData() + o); - r->next->getUniqueBlocks(a); - o = r->nextBlockOffset; - } - return; - } - - inline int addUsed( int bytes ) { - if (isTiny()) { - int t = tinyUsed; - tinyUsed += bytes; - return t; - } else { - int t = bigUsed; - bigUsed += bytes; - return t; - } - } - - void makeReference( ArenaBlock* next ) { - ArenaBlockRef* r = (ArenaBlockRef*)((char*)getData() + bigUsed); - r->next = next; - r->nextBlockOffset = nextBlockOffset; - nextBlockOffset = bigUsed; - bigUsed += sizeof(ArenaBlockRef); - } - - static void dependOn( Reference& self, ArenaBlock* other ) { - other->addref(); - if (!self || self->isTiny() || self->unused() < sizeof(ArenaBlockRef)) - create( SMALL, self )->makeReference(other); - else - self->makeReference( other ); - } - - static inline void* allocate( Reference& self, int bytes ) { - ArenaBlock* b = self.getPtr(); - if (!self || self->unused() < bytes) - b = create( bytes, self ); - - return (char*)b->getData() + b->addUsed(bytes); - } - + void getUniqueBlocks(std::set& a); + int addUsed(int bytes); + void makeReference(ArenaBlock* next); + static void dependOn(Reference& self, ArenaBlock* other); + static void* allocate(Reference& self, int bytes); // Return an appropriately-sized ArenaBlock to store the given data - static ArenaBlock* create( int dataSize, Reference& next ) { - ArenaBlock* b; - if (dataSize <= SMALL-TINY_HEADER && !next) { - if (dataSize <= 16-TINY_HEADER) { b = (ArenaBlock*)FastAllocator<16>::allocate(); b->tinySize = 16; INSTRUMENT_ALLOCATE("Arena16"); } - else if (dataSize <= 32-TINY_HEADER) { b = (ArenaBlock*)FastAllocator<32>::allocate(); b->tinySize = 32; INSTRUMENT_ALLOCATE("Arena32"); } - else { b = (ArenaBlock*)FastAllocator<64>::allocate(); b->tinySize=64; INSTRUMENT_ALLOCATE("Arena64"); } - b->tinyUsed = TINY_HEADER; + static ArenaBlock* create(int dataSize, Reference& next); + void destroy(); + void destroyLeaf(); - } else { - int reqSize = dataSize + sizeof(ArenaBlock); - if (next) reqSize += sizeof(ArenaBlockRef); - - if (reqSize < LARGE) { - // Each block should be larger than the previous block, up to a limit, to minimize allocations - // Worst-case allocation pattern: 1 +10 +17 +42 +67 +170 +323 +681 +1348 +2728 +2210 +2211 (+1K +3K+1 +4K)* - // Overhead: 4X for small arenas, 3X intermediate, 1.33X for large arenas - int prevSize = next ? next->size() : 0; - reqSize = std::max( reqSize, std::min( prevSize*2, std::max( LARGE-1, reqSize*4 ) ) ); - } - - if (reqSize < LARGE) { - if (reqSize <= 128) { b = (ArenaBlock*)FastAllocator<128>::allocate(); b->bigSize = 128; INSTRUMENT_ALLOCATE("Arena128"); } - else if (reqSize <= 256) { b = (ArenaBlock*)FastAllocator<256>::allocate(); b->bigSize = 256; INSTRUMENT_ALLOCATE("Arena256"); } - else if (reqSize <= 512) { b = (ArenaBlock*)FastAllocator<512>::allocate(); b->bigSize = 512; INSTRUMENT_ALLOCATE("Arena512"); } - else if (reqSize <= 1024) { b = (ArenaBlock*)FastAllocator<1024>::allocate(); b->bigSize = 1024; INSTRUMENT_ALLOCATE("Arena1024"); } - else if (reqSize <= 2048) { b = (ArenaBlock*)FastAllocator<2048>::allocate(); b->bigSize = 2048; INSTRUMENT_ALLOCATE("Arena2048"); } - else if (reqSize <= 4096) { b = (ArenaBlock*)FastAllocator<4096>::allocate(); b->bigSize = 4096; INSTRUMENT_ALLOCATE("Arena4096"); } - else { b = (ArenaBlock*)FastAllocator<8192>::allocate(); b->bigSize = 8192; INSTRUMENT_ALLOCATE("Arena8192"); } - b->tinySize = b->tinyUsed = NOT_TINY; - b->bigUsed = sizeof(ArenaBlock); - } else { - #ifdef ALLOC_INSTRUMENTATION - allocInstr[ "ArenaHugeKB" ].alloc( (reqSize+1023)>>10 ); - #endif - b = (ArenaBlock*)new uint8_t[ reqSize ]; - b->tinySize = b->tinyUsed = NOT_TINY; - b->bigSize = reqSize; - b->bigUsed = sizeof(ArenaBlock); - - if(FLOW_KNOBS && g_trace_depth == 0 && nondeterministicRandom()->random01() < (reqSize / FLOW_KNOBS->HUGE_ARENA_LOGGING_BYTES)) { - hugeArenaSample(reqSize); - } - g_hugeArenaMemory.fetch_add(reqSize); - - // If the new block has less free space than the old block, make the old block depend on it - if (next && !next->isTiny() && next->unused() >= reqSize-dataSize) { - b->nextBlockOffset = 0; - b->setrefCountUnsafe(1); - next->makeReference(b); - return b; - } - } - b->nextBlockOffset = 0; - if (next) b->makeReference(next.getPtr()); - } - b->setrefCountUnsafe(1); - next.setPtrUnsafe(b); - return b; - } - - inline void destroy(); - - void destroyLeaf() { - if (isTiny()) { - if (tinySize <= 16) { FastAllocator<16>::release(this); INSTRUMENT_RELEASE("Arena16");} - else if (tinySize <= 32) { FastAllocator<32>::release(this); INSTRUMENT_RELEASE("Arena32"); } - else { FastAllocator<64>::release(this); INSTRUMENT_RELEASE("Arena64"); } - } else { - if (bigSize <= 128) { FastAllocator<128>::release(this); INSTRUMENT_RELEASE("Arena128"); } - else if (bigSize <= 256) { FastAllocator<256>::release(this); INSTRUMENT_RELEASE("Arena256"); } - else if (bigSize <= 512) { FastAllocator<512>::release(this); INSTRUMENT_RELEASE("Arena512"); } - else if (bigSize <= 1024) { FastAllocator<1024>::release(this); INSTRUMENT_RELEASE("Arena1024"); } - else if (bigSize <= 2048) { FastAllocator<2048>::release(this); INSTRUMENT_RELEASE("Arena2048"); } - else if (bigSize <= 4096) { FastAllocator<4096>::release(this); INSTRUMENT_RELEASE("Arena4096"); } - else if (bigSize <= 8192) { FastAllocator<8192>::release(this); INSTRUMENT_RELEASE("Arena8192"); } - else { - #ifdef ALLOC_INSTRUMENTATION - allocInstr[ "ArenaHugeKB" ].dealloc( (bigSize+1023)>>10 ); - #endif - g_hugeArenaMemory.fetch_sub(bigSize); - delete[] (uint8_t*)this; - } - } - } private: - static void* operator new(size_t s); // not implemented + static void* operator new(size_t s); // not implemented }; inline Arena::Arena() : impl( NULL ) {} @@ -1200,28 +1061,5 @@ struct dynamic_size_traits> : std::true_typ } }; - void ArenaBlock::destroy() { - // If the stack never contains more than one item, nothing will be allocated from stackArena. - // If stackArena is used, it will always be a linked list, so destroying *it* will not create another arena - ArenaBlock* tinyStack = this; - Arena stackArena; - VectorRef stack( &tinyStack, 1 ); - - while (stack.size()) { - ArenaBlock* b = stack.end()[-1]; - stack.pop_back(); - - if (!b->isTiny()) { - int o = b->nextBlockOffset; - while (o) { - ArenaBlockRef* br = (ArenaBlockRef*)((char*)b->getData() + o); - if (br->next->delref_no_destroy()) - stack.push_back( stackArena, br->next ); - o = br->nextBlockOffset; - } - } - b->destroyLeaf(); - } -} #endif diff --git a/flow/CMakeLists.txt b/flow/CMakeLists.txt index 4af884bb8d..f102ee3a71 100644 --- a/flow/CMakeLists.txt +++ b/flow/CMakeLists.txt @@ -3,6 +3,7 @@ find_package(Threads REQUIRED) set(FLOW_SRCS ActorCollection.actor.cpp ActorCollection.h + Arena.cpp Arena.h AsioReactor.h CompressedInt.actor.cpp diff --git a/flow/flow.vcxproj b/flow/flow.vcxproj index 3a2cc7fd3b..768eb628ff 100644 --- a/flow/flow.vcxproj +++ b/flow/flow.vcxproj @@ -55,6 +55,7 @@ + diff --git a/flow/flow.vcxproj.filters b/flow/flow.vcxproj.filters index 5c65688675..7f5039df02 100644 --- a/flow/flow.vcxproj.filters +++ b/flow/flow.vcxproj.filters @@ -43,6 +43,7 @@ + From e6d36a0aa5c4d0eec133bd3719e0c63ffc5f4c3b Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Fri, 28 Feb 2020 13:16:58 -0800 Subject: [PATCH 0775/1604] Fix Makefile build --- flow/flow.vcxproj | 2 +- flow/flow.vcxproj.filters | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/flow/flow.vcxproj b/flow/flow.vcxproj index 768eb628ff..3fb6b0a517 100644 --- a/flow/flow.vcxproj +++ b/flow/flow.vcxproj @@ -13,6 +13,7 @@ + @@ -55,7 +56,6 @@ - diff --git a/flow/flow.vcxproj.filters b/flow/flow.vcxproj.filters index 7f5039df02..ae1b080c4e 100644 --- a/flow/flow.vcxproj.filters +++ b/flow/flow.vcxproj.filters @@ -14,6 +14,7 @@ + @@ -43,7 +44,6 @@ - From 1ec55f21f707330edef34b75a9cf85ae46619ac4 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Fri, 28 Feb 2020 14:25:19 -0800 Subject: [PATCH 0776/1604] Add some comments for documentation; strengthen an assert. --- fdbserver/MasterProxyServer.actor.cpp | 29 +++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/fdbserver/MasterProxyServer.actor.cpp b/fdbserver/MasterProxyServer.actor.cpp index 2aad5f149b..b973c2e818 100644 --- a/fdbserver/MasterProxyServer.actor.cpp +++ b/fdbserver/MasterProxyServer.actor.cpp @@ -107,7 +107,15 @@ struct TransactionRateInfo { TransactionRateInfo(double rate) : rate(rate), limit(0), budget(0), disabled(true), smoothRate(SERVER_KNOBS->START_TRANSACTION_RATE_WINDOW), smoothReleased(SERVER_KNOBS->START_TRANSACTION_RATE_WINDOW) {} - void reset(double elapsed) { + void reset() { + // Determine the number of transactions that this proxy is allowed to release + // Roughly speaking, this is done by computing the number of transactions over some historical window that we could + // have started but didn't, and making that our limit. More precisely, we track a smoothed rate limit and release rate, + // the difference of which is the rate of additional transactions that we could have released based on that window. + // Then we multiply by the window size to get a number of transactions. + // + // Limit can be negative in the event that we are releasing more transactions than we are allowed (due to the use of + // our budget or because of higher priority transactions). double releaseRate = smoothRate.smoothTotal() - smoothReleased.smoothRate(); limit = SERVER_KNOBS->START_TRANSACTION_RATE_WINDOW * releaseRate; } @@ -117,8 +125,21 @@ struct TransactionRateInfo { } void updateBudget(int64_t numStartedAtPriority, bool queueEmptyAtPriority, double elapsed) { + // Update the budget to accumulate any extra capacity available or remove any excess that was used. + // The actual delta is the portion of the limit we didn't use multiplied by the fraction of the window that elapsed. + // + // We may have exceeded our limit due to the budget or because of higher priority transactions, in which case this + // delta will be negative. The delta can also be negative in the event that our limit was negative, which can happen + // if we had already started more transactions in our window than our rate would have allowed. + // + // This budget has the property that when the budget is required to start transactions (because batches are big), + // the sum limit+budget will increase linearly from 0 to the batch size over time and decrease by the batch size + // upon starting a batch. In other words, this works equivalently to a model where we linearly accumulate budget over + // time in the case that our batches are too big to take advantage of the window based limits. budget = std::max(0.0, budget + elapsed * (limit - numStartedAtPriority) / SERVER_KNOBS->START_TRANSACTION_RATE_WINDOW); + // If we are emptying out the queue of requests, then we don't need to carry much budget forward + // If we did keep accumulating budget, then our responsiveness to changes in workflow could be compromised if(queueEmptyAtPriority) { budget = std::min(budget, SERVER_KNOBS->START_TRANSACTION_MAX_EMPTY_QUEUE_BUDGET); } @@ -133,7 +154,7 @@ struct TransactionRateInfo { } void setRate(double rate) { - ASSERT(rate != std::numeric_limits::infinity()); + ASSERT(rate >= 0 && rate != std::numeric_limits::infinity() && !isnan(rate)); this->rate = rate; if(disabled) { @@ -1300,8 +1321,8 @@ ACTOR static Future transactionStarter( if(elapsed == 0) elapsed = 1e-15; // resolve a possible indeterminant multiplication with infinite transaction rate - normalRateInfo.reset(elapsed); - batchRateInfo.reset(elapsed); + normalRateInfo.reset(); + batchRateInfo.reset(); int transactionsStarted[2] = {0,0}; int systemTransactionsStarted[2] = {0,0}; From 62b9043ff60216ee93b26e9a2032fdf42933c7c0 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Fri, 28 Feb 2020 11:22:53 -0800 Subject: [PATCH 0777/1604] FastRestore:DB can be destroyed before master unlock it in simulation Because retore roles run as workload in simulation, they do not know when DB is destroyed by the backup and restore test workload. So if DB is destroyed earlier than restore master unlocks DB, which is rare, restore master should abort the unlocking DB step. --- fdbserver/Knobs.cpp | 2 +- fdbserver/RestoreApplier.actor.h | 1 + fdbserver/RestoreCommon.actor.h | 6 ++++-- fdbserver/RestoreLoader.actor.cpp | 2 +- fdbserver/RestoreLoader.actor.h | 1 + fdbserver/RestoreMaster.actor.cpp | 25 +++++++++++++++++++------ 6 files changed, 27 insertions(+), 10 deletions(-) diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index a50760febe..6002410a24 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -560,7 +560,7 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs, bool isSimula init( FASTRESTORE_TRACK_LOADER_SEND_REQUESTS, false ); if( randomize && BUGGIFY ) { FASTRESTORE_TRACK_LOADER_SEND_REQUESTS = true; } init( FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT, 6144 ); if( randomize && BUGGIFY ) { FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT = 1; } init( FASTRESTORE_WAIT_FOR_MEMORY_LATENCY, 10 ); if( randomize && BUGGIFY ) { FASTRESTORE_WAIT_FOR_MEMORY_LATENCY = 60; } - init( FASTRESTORE_HEARTBEAT_DELAY, 10 ); if( randomize && BUGGIFY ) { FASTRESTORE_HEARTBEAT_DELAY = deterministicRandom()->random01() * 120; } + init( FASTRESTORE_HEARTBEAT_DELAY, 10 ); if( randomize && BUGGIFY ) { FASTRESTORE_HEARTBEAT_DELAY = deterministicRandom()->random01() * 120; } init( FASTRESTORE_HEARTBEAT_MAX_DELAY, 10 ); if( randomize && BUGGIFY ) { FASTRESTORE_HEARTBEAT_MAX_DELAY = FASTRESTORE_HEARTBEAT_DELAY * 10; } // clang-format on diff --git a/fdbserver/RestoreApplier.actor.h b/fdbserver/RestoreApplier.actor.h index cacbb882bb..4f8d65f5a9 100644 --- a/fdbserver/RestoreApplier.actor.h +++ b/fdbserver/RestoreApplier.actor.h @@ -371,6 +371,7 @@ struct RestoreApplierData : RestoreRoleData, public ReferenceCounted>::iterator item = batch.find(batchIndex); if (item == batch.end()) { + ASSERT_WE_THINK(false); return ApplierVersionBatchState::INVALID; } else { return item->second->vbState.get(); diff --git a/fdbserver/RestoreCommon.actor.h b/fdbserver/RestoreCommon.actor.h index 1baaea1c37..9b2ae89a8a 100644 --- a/fdbserver/RestoreCommon.actor.h +++ b/fdbserver/RestoreCommon.actor.h @@ -303,7 +303,7 @@ Future getBatchReplies(RequestStream Interface::*channel, std::ma for (int i = 0; i < replyDurations.size(); ++i) { double endTime = std::get<2>(replyDurations[i]); TraceEvent(SevInfo, "ProfileSendRequestBatchLatency", bathcID) - .detail("NodeID", std::get<0>(replyDurations[i])) + .detail("Node", std::get<0>(replyDurations[i])) .detail("Request", std::get<1>(replyDurations[i]).toString()) .detail("Duration", endTime - start); auto item = maxEndTime.emplace(std::get<0>(replyDurations[i]), endTime); @@ -327,7 +327,9 @@ Future getBatchReplies(RequestStream Interface::*channel, std::ma if (latest - earliest > SERVER_KNOBS->FASTRESTORE_STRAGGLER_THRESHOLD) { TraceEvent(SevWarn, "ProfileSendRequestBatchLatencyFoundStraggler", bathcID) .detail("SlowestNode", latestNode) - .detail("FatestNode", earliestNode); + .detail("FatestNode", earliestNode) + .detail("EarliestEndtime", earliest) + .detail("LagTime", latest - earliest); } } // Update replies diff --git a/fdbserver/RestoreLoader.actor.cpp b/fdbserver/RestoreLoader.actor.cpp index e9d5a598f4..073d9cbb10 100644 --- a/fdbserver/RestoreLoader.actor.cpp +++ b/fdbserver/RestoreLoader.actor.cpp @@ -202,7 +202,7 @@ ACTOR Future handleLoadFileRequest(RestoreLoadFileRequest req, Reference>::iterator item = batch.find(batchIndex); if (item != batch.end()) { + ASSERT_WE_THINK(false); return LoaderVersionBatchState::INVALID; } else { return item->second->vbState.get(); diff --git a/fdbserver/RestoreMaster.actor.cpp b/fdbserver/RestoreMaster.actor.cpp index 2a9d729387..1f89122227 100644 --- a/fdbserver/RestoreMaster.actor.cpp +++ b/fdbserver/RestoreMaster.actor.cpp @@ -82,10 +82,12 @@ ACTOR Future startRestoreMaster(Reference masterWorker, wait(startProcessRestoreRequests(self, cx)); } catch (Error& e) { - TraceEvent(SevError, "FastRestore") - .detail("StartRestoreMaster", "Unexpectedly unhandled error") - .detail("Error", e.what()) - .detail("ErrorCode", e.code()); + if (e.code() != error_code_operation_cancelled) { + TraceEvent(SevError, "FastRestoreMasterStart") + .detail("Reason", "Unexpected unhandled error") + .detail("ErrorCode", e.code()) + .detail("Error", e.what()); + } } return Void(); @@ -237,8 +239,17 @@ ACTOR Future startProcessRestoreRequests(Reference self try { wait(unlockDatabase(cx, randomUID)); } catch (Error& e) { - TraceEvent(SevError, "FastRestoreMasterUnlockDBFailed", self->id()).detail("UID", randomUID.toString()); - ASSERT_WE_THINK(false); // This unlockDatabase should always succeed, we think. + if (e.code() == error_code_operation_cancelled) { // Should only happen in simulation + TraceEvent(SevWarnAlways, "FastRestoreMasterOnCancelingActor", self->id()) + .detail("DBLock", randomUID) + .detail("ManualCheck", "Is DB locked"); + } else { + TraceEvent(SevError, "FastRestoreMasterUnlockDBFailed", self->id()) + .detail("DBLock", randomUID) + .detail("ErrorCode", e.code()) + .detail("Error", e.what()); + ASSERT_WE_THINK(false); // This unlockDatabase should always succeed, we think. + } } TraceEvent("FastRestoreMasterRestoreCompleted", self->id()); @@ -551,6 +562,8 @@ ACTOR static Future distributeWorkloadPerVersionBatch(ReferenceloadersInterf, batchIndex, false)); wait(sendMutationsFromLoaders(batchData, batchStatus, self->loadersInterf, batchIndex, true)); + // Synchronization point for version batch pipelining. + // self->finishedBatch will continuously increase by 1 per version batch. wait(notifyApplierToApplyMutations(batchData, batchStatus, self->appliersInterf, batchIndex, &self->finishedBatch)); wait(notifyLoadersVersionBatchFinished(self->loadersInterf, batchIndex)); From 7c003bfa704cf459b8a88efa742d46e8bfa62172 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Fri, 28 Feb 2020 14:27:45 -0800 Subject: [PATCH 0778/1604] FastRestore:Disable isSchedulable for test --- fdbserver/RestoreRoleCommon.actor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/fdbserver/RestoreRoleCommon.actor.cpp b/fdbserver/RestoreRoleCommon.actor.cpp index 6292da70ee..174e521052 100644 --- a/fdbserver/RestoreRoleCommon.actor.cpp +++ b/fdbserver/RestoreRoleCommon.actor.cpp @@ -106,6 +106,7 @@ ACTOR Future isSchedulable(Reference self, int actorBatch // Intentionally randomly block actors for low memory reason. // memory will be larger than threshold when deterministicRandom()->random01() > 1/2 memory = SERVER_KNOBS->FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT * 2 * deterministicRandom()->random01(); + memory = 0; } if (memory < SERVER_KNOBS->FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT || self->finishedBatch.get() + 1 == actorBatchIndex) { From d001820219bad2ba3e0065f8a2e592689162a5cd Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Fri, 28 Feb 2020 14:47:11 -0800 Subject: [PATCH 0779/1604] FastRestore:getVersionBatchState can be called before VB is init --- fdbserver/RestoreApplier.actor.h | 5 +++-- fdbserver/RestoreLoader.actor.h | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fdbserver/RestoreApplier.actor.h b/fdbserver/RestoreApplier.actor.h index 4f8d65f5a9..d27a5b2fad 100644 --- a/fdbserver/RestoreApplier.actor.h +++ b/fdbserver/RestoreApplier.actor.h @@ -368,10 +368,11 @@ struct RestoreApplierData : RestoreRoleData, public ReferenceCounted>::iterator item = batch.find(batchIndex); - if (item == batch.end()) { - ASSERT_WE_THINK(false); + if (item == batch.end()) { // Simply caller's effort in when it can call this func. return ApplierVersionBatchState::INVALID; } else { return item->second->vbState.get(); diff --git a/fdbserver/RestoreLoader.actor.h b/fdbserver/RestoreLoader.actor.h index 69a5f6ef67..4e8fbbfbd9 100644 --- a/fdbserver/RestoreLoader.actor.h +++ b/fdbserver/RestoreLoader.actor.h @@ -152,8 +152,7 @@ struct RestoreLoaderData : RestoreRoleData, public ReferenceCounted>::iterator item = batch.find(batchIndex); - if (item != batch.end()) { - ASSERT_WE_THINK(false); + if (item != batch.end()) { // Simply caller's effort in when it can call this func. return LoaderVersionBatchState::INVALID; } else { return item->second->vbState.get(); From c11c24b79dce4e125e6c1292152bdef39e5a6b67 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Fri, 28 Feb 2020 14:56:10 -0800 Subject: [PATCH 0780/1604] removed the fdbrpc version of platform.h --- fdbbackup/backup.actor.cpp | 2 +- fdbcli/fdbcli.actor.cpp | 2 +- fdbclient/BackupContainer.actor.cpp | 2 +- fdbclient/MonitorLeader.actor.cpp | 2 +- fdbrpc/CMakeLists.txt | 1 - fdbrpc/Platform.cpp | 130 ---------------------------- fdbrpc/Platform.h | 37 -------- fdbrpc/fdbrpc.vcxproj | 2 - fdbrpc/fdbrpc.vcxproj.filters | 2 - fdbrpc/sim2.actor.cpp | 4 - fdbserver/fdbserver.actor.cpp | 1 - flow/Platform.cpp | 49 +++++++++++ flow/Platform.h | 5 ++ flow/crc32c.cpp | 2 +- 14 files changed, 59 insertions(+), 182 deletions(-) delete mode 100644 fdbrpc/Platform.cpp delete mode 100644 fdbrpc/Platform.h diff --git a/fdbbackup/backup.actor.cpp b/fdbbackup/backup.actor.cpp index a43ce79578..c6ec3fbd34 100644 --- a/fdbbackup/backup.actor.cpp +++ b/fdbbackup/backup.actor.cpp @@ -36,7 +36,7 @@ #include "fdbclient/BlobStore.h" #include "fdbclient/json_spirit/json_spirit_writer_template.h" -#include "fdbrpc/Platform.h" +#include "flow/Platform.h" #include #include diff --git a/fdbcli/fdbcli.actor.cpp b/fdbcli/fdbcli.actor.cpp index e1420f1cab..14425124ff 100644 --- a/fdbcli/fdbcli.actor.cpp +++ b/fdbcli/fdbcli.actor.cpp @@ -32,7 +32,7 @@ #include "fdbclient/FDBOptions.g.h" #include "flow/DeterministicRandom.h" -#include "fdbrpc/Platform.h" +#include "flow/Platform.h" #include "flow/SimpleOpt.h" diff --git a/fdbclient/BackupContainer.actor.cpp b/fdbclient/BackupContainer.actor.cpp index acc2a85a69..53ddf397df 100644 --- a/fdbclient/BackupContainer.actor.cpp +++ b/fdbclient/BackupContainer.actor.cpp @@ -25,7 +25,7 @@ #include "flow/UnitTest.h" #include "flow/Hash3.h" #include "fdbrpc/AsyncFileReadAhead.actor.h" -#include "fdbrpc/Platform.h" +#include "flow/Platform.h" #include "fdbclient/AsyncFileBlobStore.actor.h" #include "fdbclient/Status.h" #include "fdbclient/SystemData.h" diff --git a/fdbclient/MonitorLeader.actor.cpp b/fdbclient/MonitorLeader.actor.cpp index 90b569941f..9fa873adc5 100644 --- a/fdbclient/MonitorLeader.actor.cpp +++ b/fdbclient/MonitorLeader.actor.cpp @@ -23,7 +23,7 @@ #include "flow/ActorCollection.h" #include "flow/UnitTest.h" #include "fdbrpc/genericactors.actor.h" -#include "fdbrpc/Platform.h" +#include "flow/Platform.h" #include "flow/actorcompiler.h" // has to be last include std::pair< std::string, bool > ClusterConnectionFile::lookupClusterFileName( std::string const& filename ) { diff --git a/fdbrpc/CMakeLists.txt b/fdbrpc/CMakeLists.txt index 89afe57df7..49ff93e554 100644 --- a/fdbrpc/CMakeLists.txt +++ b/fdbrpc/CMakeLists.txt @@ -18,7 +18,6 @@ set(FDBRPC_SRCS Locality.cpp Net2FileSystem.cpp networksender.actor.h - Platform.cpp QueueModel.cpp ReplicationPolicy.cpp ReplicationTypes.cpp diff --git a/fdbrpc/Platform.cpp b/fdbrpc/Platform.cpp deleted file mode 100644 index 12af7491d0..0000000000 --- a/fdbrpc/Platform.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Platform.cpp - * - * This source file is part of the FoundationDB open source project - * - * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "fdbrpc/Platform.h" -#include -#include "flow/ActorCollection.h" -#include "flow/FaultInjection.h" - - -#ifdef _WIN32 -#include -#undef max -#undef min -#include -#include -#include -#include -#include -#include -#pragma comment(lib, "pdh.lib") - -// for SHGetFolderPath -#include -#pragma comment(lib, "Shell32.lib") - -#define CANONICAL_PATH_SEPARATOR '\\' -#endif - -#ifdef __unixish__ -#define CANONICAL_PATH_SEPARATOR '/' - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __APPLE__ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#endif - -#endif - -extern bool onlyBeforeSimulatorInit(); - -namespace platform { - -// Because the lambda used with nftw below cannot capture -int __eraseDirectoryRecurseiveCount; - -int eraseDirectoryRecursive(std::string const& dir) { - __eraseDirectoryRecurseiveCount = 0; -#ifdef _WIN32 - system( ("rd /s /q \"" + dir + "\"").c_str() ); -#elif defined(__linux__) || defined(__APPLE__) - int error = - nftw(dir.c_str(), - [](const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) -> int { - int r = remove(fpath); - if(r == 0) - ++__eraseDirectoryRecurseiveCount; - return r; - }, - 64, FTW_DEPTH | FTW_PHYS); - /* Looks like calling code expects this to continue silently if - the directory we're deleting doesn't exist in the first - place */ - if (error && errno != ENOENT) { - Error e = systemErrorCodeToError(); - TraceEvent(SevError, "EraseDirectoryRecursiveError").detail("Directory", dir).GetLastError().error(e); - throw e; - } -#else -#error Port me! -#endif - //INJECT_FAULT( platform_error, "eraseDirectoryRecursive" ); - return __eraseDirectoryRecurseiveCount; -} - -bool isSse42Supported() -{ -#if defined(_WIN32) - int info[4]; - __cpuid(info, 1); - return (info[2] & (1 << 20)) != 0; -#elif defined(__unixish__) - uint32_t eax, ebx, ecx, edx, level = 1, count = 0; - __cpuid_count(level, count, eax, ebx, ecx, edx); - return ((ecx >> 20) & 1) != 0; -#else - #error Port me! -#endif -} - -} // namespace platform diff --git a/fdbrpc/Platform.h b/fdbrpc/Platform.h deleted file mode 100644 index 8051057fd4..0000000000 --- a/fdbrpc/Platform.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Platform.h - * - * This source file is part of the FoundationDB open source project - * - * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef FDBRPC_UTILS_H -#define FDBRPC_UTILS_H -#pragma once - -#include "flow/Platform.h" -#include - -namespace platform { - -// Avoid in production code: not atomic, not fast, not reliable in all environments -int eraseDirectoryRecursive(std::string const& directory); - -bool isSse42Supported(); - -} // namespace platform - -#endif diff --git a/fdbrpc/fdbrpc.vcxproj b/fdbrpc/fdbrpc.vcxproj index e0f60937d1..e579ab001c 100644 --- a/fdbrpc/fdbrpc.vcxproj +++ b/fdbrpc/fdbrpc.vcxproj @@ -20,7 +20,6 @@ - @@ -84,7 +83,6 @@ - diff --git a/fdbrpc/fdbrpc.vcxproj.filters b/fdbrpc/fdbrpc.vcxproj.filters index ca28e644e9..0c84599d09 100644 --- a/fdbrpc/fdbrpc.vcxproj.filters +++ b/fdbrpc/fdbrpc.vcxproj.filters @@ -70,7 +70,6 @@ zlib - @@ -131,7 +130,6 @@ - diff --git a/fdbrpc/sim2.actor.cpp b/fdbrpc/sim2.actor.cpp index 01411f3c88..2ff9d45531 100644 --- a/fdbrpc/sim2.actor.cpp +++ b/fdbrpc/sim2.actor.cpp @@ -95,10 +95,6 @@ public: }; } -bool onlyBeforeSimulatorInit() { - return g_network->isSimulated() && g_simulator.getAllProcesses().empty(); -} - const UID TOKEN_ENDPOINT_NOT_FOUND(-1, -1); ISimulator* g_pSimulator = 0; diff --git a/fdbserver/fdbserver.actor.cpp b/fdbserver/fdbserver.actor.cpp index 53989f0210..5b7f4959b2 100644 --- a/fdbserver/fdbserver.actor.cpp +++ b/fdbserver/fdbserver.actor.cpp @@ -52,7 +52,6 @@ #include #include "fdbserver/Status.h" #include "fdbrpc/Net2FileSystem.h" -#include "fdbrpc/Platform.h" #include "fdbrpc/AsyncFileCached.actor.h" #include "fdbserver/CoroFlow.h" #include "flow/TLSPolicy.h" diff --git a/flow/Platform.cpp b/flow/Platform.cpp index e803612f23..55e617f273 100644 --- a/flow/Platform.cpp +++ b/flow/Platform.cpp @@ -77,6 +77,7 @@ #include #include #include +#include /* Needed for disk capacity */ #include @@ -2502,6 +2503,54 @@ void outOfMemory() { criticalError(FDB_EXIT_NO_MEM, "OutOfMemory", "Out of memory"); } + +// Because the lambda used with nftw below cannot capture +int __eraseDirectoryRecurseiveCount; + +int eraseDirectoryRecursive(std::string const& dir) { + __eraseDirectoryRecurseiveCount = 0; +#ifdef _WIN32 + system( ("rd /s /q \"" + dir + "\"").c_str() ); +#elif defined(__linux__) || defined(__APPLE__) + int error = + nftw(dir.c_str(), + [](const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) -> int { + int r = remove(fpath); + if(r == 0) + ++__eraseDirectoryRecurseiveCount; + return r; + }, + 64, FTW_DEPTH | FTW_PHYS); + /* Looks like calling code expects this to continue silently if + the directory we're deleting doesn't exist in the first + place */ + if (error && errno != ENOENT) { + Error e = systemErrorCodeToError(); + TraceEvent(SevError, "EraseDirectoryRecursiveError").detail("Directory", dir).GetLastError().error(e); + throw e; + } +#else +#error Port me! +#endif + //INJECT_FAULT( platform_error, "eraseDirectoryRecursive" ); + return __eraseDirectoryRecurseiveCount; +} + +bool isSse42Supported() +{ +#if defined(_WIN32) + int info[4]; + __cpuid(info, 1); + return (info[2] & (1 << 20)) != 0; +#elif defined(__unixish__) + uint32_t eax, ebx, ecx, edx, level = 1, count = 0; + __cpuid_count(level, count, eax, ebx, ecx, edx); + return ((ecx >> 20) & 1) != 0; +#else + #error Port me! +#endif +} + } // namespace platform extern "C" void criticalError(int exitCode, const char *type, const char *message) { diff --git a/flow/Platform.h b/flow/Platform.h index 282f465df6..4d2d5ed357 100644 --- a/flow/Platform.h +++ b/flow/Platform.h @@ -388,6 +388,11 @@ size_t raw_backtrace(void** addresses, int maxStackDepth); std::string get_backtrace(); std::string format_backtrace(void **addresses, int numAddresses); +// Avoid in production code: not atomic, not fast, not reliable in all environments +int eraseDirectoryRecursive(std::string const& directory); + +bool isSse42Supported(); + } // namespace platform #ifdef __linux__ diff --git a/flow/crc32c.cpp b/flow/crc32c.cpp index 150808fd7e..12f308a3a5 100644 --- a/flow/crc32c.cpp +++ b/flow/crc32c.cpp @@ -34,7 +34,7 @@ #include #include #include -#include "fdbrpc/Platform.h" +#include "flow/Platform.h" #include "crc32c-generated-constants.cpp" static uint32_t append_trivial(uint32_t crc, const uint8_t * input, size_t length) From 3e507e051562295b8f2984c656c080ba00269ebe Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Fri, 28 Feb 2020 15:08:45 -0800 Subject: [PATCH 0781/1604] FastRestore:Reenable isSchedulable --- fdbserver/RestoreRoleCommon.actor.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/fdbserver/RestoreRoleCommon.actor.cpp b/fdbserver/RestoreRoleCommon.actor.cpp index 174e521052..6292da70ee 100644 --- a/fdbserver/RestoreRoleCommon.actor.cpp +++ b/fdbserver/RestoreRoleCommon.actor.cpp @@ -106,7 +106,6 @@ ACTOR Future isSchedulable(Reference self, int actorBatch // Intentionally randomly block actors for low memory reason. // memory will be larger than threshold when deterministicRandom()->random01() > 1/2 memory = SERVER_KNOBS->FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT * 2 * deterministicRandom()->random01(); - memory = 0; } if (memory < SERVER_KNOBS->FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT || self->finishedBatch.get() + 1 == actorBatchIndex) { From b0062f58d30de640368aca3c725317580799431a Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Fri, 28 Feb 2020 15:44:22 -0800 Subject: [PATCH 0782/1604] fix: blobstore needs to handshake tls connections --- fdbclient/BlobStore.actor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/fdbclient/BlobStore.actor.cpp b/fdbclient/BlobStore.actor.cpp index 6e121491bb..4cba0297f0 100644 --- a/fdbclient/BlobStore.actor.cpp +++ b/fdbclient/BlobStore.actor.cpp @@ -507,6 +507,7 @@ ACTOR Future connect_impl(Referenceknobs.secure_connection ? "https" : "http"; state Reference conn = wait(INetworkConnections::net()->connect(b->host, service, b->knobs.secure_connection ? true : false)); + wait(conn->connectHandshake()); TraceEvent("BlobStoreEndpointNewConnection").suppressFor(60) .detail("RemoteEndpoint", conn->getPeerAddress()) From 01c1a15caf4831060bfec2effca4060de7485517 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Fri, 28 Feb 2020 16:00:47 -0800 Subject: [PATCH 0783/1604] FastRestore:Applier:Limit fetch keys number in a txn in getAndComputeStagingKeys --- fdbserver/Knobs.cpp | 1 + fdbserver/Knobs.h | 1 + fdbserver/RestoreApplier.actor.cpp | 20 ++++++++++++++++---- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index 6002410a24..4d9635e4c6 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -562,6 +562,7 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs, bool isSimula init( FASTRESTORE_WAIT_FOR_MEMORY_LATENCY, 10 ); if( randomize && BUGGIFY ) { FASTRESTORE_WAIT_FOR_MEMORY_LATENCY = 60; } init( FASTRESTORE_HEARTBEAT_DELAY, 10 ); if( randomize && BUGGIFY ) { FASTRESTORE_HEARTBEAT_DELAY = deterministicRandom()->random01() * 120; } init( FASTRESTORE_HEARTBEAT_MAX_DELAY, 10 ); if( randomize && BUGGIFY ) { FASTRESTORE_HEARTBEAT_MAX_DELAY = FASTRESTORE_HEARTBEAT_DELAY * 10; } + init( FASTRESTORE_APPLIER_FETCH_KEYS_SIZE, 100 ); if( randomize && BUGGIFY ) { FASTRESTORE_APPLIER_FETCH_KEYS_SIZE = deterministicRandom()->random01() * 10240 + 1; } // clang-format on diff --git a/fdbserver/Knobs.h b/fdbserver/Knobs.h index 6cab39ac5b..d7a9e4b705 100644 --- a/fdbserver/Knobs.h +++ b/fdbserver/Knobs.h @@ -504,6 +504,7 @@ public: int64_t FASTRESTORE_WAIT_FOR_MEMORY_LATENCY; int64_t FASTRESTORE_HEARTBEAT_DELAY; // interval for master to ping loaders and appliers int64_t FASTRESTORE_HEARTBEAT_MAX_DELAY; // master claim a node is down if no heart beat from the node for this delay + int64_t FASTRESTORE_APPLIER_FETCH_KEYS_SIZE; // number of keys to fetch in a txn on applier ServerKnobs(bool randomize = false, ClientKnobs* clientKnobs = NULL, bool isSimulated = false); }; diff --git a/fdbserver/RestoreApplier.actor.cpp b/fdbserver/RestoreApplier.actor.cpp index e8075f76a0..5ded564898 100644 --- a/fdbserver/RestoreApplier.actor.cpp +++ b/fdbserver/RestoreApplier.actor.cpp @@ -27,6 +27,7 @@ #include "fdbclient/ManagementAPI.actor.h" #include "fdbclient/MutationList.h" #include "fdbclient/BackupContainer.h" +#include "fdbserver/Knobs.h" #include "fdbserver/RestoreCommon.actor.h" #include "fdbserver/RestoreUtil.h" #include "fdbserver/RestoreRoleCommon.actor.h" @@ -202,6 +203,7 @@ ACTOR static Future getAndComputeStagingKeys( state Reference tr(new ReadYourWritesTransaction(cx)); state std::vector>> fValues; state int i = 0; + state int retries = 0; TraceEvent("FastRestoreApplierGetAndComputeStagingKeysStart", applierID) .detail("GetKeys", imcompleteStagingKeys.size()); loop { @@ -215,7 +217,8 @@ ACTOR static Future getAndComputeStagingKeys( wait(waitForAll(fValues)); break; } catch (Error& e) { - TraceEvent(SevError, "FastRestoreApplierGetAndComputeStagingKeysUnhandledError") + retries++; + TraceEvent(retries > 10 ? SevError : SevWarn, "FastRestoreApplierGetAndComputeStagingKeysUnhandledError") .detail("GetKeys", imcompleteStagingKeys.size()) .detail("Error", e.what()) .detail("ErrorCode", e.code()); @@ -307,16 +310,25 @@ ACTOR static Future precomputeMutationsResult(Reference .detail("StagingKeys", batchData->stagingKeys.size()); // Get keys in stagingKeys which does not have a baseline key by reading database cx, and precompute the key's value + std::vector> fGetAndComputeKeys; std::map::iterator> imcompleteStagingKeys; std::map::iterator stagingKeyIter = batchData->stagingKeys.begin(); + int numKeysInBatch = 0; for (; stagingKeyIter != batchData->stagingKeys.end(); stagingKeyIter++) { if (!stagingKeyIter->second.hasBaseValue()) { imcompleteStagingKeys.emplace(stagingKeyIter->first, stagingKeyIter); batchData->counters.fetchKeys += 1; + numKeysInBatch++; + } + if (numKeysInBatch == SERVER_KNOBS->FASTRESTORE_APPLIER_FETCH_KEYS_SIZE) { + fGetAndComputeKeys.push_back(getAndComputeStagingKeys(imcompleteStagingKeys, cx, applierID)); + numKeysInBatch = 0; + imcompleteStagingKeys.clear(); } } - - Future fGetAndComputeKeys = getAndComputeStagingKeys(imcompleteStagingKeys, cx, applierID); + if (numKeysInBatch > 0) { + fGetAndComputeKeys.push_back(getAndComputeStagingKeys(imcompleteStagingKeys, cx, applierID)); + } TraceEvent("FastRestoreApplerPhasePrecomputeMutationsResult", applierID) .detail("BatchIndex", batchIndex) @@ -331,7 +343,7 @@ ACTOR static Future precomputeMutationsResult(Reference } TraceEvent("FastRestoreApplierGetAndComputeStagingKeysWaitOn", applierID); - wait(fGetAndComputeKeys); + wait(waitForAll(fGetAndComputeKeys)); // Sanity check all stagingKeys have been precomputed ASSERT_WE_THINK(batchData->allKeysPrecomputed()); From 5c562e37140a9733828bd7ba17b89872425c0813 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Fri, 28 Feb 2020 20:31:59 -0800 Subject: [PATCH 0784/1604] FastRestore:Optimize memory usage in updateHeartbeatTime Only allocate memory once. Reset vector element value for each loop. Also ensure FASTRESTORE_HEARTBEAT_DELAY not 0 --- fdbserver/Knobs.cpp | 2 +- fdbserver/RestoreMaster.actor.cpp | 28 +++++++++++++++++++++------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index 4d9635e4c6..c7273afea0 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -560,7 +560,7 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs, bool isSimula init( FASTRESTORE_TRACK_LOADER_SEND_REQUESTS, false ); if( randomize && BUGGIFY ) { FASTRESTORE_TRACK_LOADER_SEND_REQUESTS = true; } init( FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT, 6144 ); if( randomize && BUGGIFY ) { FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT = 1; } init( FASTRESTORE_WAIT_FOR_MEMORY_LATENCY, 10 ); if( randomize && BUGGIFY ) { FASTRESTORE_WAIT_FOR_MEMORY_LATENCY = 60; } - init( FASTRESTORE_HEARTBEAT_DELAY, 10 ); if( randomize && BUGGIFY ) { FASTRESTORE_HEARTBEAT_DELAY = deterministicRandom()->random01() * 120; } + init( FASTRESTORE_HEARTBEAT_DELAY, 10 ); if( randomize && BUGGIFY ) { FASTRESTORE_HEARTBEAT_DELAY = deterministicRandom()->random01() * 120 + 2; } init( FASTRESTORE_HEARTBEAT_MAX_DELAY, 10 ); if( randomize && BUGGIFY ) { FASTRESTORE_HEARTBEAT_MAX_DELAY = FASTRESTORE_HEARTBEAT_DELAY * 10; } init( FASTRESTORE_APPLIER_FETCH_KEYS_SIZE, 100 ); if( randomize && BUGGIFY ) { FASTRESTORE_APPLIER_FETCH_KEYS_SIZE = deterministicRandom()->random01() * 10240 + 1; } diff --git a/fdbserver/RestoreMaster.actor.cpp b/fdbserver/RestoreMaster.actor.cpp index 1f89122227..3f07ccaa4d 100644 --- a/fdbserver/RestoreMaster.actor.cpp +++ b/fdbserver/RestoreMaster.actor.cpp @@ -908,27 +908,41 @@ ACTOR static Future signalRestoreCompleted(Reference se // Update the most recent time when master receives hearbeat from each loader and applier ACTOR static Future updateHeartbeatTime(Reference self) { + int numRoles = self->loadersInterf.size() + self->appliersInterf.size(); state std::map::iterator loader = self->loadersInterf.begin(); state std::map::iterator applier = self->appliersInterf.begin(); - state std::vector> fReplies; // TODO: Reserve memory for this vector + state std::vector> fReplies(numRoles, Never()); // TODO: Reserve memory for this vector state std::vector nodes; + state int index = 0; + + // Initialize nodes only once + loader = self->loadersInterf.begin(); + applier = self->appliersInterf.begin(); + while (loader != self->loadersInterf.end()) { + nodes.push_back(loader->first); + loader++; + } + while (applier != self->appliersInterf.end()) { + nodes.push_back(applier->first); + applier++; + } loop { wait(delay(SERVER_KNOBS->FASTRESTORE_HEARTBEAT_DELAY)); loader = self->loadersInterf.begin(); applier = self->appliersInterf.begin(); - fReplies.clear(); - nodes.clear(); + index = 0; + std::fill(fReplies.begin(), fReplies.end(), Never()); // ping loaders and appliers while(loader != self->loadersInterf.end()) { - fReplies.push_back(loader->second.heartbeat.getReply(RestoreSimpleRequest())); - nodes.push_back(loader->first); + fReplies[index] = loader->second.heartbeat.getReply(RestoreSimpleRequest()); loader++; + index++; } while(applier != self->appliersInterf.end()) { - fReplies.push_back(applier->second.heartbeat.getReply(RestoreSimpleRequest())); - nodes.push_back(applier->first); + fReplies[index] = applier->second.heartbeat.getReply(RestoreSimpleRequest()); applier++; + index++; } wait(waitForAll(fReplies) || delay(SERVER_KNOBS->FASTRESTORE_HEARTBEAT_DELAY)); From f0be82752a0d614f80650ecb46169487d2c2f4f5 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Fri, 28 Feb 2020 21:25:45 -0800 Subject: [PATCH 0785/1604] commit_debug can visualize CommitDebug TraceEvents via chrome://tracing --- contrib/commit_debug.py | 128 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100755 contrib/commit_debug.py diff --git a/contrib/commit_debug.py b/contrib/commit_debug.py new file mode 100755 index 0000000000..db45e220d5 --- /dev/null +++ b/contrib/commit_debug.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python +import argparse +import glob +import gzip +import os.path +import sys +import xml.sax +import heapq +import json + +# Usage: ./commit_debug.py trace.xml tracing.json +# ./commit_debug.py trace.xml.gz tracing.json +# ./commit_debug.py folder-of-traces/ tracing.json +# +# And then open Chrome, navigate to chrome://tracing , and load the tracing.json + +def parse_args(): + args = argparse.ArgumentParser() + args.add_argument('path') + args.add_argument('output') + return args.parse_args() + +# When encountering an event with this location, use this as the (b)egin or +# (e)nd of a span with a better given name +locationToPhase = { + "NativeAPI.commit.Before": [], + "MasterProxyServer.batcher": [("b", "Commit")], + "MasterProxyServer.commitBatch.Before": [], + "MasterProxyServer.commitBatch.GettingCommitVersion": [("b", "CommitVersion")], + "MasterProxyServer.commitBatch.GotCommitVersion": [("e", "CommitVersion")], + "Resolver.resolveBatch.Before": [("b", "Resolver.PipelineWait")], + "Resolver.resolveBatch.AfterQueueSizeCheck": [], + "Resolver.resolveBatch.AfterOrderer": [("e", "Resolver.PipelineWait"), ("b", "Resolver.Conflicts")], + "Resolver.resolveBatch.After": [("e", "Resolver.Conflicts")], + "MasterProxyServer.commitBatch.AfterResolution": [("b", "Proxy.Processing")], + "MasterProxyServer.commitBatch.ProcessingMutations": [], + "MasterProxyServer.commitBatch.AfterStoreCommits": [("e", "Proxy.Processing")], + "TLog.tLogCommit.BeforeWaitForVersion": [("b", "TLog.PipelineWait")], + "TLog.tLogCommit.Before": [("e", "TLog.PipelineWait")], + "TLog.tLogCommit.AfterTLogCommit": [("b", "TLog.FSync")], + "TLog.tLogCommit.After": [("e", "TLog.FSync")], + "MasterProxyServer.commitBatch.AfterLogPush": [("e", "Commit")], + "NativeAPI.commit.After": [], +} + +class CommitDebugHandler(xml.sax.ContentHandler, object): + def __init__(self, f): + self._f = f + self._f.write('[ ') # Trace viewer adds the missing ] for us + self._starttime = None + self._data = dict() + + def _emit(self, d): + self._f.write(json.dumps(d) + ', ') + + def startElement(self, name, attrs): + if self._starttime is None: + self._starttime = float(attrs['Time']) + + # I've flipped from using Async spans to Duration spans, because + # I kept on running into issues with trace viewer believeing there + # is no start or end of an emitted span even when there actually is. + + if name == "Event" and attrs.get('Type') == "CommitDebug": + attr_id = attrs['ID'] + trace_id = self._idmap.setdefault(attr_id, attr_id) + # Trace viewer doesn't seem to care about types, so use host as pid and port as tid + (pid, tid) = attrs['Machine'].split(':') + traces = locationToPhase[attrs["Location"]] + for (phase, name) in traces: + if phase == "b": + self._data[(attrs['Machine'], name)] = float(attrs['Time']) + else: + starttime = self._data.get((attrs['Machine'], name)) + if starttime is None: + return + trace = { + # ts and dur are in microseconds + "ts": (starttime - self._starttime) * 1000 * 1000 + 0.001, + "dur": (float(attrs['Time']) - starttime) * 1000 * 1000, + "cat": "commit", + "name": name, + "ph": "X", + "pid": pid, + "tid": tid } + self._emit(trace) + + +def do_file(args, handler, filename): + openfn = gzip.open if filename.endswith('.gz') else open + try: + with openfn(filename) as f: + xml.sax.parse(f, handler) + except xml.sax._exceptions.SAXParseException as e: + print(e) + +def main(): + args = parse_args() + + handler = CommitDebugHandler(open(args.output, 'w')) + + def xmliter(filename): + for line in gzip.open(filename): + if line.startswith("') + for line in merged: + f.write(line[1]) + f.write('') + do_file(args, handler, combined_xml) + else: + do_file(args, handler, args.path) + + return 0 + + +if __name__ == '__main__': + sys.exit(main()) From e997be3e2dce91dc03eb33b4cb04a137a35c6813 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 29 Feb 2020 03:04:25 -0800 Subject: [PATCH 0786/1604] Make fdbcli act like redis-cli, and a couple fixes. Hints now show up as a darker gray text. Hint components now only show for pieces of the command string that hasn't yet been typed. The code now correctly handles offering hints if multiple commands are issued at once seperated by ';'. A hint of "{malformed escape sequence}" is displayed if a bad escape sequence was typed. e.g. \e --- fdbcli/fdbcli.actor.cpp | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/fdbcli/fdbcli.actor.cpp b/fdbcli/fdbcli.actor.cpp index e76095f737..ce8cf7bb09 100644 --- a/fdbcli/fdbcli.actor.cpp +++ b/fdbcli/fdbcli.actor.cpp @@ -490,7 +490,7 @@ void initHelp() { "change the class of a process", "If no address and class are specified, lists the classes of all servers.\n\nSetting the class to `default' resets the process class to the class specified on the command line."); helpMap["status"] = CommandHelp( - "status [minimal] [details] [json]", + "status [minimal|details|json]", "get the status of a FoundationDB cluster", "If the cluster is down, this command will print a diagnostic which may be useful in figuring out what is wrong. If the cluster is running, this command will print cluster statistics.\n\nSpecifying 'minimal' will provide a minimal description of the status of your database.\n\nSpecifying 'details' will provide load information for individual workers.\n\nSpecifying 'json' will provide status information in a machine readable JSON format."); helpMap["exit"] = CommandHelp("exit", "exit the CLI", ""); @@ -541,7 +541,7 @@ void initHelp() { "attempts to kill one or more processes in the cluster", "If no addresses are specified, populates the list of processes which can be killed. Processes cannot be killed before this list has been populated.\n\nIf `all' is specified, attempts to kill all known processes.\n\nIf `list' is specified, displays all known processes. This is only useful when the database is unresponsive.\n\nFor each IP:port pair in
*, attempt to kill the specified process."); helpMap["profile"] = CommandHelp( - "profile ", + "profile ", "namespace for all the profiling-related commands.", "Different types support different actions. Run `profile` to get a list of types, and iteratively explore the help.\n"); helpMap["force_recovery_with_data_loss"] = CommandHelp( @@ -3659,13 +3659,26 @@ ACTOR Future runCli(CLIOptions opt) { }, [enabled=opt.cliHints](std::string const& line)->LineNoise::Hint { if (enabled) { - int firstWordIdx = line.find(' '); - if (firstWordIdx == std::string::npos) { - firstWordIdx = line.size(); - } - auto iter = helpMap.find(line.substr(0, firstWordIdx)); + bool error = false; + bool partial = false; + std::string linecopy = line; + std::vector> parsed = parseLine(linecopy, error, partial); + if (parsed.size() == 0 || parsed.back().size() == 0) return LineNoise::Hint(); + StringRef command = parsed.back().front(); + int finishedParameters = parsed.back().size() + error; + + // We don't want the hint to flip to parse error and back, e.g. while \" is being typed. + if (error && line.back() != '\\') return LineNoise::Hint(std::string(" {parse error}"), 90, false); + + auto iter = helpMap.find(command.toString()); if (iter != helpMap.end()) { - return LineNoise::Hint(iter->second.usage.substr(firstWordIdx), 0, false); + std::string helpLine = iter->second.usage; + std::vector> parsedHelp = parseLine(helpLine, error, partial); + std::string hintLine = (*(line.end() - 1) == ' ' ? "" : " "); + for (int i = finishedParameters; i < parsedHelp.back().size(); i++) { + hintLine = hintLine + parsedHelp.back()[i].toString() + " "; + } + return LineNoise::Hint(hintLine, 90, false); } } return LineNoise::Hint(); From ad9b3fb4a87ee295693903c69044221ac393a439 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Sat, 29 Feb 2020 13:45:00 -0800 Subject: [PATCH 0787/1604] DD:Add trace for detailed relocate shard info --- fdbclient/Knobs.h | 4 ++-- fdbserver/DataDistributionQueue.actor.cpp | 21 +++++++++++++++++---- fdbserver/Knobs.cpp | 1 + fdbserver/Knobs.h | 3 ++- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/fdbclient/Knobs.h b/fdbclient/Knobs.h index b2a2348061..ed6dfec15f 100644 --- a/fdbclient/Knobs.h +++ b/fdbclient/Knobs.h @@ -192,10 +192,10 @@ public: int CONSISTENCY_CHECK_RATE_LIMIT_MAX; int CONSISTENCY_CHECK_ONE_ROUND_TARGET_COMPLETION_TIME; - // fdbcli + // fdbcli int CLI_CONNECT_PARALLELISM; double CLI_CONNECT_TIMEOUT; - + ClientKnobs(bool randomize = false); }; diff --git a/fdbserver/DataDistributionQueue.actor.cpp b/fdbserver/DataDistributionQueue.actor.cpp index c534cda824..2b649f768d 100644 --- a/fdbserver/DataDistributionQueue.actor.cpp +++ b/fdbserver/DataDistributionQueue.actor.cpp @@ -1019,10 +1019,23 @@ ACTOR Future dataDistributionRelocator( DDQueueData *self, RelocateData rd //FIXME: do not add data in flight to servers that were already in the src. healthyDestinations.addDataInFlightToTeam(+metrics.bytes); - TraceEvent(relocateShardInterval.severity, "RelocateShardHasDestination", distributorId) - .detail("PairId", relocateShardInterval.pairID) - .detail("DestinationTeam", describe(destIds)) - .detail("ExtraIds", describe(extraIds)); + if (SERVER_KNOBS->DD_ENABLE_VERBOSE_TRACING) { + // StorageMetrics is the rd shard's metrics, e.g., bytes and write bandwidth + TraceEvent(SevInfo, "RelocateShardDecision", distributorId) + .detail("PairId", relocateShardInterval.pairID) + .detail("Priority", rd.priority) + .detail("KeyBegin", rd.keys.begin) + .detail("KeyEnd", rd.keys.end) + .detail("StorageMetrics", metrics.toString()) + .detail("SourceServers", describe(rd.src)) + .detail("DestinationTeam", describe(destIds)) + .detail("ExtraIds", describe(extraIds)); + } else { + TraceEvent(relocateShardInterval.severity, "RelocateShardHasDestination", distributorId) + .detail("PairId", relocateShardInterval.pairID) + .detail("DestinationTeam", describe(destIds)) + .detail("ExtraIds", describe(extraIds)); + } state Error error = success(); state Promise dataMovementComplete; diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index 2ce0aac021..99aa3b7336 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -201,6 +201,7 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs, bool isSimula init( DD_OVERLAP_PENALTY, 10000 ); init( DD_VALIDATE_LOCALITY, true ); if( randomize && BUGGIFY ) DD_VALIDATE_LOCALITY = false; init( DD_CHECK_INVALID_LOCALITY_DELAY, 60 ); if( randomize && BUGGIFY ) DD_CHECK_INVALID_LOCALITY_DELAY = 1 + deterministicRandom()->random01() * 600; + init( DD_ENABLE_VERBOSE_TRACING, false ); if( randomize && BUGGIFY ) DD_ENABLE_VERBOSE_TRACING = true; // TeamRemover init( TR_FLAG_DISABLE_MACHINE_TEAM_REMOVER, false ); if( randomize && BUGGIFY ) TR_FLAG_DISABLE_MACHINE_TEAM_REMOVER = deterministicRandom()->random01() < 0.1 ? true : false; // false by default. disable the consistency check when it's true diff --git a/fdbserver/Knobs.h b/fdbserver/Knobs.h index c5c41fc58f..d1c7617448 100644 --- a/fdbserver/Knobs.h +++ b/fdbserver/Knobs.h @@ -104,7 +104,7 @@ public: double INFLIGHT_PENALTY_REDUNDANT; double INFLIGHT_PENALTY_UNHEALTHY; double INFLIGHT_PENALTY_ONE_LEFT; - + // Higher priorities are executed first // Priority/100 is the "priority group"/"superpriority". Priority inversion // is possible within but not between priority groups; fewer priority groups @@ -164,6 +164,7 @@ public: int DD_OVERLAP_PENALTY; bool DD_VALIDATE_LOCALITY; int DD_CHECK_INVALID_LOCALITY_DELAY; + bool DD_ENABLE_VERBOSE_TRACING; // TeamRemover to remove redundant teams bool TR_FLAG_DISABLE_MACHINE_TEAM_REMOVER; // disable the machineTeamRemover actor From 6d14aacb409610906e6d4b43ee2df678a15f4b5f Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sun, 1 Mar 2020 02:00:45 -0800 Subject: [PATCH 0788/1604] I forgot to add the last change before committing and pushing the last commit. --- fdbcli/fdbcli.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbcli/fdbcli.actor.cpp b/fdbcli/fdbcli.actor.cpp index ce8cf7bb09..3a9ad1d01e 100644 --- a/fdbcli/fdbcli.actor.cpp +++ b/fdbcli/fdbcli.actor.cpp @@ -3668,7 +3668,7 @@ ACTOR Future runCli(CLIOptions opt) { int finishedParameters = parsed.back().size() + error; // We don't want the hint to flip to parse error and back, e.g. while \" is being typed. - if (error && line.back() != '\\') return LineNoise::Hint(std::string(" {parse error}"), 90, false); + if (error && line.back() != '\\') return LineNoise::Hint(std::string(" {malformed escape sequence}"), 90, false); auto iter = helpMap.find(command.toString()); if (iter != helpMap.end()) { From 2520e8d44cb3c1d5659fe69833375e19faa09e96 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Sun, 1 Mar 2020 20:39:56 -0800 Subject: [PATCH 0789/1604] FastRestore:Use more concise code as suggested in review --- fdbserver/Knobs.h | 2 +- fdbserver/RestoreApplier.actor.cpp | 2 +- fdbserver/RestoreApplier.actor.h | 4 ++-- fdbserver/RestoreCommon.actor.h | 6 +++--- fdbserver/RestoreLoader.actor.cpp | 2 +- fdbserver/RestoreLoader.actor.h | 4 ++-- fdbserver/RestoreMaster.actor.cpp | 18 ++++++++---------- fdbserver/RestoreMaster.actor.h | 7 ++----- 8 files changed, 20 insertions(+), 25 deletions(-) diff --git a/fdbserver/Knobs.h b/fdbserver/Knobs.h index 6f777fdf2b..7371779b2f 100644 --- a/fdbserver/Knobs.h +++ b/fdbserver/Knobs.h @@ -504,7 +504,7 @@ public: int64_t FASTRESTORE_APPLYING_PARALLELISM; // number of outstanding txns writing to dest. DB int64_t FASTRESTORE_MONITOR_LEADER_DELAY; int64_t FASTRESTORE_STRAGGLER_THRESHOLD; - bool FASTRESTORE_TRACK_REQUEST_LATENCY; + bool FASTRESTORE_TRACK_REQUEST_LATENCY; // true to track reply latency of each request in a request batch bool FASTRESTORE_TRACK_LOADER_SEND_REQUESTS; // track requests of load send mutations to appliers? int64_t FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT; // threshold when pipelined actors should be delayed int64_t FASTRESTORE_WAIT_FOR_MEMORY_LATENCY; diff --git a/fdbserver/RestoreApplier.actor.cpp b/fdbserver/RestoreApplier.actor.cpp index 5ded564898..0a07ed0481 100644 --- a/fdbserver/RestoreApplier.actor.cpp +++ b/fdbserver/RestoreApplier.actor.cpp @@ -512,4 +512,4 @@ Value applyAtomicOp(Optional existingValue, Value value, MutationRef: ASSERT(false); } return Value(); -} \ No newline at end of file +} diff --git a/fdbserver/RestoreApplier.actor.h b/fdbserver/RestoreApplier.actor.h index d27a5b2fad..72424eed62 100644 --- a/fdbserver/RestoreApplier.actor.h +++ b/fdbserver/RestoreApplier.actor.h @@ -370,7 +370,7 @@ struct RestoreApplierData : RestoreRoleData, public ReferenceCounted>::iterator item = batch.find(batchIndex); if (item == batch.end()) { // Simply caller's effort in when it can call this func. return ApplierVersionBatchState::INVALID; @@ -378,7 +378,7 @@ struct RestoreApplierData : RestoreRoleData, public ReferenceCountedsecond->vbState.get(); } } - void setVersionBatchState(int batchIndex, int vbState) { + void setVersionBatchState(int batchIndex, int vbState) final { std::map>::iterator item = batch.find(batchIndex); ASSERT(item != batch.end()); item->second->vbState = vbState; diff --git a/fdbserver/RestoreCommon.actor.h b/fdbserver/RestoreCommon.actor.h index 9b2ae89a8a..bc1780c735 100644 --- a/fdbserver/RestoreCommon.actor.h +++ b/fdbserver/RestoreCommon.actor.h @@ -249,7 +249,7 @@ ACTOR Future>> decodeLogFileBlock(Reference Future getBatchReplies(RequestStream Interface::*channel, std::map interfaces, @@ -333,7 +333,7 @@ Future getBatchReplies(RequestStream Interface::*channel, std::ma } } // Update replies - if (replies != NULL) { + if (replies != nullptr) { for(int i = 0; i < cmdReplies.size(); ++i) { replies->emplace_back(cmdReplies[i].get()); } @@ -359,7 +359,7 @@ ACTOR template Future sendBatchRequests(RequestStream Interface::*channel, std::map interfaces, std::vector> requests, TaskPriority taskID = TaskPriority::Low, bool trackRequestLatency = true) { - wait(getBatchReplies(channel, interfaces, requests, NULL, taskID, trackRequestLatency)); + wait(getBatchReplies(channel, interfaces, requests, nullptr, taskID, trackRequestLatency)); return Void(); } diff --git a/fdbserver/RestoreLoader.actor.cpp b/fdbserver/RestoreLoader.actor.cpp index 073d9cbb10..70eedf06f0 100644 --- a/fdbserver/RestoreLoader.actor.cpp +++ b/fdbserver/RestoreLoader.actor.cpp @@ -756,4 +756,4 @@ ACTOR Future handleFinishVersionBatchRequest(RestoreVersionBatchRequest re } req.reply.send(RestoreCommonReply(self->id(), false)); return Void(); -} \ No newline at end of file +} diff --git a/fdbserver/RestoreLoader.actor.h b/fdbserver/RestoreLoader.actor.h index 4e8fbbfbd9..536fe2b9b9 100644 --- a/fdbserver/RestoreLoader.actor.h +++ b/fdbserver/RestoreLoader.actor.h @@ -150,7 +150,7 @@ struct RestoreLoaderData : RestoreRoleData, public ReferenceCounted>::iterator item = batch.find(batchIndex); if (item != batch.end()) { // Simply caller's effort in when it can call this func. return LoaderVersionBatchState::INVALID; @@ -158,7 +158,7 @@ struct RestoreLoaderData : RestoreRoleData, public ReferenceCountedsecond->vbState.get(); } } - void setVersionBatchState(int batchIndex, int vbState) { + void setVersionBatchState(int batchIndex, int vbState) final { std::map>::iterator item = batch.find(batchIndex); ASSERT(item != batch.end()); item->second->vbState = vbState; diff --git a/fdbserver/RestoreMaster.actor.cpp b/fdbserver/RestoreMaster.actor.cpp index 3f07ccaa4d..79d7d027f7 100644 --- a/fdbserver/RestoreMaster.actor.cpp +++ b/fdbserver/RestoreMaster.actor.cpp @@ -914,21 +914,17 @@ ACTOR static Future updateHeartbeatTime(Reference self) state std::vector> fReplies(numRoles, Never()); // TODO: Reserve memory for this vector state std::vector nodes; state int index = 0; + state Future fTimeout = Void(); // Initialize nodes only once loader = self->loadersInterf.begin(); applier = self->appliersInterf.begin(); - while (loader != self->loadersInterf.end()) { - nodes.push_back(loader->first); - loader++; - } - while (applier != self->appliersInterf.end()) { - nodes.push_back(applier->first); - applier++; - } + std::transform(self->loadersInterf.begin(), self->loadersInterf.end(), std::back_inserter(nodes), + [](const std::pair& in) { return in.first; }); + std::transform(self->appliersInterf.begin(), self->appliersInterf.end(), std::back_inserter(nodes), + [](const std::pair& in) { return in.first; }); loop { - wait(delay(SERVER_KNOBS->FASTRESTORE_HEARTBEAT_DELAY)); loader = self->loadersInterf.begin(); applier = self->appliersInterf.begin(); index = 0; @@ -945,7 +941,8 @@ ACTOR static Future updateHeartbeatTime(Reference self) index++; } - wait(waitForAll(fReplies) || delay(SERVER_KNOBS->FASTRESTORE_HEARTBEAT_DELAY)); + fTimeout = delay(SERVER_KNOBS->FASTRESTORE_HEARTBEAT_DELAY); + wait(waitForAll(fReplies) || fTimeout); // Update the most recent heart beat time for each role for (int i = 0; i < fReplies.size(); ++i) { if (fReplies[i].isReady()) { @@ -954,6 +951,7 @@ ACTOR static Future updateHeartbeatTime(Reference self) item.first->second = currentTime; } } + wait(fTimeout); // Ensure not updating heartbeat too quickly } } diff --git a/fdbserver/RestoreMaster.actor.h b/fdbserver/RestoreMaster.actor.h index 92ecd83f32..434e4d6f0d 100644 --- a/fdbserver/RestoreMaster.actor.h +++ b/fdbserver/RestoreMaster.actor.h @@ -157,11 +157,8 @@ struct RestoreMasterData : RestoreRoleData, public ReferenceCounted Date: Mon, 2 Mar 2020 10:52:44 -0800 Subject: [PATCH 0790/1604] FastRestore:Add unit name to threshold knob name --- fdbserver/Knobs.cpp | 4 ++-- fdbserver/Knobs.h | 2 +- fdbserver/RestoreCommon.actor.h | 2 +- fdbserver/RestoreMaster.actor.cpp | 2 -- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index c91d4a9729..e5bb0c972b 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -560,8 +560,8 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs, bool isSimula init( FASTRESTORE_UPDATE_PROCESS_STATS_INTERVAL, 5 ); if( randomize ) { FASTRESTORE_UPDATE_PROCESS_STATS_INTERVAL = deterministicRandom()->random01() * 60 + 1; } init( FASTRESTORE_ATOMICOP_WEIGHT, 100 ); if( randomize ) { FASTRESTORE_ATOMICOP_WEIGHT = deterministicRandom()->random01() * 200 + 1; } init( FASTRESTORE_APPLYING_PARALLELISM, 100 ); if( randomize ) { FASTRESTORE_APPLYING_PARALLELISM = deterministicRandom()->random01() * 10 + 1; } - init( FASTRESTORE_MONITOR_LEADER_DELAY, 5 ); if( randomize ) { FASTRESTORE_MONITOR_LEADER_DELAY = deterministicRandom()->random01() * 100; } - init( FASTRESTORE_STRAGGLER_THRESHOLD, 60 ); if( randomize && BUGGIFY ) { FASTRESTORE_STRAGGLER_THRESHOLD = deterministicRandom()->random01() * 240 + 10; } + init( FASTRESTORE_MONITOR_LEADER_DELAY, 5 ); if( randomize ) { FASTRESTORE_MONITOR_LEADER_DELAY = deterministicRandom()->random01() * 100; } + init( FASTRESTORE_STRAGGLER_THRESHOLD_SECONDS, 60 ); if( randomize && BUGGIFY ) { FASTRESTORE_STRAGGLER_THRESHOLD_SECONDS = deterministicRandom()->random01() * 240 + 10; } init( FASTRESTORE_TRACK_REQUEST_LATENCY, true ); if( randomize && BUGGIFY ) { FASTRESTORE_TRACK_REQUEST_LATENCY = false; } init( FASTRESTORE_TRACK_LOADER_SEND_REQUESTS, false ); if( randomize && BUGGIFY ) { FASTRESTORE_TRACK_LOADER_SEND_REQUESTS = true; } init( FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT, 6144 ); if( randomize && BUGGIFY ) { FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT = 1; } diff --git a/fdbserver/Knobs.h b/fdbserver/Knobs.h index 7371779b2f..337673a332 100644 --- a/fdbserver/Knobs.h +++ b/fdbserver/Knobs.h @@ -503,7 +503,7 @@ public: int64_t FASTRESTORE_ATOMICOP_WEIGHT; // workload amplication factor for atomic op int64_t FASTRESTORE_APPLYING_PARALLELISM; // number of outstanding txns writing to dest. DB int64_t FASTRESTORE_MONITOR_LEADER_DELAY; - int64_t FASTRESTORE_STRAGGLER_THRESHOLD; + int64_t FASTRESTORE_STRAGGLER_THRESHOLD_SECONDS; bool FASTRESTORE_TRACK_REQUEST_LATENCY; // true to track reply latency of each request in a request batch bool FASTRESTORE_TRACK_LOADER_SEND_REQUESTS; // track requests of load send mutations to appliers? int64_t FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT; // threshold when pipelined actors should be delayed diff --git a/fdbserver/RestoreCommon.actor.h b/fdbserver/RestoreCommon.actor.h index bc1780c735..72d86d8d49 100644 --- a/fdbserver/RestoreCommon.actor.h +++ b/fdbserver/RestoreCommon.actor.h @@ -324,7 +324,7 @@ Future getBatchReplies(RequestStream Interface::*channel, std::ma latestNode = endTime.first; } } - if (latest - earliest > SERVER_KNOBS->FASTRESTORE_STRAGGLER_THRESHOLD) { + if (latest - earliest > SERVER_KNOBS->FASTRESTORE_STRAGGLER_THRESHOLD_SECONDS) { TraceEvent(SevWarn, "ProfileSendRequestBatchLatencyFoundStraggler", bathcID) .detail("SlowestNode", latestNode) .detail("FatestNode", earliestNode) diff --git a/fdbserver/RestoreMaster.actor.cpp b/fdbserver/RestoreMaster.actor.cpp index 79d7d027f7..59a3c1d63c 100644 --- a/fdbserver/RestoreMaster.actor.cpp +++ b/fdbserver/RestoreMaster.actor.cpp @@ -917,8 +917,6 @@ ACTOR static Future updateHeartbeatTime(Reference self) state Future fTimeout = Void(); // Initialize nodes only once - loader = self->loadersInterf.begin(); - applier = self->appliersInterf.begin(); std::transform(self->loadersInterf.begin(), self->loadersInterf.end(), std::back_inserter(nodes), [](const std::pair& in) { return in.first; }); std::transform(self->appliersInterf.begin(), self->appliersInterf.end(), std::back_inserter(nodes), From e6457ba0d5cd29fbab7d9fd193c21820983991e3 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Mon, 2 Mar 2020 11:33:07 -0800 Subject: [PATCH 0791/1604] FastRestore:Correct type for imcompleteStagingKeys --- fdbserver/RestoreApplier.actor.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/fdbserver/RestoreApplier.actor.cpp b/fdbserver/RestoreApplier.actor.cpp index 0a07ed0481..e5fd6c7a14 100644 --- a/fdbserver/RestoreApplier.actor.cpp +++ b/fdbserver/RestoreApplier.actor.cpp @@ -197,21 +197,21 @@ ACTOR static Future applyClearRangeMutations(StandalonestagingKeys +// Get keys in incompleteStagingKeys and precompute the stagingKey which is stored in batchData->stagingKeys ACTOR static Future getAndComputeStagingKeys( - std::map::iterator> imcompleteStagingKeys, Database cx, UID applierID) { + std::map::iterator> incompleteStagingKeys, Database cx, UID applierID) { state Reference tr(new ReadYourWritesTransaction(cx)); state std::vector>> fValues; state int i = 0; state int retries = 0; TraceEvent("FastRestoreApplierGetAndComputeStagingKeysStart", applierID) - .detail("GetKeys", imcompleteStagingKeys.size()); + .detail("GetKeys", incompleteStagingKeys.size()); loop { try { tr->reset(); tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); tr->setOption(FDBTransactionOptions::LOCK_AWARE); - for (auto& key : imcompleteStagingKeys) { + for (auto& key : incompleteStagingKeys) { fValues.push_back(tr->get(key.first)); } wait(waitForAll(fValues)); @@ -219,7 +219,7 @@ ACTOR static Future getAndComputeStagingKeys( } catch (Error& e) { retries++; TraceEvent(retries > 10 ? SevError : SevWarn, "FastRestoreApplierGetAndComputeStagingKeysUnhandledError") - .detail("GetKeys", imcompleteStagingKeys.size()) + .detail("GetKeys", incompleteStagingKeys.size()) .detail("Error", e.what()) .detail("ErrorCode", e.code()); wait(tr->onError(e)); @@ -227,9 +227,9 @@ ACTOR static Future getAndComputeStagingKeys( } } - ASSERT(fValues.size() == imcompleteStagingKeys.size()); + ASSERT(fValues.size() == incompleteStagingKeys.size()); int i = 0; - for (auto& key : imcompleteStagingKeys) { + for (auto& key : incompleteStagingKeys) { if (!fValues[i].get().present()) { TraceEvent(SevWarnAlways, "FastRestoreApplierGetAndComputeStagingKeysUnhandledError") .detail("Key", key.first) @@ -257,7 +257,7 @@ ACTOR static Future getAndComputeStagingKeys( } TraceEvent("FastRestoreApplierGetAndComputeStagingKeysDone", applierID) - .detail("GetKeys", imcompleteStagingKeys.size()); + .detail("GetKeys", incompleteStagingKeys.size()); return Void(); } @@ -311,23 +311,23 @@ ACTOR static Future precomputeMutationsResult(Reference // Get keys in stagingKeys which does not have a baseline key by reading database cx, and precompute the key's value std::vector> fGetAndComputeKeys; - std::map::iterator> imcompleteStagingKeys; + std::map::iterator> incompleteStagingKeys; std::map::iterator stagingKeyIter = batchData->stagingKeys.begin(); int numKeysInBatch = 0; for (; stagingKeyIter != batchData->stagingKeys.end(); stagingKeyIter++) { if (!stagingKeyIter->second.hasBaseValue()) { - imcompleteStagingKeys.emplace(stagingKeyIter->first, stagingKeyIter); + incompleteStagingKeys.emplace(stagingKeyIter->first, stagingKeyIter); batchData->counters.fetchKeys += 1; numKeysInBatch++; } if (numKeysInBatch == SERVER_KNOBS->FASTRESTORE_APPLIER_FETCH_KEYS_SIZE) { - fGetAndComputeKeys.push_back(getAndComputeStagingKeys(imcompleteStagingKeys, cx, applierID)); + fGetAndComputeKeys.push_back(getAndComputeStagingKeys(incompleteStagingKeys, cx, applierID)); numKeysInBatch = 0; - imcompleteStagingKeys.clear(); + incompleteStagingKeys.clear(); } } if (numKeysInBatch > 0) { - fGetAndComputeKeys.push_back(getAndComputeStagingKeys(imcompleteStagingKeys, cx, applierID)); + fGetAndComputeKeys.push_back(getAndComputeStagingKeys(incompleteStagingKeys, cx, applierID)); } TraceEvent("FastRestoreApplerPhasePrecomputeMutationsResult", applierID) From 7119b46eb21a5c43a5c0d208cd2d8e742ad93a31 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Fri, 28 Feb 2020 18:31:33 -0800 Subject: [PATCH 0792/1604] Add unit test --- flow/flat_buffers.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/flow/flat_buffers.cpp b/flow/flat_buffers.cpp index 9d1211b67f..1c0b12fb3f 100644 --- a/flow/flat_buffers.cpp +++ b/flow/flat_buffers.cpp @@ -485,4 +485,16 @@ TEST_CASE("/flow/FlatBuffers/Standalone") { return Void(); } +// Meant to be run with valgrind or asan, to catch heap buffer overflows +TEST_CASE("/flow/FlatBuffers/Void") { + Standalone msg = ObjectWriter::toValue(Void(), Unversioned()); + auto buffer = std::make_unique(msg.size()); // Make a heap allocation of precisely the right size, to + // that asan or valgrind will catch any overflows + memcpy(buffer.get(), msg.begin(), msg.size()); + ObjectReader rd(buffer.get(), Unversioned()); + Void x; + rd.deserialize(x); + return Void(); +} + } // namespace unit_tests From cdbe3117d715799e6de3c87f193568efa162a3d7 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Fri, 28 Feb 2020 21:45:57 -0800 Subject: [PATCH 0793/1604] Fix typo --- flow/flat_buffers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/flat_buffers.cpp b/flow/flat_buffers.cpp index 1c0b12fb3f..1cb4b1099d 100644 --- a/flow/flat_buffers.cpp +++ b/flow/flat_buffers.cpp @@ -488,7 +488,7 @@ TEST_CASE("/flow/FlatBuffers/Standalone") { // Meant to be run with valgrind or asan, to catch heap buffer overflows TEST_CASE("/flow/FlatBuffers/Void") { Standalone msg = ObjectWriter::toValue(Void(), Unversioned()); - auto buffer = std::make_unique(msg.size()); // Make a heap allocation of precisely the right size, to + auto buffer = std::make_unique(msg.size()); // Make a heap allocation of precisely the right size, so // that asan or valgrind will catch any overflows memcpy(buffer.get(), msg.begin(), msg.size()); ObjectReader rd(buffer.get(), Unversioned()); From 24bbf5a8f003ce6b9095a5dcaad52c8848d62606 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Mon, 2 Mar 2020 12:10:47 -0800 Subject: [PATCH 0794/1604] Avoid invalid read on invalid Void msg --- flow/flat_buffers.h | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/flow/flat_buffers.h b/flow/flat_buffers.h index 2005bcf2a1..ff8f7ccceb 100644 --- a/flow/flat_buffers.h +++ b/flow/flat_buffers.h @@ -922,28 +922,30 @@ struct LoadSaveHelper : Context { static constexpr bool isSerializing = false; static constexpr bool is_fb_visitor = true; - const uint16_t* vtable; const uint8_t* current; - SerializeFun(const uint16_t* vtable, const uint8_t* current, Context& context) - : Context(context), vtable(vtable), current(current) {} + SerializeFun(const uint8_t* current, Context& context) : Context(context), current(current) {} template void operator()(Args&... members) { + if (sizeof...(Args) == 0) { + return; + } + uint32_t current_offset = interpret_as(current); + current += current_offset; + int32_t vtable_offset = interpret_as(current); + const uint16_t* vtable = reinterpret_cast(current - vtable_offset); int i = 0; uint16_t vtable_length = vtable[i++] / sizeof(uint16_t); uint16_t table_length = vtable[i++]; - for_each(LoadMember{ vtable, current, vtable_length, table_length, i, this->context() }, members...); + for_each(LoadMember{ vtable, current, vtable_length, table_length, i, this->context() }, + members...); } }; template std::enable_if_t> load(Member& member, const uint8_t* current) { - uint32_t current_offset = interpret_as(current); - current += current_offset; - int32_t vtable_offset = interpret_as(current); - const uint16_t* vtable = reinterpret_cast(current - vtable_offset); - SerializeFun fun(vtable, current, this->context()); + SerializeFun fun(current, this->context()); if constexpr (serializable_traits::value) { serializable_traits::serialize(fun, member); } else { From 7f28719229fcca00209db1282fb18a06fae8872c Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Mon, 2 Mar 2020 18:18:22 -0800 Subject: [PATCH 0795/1604] Fix issues in comments --- fdbrpc/FlowTests.actor.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fdbrpc/FlowTests.actor.cpp b/fdbrpc/FlowTests.actor.cpp index eb99b3fdf9..a2238a7e92 100644 --- a/fdbrpc/FlowTests.actor.cpp +++ b/fdbrpc/FlowTests.actor.cpp @@ -1275,9 +1275,8 @@ TEST_CASE("/flow/DeterministicRandom/SignedOverflow") { class PrivateKeyRangeTestImpl : public PrivateKeyRangeBaseImpl { public: - explicit PrivateKeyRangeTestImpl(KeyRef start, KeyRef end, std::string prefix, int size) : PrivateKeyRangeBaseImpl(start, end) { - this->prefix = prefix; - this->size = size; + explicit PrivateKeyRangeTestImpl(KeyRef start, KeyRef end, const std::string& prefix, int size) : + PrivateKeyRangeBaseImpl(start, end), prefix(prefix), size(size) { ASSERT(size > 0); for (int i = 0; i < size; ++i) { kvs.push_back_deep(kvs.arena(), KeyValueRef(getKeyForIndex(i), deterministicRandom()->randomAlphaNumeric(16))); From 146191e4115c2d907bb18940b4b5d437fffcb826 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Mon, 2 Mar 2020 19:02:35 -0800 Subject: [PATCH 0796/1604] Fix bug: change RYW* to Reference --- fdbclient/DatabaseContext.h | 5 +++-- fdbclient/PrivateKeySpace.actor.cpp | 10 +++++----- fdbclient/PrivateKeySpace.h | 9 +++++---- fdbclient/ReadYourWrites.actor.cpp | 3 ++- fdbrpc/FlowTests.actor.cpp | 17 +++++++++-------- 5 files changed, 24 insertions(+), 20 deletions(-) diff --git a/fdbclient/DatabaseContext.h b/fdbclient/DatabaseContext.h index 7f8d93861d..a11be86862 100644 --- a/fdbclient/DatabaseContext.h +++ b/fdbclient/DatabaseContext.h @@ -25,7 +25,7 @@ #include "fdbclient/NativeAPI.actor.h" #include "fdbclient/KeyRangeMap.h" #include "fdbclient/MasterProxyInterface.h" -#include "fdbclient/PrivateKeySpace.h" +// #include "fdbclient/PrivateKeySpace.h" #include "fdbrpc/QueueModel.h" #include "fdbrpc/MultiInterface.h" #include "flow/TDMetric.actor.h" @@ -46,6 +46,7 @@ private: typedef MultiInterface> LocationInfo; typedef MultiInterface ProxyInfo; +class PrivateKeySpace; //forward declaration class DatabaseContext : public ReferenceCounted, public FastAllocated, NonCopyable { public: static DatabaseContext* allocateOnForeignThread() { @@ -203,7 +204,7 @@ public: double detailedHealthMetricsLastUpdated; UniqueOrderedOptionList transactionDefaults; - PrivateKeySpace privateKeySpace; + PrivateKeySpace* privateKeySpace; }; #endif diff --git a/fdbclient/PrivateKeySpace.actor.cpp b/fdbclient/PrivateKeySpace.actor.cpp index aa88a28345..f5ca2f3a22 100644 --- a/fdbclient/PrivateKeySpace.actor.cpp +++ b/fdbclient/PrivateKeySpace.actor.cpp @@ -4,7 +4,7 @@ namespace { ACTOR Future> getActor( PrivateKeySpace* pks, - ReadYourWritesTransaction* ryw, + Reference ryw, KeyRef key ) { // use getRange to workaround this @@ -26,7 +26,7 @@ ACTOR Future> getActor( // Seperate each part to make the code easy to understand and more compact ACTOR Future normalizeKeySelectorActor( const PrivateKeyRangeBaseImpl* pkrImpl, - ReadYourWritesTransaction* ryw, + Reference ryw, KeySelector* ks ) { ASSERT(!ks->orEqual); // should be removed before calling @@ -82,7 +82,7 @@ ACTOR Future normalizeKeySelectorActor( ACTOR Future> getRangeAggregationActor( PrivateKeySpace* pks, - ReadYourWritesTransaction* ryw, + Reference ryw, KeySelector begin, KeySelector end, GetRangeLimits limits, @@ -177,7 +177,7 @@ ACTOR Future> getRangeAggregationActor( } // namespace end Future> PrivateKeySpace::getRange( - ReadYourWritesTransaction* ryw, + Reference ryw, KeySelector begin, KeySelector end, GetRangeLimits limits, @@ -196,7 +196,7 @@ Future> PrivateKeySpace::getRange( } Future> PrivateKeySpace::get( - ReadYourWritesTransaction* ryw, + Reference ryw, const Key& key, bool snapshot) { diff --git a/fdbclient/PrivateKeySpace.h b/fdbclient/PrivateKeySpace.h index 64715d0811..8a6c3dff83 100644 --- a/fdbclient/PrivateKeySpace.h +++ b/fdbclient/PrivateKeySpace.h @@ -6,8 +6,9 @@ #include "flow/Arena.h" #include "fdbclient/FDBTypes.h" #include "fdbclient/KeyRangeMap.h" +#include "fdbclient/ReadYourWrites.h" -class ReadYourWritesTransaction; +// class ReadYourWritesTransaction; class PrivateKeyRangeBaseImpl { public: @@ -18,7 +19,7 @@ public: // virtual Future> getRange(ReadYourWritesTransaction* ryw, KeySelector begin, KeySelector end, GetRangeLimits limits, bool snapshot = false, bool reverse = false) const = 0; // Each derived class only needs to implement this simple version of getRange - virtual Future> getRange(ReadYourWritesTransaction* ryw, KeyRangeRef kr) const = 0; + virtual Future> getRange(Reference ryw, KeyRangeRef kr) const = 0; explicit PrivateKeyRangeBaseImpl(KeyRef start, KeyRef end) { range = KeyRangeRef(range.arena(), KeyRangeRef(start, end)); @@ -39,9 +40,9 @@ protected: class PrivateKeySpace { public: - Future> get(ReadYourWritesTransaction* ryw, const Key& key, bool snapshot = false); + Future> get(Reference ryw, const Key& key, bool snapshot = false); - Future> getRange(ReadYourWritesTransaction* ryw, KeySelector begin, KeySelector end, GetRangeLimits limits, bool snapshot = false, bool reverse = false); + Future> getRange(Reference ryw, KeySelector begin, KeySelector end, GetRangeLimits limits, bool snapshot = false, bool reverse = false); PrivateKeySpace(KeyRef spaceStartKey = Key(), KeyRef spaceEndKey = allKeys.end) { // Default value is NULL, begin of KeyRangeMap is Key() diff --git a/fdbclient/ReadYourWrites.actor.cpp b/fdbclient/ReadYourWrites.actor.cpp index ef13f82b1a..e31c5cfdb3 100644 --- a/fdbclient/ReadYourWrites.actor.cpp +++ b/fdbclient/ReadYourWrites.actor.cpp @@ -21,6 +21,7 @@ #include "fdbclient/ReadYourWrites.h" #include "fdbclient/Atomic.h" #include "fdbclient/DatabaseContext.h" +#include "fdbclient/PrivateKeySpace.h" #include "fdbclient/StatusClient.h" #include "fdbclient/MonitorLeader.h" #include "flow/Util.h" @@ -1282,7 +1283,7 @@ Future< Standalone > ReadYourWritesTransaction::getRange( // start with simplest point, private key space are only allowed to query if both begin and end start with \xff\xff const KeyRef privateKeyPrefix = systemKeys.end; if (begin.getKey().startsWith(privateKeyPrefix) && end.getKey().startsWith(privateKeyPrefix)) - return getDatabase()->privateKeySpace.getRange(this, begin, end, limits, snapshot, reverse); + return getDatabase()->privateKeySpace->getRange(Reference(this), begin, end, limits, snapshot, reverse); if(checkUsedDuringCommit()) { return used_during_commit(); diff --git a/fdbrpc/FlowTests.actor.cpp b/fdbrpc/FlowTests.actor.cpp index a2238a7e92..87b094415d 100644 --- a/fdbrpc/FlowTests.actor.cpp +++ b/fdbrpc/FlowTests.actor.cpp @@ -1289,7 +1289,7 @@ public: return Key( prefix + format("%010d", idx)).withPrefix(range.begin); } - virtual Future> getRange(ReadYourWritesTransaction* ryw, KeyRangeRef kr) const override { + virtual Future> getRange(Reference ryw, KeyRangeRef kr) const override { int startIndex=0, endIndex= size; while (startIndex < size && kvs[startIndex].key < kr.begin) ++startIndex; @@ -1314,13 +1314,14 @@ TEST_CASE("/fdbclient/PrivateKeySpace/Aggregation") { pks.registerKeyRange(pkr1.getKeyRange(), &pkr1); pks.registerKeyRange(pkr2.getKeyRange(), &pkr2); pks.registerKeyRange(pkr3.getKeyRange(), &pkr3); + auto nullRef = Reference(); // get { - auto resultFuture = pks.get(NULL, LiteralStringRef("\xff\xff/cat/small0000000009")); + auto resultFuture = pks.get(nullRef, LiteralStringRef("\xff\xff/cat/small0000000009")); ASSERT(resultFuture.isReady()); auto result = resultFuture.getValue().get(); ASSERT(result == pkr1.getKeyValueForIndex(9).value); - auto emptyFuture = pks.get(NULL, LiteralStringRef("\xff\xff/cat/small0000000010")); + auto emptyFuture = pks.get(nullRef, LiteralStringRef("\xff\xff/cat/small0000000010")); ASSERT(emptyFuture.isReady()); auto emptyResult = emptyFuture.getValue(); ASSERT(!emptyResult.present()); @@ -1329,7 +1330,7 @@ TEST_CASE("/fdbclient/PrivateKeySpace/Aggregation") { { KeySelector start = KeySelectorRef(LiteralStringRef("\xff\xff/elepant"), false, -9); KeySelector end = KeySelectorRef(LiteralStringRef("\xff\xff/frog"), false, +11); - auto resultFuture = pks.getRange(NULL, start, end, GetRangeLimits()); + auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits()); ASSERT(resultFuture.isReady()); auto result = resultFuture.getValue(); ASSERT(result.size() == 20); @@ -1340,7 +1341,7 @@ TEST_CASE("/fdbclient/PrivateKeySpace/Aggregation") { { KeySelector start = KeySelectorRef(pkr3.getKeyForIndex(999), true, -1110); KeySelector end = KeySelectorRef(pkr1.getKeyForIndex(0), false, +1112); - auto resultFuture = pks.getRange(NULL, start, end, GetRangeLimits()); + auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits()); ASSERT(resultFuture.isReady()); auto result = resultFuture.getValue(); ASSERT(result.size() == 1110); @@ -1351,7 +1352,7 @@ TEST_CASE("/fdbclient/PrivateKeySpace/Aggregation") { { KeySelector start = KeySelectorRef(pkr2.getKeyForIndex(0), true, 0); KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(0), false, 0); - auto resultFuture = pks.getRange(NULL, start, end, GetRangeLimits(2)); + auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits(2)); ASSERT(resultFuture.isReady()); auto result = resultFuture.getValue(); ASSERT(result.size() == 2); @@ -1362,7 +1363,7 @@ TEST_CASE("/fdbclient/PrivateKeySpace/Aggregation") { { KeySelector start = KeySelectorRef(pkr2.getKeyForIndex(0), true, 0); KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(0), false, 0); - auto resultFuture = pks.getRange(NULL, start, end, GetRangeLimits(10, 100)); + auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits(10, 100)); ASSERT(resultFuture.isReady()); auto result = resultFuture.getValue(); int bytes = 0; @@ -1375,7 +1376,7 @@ TEST_CASE("/fdbclient/PrivateKeySpace/Aggregation") { { KeySelector start = KeySelectorRef(pkr2.getKeyForIndex(0), true, 0); KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(0), false, 0); - auto resultFuture = pks.getRange(NULL, start, end, GetRangeLimits(100), false, true); + auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits(100), false, true); ASSERT(resultFuture.isReady()); auto result = resultFuture.getValue(); for (int i = 0; i < result.size(); ++i) From 4ee9ce15fcc218b4b1ad48f56d5380793dcee942 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Tue, 3 Mar 2020 00:29:40 -0800 Subject: [PATCH 0797/1604] Change from NULL to nullptr --- fdbclient/DatabaseContext.h | 2 +- fdbclient/NativeAPI.actor.cpp | 2 ++ fdbclient/PrivateKeySpace.actor.cpp | 8 ++++---- fdbclient/PrivateKeySpace.h | 4 ++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/fdbclient/DatabaseContext.h b/fdbclient/DatabaseContext.h index a11be86862..a52aa029f7 100644 --- a/fdbclient/DatabaseContext.h +++ b/fdbclient/DatabaseContext.h @@ -204,7 +204,7 @@ public: double detailedHealthMetricsLastUpdated; UniqueOrderedOptionList transactionDefaults; - PrivateKeySpace* privateKeySpace; + std::unique_ptr privateKeySpace; }; #endif diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index 1746e799f4..31cbb94260 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -32,6 +32,7 @@ #include "fdbclient/MasterProxyInterface.h" #include "fdbclient/MonitorLeader.h" #include "fdbclient/MutationList.h" +#include "fdbclient/PrivateKeySpace.h" #include "fdbclient/StorageServerInterface.h" #include "fdbclient/SystemData.h" #include "fdbrpc/LoadBalance.h" @@ -545,6 +546,7 @@ DatabaseContext::DatabaseContext(Reference(LiteralStringRef("\xff\xff"), LiteralStringRef("\xff\xff\xff\xff")); } DatabaseContext::DatabaseContext(const Error& err) diff --git a/fdbclient/PrivateKeySpace.actor.cpp b/fdbclient/PrivateKeySpace.actor.cpp index f5ca2f3a22..b92692e386 100644 --- a/fdbclient/PrivateKeySpace.actor.cpp +++ b/fdbclient/PrivateKeySpace.actor.cpp @@ -101,7 +101,7 @@ ACTOR Future> getRangeAggregationActor( state RangeMap::Iterator iter = pks->getKeyRangeMap().rangeContaining(begin.getKey()); while (begin.offset != 1 && iter != pks->getKeyRangeMap().ranges().begin()) { - if (iter->value() != NULL) + if (iter->value() != nullptr) wait(normalizeKeySelectorActor(iter->value(), ryw, &begin)); begin.offset < 1 ? --iter : ++iter; } @@ -113,7 +113,7 @@ ACTOR Future> getRangeAggregationActor( } iter = pks->getKeyRangeMap().rangeContaining(end.getKey()); while (end.offset != 1 && iter != pks->getKeyRangeMap().ranges().end()) { - if (iter->value() != NULL) + if (iter->value() != nullptr) wait(normalizeKeySelectorActor(iter->value(), ryw, &end)); end.offset < 1 ? --iter : ++iter; } @@ -137,7 +137,7 @@ ACTOR Future> getRangeAggregationActor( if (reverse) { while (iter != ranges.begin()) { --iter; - if (iter->value() == NULL) + if (iter->value() == nullptr) continue; KeyRangeRef kr = iter->range(); KeyRef keyStart = kr.contains(begin.getKey()) ? begin.getKey() : kr.begin; @@ -155,7 +155,7 @@ ACTOR Future> getRangeAggregationActor( } } else { for (iter = ranges.begin(); iter != ranges.end(); ++iter) { - if (iter->value() == NULL) + if (iter->value() == nullptr) continue; KeyRangeRef kr = iter->range(); KeyRef keyStart = kr.contains(begin.getKey()) ? begin.getKey() : kr.begin; diff --git a/fdbclient/PrivateKeySpace.h b/fdbclient/PrivateKeySpace.h index 8a6c3dff83..3254aa9744 100644 --- a/fdbclient/PrivateKeySpace.h +++ b/fdbclient/PrivateKeySpace.h @@ -45,8 +45,8 @@ public: Future> getRange(Reference ryw, KeySelector begin, KeySelector end, GetRangeLimits limits, bool snapshot = false, bool reverse = false); PrivateKeySpace(KeyRef spaceStartKey = Key(), KeyRef spaceEndKey = allKeys.end) { - // Default value is NULL, begin of KeyRangeMap is Key() - impls = KeyRangeMap(NULL, spaceEndKey); + // Default value is nullptr, begin of KeyRangeMap is Key() + impls = KeyRangeMap(nullptr, spaceEndKey); range = KeyRangeRef(spaceStartKey, spaceEndKey); } void registerKeyRange(const KeyRangeRef& kr, PrivateKeyRangeBaseImpl* impl) { From c3b67c0c637e7a2ef050b52fc51cadd53b4e9b71 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Tue, 3 Mar 2020 11:32:43 -0800 Subject: [PATCH 0798/1604] Fix OPEN_FOR_IDE build --- fdbserver/RestoreApplier.actor.cpp | 2 +- fdbserver/RestoreWorker.actor.cpp | 4 ++-- fdbserver/networktest.actor.cpp | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/fdbserver/RestoreApplier.actor.cpp b/fdbserver/RestoreApplier.actor.cpp index e5fd6c7a14..6df9baec32 100644 --- a/fdbserver/RestoreApplier.actor.cpp +++ b/fdbserver/RestoreApplier.actor.cpp @@ -228,7 +228,7 @@ ACTOR static Future getAndComputeStagingKeys( } ASSERT(fValues.size() == incompleteStagingKeys.size()); - int i = 0; + i = 0; for (auto& key : incompleteStagingKeys) { if (!fValues[i].get().present()) { TraceEvent(SevWarnAlways, "FastRestoreApplierGetAndComputeStagingKeysUnhandledError") diff --git a/fdbserver/RestoreWorker.actor.cpp b/fdbserver/RestoreWorker.actor.cpp index 393466aa2b..2970485186 100644 --- a/fdbserver/RestoreWorker.actor.cpp +++ b/fdbserver/RestoreWorker.actor.cpp @@ -253,10 +253,10 @@ ACTOR Future monitorleader(Reference> lea wait(delay(SERVER_KNOBS->FASTRESTORE_MONITOR_LEADER_DELAY)); TraceEvent("FastRestoreWorker", myWorkerInterf.id()).detail("MonitorLeader", "StartLeaderElection"); state int count = 0; + state RestoreWorkerInterface leaderInterf; + state ReadYourWritesTransaction tr(cx); // MX: Somewhere here program gets stuck loop { try { - state RestoreWorkerInterface leaderInterf; - state ReadYourWritesTransaction tr(cx); // MX: Somewhere here program gets stuck count++; tr.reset(); tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); diff --git a/fdbserver/networktest.actor.cpp b/fdbserver/networktest.actor.cpp index feb59e9df6..fff160d8d7 100644 --- a/fdbserver/networktest.actor.cpp +++ b/fdbserver/networktest.actor.cpp @@ -113,7 +113,6 @@ static bool moreLoggingNeeded(int count, int iteration) { ACTOR Future testClient(std::vector interfs, int* sent, int* completed, LatencyStats* latency) { state std::string request_payload(FLOW_KNOBS->NETWORK_TEST_REQUEST_SIZE, '.'); - state int count = FLOW_KNOBS->NETWORK_TEST_REQUEST_COUNT; state LatencyStats::sample sample; while (moreRequestsPending(*sent)) { From 2c1553a3c8ebdf594e05bcb07bd6a2a73bdd3ae5 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Tue, 3 Mar 2020 11:59:55 -0800 Subject: [PATCH 0799/1604] clang-format code --- fdbclient/PrivateKeySpace.actor.cpp | 328 +++++++++++++--------------- fdbclient/PrivateKeySpace.h | 25 ++- 2 files changed, 161 insertions(+), 192 deletions(-) diff --git a/fdbclient/PrivateKeySpace.actor.cpp b/fdbclient/PrivateKeySpace.actor.cpp index b92692e386..5ce5c69667 100644 --- a/fdbclient/PrivateKeySpace.actor.cpp +++ b/fdbclient/PrivateKeySpace.actor.cpp @@ -1,205 +1,173 @@ #include "fdbclient/PrivateKeySpace.h" -#include "flow/actorcompiler.h" // This must be the last #include. +#include "flow/actorcompiler.h" // This must be the last #include. namespace { -ACTOR Future> getActor( - PrivateKeySpace* pks, - Reference ryw, - KeyRef key ) -{ - // use getRange to workaround this - Standalone result = wait(pks->getRange(ryw, KeySelector( firstGreaterOrEqual(key) ), - KeySelector( firstGreaterOrEqual(keyAfter(key)) ), GetRangeLimits())); - ASSERT(result.size() <= 1); - if (result.size()) { - return Optional(result[0].value); - } else { - return Optional(); - } +ACTOR Future> getActor(PrivateKeySpace* pks, Reference ryw, KeyRef key) { + // use getRange to workaround this + Standalone result = wait(pks->getRange( + ryw, KeySelector(firstGreaterOrEqual(key)), KeySelector(firstGreaterOrEqual(keyAfter(key))), GetRangeLimits())); + ASSERT(result.size() <= 1); + if (result.size()) { + return Optional(result[0].value); + } else { + return Optional(); + } } // This function will normalize the given KeySelector to a standard KeySelector: // orEqual == false && offset == 1 (Standard form) // If the corresponding key is not in this private key range, it will move as far as possible to adjust the offset to 1 // It does have overhead here since we query all keys twice in the worst case. -// However, moving the KeySelector while handling other parameters like limits makes the code much more complex and hard to maintain -// Seperate each part to make the code easy to understand and more compact -ACTOR Future normalizeKeySelectorActor( - const PrivateKeyRangeBaseImpl* pkrImpl, - Reference ryw, - KeySelector* ks ) -{ - ASSERT(!ks->orEqual); // should be removed before calling - ASSERT(ks->offset != 1); // The function is never called when KeySelector is already normalized - - state KeyRangeRef range = pkrImpl->getKeyRange(); - state Key startKey(range.begin); - state Key endKey(range.end); +// However, moving the KeySelector while handling other parameters like limits makes the code much more complex and hard +// to maintain Seperate each part to make the code easy to understand and more compact +ACTOR Future normalizeKeySelectorActor(const PrivateKeyRangeBaseImpl* pkrImpl, + Reference ryw, KeySelector* ks) { + ASSERT(!ks->orEqual); // should be removed before calling + ASSERT(ks->offset != 1); // The function is never called when KeySelector is already normalized - if (ks->offset < 1) { - // less than the given key - if (range.contains(ks->getKey())) - endKey = keyAfter(ks->getKey()); - } - else { - // greater than the given key - if (range.contains(ks->getKey())) - startKey = ks->getKey(); - } + state KeyRangeRef range = pkrImpl->getKeyRange(); + state Key startKey(range.begin); + state Key endKey(range.end); - TraceEvent("NormalizeKeySelector"). - detail("OriginalKey", ks->getKey()). - detail("OriginalOffset", ks->offset). - detail("PrivateKeyRangeStart", range.begin). - detail("PrivateKeyRangeEnd", range.end); - - Standalone result = wait(pkrImpl->getRange(ryw, KeyRangeRef(startKey, endKey))); - // TODO : KeySelector::setKey has byte limit according to the knobs, customize it if needed - if (ks->offset < 1) { - if (result.size() >= 1 - ks->offset) { - ks->setKey(KeyRef(ks->arena(), result[result.size()-(1-ks->offset)].key)); - ks->offset = 1; - } else { - ks->setKey(KeyRef(ks->arena(), result[0].key)); - ks->offset += result.size(); - } - } else { - if (result.size() >= ks->offset) { - ks->setKey(KeyRef(ks->arena(), result[ks->offset - 1].key)); - ks->offset = 1; - } else { - ks->setKey(KeyRef(ks->arena(), keyAfter(result[result.size()-1].key))); - ks->offset -= result.size(); - } - } - TraceEvent("NormalizeKeySelector"). - detail("NormalizedKey", ks->getKey()). - detail("NormalizedOffset", ks->offset). - detail("PrivateKeyRangeStart", range.begin). - detail("PrivateKeyRangeEnd", range.end); - return Void(); + if (ks->offset < 1) { + // less than the given key + if (range.contains(ks->getKey())) endKey = keyAfter(ks->getKey()); + } else { + // greater than the given key + if (range.contains(ks->getKey())) startKey = ks->getKey(); + } + + TraceEvent("NormalizeKeySelector") + .detail("OriginalKey", ks->getKey()) + .detail("OriginalOffset", ks->offset) + .detail("PrivateKeyRangeStart", range.begin) + .detail("PrivateKeyRangeEnd", range.end); + + Standalone result = wait(pkrImpl->getRange(ryw, KeyRangeRef(startKey, endKey))); + // TODO : KeySelector::setKey has byte limit according to the knobs, customize it if needed + if (ks->offset < 1) { + if (result.size() >= 1 - ks->offset) { + ks->setKey(KeyRef(ks->arena(), result[result.size() - (1 - ks->offset)].key)); + ks->offset = 1; + } else { + ks->setKey(KeyRef(ks->arena(), result[0].key)); + ks->offset += result.size(); + } + } else { + if (result.size() >= ks->offset) { + ks->setKey(KeyRef(ks->arena(), result[ks->offset - 1].key)); + ks->offset = 1; + } else { + ks->setKey(KeyRef(ks->arena(), keyAfter(result[result.size() - 1].key))); + ks->offset -= result.size(); + } + } + TraceEvent("NormalizeKeySelector") + .detail("NormalizedKey", ks->getKey()) + .detail("NormalizedOffset", ks->offset) + .detail("PrivateKeyRangeStart", range.begin) + .detail("PrivateKeyRangeEnd", range.end); + return Void(); } -ACTOR Future> getRangeAggregationActor( - PrivateKeySpace* pks, - Reference ryw, - KeySelector begin, - KeySelector end, - GetRangeLimits limits, - bool reverse ) -{ - // This function handles ranges which cover more than one keyrange and aggregates all results - // KeySelector, GetRangeLimits and reverse are all handled here - - // make sure orEqual == false - if(begin.orEqual) - begin.removeOrEqual(begin.arena()); - if(end.orEqual) - end.removeOrEqual(end.arena()); +ACTOR Future> getRangeAggregationActor(PrivateKeySpace* pks, + Reference ryw, + KeySelector begin, KeySelector end, + GetRangeLimits limits, bool reverse) { + // This function handles ranges which cover more than one keyrange and aggregates all results + // KeySelector, GetRangeLimits and reverse are all handled here - // make sure offset == 1 - state RangeMap::Iterator iter = - pks->getKeyRangeMap().rangeContaining(begin.getKey()); - while (begin.offset != 1 && iter != pks->getKeyRangeMap().ranges().begin()) { - if (iter->value() != nullptr) - wait(normalizeKeySelectorActor(iter->value(), ryw, &begin)); - begin.offset < 1 ? --iter : ++iter; - } - if (begin.offset != 1) { - // The Key Selector points to key outside the whole private key space - TraceEvent(SevError, "IllegalBeginKeySelector"). - detail("TerminateKey", begin.getKey()). - detail("TerminateOffset", begin.offset); - } - iter = pks->getKeyRangeMap().rangeContaining(end.getKey()); - while (end.offset != 1 && iter != pks->getKeyRangeMap().ranges().end()) { - if (iter->value() != nullptr) - wait(normalizeKeySelectorActor(iter->value(), ryw, &end)); - end.offset < 1 ? --iter : ++iter; - } - if (end.offset != 1) { - // The Key Selector points to key outside the whole private key space - TraceEvent(SevError, "IllegalEndKeySelector"). - detail("TerminateKey", end.getKey()). - detail("TerminateOffset", end.offset); - } - // return if range inverted - if( begin.offset >= end.offset && begin.getKey() >= end.getKey() ) { + // make sure orEqual == false + if (begin.orEqual) begin.removeOrEqual(begin.arena()); + if (end.orEqual) end.removeOrEqual(end.arena()); + + // make sure offset == 1 + state RangeMap::Iterator iter = + pks->getKeyRangeMap().rangeContaining(begin.getKey()); + while (begin.offset != 1 && iter != pks->getKeyRangeMap().ranges().begin()) { + if (iter->value() != nullptr) wait(normalizeKeySelectorActor(iter->value(), ryw, &begin)); + begin.offset < 1 ? --iter : ++iter; + } + if (begin.offset != 1) { + // The Key Selector points to key outside the whole private key space + TraceEvent(SevError, "IllegalBeginKeySelector") + .detail("TerminateKey", begin.getKey()) + .detail("TerminateOffset", begin.offset); + } + iter = pks->getKeyRangeMap().rangeContaining(end.getKey()); + while (end.offset != 1 && iter != pks->getKeyRangeMap().ranges().end()) { + if (iter->value() != nullptr) wait(normalizeKeySelectorActor(iter->value(), ryw, &end)); + end.offset < 1 ? --iter : ++iter; + } + if (end.offset != 1) { + // The Key Selector points to key outside the whole private key space + TraceEvent(SevError, "IllegalEndKeySelector") + .detail("TerminateKey", end.getKey()) + .detail("TerminateOffset", end.offset); + } + // return if range inverted + if (begin.offset >= end.offset && begin.getKey() >= end.getKey()) { TEST(true); return Standalone(); } - state Standalone result; - state RangeMap::Ranges ranges = - pks->getKeyRangeMap().intersectingRanges(KeyRangeRef(begin.getKey(), end.getKey())); - // 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(); - if (reverse) { - while (iter != ranges.begin()) { - --iter; - if (iter->value() == nullptr) - continue; - KeyRangeRef kr = iter->range(); - KeyRef keyStart = kr.contains(begin.getKey()) ? begin.getKey() : kr.begin; - KeyRef keyEnd = kr.contains(end.getKey()) ? end.getKey() : kr.end; - Standalone pairs = wait(iter->value()->getRange(ryw, KeyRangeRef(keyStart, keyEnd))); - // limits handler - for (int i = pairs.size() - 1; i >= 0; --i) { - result.push_back_deep(result.arena(), pairs[i]); - // TODO : the behavior here is even the last kv makes bytes larger than specified, - // it is still returned and set limits.bytes to zero - limits.decrement(pairs[i]); - if (limits.isReached()) - return result; - } - } - } else { - for (iter = ranges.begin(); iter != ranges.end(); ++iter) { - if (iter->value() == nullptr) - continue; - KeyRangeRef kr = iter->range(); - KeyRef keyStart = kr.contains(begin.getKey()) ? begin.getKey() : kr.begin; - KeyRef keyEnd = kr.contains(end.getKey()) ? end.getKey() : kr.end; - Standalone pairs = wait(iter->value()->getRange(ryw, KeyRangeRef(keyStart, keyEnd))); - // limits handler - for (const KeyValueRef & kv : pairs) { - result.push_back_deep(result.arena(), kv); - // TODO : behavior here is even the last kv makes bytes larger than specified, - // it is still returned and set limits.bytes to zero - limits.decrement(kv); - if (limits.isReached()) - return result; - } - } - } - return result; + state Standalone result; + state RangeMap::Ranges ranges = + pks->getKeyRangeMap().intersectingRanges(KeyRangeRef(begin.getKey(), end.getKey())); + // 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(); + if (reverse) { + while (iter != ranges.begin()) { + --iter; + if (iter->value() == nullptr) continue; + KeyRangeRef kr = iter->range(); + KeyRef keyStart = kr.contains(begin.getKey()) ? begin.getKey() : kr.begin; + KeyRef keyEnd = kr.contains(end.getKey()) ? end.getKey() : kr.end; + Standalone pairs = wait(iter->value()->getRange(ryw, KeyRangeRef(keyStart, keyEnd))); + // limits handler + for (int i = pairs.size() - 1; i >= 0; --i) { + result.push_back_deep(result.arena(), pairs[i]); + // TODO : the behavior here is even the last kv makes bytes larger than specified, + // it is still returned and set limits.bytes to zero + limits.decrement(pairs[i]); + if (limits.isReached()) return result; + } + } + } else { + for (iter = ranges.begin(); iter != ranges.end(); ++iter) { + if (iter->value() == nullptr) continue; + KeyRangeRef kr = iter->range(); + KeyRef keyStart = kr.contains(begin.getKey()) ? begin.getKey() : kr.begin; + KeyRef keyEnd = kr.contains(end.getKey()) ? end.getKey() : kr.end; + Standalone pairs = wait(iter->value()->getRange(ryw, KeyRangeRef(keyStart, keyEnd))); + // limits handler + for (const KeyValueRef& kv : pairs) { + result.push_back_deep(result.arena(), kv); + // TODO : behavior here is even the last kv makes bytes larger than specified, + // it is still returned and set limits.bytes to zero + limits.decrement(kv); + if (limits.isReached()) return result; + } + } + } + return result; } -} // namespace end -Future> PrivateKeySpace::getRange( - Reference ryw, - KeySelector begin, - KeySelector end, - GetRangeLimits limits, - bool snapshot, - bool reverse ) -{ - // validate limits here - if( !limits.isValid() ) - return range_limits_invalid(); - if( limits.isReached() ) { +} // namespace +Future> PrivateKeySpace::getRange(Reference ryw, + KeySelector begin, KeySelector end, GetRangeLimits limits, + bool snapshot, bool reverse) { + // validate limits here + if (!limits.isValid()) return range_limits_invalid(); + if (limits.isReached()) { TEST(true); // read limit 0 return Standalone(); } - // ignore snapshot, which is not used - return getRangeAggregationActor(this, ryw, begin, end, limits, reverse); + // ignore snapshot, which is not used + return getRangeAggregationActor(this, ryw, begin, end, limits, reverse); } -Future> PrivateKeySpace::get( - Reference ryw, - const Key& key, - bool snapshot) -{ - // ignore snapshot, which is not used - return getActor(this, ryw, key); +Future> PrivateKeySpace::get(Reference ryw, const Key& key, bool snapshot) { + // ignore snapshot, which is not used + return getActor(this, ryw, key); } diff --git a/fdbclient/PrivateKeySpace.h b/fdbclient/PrivateKeySpace.h index 3254aa9744..81d468c4c8 100644 --- a/fdbclient/PrivateKeySpace.h +++ b/fdbclient/PrivateKeySpace.h @@ -16,36 +16,39 @@ public: // Since a keyRange doesn't have any knowledge about other keyRanges, parameters like KeySelector, // GetRangeLimits should be handled together in PrivateKeySpace // Thus, having this general interface looks unnessary. - // virtual Future> getRange(ReadYourWritesTransaction* ryw, KeySelector begin, KeySelector end, GetRangeLimits limits, bool snapshot = false, bool reverse = false) const = 0; + // virtual Future> getRange(ReadYourWritesTransaction* ryw, KeySelector begin, + // KeySelector end, GetRangeLimits limits, bool snapshot = false, bool reverse = false) const = 0; // Each derived class only needs to implement this simple version of getRange - virtual Future> getRange(Reference ryw, KeyRangeRef kr) const = 0; + virtual Future> getRange(Reference ryw, + KeyRangeRef kr) const = 0; explicit PrivateKeyRangeBaseImpl(KeyRef start, KeyRef end) { range = KeyRangeRef(range.arena(), KeyRangeRef(start, end)); } - KeyRangeRef getKeyRange() const { - return range; - } + KeyRangeRef getKeyRange() const { return range; } + protected: KeyRange range; // underlying key range for this function }; - // class PrivateKeyRangeSimpleImpl : public PrivateKeyRangeBaseImpl { // public: // virtual Future> getRange(ReadYourWritesTransaction* ryw, KeyRangeRef kr) const = 0; -// virtual Future> getRange(ReadYourWritesTransaction* ryw, KeySelector begin, KeySelector end, GetRangeLimits limits, bool snapshot = false, bool reverse = false) const; +// virtual Future> getRange(ReadYourWritesTransaction* ryw, KeySelector begin, KeySelector +// end, GetRangeLimits limits, bool snapshot = false, bool reverse = false) const; // }; class PrivateKeySpace { public: Future> get(Reference ryw, const Key& key, bool snapshot = false); - Future> getRange(Reference ryw, KeySelector begin, KeySelector end, GetRangeLimits limits, bool snapshot = false, bool reverse = false); + Future> getRange(Reference ryw, KeySelector begin, + KeySelector end, GetRangeLimits limits, bool snapshot = false, + bool reverse = false); PrivateKeySpace(KeyRef spaceStartKey = Key(), KeyRef spaceEndKey = allKeys.end) { - // Default value is nullptr, begin of KeyRangeMap is Key() + // Default value is nullptr, begin of KeyRangeMap is Key() impls = KeyRangeMap(nullptr, spaceEndKey); range = KeyRangeRef(spaceStartKey, spaceEndKey); } @@ -55,9 +58,7 @@ public: impls.insert(kr, impl); } - KeyRangeMap& getKeyRangeMap(){ - return impls; - } + KeyRangeMap& getKeyRangeMap() { return impls; } private: KeyRangeMap impls; From 76e04541ca6b69265d5e6143a8933986ee30609c Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Tue, 3 Mar 2020 12:18:41 -0800 Subject: [PATCH 0800/1604] remove unecessary code --- fdbclient/PrivateKeySpace.actor.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/fdbclient/PrivateKeySpace.actor.cpp b/fdbclient/PrivateKeySpace.actor.cpp index 5ce5c69667..0da0b464b3 100644 --- a/fdbclient/PrivateKeySpace.actor.cpp +++ b/fdbclient/PrivateKeySpace.actor.cpp @@ -78,13 +78,14 @@ ACTOR Future> getRangeAggregationActor(PrivateKeySpac // KeySelector, GetRangeLimits and reverse are all handled here // make sure orEqual == false - if (begin.orEqual) begin.removeOrEqual(begin.arena()); - if (end.orEqual) end.removeOrEqual(end.arena()); + begin.removeOrEqual(begin.arena()); + end.removeOrEqual(end.arena()); // make sure offset == 1 state RangeMap::Iterator iter = pks->getKeyRangeMap().rangeContaining(begin.getKey()); - while (begin.offset != 1 && iter != pks->getKeyRangeMap().ranges().begin()) { + while (begin.offset != 1 && iter != pks->getKeyRangeMap().ranges().begin() && + iter != pks->getKeyRangeMap().ranges().end()) { if (iter->value() != nullptr) wait(normalizeKeySelectorActor(iter->value(), ryw, &begin)); begin.offset < 1 ? --iter : ++iter; } @@ -95,7 +96,8 @@ ACTOR Future> getRangeAggregationActor(PrivateKeySpac .detail("TerminateOffset", begin.offset); } iter = pks->getKeyRangeMap().rangeContaining(end.getKey()); - while (end.offset != 1 && iter != pks->getKeyRangeMap().ranges().end()) { + while (end.offset != 1 && iter != pks->getKeyRangeMap().ranges().begin() && + iter != pks->getKeyRangeMap().ranges().end()) { if (iter->value() != nullptr) wait(normalizeKeySelectorActor(iter->value(), ryw, &end)); end.offset < 1 ? --iter : ++iter; } From 94102969fd177f544a8c752bcb14540e15ad6f2e Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Tue, 3 Mar 2020 14:55:03 -0800 Subject: [PATCH 0801/1604] Merge code from upstream and change ACTOR to member class --- fdbclient/CMakeLists.txt | 2 +- fdbclient/DatabaseContext.h | 1 - fdbclient/NativeAPI.actor.cpp | 2 +- fdbclient/PrivateKeySpace.actor.cpp | 48 +++++++++---------- ...vateKeySpace.h => PrivateKeySpace.actor.h} | 19 ++++++-- fdbclient/ReadYourWrites.actor.cpp | 2 +- fdbclient/fdbclient.vcxproj | 2 +- fdbrpc/FlowTests.actor.cpp | 2 +- 8 files changed, 43 insertions(+), 35 deletions(-) rename fdbclient/{PrivateKeySpace.h => PrivateKeySpace.actor.h} (74%) diff --git a/fdbclient/CMakeLists.txt b/fdbclient/CMakeLists.txt index cc1f60dfad..0d23580c18 100644 --- a/fdbclient/CMakeLists.txt +++ b/fdbclient/CMakeLists.txt @@ -45,7 +45,7 @@ set(FDBCLIENT_SRCS NativeAPI.actor.h Notified.h PrivateKeySpace.actor.cpp - PrivateKeySpace.h + PrivateKeySpace.actor.h ReadYourWrites.actor.cpp ReadYourWrites.h RestoreWorkerInterface.actor.h diff --git a/fdbclient/DatabaseContext.h b/fdbclient/DatabaseContext.h index 44f9fbec67..c74a8de6b6 100644 --- a/fdbclient/DatabaseContext.h +++ b/fdbclient/DatabaseContext.h @@ -25,7 +25,6 @@ #include "fdbclient/NativeAPI.actor.h" #include "fdbclient/KeyRangeMap.h" #include "fdbclient/MasterProxyInterface.h" -// #include "fdbclient/PrivateKeySpace.h" #include "fdbrpc/QueueModel.h" #include "fdbrpc/MultiInterface.h" #include "flow/TDMetric.actor.h" diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index 5706a0fba0..f6a7c3c66e 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -32,7 +32,7 @@ #include "fdbclient/MasterProxyInterface.h" #include "fdbclient/MonitorLeader.h" #include "fdbclient/MutationList.h" -#include "fdbclient/PrivateKeySpace.h" +#include "fdbclient/PrivateKeySpace.actor.h" #include "fdbclient/StorageServerInterface.h" #include "fdbclient/SystemData.h" #include "fdbrpc/LoadBalance.h" diff --git a/fdbclient/PrivateKeySpace.actor.cpp b/fdbclient/PrivateKeySpace.actor.cpp index 0da0b464b3..f7401763fa 100644 --- a/fdbclient/PrivateKeySpace.actor.cpp +++ b/fdbclient/PrivateKeySpace.actor.cpp @@ -1,19 +1,7 @@ -#include "fdbclient/PrivateKeySpace.h" +#include "fdbclient/PrivateKeySpace.actor.h" #include "flow/actorcompiler.h" // This must be the last #include. namespace { -ACTOR Future> getActor(PrivateKeySpace* pks, Reference ryw, KeyRef key) { - // use getRange to workaround this - Standalone result = wait(pks->getRange( - ryw, KeySelector(firstGreaterOrEqual(key)), KeySelector(firstGreaterOrEqual(keyAfter(key))), GetRangeLimits())); - ASSERT(result.size() <= 1); - if (result.size()) { - return Optional(result[0].value); - } else { - return Optional(); - } -} - // This function will normalize the given KeySelector to a standard KeySelector: // orEqual == false && offset == 1 (Standard form) // If the corresponding key is not in this private key range, it will move as far as possible to adjust the offset to 1 @@ -70,10 +58,10 @@ ACTOR Future normalizeKeySelectorActor(const PrivateKeyRangeBaseImpl* pkrI return Void(); } -ACTOR Future> getRangeAggregationActor(PrivateKeySpace* pks, - Reference ryw, - KeySelector begin, KeySelector end, - GetRangeLimits limits, bool reverse) { +} // namespace +ACTOR Future> PrivateKeySpace::getRangeAggregationActor( + PrivateKeySpace* pks, Reference ryw, KeySelector begin, KeySelector end, + GetRangeLimits limits, bool reverse) { // This function handles ranges which cover more than one keyrange and aggregates all results // KeySelector, GetRangeLimits and reverse are all handled here @@ -83,9 +71,8 @@ ACTOR Future> getRangeAggregationActor(PrivateKeySpac // make sure offset == 1 state RangeMap::Iterator iter = - pks->getKeyRangeMap().rangeContaining(begin.getKey()); - while (begin.offset != 1 && iter != pks->getKeyRangeMap().ranges().begin() && - iter != pks->getKeyRangeMap().ranges().end()) { + pks->impls.rangeContaining(begin.getKey()); + while (begin.offset != 1 && iter != pks->impls.ranges().begin() && iter != pks->impls.ranges().end()) { if (iter->value() != nullptr) wait(normalizeKeySelectorActor(iter->value(), ryw, &begin)); begin.offset < 1 ? --iter : ++iter; } @@ -95,9 +82,8 @@ ACTOR Future> getRangeAggregationActor(PrivateKeySpac .detail("TerminateKey", begin.getKey()) .detail("TerminateOffset", begin.offset); } - iter = pks->getKeyRangeMap().rangeContaining(end.getKey()); - while (end.offset != 1 && iter != pks->getKeyRangeMap().ranges().begin() && - iter != pks->getKeyRangeMap().ranges().end()) { + iter = pks->impls.rangeContaining(end.getKey()); + while (end.offset != 1 && iter != pks->impls.ranges().begin() && iter != pks->impls.ranges().end()) { if (iter->value() != nullptr) wait(normalizeKeySelectorActor(iter->value(), ryw, &end)); end.offset < 1 ? --iter : ++iter; } @@ -114,7 +100,7 @@ ACTOR Future> getRangeAggregationActor(PrivateKeySpac } state Standalone result; state RangeMap::Ranges ranges = - pks->getKeyRangeMap().intersectingRanges(KeyRangeRef(begin.getKey(), end.getKey())); + pks->impls.intersectingRanges(KeyRangeRef(begin.getKey(), end.getKey())); // 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(); @@ -155,7 +141,6 @@ ACTOR Future> getRangeAggregationActor(PrivateKeySpac return result; } -} // namespace Future> PrivateKeySpace::getRange(Reference ryw, KeySelector begin, KeySelector end, GetRangeLimits limits, bool snapshot, bool reverse) { @@ -169,6 +154,19 @@ Future> PrivateKeySpace::getRange(Reference> PrivateKeySpace::getActor(PrivateKeySpace* pks, Reference ryw, + KeyRef key) { + // use getRange to workaround this + Standalone result = wait(pks->getRange( + ryw, KeySelector(firstGreaterOrEqual(key)), KeySelector(firstGreaterOrEqual(keyAfter(key))), GetRangeLimits())); + ASSERT(result.size() <= 1); + if (result.size()) { + return Optional(result[0].value); + } else { + return Optional(); + } +} + Future> PrivateKeySpace::get(Reference ryw, const Key& key, bool snapshot) { // ignore snapshot, which is not used return getActor(this, ryw, key); diff --git a/fdbclient/PrivateKeySpace.h b/fdbclient/PrivateKeySpace.actor.h similarity index 74% rename from fdbclient/PrivateKeySpace.h rename to fdbclient/PrivateKeySpace.actor.h index 81d468c4c8..bebe414a09 100644 --- a/fdbclient/PrivateKeySpace.h +++ b/fdbclient/PrivateKeySpace.actor.h @@ -1,14 +1,17 @@ -#ifndef FDBCLIENT_PRIVATEKEYSPACE_H -#define FDBCLIENT_PRIVATEKEYSPACE_H #pragma once +#if defined(NO_INTELLISENSE) && !defined(FDBCLIENT_PRIVATEKEYSPACE_ACTOR_G_H) +#define FDBCLIENT_PRIVATEKEYSPACE_ACTOR_G_H +#include "fdbclient/PrivateKeySpace.actor.g.h" +#elif !defined(FDBCLIENT_PRIVATEKEYSPACE_ACTOR_H) +#define FDBCLIENT_PRIVATEKEYSPACE_ACTOR_H + #include "flow/flow.h" #include "flow/Arena.h" #include "fdbclient/FDBTypes.h" #include "fdbclient/KeyRangeMap.h" #include "fdbclient/ReadYourWrites.h" - -// class ReadYourWritesTransaction; +#include "flow/actorcompiler.h" // This must be the last #include. class PrivateKeyRangeBaseImpl { public: @@ -58,6 +61,13 @@ public: impls.insert(kr, impl); } + ACTOR Future> getActor(PrivateKeySpace* pks, Reference ryw, KeyRef key); + + ACTOR Future> getRangeAggregationActor(PrivateKeySpace* pks, + Reference ryw, + KeySelector begin, KeySelector end, + GetRangeLimits limits, bool reverse); + KeyRangeMap& getKeyRangeMap() { return impls; } private: @@ -65,4 +75,5 @@ private: KeyRange range; }; +#include "flow/unactorcompiler.h" #endif diff --git a/fdbclient/ReadYourWrites.actor.cpp b/fdbclient/ReadYourWrites.actor.cpp index 9256d28ab7..6a5ddfe904 100644 --- a/fdbclient/ReadYourWrites.actor.cpp +++ b/fdbclient/ReadYourWrites.actor.cpp @@ -21,7 +21,7 @@ #include "fdbclient/ReadYourWrites.h" #include "fdbclient/Atomic.h" #include "fdbclient/DatabaseContext.h" -#include "fdbclient/PrivateKeySpace.h" +#include "fdbclient/PrivateKeySpace.actor.h" #include "fdbclient/StatusClient.h" #include "fdbclient/MonitorLeader.h" #include "flow/Util.h" diff --git a/fdbclient/fdbclient.vcxproj b/fdbclient/fdbclient.vcxproj index 7a0551146f..cd8d22e0af 100644 --- a/fdbclient/fdbclient.vcxproj +++ b/fdbclient/fdbclient.vcxproj @@ -77,7 +77,7 @@ false - + diff --git a/fdbrpc/FlowTests.actor.cpp b/fdbrpc/FlowTests.actor.cpp index fb98df7397..d90d855eef 100644 --- a/fdbrpc/FlowTests.actor.cpp +++ b/fdbrpc/FlowTests.actor.cpp @@ -25,7 +25,7 @@ #include "flow/IThreadPool.h" #include "fdbrpc/fdbrpc.h" #include "fdbrpc/IAsyncFile.h" -#include "fdbclient/PrivateKeySpace.h" +#include "fdbclient/PrivateKeySpace.actor.h" #include "flow/actorcompiler.h" // This must be the last #include. void forceLinkFlowTests() {} From 20058729d80e7a9277868d18468b2759e5974b97 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Tue, 3 Mar 2020 15:29:37 -0800 Subject: [PATCH 0802/1604] Format code --- fdbclient/PrivateKeySpace.actor.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/fdbclient/PrivateKeySpace.actor.h b/fdbclient/PrivateKeySpace.actor.h index bebe414a09..ed20258d35 100644 --- a/fdbclient/PrivateKeySpace.actor.h +++ b/fdbclient/PrivateKeySpace.actor.h @@ -30,7 +30,8 @@ public: range = KeyRangeRef(range.arena(), KeyRangeRef(start, end)); } KeyRangeRef getKeyRange() const { return range; } - + ACTOR Future normalizeKeySelectorActor(const PrivateKeyRangeBaseImpl* pkrImpl, + Reference ryw, KeySelector* ks); protected: KeyRange range; // underlying key range for this function }; @@ -61,6 +62,7 @@ public: impls.insert(kr, impl); } +private: ACTOR Future> getActor(PrivateKeySpace* pks, Reference ryw, KeyRef key); ACTOR Future> getRangeAggregationActor(PrivateKeySpace* pks, @@ -68,9 +70,6 @@ public: KeySelector begin, KeySelector end, GetRangeLimits limits, bool reverse); - KeyRangeMap& getKeyRangeMap() { return impls; } - -private: KeyRangeMap impls; KeyRange range; }; From 21dc34248e0225c342694f2040b277c5d2a16c99 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Tue, 3 Mar 2020 15:36:35 -0800 Subject: [PATCH 0803/1604] Move unit test to the right place --- fdbclient/PrivateKeySpace.actor.cpp | 139 +++++++++++++++++++++++++--- fdbrpc/FlowTests.actor.cpp | 113 ---------------------- 2 files changed, 125 insertions(+), 127 deletions(-) diff --git a/fdbclient/PrivateKeySpace.actor.cpp b/fdbclient/PrivateKeySpace.actor.cpp index f7401763fa..2c6d705d6d 100644 --- a/fdbclient/PrivateKeySpace.actor.cpp +++ b/fdbclient/PrivateKeySpace.actor.cpp @@ -1,35 +1,34 @@ #include "fdbclient/PrivateKeySpace.actor.h" +#include "flow/UnitTest.h" #include "flow/actorcompiler.h" // This must be the last #include. -namespace { // This function will normalize the given KeySelector to a standard KeySelector: // orEqual == false && offset == 1 (Standard form) // If the corresponding key is not in this private key range, it will move as far as possible to adjust the offset to 1 // It does have overhead here since we query all keys twice in the worst case. // However, moving the KeySelector while handling other parameters like limits makes the code much more complex and hard // to maintain Seperate each part to make the code easy to understand and more compact -ACTOR Future normalizeKeySelectorActor(const PrivateKeyRangeBaseImpl* pkrImpl, +ACTOR Future PrivateKeyRangeBaseImpl::normalizeKeySelectorActor(const PrivateKeyRangeBaseImpl* pkrImpl, Reference ryw, KeySelector* ks) { ASSERT(!ks->orEqual); // should be removed before calling ASSERT(ks->offset != 1); // The function is never called when KeySelector is already normalized - state KeyRangeRef range = pkrImpl->getKeyRange(); - state Key startKey(range.begin); - state Key endKey(range.end); + state Key startKey(pkrImpl->range.begin); + state Key endKey(pkrImpl->range.end); if (ks->offset < 1) { // less than the given key - if (range.contains(ks->getKey())) endKey = keyAfter(ks->getKey()); + if (pkrImpl->range.contains(ks->getKey())) endKey = keyAfter(ks->getKey()); } else { // greater than the given key - if (range.contains(ks->getKey())) startKey = ks->getKey(); + if (pkrImpl->range.contains(ks->getKey())) startKey = ks->getKey(); } TraceEvent("NormalizeKeySelector") .detail("OriginalKey", ks->getKey()) .detail("OriginalOffset", ks->offset) - .detail("PrivateKeyRangeStart", range.begin) - .detail("PrivateKeyRangeEnd", range.end); + .detail("PrivateKeyRangeStart", pkrImpl->range.begin) + .detail("PrivateKeyRangeEnd", pkrImpl->range.end); Standalone result = wait(pkrImpl->getRange(ryw, KeyRangeRef(startKey, endKey))); // TODO : KeySelector::setKey has byte limit according to the knobs, customize it if needed @@ -53,12 +52,11 @@ ACTOR Future normalizeKeySelectorActor(const PrivateKeyRangeBaseImpl* pkrI TraceEvent("NormalizeKeySelector") .detail("NormalizedKey", ks->getKey()) .detail("NormalizedOffset", ks->offset) - .detail("PrivateKeyRangeStart", range.begin) - .detail("PrivateKeyRangeEnd", range.end); + .detail("PrivateKeyRangeStart", pkrImpl->range.begin) + .detail("PrivateKeyRangeEnd", pkrImpl->range.end); return Void(); } -} // namespace ACTOR Future> PrivateKeySpace::getRangeAggregationActor( PrivateKeySpace* pks, Reference ryw, KeySelector begin, KeySelector end, GetRangeLimits limits, bool reverse) { @@ -73,7 +71,7 @@ ACTOR Future> PrivateKeySpace::getRangeAggregationAct state RangeMap::Iterator iter = pks->impls.rangeContaining(begin.getKey()); while (begin.offset != 1 && iter != pks->impls.ranges().begin() && iter != pks->impls.ranges().end()) { - if (iter->value() != nullptr) wait(normalizeKeySelectorActor(iter->value(), ryw, &begin)); + if (iter->value() != nullptr) wait(iter->value()->normalizeKeySelectorActor(iter->value(), ryw, &begin)); begin.offset < 1 ? --iter : ++iter; } if (begin.offset != 1) { @@ -84,7 +82,7 @@ ACTOR Future> PrivateKeySpace::getRangeAggregationAct } iter = pks->impls.rangeContaining(end.getKey()); while (end.offset != 1 && iter != pks->impls.ranges().begin() && iter != pks->impls.ranges().end()) { - if (iter->value() != nullptr) wait(normalizeKeySelectorActor(iter->value(), ryw, &end)); + if (iter->value() != nullptr) wait(iter->value()->normalizeKeySelectorActor(iter->value(), ryw, &end)); end.offset < 1 ? --iter : ++iter; } if (end.offset != 1) { @@ -171,3 +169,116 @@ Future> PrivateKeySpace::get(Reference 0); + for (int i = 0; i < size; ++i) { + kvs.push_back_deep(kvs.arena(), KeyValueRef(getKeyForIndex(i), deterministicRandom()->randomAlphaNumeric(16))); + } + } + + KeyValueRef getKeyValueForIndex(int idx) {return kvs[idx];} + + Key getKeyForIndex( int idx ) { + return Key( prefix + format("%010d", idx)).withPrefix(range.begin); + } + + virtual Future> getRange(Reference ryw, KeyRangeRef kr) const override { + int startIndex=0, endIndex= size; + while (startIndex < size && kvs[startIndex].key < kr.begin) + ++startIndex; + while (endIndex > startIndex && kvs[endIndex-1].key >= kr.end) + --endIndex; + if (startIndex == endIndex) + return Standalone(); + else + return Standalone(RangeResultRef(kvs.slice(startIndex, endIndex), false)); + } +private: + Standalone> kvs; + std::string prefix; + int size; +}; + +TEST_CASE("/fdbclient/PrivateKeySpace/Unittest") { + PrivateKeySpace pks(LiteralStringRef("\xff\xff"), LiteralStringRef("\xff\xff\xff")); + PrivateKeyRangeTestImpl pkr1(LiteralStringRef("\xff\xff/cat/"), LiteralStringRef("\xff\xff/cat/\xff"), "small", 10); + PrivateKeyRangeTestImpl pkr2(LiteralStringRef("\xff\xff/dog/"), LiteralStringRef("\xff\xff/dog/\xff"), "medium", 100); + PrivateKeyRangeTestImpl pkr3(LiteralStringRef("\xff\xff/pig/"), LiteralStringRef("\xff\xff/pig/\xff"), "large", 1000); + pks.registerKeyRange(pkr1.getKeyRange(), &pkr1); + pks.registerKeyRange(pkr2.getKeyRange(), &pkr2); + pks.registerKeyRange(pkr3.getKeyRange(), &pkr3); + auto nullRef = Reference(); + // get + { + auto resultFuture = pks.get(nullRef, LiteralStringRef("\xff\xff/cat/small0000000009")); + ASSERT(resultFuture.isReady()); + auto result = resultFuture.getValue().get(); + ASSERT(result == pkr1.getKeyValueForIndex(9).value); + auto emptyFuture = pks.get(nullRef, LiteralStringRef("\xff\xff/cat/small0000000010")); + ASSERT(emptyFuture.isReady()); + auto emptyResult = emptyFuture.getValue(); + ASSERT(!emptyResult.present()); + } + // general getRange + { + KeySelector start = KeySelectorRef(LiteralStringRef("\xff\xff/elepant"), false, -9); + KeySelector end = KeySelectorRef(LiteralStringRef("\xff\xff/frog"), false, +11); + auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits()); + ASSERT(resultFuture.isReady()); + auto result = resultFuture.getValue(); + ASSERT(result.size() == 20); + ASSERT(result[0].key == pkr2.getKeyForIndex(90)) ; + ASSERT(result[result.size()-1].key == pkr3.getKeyForIndex(9)); + } + // KeySelector points outside + { + KeySelector start = KeySelectorRef(pkr3.getKeyForIndex(999), true, -1110); + KeySelector end = KeySelectorRef(pkr1.getKeyForIndex(0), false, +1112); + auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits()); + ASSERT(resultFuture.isReady()); + auto result = resultFuture.getValue(); + ASSERT(result.size() == 1110); + ASSERT(result[0].key == pkr1.getKeyForIndex(0)) ; + ASSERT(result[result.size()-1].key == pkr3.getKeyForIndex(999)); + } + // GetRangeLimits with row limit + { + KeySelector start = KeySelectorRef(pkr2.getKeyForIndex(0), true, 0); + KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(0), false, 0); + auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits(2)); + ASSERT(resultFuture.isReady()); + auto result = resultFuture.getValue(); + ASSERT(result.size() == 2); + ASSERT(result[0].key == pkr2.getKeyForIndex(0)); + ASSERT(result[1].key == pkr2.getKeyForIndex(1)); + } + // GetRangeLimits with byte limit + { + KeySelector start = KeySelectorRef(pkr2.getKeyForIndex(0), true, 0); + KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(0), false, 0); + auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits(10, 100)); + ASSERT(resultFuture.isReady()); + auto result = resultFuture.getValue(); + int bytes = 0; + for (int i = 0; i < result.size()-1; ++i) + bytes += 8 + pkr2.getKeyValueForIndex(i).expectedSize(); + ASSERT(bytes < 100); + ASSERT(bytes + 8 + pkr2.getKeyValueForIndex(result.size()).expectedSize() >= 100); + } + // reverse test + { + KeySelector start = KeySelectorRef(pkr2.getKeyForIndex(0), true, 0); + KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(0), false, 0); + auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits(100), false, true); + ASSERT(resultFuture.isReady()); + auto result = resultFuture.getValue(); + for (int i = 0; i < result.size(); ++i) + ASSERT(result[i] == pkr2.getKeyValueForIndex(result.size() - 1 - i)); + } + return Void(); +} diff --git a/fdbrpc/FlowTests.actor.cpp b/fdbrpc/FlowTests.actor.cpp index d90d855eef..cd9cebb56b 100644 --- a/fdbrpc/FlowTests.actor.cpp +++ b/fdbrpc/FlowTests.actor.cpp @@ -25,7 +25,6 @@ #include "flow/IThreadPool.h" #include "fdbrpc/fdbrpc.h" #include "fdbrpc/IAsyncFile.h" -#include "fdbclient/PrivateKeySpace.actor.h" #include "flow/actorcompiler.h" // This must be the last #include. void forceLinkFlowTests() {} @@ -1356,115 +1355,3 @@ TEST_CASE("/flow/DeterministicRandom/SignedOverflow") { std::numeric_limits::max() - 1); return Void(); } - -class PrivateKeyRangeTestImpl : public PrivateKeyRangeBaseImpl { -public: - explicit PrivateKeyRangeTestImpl(KeyRef start, KeyRef end, const std::string& prefix, int size) : - PrivateKeyRangeBaseImpl(start, end), prefix(prefix), size(size) { - ASSERT(size > 0); - for (int i = 0; i < size; ++i) { - kvs.push_back_deep(kvs.arena(), KeyValueRef(getKeyForIndex(i), deterministicRandom()->randomAlphaNumeric(16))); - } - } - - KeyValueRef getKeyValueForIndex(int idx) {return kvs[idx];} - - Key getKeyForIndex( int idx ) { - return Key( prefix + format("%010d", idx)).withPrefix(range.begin); - } - - virtual Future> getRange(Reference ryw, KeyRangeRef kr) const override { - int startIndex=0, endIndex= size; - while (startIndex < size && kvs[startIndex].key < kr.begin) - ++startIndex; - while (endIndex > startIndex && kvs[endIndex-1].key >= kr.end) - --endIndex; - if (startIndex == endIndex) - return Standalone(); - else - return Standalone(RangeResultRef(kvs.slice(startIndex, endIndex), false)); - } -private: - Standalone> kvs; - std::string prefix; - int size; -}; - -TEST_CASE("/fdbclient/PrivateKeySpace/Aggregation") { - PrivateKeySpace pks(LiteralStringRef("\xff\xff"), LiteralStringRef("\xff\xff\xff")); - PrivateKeyRangeTestImpl pkr1(LiteralStringRef("\xff\xff/cat/"), LiteralStringRef("\xff\xff/cat/\xff"), "small", 10); - PrivateKeyRangeTestImpl pkr2(LiteralStringRef("\xff\xff/dog/"), LiteralStringRef("\xff\xff/dog/\xff"), "medium", 100); - PrivateKeyRangeTestImpl pkr3(LiteralStringRef("\xff\xff/pig/"), LiteralStringRef("\xff\xff/pig/\xff"), "large", 1000); - pks.registerKeyRange(pkr1.getKeyRange(), &pkr1); - pks.registerKeyRange(pkr2.getKeyRange(), &pkr2); - pks.registerKeyRange(pkr3.getKeyRange(), &pkr3); - auto nullRef = Reference(); - // get - { - auto resultFuture = pks.get(nullRef, LiteralStringRef("\xff\xff/cat/small0000000009")); - ASSERT(resultFuture.isReady()); - auto result = resultFuture.getValue().get(); - ASSERT(result == pkr1.getKeyValueForIndex(9).value); - auto emptyFuture = pks.get(nullRef, LiteralStringRef("\xff\xff/cat/small0000000010")); - ASSERT(emptyFuture.isReady()); - auto emptyResult = emptyFuture.getValue(); - ASSERT(!emptyResult.present()); - } - // general getRange - { - KeySelector start = KeySelectorRef(LiteralStringRef("\xff\xff/elepant"), false, -9); - KeySelector end = KeySelectorRef(LiteralStringRef("\xff\xff/frog"), false, +11); - auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits()); - ASSERT(resultFuture.isReady()); - auto result = resultFuture.getValue(); - ASSERT(result.size() == 20); - ASSERT(result[0].key == pkr2.getKeyForIndex(90)) ; - ASSERT(result[result.size()-1].key == pkr3.getKeyForIndex(9)); - } - // KeySelector points outside - { - KeySelector start = KeySelectorRef(pkr3.getKeyForIndex(999), true, -1110); - KeySelector end = KeySelectorRef(pkr1.getKeyForIndex(0), false, +1112); - auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits()); - ASSERT(resultFuture.isReady()); - auto result = resultFuture.getValue(); - ASSERT(result.size() == 1110); - ASSERT(result[0].key == pkr1.getKeyForIndex(0)) ; - ASSERT(result[result.size()-1].key == pkr3.getKeyForIndex(999)); - } - // GetRangeLimits with row limit - { - KeySelector start = KeySelectorRef(pkr2.getKeyForIndex(0), true, 0); - KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(0), false, 0); - auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits(2)); - ASSERT(resultFuture.isReady()); - auto result = resultFuture.getValue(); - ASSERT(result.size() == 2); - ASSERT(result[0].key == pkr2.getKeyForIndex(0)); - ASSERT(result[1].key == pkr2.getKeyForIndex(1)); - } - // GetRangeLimits with byte limit - { - KeySelector start = KeySelectorRef(pkr2.getKeyForIndex(0), true, 0); - KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(0), false, 0); - auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits(10, 100)); - ASSERT(resultFuture.isReady()); - auto result = resultFuture.getValue(); - int bytes = 0; - for (int i = 0; i < result.size()-1; ++i) - bytes += 8 + pkr2.getKeyValueForIndex(i).expectedSize(); - ASSERT(bytes < 100); - ASSERT(bytes + 8 + pkr2.getKeyValueForIndex(result.size()).expectedSize() >= 100); - } - // reverse test - { - KeySelector start = KeySelectorRef(pkr2.getKeyForIndex(0), true, 0); - KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(0), false, 0); - auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits(100), false, true); - ASSERT(resultFuture.isReady()); - auto result = resultFuture.getValue(); - for (int i = 0; i < result.size(); ++i) - ASSERT(result[i] == pkr2.getKeyValueForIndex(result.size() - 1 - i)); - } - return Void(); -} From 5928367b61b3146f4b51c8e104af1a4f3fd4eede Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Tue, 3 Mar 2020 15:47:14 -0800 Subject: [PATCH 0804/1604] Add unit test case for overlapping key ranges --- fdbclient/PrivateKeySpace.actor.cpp | 59 +++++++++++++++-------------- fdbclient/PrivateKeySpace.actor.h | 3 +- 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/fdbclient/PrivateKeySpace.actor.cpp b/fdbclient/PrivateKeySpace.actor.cpp index 2c6d705d6d..e93307f470 100644 --- a/fdbclient/PrivateKeySpace.actor.cpp +++ b/fdbclient/PrivateKeySpace.actor.cpp @@ -9,7 +9,8 @@ // However, moving the KeySelector while handling other parameters like limits makes the code much more complex and hard // to maintain Seperate each part to make the code easy to understand and more compact ACTOR Future PrivateKeyRangeBaseImpl::normalizeKeySelectorActor(const PrivateKeyRangeBaseImpl* pkrImpl, - Reference ryw, KeySelector* ks) { + Reference ryw, + KeySelector* ks) { ASSERT(!ks->orEqual); // should be removed before calling ASSERT(ks->offset != 1); // The function is never called when KeySelector is already normalized @@ -170,34 +171,32 @@ Future> PrivateKeySpace::get(Reference 0); for (int i = 0; i < size; ++i) { - kvs.push_back_deep(kvs.arena(), KeyValueRef(getKeyForIndex(i), deterministicRandom()->randomAlphaNumeric(16))); + kvs.push_back_deep(kvs.arena(), + KeyValueRef(getKeyForIndex(i), deterministicRandom()->randomAlphaNumeric(16))); } } - KeyValueRef getKeyValueForIndex(int idx) {return kvs[idx];} + KeyValueRef getKeyValueForIndex(int idx) { return kvs[idx]; } - Key getKeyForIndex( int idx ) { - return Key( prefix + format("%010d", idx)).withPrefix(range.begin); - } - - virtual Future> getRange(Reference ryw, KeyRangeRef kr) const override { - int startIndex=0, endIndex= size; - while (startIndex < size && kvs[startIndex].key < kr.begin) - ++startIndex; - while (endIndex > startIndex && kvs[endIndex-1].key >= kr.end) - --endIndex; + Key getKeyForIndex(int idx) { return Key(prefix + format("%010d", idx)).withPrefix(range.begin); } + int getSize() { return size; } + virtual Future> getRange(Reference ryw, + KeyRangeRef kr) const override { + int startIndex = 0, endIndex = size; + while (startIndex < size && kvs[startIndex].key < kr.begin) ++startIndex; + while (endIndex > startIndex && kvs[endIndex - 1].key >= kr.end) --endIndex; if (startIndex == endIndex) return Standalone(); else return Standalone(RangeResultRef(kvs.slice(startIndex, endIndex), false)); } + private: Standalone> kvs; std::string prefix; @@ -207,8 +206,10 @@ private: TEST_CASE("/fdbclient/PrivateKeySpace/Unittest") { PrivateKeySpace pks(LiteralStringRef("\xff\xff"), LiteralStringRef("\xff\xff\xff")); PrivateKeyRangeTestImpl pkr1(LiteralStringRef("\xff\xff/cat/"), LiteralStringRef("\xff\xff/cat/\xff"), "small", 10); - PrivateKeyRangeTestImpl pkr2(LiteralStringRef("\xff\xff/dog/"), LiteralStringRef("\xff\xff/dog/\xff"), "medium", 100); - PrivateKeyRangeTestImpl pkr3(LiteralStringRef("\xff\xff/pig/"), LiteralStringRef("\xff\xff/pig/\xff"), "large", 1000); + PrivateKeyRangeTestImpl pkr2(LiteralStringRef("\xff\xff/dog/"), LiteralStringRef("\xff\xff/dog/\xff"), "medium", + 100); + PrivateKeyRangeTestImpl pkr3(LiteralStringRef("\xff\xff/pig/"), LiteralStringRef("\xff\xff/pig/\xff"), "large", + 1000); pks.registerKeyRange(pkr1.getKeyRange(), &pkr1); pks.registerKeyRange(pkr2.getKeyRange(), &pkr2); pks.registerKeyRange(pkr3.getKeyRange(), &pkr3); @@ -232,8 +233,8 @@ TEST_CASE("/fdbclient/PrivateKeySpace/Unittest") { ASSERT(resultFuture.isReady()); auto result = resultFuture.getValue(); ASSERT(result.size() == 20); - ASSERT(result[0].key == pkr2.getKeyForIndex(90)) ; - ASSERT(result[result.size()-1].key == pkr3.getKeyForIndex(9)); + ASSERT(result[0].key == pkr2.getKeyForIndex(90)); + ASSERT(result[result.size() - 1].key == pkr3.getKeyForIndex(9)); } // KeySelector points outside { @@ -243,8 +244,8 @@ TEST_CASE("/fdbclient/PrivateKeySpace/Unittest") { ASSERT(resultFuture.isReady()); auto result = resultFuture.getValue(); ASSERT(result.size() == 1110); - ASSERT(result[0].key == pkr1.getKeyForIndex(0)) ; - ASSERT(result[result.size()-1].key == pkr3.getKeyForIndex(999)); + ASSERT(result[0].key == pkr1.getKeyForIndex(0)); + ASSERT(result[result.size() - 1].key == pkr3.getKeyForIndex(999)); } // GetRangeLimits with row limit { @@ -265,20 +266,20 @@ TEST_CASE("/fdbclient/PrivateKeySpace/Unittest") { ASSERT(resultFuture.isReady()); auto result = resultFuture.getValue(); int bytes = 0; - for (int i = 0; i < result.size()-1; ++i) - bytes += 8 + pkr2.getKeyValueForIndex(i).expectedSize(); + for (int i = 0; i < result.size() - 1; ++i) bytes += 8 + pkr2.getKeyValueForIndex(i).expectedSize(); ASSERT(bytes < 100); ASSERT(bytes + 8 + pkr2.getKeyValueForIndex(result.size()).expectedSize() >= 100); } - // reverse test + // reverse test with overlapping key range { KeySelector start = KeySelectorRef(pkr2.getKeyForIndex(0), true, 0); - KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(0), false, 0); - auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits(100), false, true); + KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(999), true, +1); + auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits(1100), false, true); ASSERT(resultFuture.isReady()); auto result = resultFuture.getValue(); - for (int i = 0; i < result.size(); ++i) - ASSERT(result[i] == pkr2.getKeyValueForIndex(result.size() - 1 - i)); + for (int i = 0; i < pkr3.getSize(); ++i) ASSERT(result[i] == pkr3.getKeyValueForIndex(pkr3.getSize() - 1 - i)); + for (int i = 0; i < pkr2.getSize(); ++i) + ASSERT(result[i + pkr3.getSize()] == pkr2.getKeyValueForIndex(pkr2.getSize() - 1 - i)); } return Void(); } diff --git a/fdbclient/PrivateKeySpace.actor.h b/fdbclient/PrivateKeySpace.actor.h index ed20258d35..417dcdf624 100644 --- a/fdbclient/PrivateKeySpace.actor.h +++ b/fdbclient/PrivateKeySpace.actor.h @@ -31,7 +31,8 @@ public: } KeyRangeRef getKeyRange() const { return range; } ACTOR Future normalizeKeySelectorActor(const PrivateKeyRangeBaseImpl* pkrImpl, - Reference ryw, KeySelector* ks); + Reference ryw, KeySelector* ks); + protected: KeyRange range; // underlying key range for this function }; From 19d21aa0c7718338fb1b08123a18fac1c6d219da Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Tue, 3 Mar 2020 15:55:33 -0800 Subject: [PATCH 0805/1604] Delete commented code and fix typos --- fdbclient/PrivateKeySpace.actor.cpp | 2 +- fdbclient/PrivateKeySpace.actor.h | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/fdbclient/PrivateKeySpace.actor.cpp b/fdbclient/PrivateKeySpace.actor.cpp index e93307f470..9d39a379dd 100644 --- a/fdbclient/PrivateKeySpace.actor.cpp +++ b/fdbclient/PrivateKeySpace.actor.cpp @@ -7,7 +7,7 @@ // If the corresponding key is not in this private key range, it will move as far as possible to adjust the offset to 1 // It does have overhead here since we query all keys twice in the worst case. // However, moving the KeySelector while handling other parameters like limits makes the code much more complex and hard -// to maintain Seperate each part to make the code easy to understand and more compact +// to maintain Separate each part to make the code easy to understand and more compact ACTOR Future PrivateKeyRangeBaseImpl::normalizeKeySelectorActor(const PrivateKeyRangeBaseImpl* pkrImpl, Reference ryw, KeySelector* ks) { diff --git a/fdbclient/PrivateKeySpace.actor.h b/fdbclient/PrivateKeySpace.actor.h index 417dcdf624..c71272bee0 100644 --- a/fdbclient/PrivateKeySpace.actor.h +++ b/fdbclient/PrivateKeySpace.actor.h @@ -37,13 +37,6 @@ protected: KeyRange range; // underlying key range for this function }; -// class PrivateKeyRangeSimpleImpl : public PrivateKeyRangeBaseImpl { -// public: -// virtual Future> getRange(ReadYourWritesTransaction* ryw, KeyRangeRef kr) const = 0; -// virtual Future> getRange(ReadYourWritesTransaction* ryw, KeySelector begin, KeySelector -// end, GetRangeLimits limits, bool snapshot = false, bool reverse = false) const; -// }; - class PrivateKeySpace { public: Future> get(Reference ryw, const Key& key, bool snapshot = false); From af4eb2a2d0dc379a5091122c22aeb1369a48e1ee Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Tue, 3 Mar 2020 15:56:45 -0800 Subject: [PATCH 0806/1604] update comments --- fdbclient/PrivateKeySpace.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbclient/PrivateKeySpace.actor.cpp b/fdbclient/PrivateKeySpace.actor.cpp index 9d39a379dd..cde3a2c574 100644 --- a/fdbclient/PrivateKeySpace.actor.cpp +++ b/fdbclient/PrivateKeySpace.actor.cpp @@ -12,7 +12,7 @@ ACTOR Future PrivateKeyRangeBaseImpl::normalizeKeySelectorActor(const Priv Reference ryw, KeySelector* ks) { ASSERT(!ks->orEqual); // should be removed before calling - ASSERT(ks->offset != 1); // The function is never called when KeySelector is already normalized + ASSERT(ks->offset != 1); // never being called if KeySelector is already normalized state Key startKey(pkrImpl->range.begin); state Key endKey(pkrImpl->range.end); From 064fb1a2d8625b65443c9aeddfc89b74d84ac128 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Tue, 3 Mar 2020 16:04:04 -0800 Subject: [PATCH 0807/1604] fix windows build --- fdbclient/fdbclient.vcxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbclient/fdbclient.vcxproj b/fdbclient/fdbclient.vcxproj index cd8d22e0af..3b1ee7b06e 100644 --- a/fdbclient/fdbclient.vcxproj +++ b/fdbclient/fdbclient.vcxproj @@ -77,7 +77,7 @@ false - + From 243a4c448025ac1769987ed8f8d3a3e544881312 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Tue, 3 Mar 2020 18:14:57 -0800 Subject: [PATCH 0808/1604] Reduce code indentation --- fdbcli/fdbcli.actor.cpp | 49 ++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/fdbcli/fdbcli.actor.cpp b/fdbcli/fdbcli.actor.cpp index 3a9ad1d01e..df4aebb46f 100644 --- a/fdbcli/fdbcli.actor.cpp +++ b/fdbcli/fdbcli.actor.cpp @@ -3658,30 +3658,33 @@ ACTOR Future runCli(CLIOptions opt) { fdbcli_comp_cmd(line, completions); }, [enabled=opt.cliHints](std::string const& line)->LineNoise::Hint { - if (enabled) { - bool error = false; - bool partial = false; - std::string linecopy = line; - std::vector> parsed = parseLine(linecopy, error, partial); - if (parsed.size() == 0 || parsed.back().size() == 0) return LineNoise::Hint(); - StringRef command = parsed.back().front(); - int finishedParameters = parsed.back().size() + error; - - // We don't want the hint to flip to parse error and back, e.g. while \" is being typed. - if (error && line.back() != '\\') return LineNoise::Hint(std::string(" {malformed escape sequence}"), 90, false); - - auto iter = helpMap.find(command.toString()); - if (iter != helpMap.end()) { - std::string helpLine = iter->second.usage; - std::vector> parsedHelp = parseLine(helpLine, error, partial); - std::string hintLine = (*(line.end() - 1) == ' ' ? "" : " "); - for (int i = finishedParameters; i < parsedHelp.back().size(); i++) { - hintLine = hintLine + parsedHelp.back()[i].toString() + " "; - } - return LineNoise::Hint(hintLine, 90, false); - } + if (!enabled) { + return LineNoise::Hint(); + } + + bool error = false; + bool partial = false; + std::string linecopy = line; + std::vector> parsed = parseLine(linecopy, error, partial); + if (parsed.size() == 0 || parsed.back().size() == 0) return LineNoise::Hint(); + StringRef command = parsed.back().front(); + int finishedParameters = parsed.back().size() + error; + + // We don't want the hint to flip to parse error and back, e.g. while \" is being typed. + if (error && line.back() != '\\') return LineNoise::Hint(std::string(" {malformed escape sequence}"), 90, false); + + auto iter = helpMap.find(command.toString()); + if (iter != helpMap.end()) { + std::string helpLine = iter->second.usage; + std::vector> parsedHelp = parseLine(helpLine, error, partial); + std::string hintLine = (*(line.end() - 1) == ' ' ? "" : " "); + for (int i = finishedParameters; i < parsedHelp.back().size(); i++) { + hintLine = hintLine + parsedHelp.back()[i].toString() + " "; + } + return LineNoise::Hint(hintLine, 90, false); + } else { + return LineNoise::Hint(); } - return LineNoise::Hint(); }, 1000, false); From 9c50b8369df5be06fd46a0b8deb816d59ee3b3d4 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Tue, 3 Mar 2020 18:35:04 -0800 Subject: [PATCH 0809/1604] Change name from private-key-space to special-key-space --- fdbclient/CMakeLists.txt | 4 +- fdbclient/DatabaseContext.h | 4 +- fdbclient/NativeAPI.actor.cpp | 4 +- fdbclient/PrivateKeySpace.actor.cpp | 285 ---------------------------- fdbclient/PrivateKeySpace.actor.h | 72 ------- fdbclient/ReadYourWrites.actor.cpp | 10 +- fdbclient/fdbclient.vcxproj | 4 +- 7 files changed, 13 insertions(+), 370 deletions(-) delete mode 100644 fdbclient/PrivateKeySpace.actor.cpp delete mode 100644 fdbclient/PrivateKeySpace.actor.h diff --git a/fdbclient/CMakeLists.txt b/fdbclient/CMakeLists.txt index 0d23580c18..0782c65360 100644 --- a/fdbclient/CMakeLists.txt +++ b/fdbclient/CMakeLists.txt @@ -44,8 +44,8 @@ set(FDBCLIENT_SRCS NativeAPI.actor.cpp NativeAPI.actor.h Notified.h - PrivateKeySpace.actor.cpp - PrivateKeySpace.actor.h + SpecialKeySpace.actor.cpp + SpecialKeySpace.actor.h ReadYourWrites.actor.cpp ReadYourWrites.h RestoreWorkerInterface.actor.h diff --git a/fdbclient/DatabaseContext.h b/fdbclient/DatabaseContext.h index c74a8de6b6..75e7bb8240 100644 --- a/fdbclient/DatabaseContext.h +++ b/fdbclient/DatabaseContext.h @@ -45,7 +45,7 @@ private: typedef MultiInterface> LocationInfo; typedef MultiInterface ProxyInfo; -class PrivateKeySpace; //forward declaration +class SpecialKeySpace; //forward declaration class DatabaseContext : public ReferenceCounted, public FastAllocated, NonCopyable { public: static DatabaseContext* allocateOnForeignThread() { @@ -207,7 +207,7 @@ public: double detailedHealthMetricsLastUpdated; UniqueOrderedOptionList transactionDefaults; - std::unique_ptr privateKeySpace; + std::unique_ptr specialKeySpace; }; #endif diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index f6a7c3c66e..09dd1784ea 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -32,7 +32,7 @@ #include "fdbclient/MasterProxyInterface.h" #include "fdbclient/MonitorLeader.h" #include "fdbclient/MutationList.h" -#include "fdbclient/PrivateKeySpace.actor.h" +#include "fdbclient/SpecialKeySpace.actor.h" #include "fdbclient/StorageServerInterface.h" #include "fdbclient/SystemData.h" #include "fdbrpc/LoadBalance.h" @@ -531,7 +531,7 @@ DatabaseContext::DatabaseContext(Reference(LiteralStringRef("\xff\xff"), LiteralStringRef("\xff\xff\xff\xff")); + specialKeySpace = std::make_unique(LiteralStringRef("\xff\xff"), LiteralStringRef("\xff\xff\xff\xff")); } DatabaseContext::DatabaseContext(const Error& err) diff --git a/fdbclient/PrivateKeySpace.actor.cpp b/fdbclient/PrivateKeySpace.actor.cpp deleted file mode 100644 index cde3a2c574..0000000000 --- a/fdbclient/PrivateKeySpace.actor.cpp +++ /dev/null @@ -1,285 +0,0 @@ -#include "fdbclient/PrivateKeySpace.actor.h" -#include "flow/UnitTest.h" -#include "flow/actorcompiler.h" // This must be the last #include. - -// This function will normalize the given KeySelector to a standard KeySelector: -// orEqual == false && offset == 1 (Standard form) -// If the corresponding key is not in this private key range, it will move as far as possible to adjust the offset to 1 -// It does have overhead here since we query all keys twice in the worst case. -// However, moving the KeySelector while handling other parameters like limits makes the code much more complex and hard -// to maintain Separate each part to make the code easy to understand and more compact -ACTOR Future PrivateKeyRangeBaseImpl::normalizeKeySelectorActor(const PrivateKeyRangeBaseImpl* pkrImpl, - Reference ryw, - KeySelector* ks) { - ASSERT(!ks->orEqual); // should be removed before calling - ASSERT(ks->offset != 1); // never being called if KeySelector is already normalized - - state Key startKey(pkrImpl->range.begin); - state Key endKey(pkrImpl->range.end); - - if (ks->offset < 1) { - // less than the given key - if (pkrImpl->range.contains(ks->getKey())) endKey = keyAfter(ks->getKey()); - } else { - // greater than the given key - if (pkrImpl->range.contains(ks->getKey())) startKey = ks->getKey(); - } - - TraceEvent("NormalizeKeySelector") - .detail("OriginalKey", ks->getKey()) - .detail("OriginalOffset", ks->offset) - .detail("PrivateKeyRangeStart", pkrImpl->range.begin) - .detail("PrivateKeyRangeEnd", pkrImpl->range.end); - - Standalone result = wait(pkrImpl->getRange(ryw, KeyRangeRef(startKey, endKey))); - // TODO : KeySelector::setKey has byte limit according to the knobs, customize it if needed - if (ks->offset < 1) { - if (result.size() >= 1 - ks->offset) { - ks->setKey(KeyRef(ks->arena(), result[result.size() - (1 - ks->offset)].key)); - ks->offset = 1; - } else { - ks->setKey(KeyRef(ks->arena(), result[0].key)); - ks->offset += result.size(); - } - } else { - if (result.size() >= ks->offset) { - ks->setKey(KeyRef(ks->arena(), result[ks->offset - 1].key)); - ks->offset = 1; - } else { - ks->setKey(KeyRef(ks->arena(), keyAfter(result[result.size() - 1].key))); - ks->offset -= result.size(); - } - } - TraceEvent("NormalizeKeySelector") - .detail("NormalizedKey", ks->getKey()) - .detail("NormalizedOffset", ks->offset) - .detail("PrivateKeyRangeStart", pkrImpl->range.begin) - .detail("PrivateKeyRangeEnd", pkrImpl->range.end); - return Void(); -} - -ACTOR Future> PrivateKeySpace::getRangeAggregationActor( - PrivateKeySpace* pks, Reference ryw, KeySelector begin, KeySelector end, - GetRangeLimits limits, bool reverse) { - // This function handles ranges which cover more than one keyrange and aggregates all results - // KeySelector, GetRangeLimits and reverse are all handled here - - // make sure orEqual == false - begin.removeOrEqual(begin.arena()); - end.removeOrEqual(end.arena()); - - // make sure offset == 1 - state RangeMap::Iterator iter = - pks->impls.rangeContaining(begin.getKey()); - while (begin.offset != 1 && iter != pks->impls.ranges().begin() && iter != pks->impls.ranges().end()) { - if (iter->value() != nullptr) wait(iter->value()->normalizeKeySelectorActor(iter->value(), ryw, &begin)); - begin.offset < 1 ? --iter : ++iter; - } - if (begin.offset != 1) { - // The Key Selector points to key outside the whole private key space - TraceEvent(SevError, "IllegalBeginKeySelector") - .detail("TerminateKey", begin.getKey()) - .detail("TerminateOffset", begin.offset); - } - iter = pks->impls.rangeContaining(end.getKey()); - while (end.offset != 1 && iter != pks->impls.ranges().begin() && iter != pks->impls.ranges().end()) { - if (iter->value() != nullptr) wait(iter->value()->normalizeKeySelectorActor(iter->value(), ryw, &end)); - end.offset < 1 ? --iter : ++iter; - } - if (end.offset != 1) { - // The Key Selector points to key outside the whole private key space - TraceEvent(SevError, "IllegalEndKeySelector") - .detail("TerminateKey", end.getKey()) - .detail("TerminateOffset", end.offset); - } - // return if range inverted - if (begin.offset >= end.offset && begin.getKey() >= end.getKey()) { - TEST(true); - return Standalone(); - } - state Standalone result; - state RangeMap::Ranges ranges = - pks->impls.intersectingRanges(KeyRangeRef(begin.getKey(), end.getKey())); - // 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(); - if (reverse) { - while (iter != ranges.begin()) { - --iter; - if (iter->value() == nullptr) continue; - KeyRangeRef kr = iter->range(); - KeyRef keyStart = kr.contains(begin.getKey()) ? begin.getKey() : kr.begin; - KeyRef keyEnd = kr.contains(end.getKey()) ? end.getKey() : kr.end; - Standalone pairs = wait(iter->value()->getRange(ryw, KeyRangeRef(keyStart, keyEnd))); - // limits handler - for (int i = pairs.size() - 1; i >= 0; --i) { - result.push_back_deep(result.arena(), pairs[i]); - // TODO : the behavior here is even the last kv makes bytes larger than specified, - // it is still returned and set limits.bytes to zero - limits.decrement(pairs[i]); - if (limits.isReached()) return result; - } - } - } else { - for (iter = ranges.begin(); iter != ranges.end(); ++iter) { - if (iter->value() == nullptr) continue; - KeyRangeRef kr = iter->range(); - KeyRef keyStart = kr.contains(begin.getKey()) ? begin.getKey() : kr.begin; - KeyRef keyEnd = kr.contains(end.getKey()) ? end.getKey() : kr.end; - Standalone pairs = wait(iter->value()->getRange(ryw, KeyRangeRef(keyStart, keyEnd))); - // limits handler - for (const KeyValueRef& kv : pairs) { - result.push_back_deep(result.arena(), kv); - // TODO : behavior here is even the last kv makes bytes larger than specified, - // it is still returned and set limits.bytes to zero - limits.decrement(kv); - if (limits.isReached()) return result; - } - } - } - return result; -} - -Future> PrivateKeySpace::getRange(Reference ryw, - KeySelector begin, KeySelector end, GetRangeLimits limits, - bool snapshot, bool reverse) { - // validate limits here - if (!limits.isValid()) return range_limits_invalid(); - if (limits.isReached()) { - TEST(true); // read limit 0 - return Standalone(); - } - // ignore snapshot, which is not used - return getRangeAggregationActor(this, ryw, begin, end, limits, reverse); -} - -ACTOR Future> PrivateKeySpace::getActor(PrivateKeySpace* pks, Reference ryw, - KeyRef key) { - // use getRange to workaround this - Standalone result = wait(pks->getRange( - ryw, KeySelector(firstGreaterOrEqual(key)), KeySelector(firstGreaterOrEqual(keyAfter(key))), GetRangeLimits())); - ASSERT(result.size() <= 1); - if (result.size()) { - return Optional(result[0].value); - } else { - return Optional(); - } -} - -Future> PrivateKeySpace::get(Reference ryw, const Key& key, bool snapshot) { - // ignore snapshot, which is not used - return getActor(this, ryw, key); -} - -class PrivateKeyRangeTestImpl : public PrivateKeyRangeBaseImpl { -public: - explicit PrivateKeyRangeTestImpl(KeyRef start, KeyRef end, const std::string& prefix, int size) - : PrivateKeyRangeBaseImpl(start, end), prefix(prefix), size(size) { - ASSERT(size > 0); - for (int i = 0; i < size; ++i) { - kvs.push_back_deep(kvs.arena(), - KeyValueRef(getKeyForIndex(i), deterministicRandom()->randomAlphaNumeric(16))); - } - } - - KeyValueRef getKeyValueForIndex(int idx) { return kvs[idx]; } - - Key getKeyForIndex(int idx) { return Key(prefix + format("%010d", idx)).withPrefix(range.begin); } - int getSize() { return size; } - virtual Future> getRange(Reference ryw, - KeyRangeRef kr) const override { - int startIndex = 0, endIndex = size; - while (startIndex < size && kvs[startIndex].key < kr.begin) ++startIndex; - while (endIndex > startIndex && kvs[endIndex - 1].key >= kr.end) --endIndex; - if (startIndex == endIndex) - return Standalone(); - else - return Standalone(RangeResultRef(kvs.slice(startIndex, endIndex), false)); - } - -private: - Standalone> kvs; - std::string prefix; - int size; -}; - -TEST_CASE("/fdbclient/PrivateKeySpace/Unittest") { - PrivateKeySpace pks(LiteralStringRef("\xff\xff"), LiteralStringRef("\xff\xff\xff")); - PrivateKeyRangeTestImpl pkr1(LiteralStringRef("\xff\xff/cat/"), LiteralStringRef("\xff\xff/cat/\xff"), "small", 10); - PrivateKeyRangeTestImpl pkr2(LiteralStringRef("\xff\xff/dog/"), LiteralStringRef("\xff\xff/dog/\xff"), "medium", - 100); - PrivateKeyRangeTestImpl pkr3(LiteralStringRef("\xff\xff/pig/"), LiteralStringRef("\xff\xff/pig/\xff"), "large", - 1000); - pks.registerKeyRange(pkr1.getKeyRange(), &pkr1); - pks.registerKeyRange(pkr2.getKeyRange(), &pkr2); - pks.registerKeyRange(pkr3.getKeyRange(), &pkr3); - auto nullRef = Reference(); - // get - { - auto resultFuture = pks.get(nullRef, LiteralStringRef("\xff\xff/cat/small0000000009")); - ASSERT(resultFuture.isReady()); - auto result = resultFuture.getValue().get(); - ASSERT(result == pkr1.getKeyValueForIndex(9).value); - auto emptyFuture = pks.get(nullRef, LiteralStringRef("\xff\xff/cat/small0000000010")); - ASSERT(emptyFuture.isReady()); - auto emptyResult = emptyFuture.getValue(); - ASSERT(!emptyResult.present()); - } - // general getRange - { - KeySelector start = KeySelectorRef(LiteralStringRef("\xff\xff/elepant"), false, -9); - KeySelector end = KeySelectorRef(LiteralStringRef("\xff\xff/frog"), false, +11); - auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits()); - ASSERT(resultFuture.isReady()); - auto result = resultFuture.getValue(); - ASSERT(result.size() == 20); - ASSERT(result[0].key == pkr2.getKeyForIndex(90)); - ASSERT(result[result.size() - 1].key == pkr3.getKeyForIndex(9)); - } - // KeySelector points outside - { - KeySelector start = KeySelectorRef(pkr3.getKeyForIndex(999), true, -1110); - KeySelector end = KeySelectorRef(pkr1.getKeyForIndex(0), false, +1112); - auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits()); - ASSERT(resultFuture.isReady()); - auto result = resultFuture.getValue(); - ASSERT(result.size() == 1110); - ASSERT(result[0].key == pkr1.getKeyForIndex(0)); - ASSERT(result[result.size() - 1].key == pkr3.getKeyForIndex(999)); - } - // GetRangeLimits with row limit - { - KeySelector start = KeySelectorRef(pkr2.getKeyForIndex(0), true, 0); - KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(0), false, 0); - auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits(2)); - ASSERT(resultFuture.isReady()); - auto result = resultFuture.getValue(); - ASSERT(result.size() == 2); - ASSERT(result[0].key == pkr2.getKeyForIndex(0)); - ASSERT(result[1].key == pkr2.getKeyForIndex(1)); - } - // GetRangeLimits with byte limit - { - KeySelector start = KeySelectorRef(pkr2.getKeyForIndex(0), true, 0); - KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(0), false, 0); - auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits(10, 100)); - ASSERT(resultFuture.isReady()); - auto result = resultFuture.getValue(); - int bytes = 0; - for (int i = 0; i < result.size() - 1; ++i) bytes += 8 + pkr2.getKeyValueForIndex(i).expectedSize(); - ASSERT(bytes < 100); - ASSERT(bytes + 8 + pkr2.getKeyValueForIndex(result.size()).expectedSize() >= 100); - } - // reverse test with overlapping key range - { - KeySelector start = KeySelectorRef(pkr2.getKeyForIndex(0), true, 0); - KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(999), true, +1); - auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits(1100), false, true); - ASSERT(resultFuture.isReady()); - auto result = resultFuture.getValue(); - for (int i = 0; i < pkr3.getSize(); ++i) ASSERT(result[i] == pkr3.getKeyValueForIndex(pkr3.getSize() - 1 - i)); - for (int i = 0; i < pkr2.getSize(); ++i) - ASSERT(result[i + pkr3.getSize()] == pkr2.getKeyValueForIndex(pkr2.getSize() - 1 - i)); - } - return Void(); -} diff --git a/fdbclient/PrivateKeySpace.actor.h b/fdbclient/PrivateKeySpace.actor.h deleted file mode 100644 index c71272bee0..0000000000 --- a/fdbclient/PrivateKeySpace.actor.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#if defined(NO_INTELLISENSE) && !defined(FDBCLIENT_PRIVATEKEYSPACE_ACTOR_G_H) -#define FDBCLIENT_PRIVATEKEYSPACE_ACTOR_G_H -#include "fdbclient/PrivateKeySpace.actor.g.h" -#elif !defined(FDBCLIENT_PRIVATEKEYSPACE_ACTOR_H) -#define FDBCLIENT_PRIVATEKEYSPACE_ACTOR_H - -#include "flow/flow.h" -#include "flow/Arena.h" -#include "fdbclient/FDBTypes.h" -#include "fdbclient/KeyRangeMap.h" -#include "fdbclient/ReadYourWrites.h" -#include "flow/actorcompiler.h" // This must be the last #include. - -class PrivateKeyRangeBaseImpl { -public: - // TO DISCUSS : do we need this general getRange interface here? - // Since a keyRange doesn't have any knowledge about other keyRanges, parameters like KeySelector, - // GetRangeLimits should be handled together in PrivateKeySpace - // Thus, having this general interface looks unnessary. - // virtual Future> getRange(ReadYourWritesTransaction* ryw, KeySelector begin, - // KeySelector end, GetRangeLimits limits, bool snapshot = false, bool reverse = false) const = 0; - - // Each derived class only needs to implement this simple version of getRange - virtual Future> getRange(Reference ryw, - KeyRangeRef kr) const = 0; - - explicit PrivateKeyRangeBaseImpl(KeyRef start, KeyRef end) { - range = KeyRangeRef(range.arena(), KeyRangeRef(start, end)); - } - KeyRangeRef getKeyRange() const { return range; } - ACTOR Future normalizeKeySelectorActor(const PrivateKeyRangeBaseImpl* pkrImpl, - Reference ryw, KeySelector* ks); - -protected: - KeyRange range; // underlying key range for this function -}; - -class PrivateKeySpace { -public: - Future> get(Reference ryw, const Key& key, bool snapshot = false); - - Future> getRange(Reference ryw, KeySelector begin, - KeySelector end, GetRangeLimits limits, bool snapshot = false, - bool reverse = false); - - PrivateKeySpace(KeyRef spaceStartKey = Key(), KeyRef spaceEndKey = allKeys.end) { - // Default value is nullptr, begin of KeyRangeMap is Key() - impls = KeyRangeMap(nullptr, spaceEndKey); - range = KeyRangeRef(spaceStartKey, spaceEndKey); - } - void registerKeyRange(const KeyRangeRef& kr, PrivateKeyRangeBaseImpl* impl) { - // range check - ASSERT(kr.begin >= range.begin && kr.end <= range.end); - impls.insert(kr, impl); - } - -private: - ACTOR Future> getActor(PrivateKeySpace* pks, Reference ryw, KeyRef key); - - ACTOR Future> getRangeAggregationActor(PrivateKeySpace* pks, - Reference ryw, - KeySelector begin, KeySelector end, - GetRangeLimits limits, bool reverse); - - KeyRangeMap impls; - KeyRange range; -}; - -#include "flow/unactorcompiler.h" -#endif diff --git a/fdbclient/ReadYourWrites.actor.cpp b/fdbclient/ReadYourWrites.actor.cpp index 6a5ddfe904..d42c25860d 100644 --- a/fdbclient/ReadYourWrites.actor.cpp +++ b/fdbclient/ReadYourWrites.actor.cpp @@ -21,7 +21,7 @@ #include "fdbclient/ReadYourWrites.h" #include "fdbclient/Atomic.h" #include "fdbclient/DatabaseContext.h" -#include "fdbclient/PrivateKeySpace.actor.h" +#include "fdbclient/SpecialKeySpace.actor.h" #include "fdbclient/StatusClient.h" #include "fdbclient/MonitorLeader.h" #include "flow/Util.h" @@ -1280,10 +1280,10 @@ Future< Standalone > ReadYourWritesTransaction::getRange( } } - // start with simplest point, private key space are only allowed to query if both begin and end start with \xff\xff - const KeyRef privateKeyPrefix = systemKeys.end; - if (begin.getKey().startsWith(privateKeyPrefix) && end.getKey().startsWith(privateKeyPrefix)) - return getDatabase()->privateKeySpace->getRange(Reference(this), begin, end, limits, snapshot, reverse); + // start with simplest point, special key space are only allowed to query if both begin and end start with \xff\xff + const KeyRef specialKeyPrefix = systemKeys.end; + if (begin.getKey().startsWith(specialKeyPrefix) && end.getKey().startsWith(specialKeyPrefix)) + return getDatabase()->specialKeySpace->getRange(Reference(this), begin, end, limits, snapshot, reverse); if(checkUsedDuringCommit()) { return used_during_commit(); diff --git a/fdbclient/fdbclient.vcxproj b/fdbclient/fdbclient.vcxproj index 3b1ee7b06e..613f5f0ac1 100644 --- a/fdbclient/fdbclient.vcxproj +++ b/fdbclient/fdbclient.vcxproj @@ -77,13 +77,13 @@ false - + @@ -124,13 +124,13 @@ - + From 457f95156d8285eb673b45fff42cbf0830dd4d32 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Tue, 3 Mar 2020 18:35:24 -0800 Subject: [PATCH 0810/1604] Change name from private-key-space to special-key-space --- fdbclient/SpecialKeySpace.actor.cpp | 285 ++++++++++++++++++++++++++++ fdbclient/SpecialKeySpace.actor.h | 72 +++++++ 2 files changed, 357 insertions(+) create mode 100644 fdbclient/SpecialKeySpace.actor.cpp create mode 100644 fdbclient/SpecialKeySpace.actor.h diff --git a/fdbclient/SpecialKeySpace.actor.cpp b/fdbclient/SpecialKeySpace.actor.cpp new file mode 100644 index 0000000000..a2e02f1bc8 --- /dev/null +++ b/fdbclient/SpecialKeySpace.actor.cpp @@ -0,0 +1,285 @@ +#include "fdbclient/SpecialKeySpace.actor.h" +#include "flow/UnitTest.h" +#include "flow/actorcompiler.h" // This must be the last #include. + +// This function will normalize the given KeySelector to a standard KeySelector: +// orEqual == false && offset == 1 (Standard form) +// If the corresponding key is not in this special key range, it will move as far as possible to adjust the offset to 1 +// It does have overhead here since we query all keys twice in the worst case. +// However, moving the KeySelector while handling other parameters like limits makes the code much more complex and hard +// to maintain Separate each part to make the code easy to understand and more compact +ACTOR Future SpecialKeyRangeBaseImpl::normalizeKeySelectorActor(const SpecialKeyRangeBaseImpl* pkrImpl, + Reference ryw, + KeySelector* ks) { + ASSERT(!ks->orEqual); // should be removed before calling + ASSERT(ks->offset != 1); // never being called if KeySelector is already normalized + + state Key startKey(pkrImpl->range.begin); + state Key endKey(pkrImpl->range.end); + + if (ks->offset < 1) { + // less than the given key + if (pkrImpl->range.contains(ks->getKey())) endKey = keyAfter(ks->getKey()); + } else { + // greater than the given key + if (pkrImpl->range.contains(ks->getKey())) startKey = ks->getKey(); + } + + TraceEvent("NormalizeKeySelector") + .detail("OriginalKey", ks->getKey()) + .detail("OriginalOffset", ks->offset) + .detail("SpecialKeyRangeStart", pkrImpl->range.begin) + .detail("SpecialKeyRangeEnd", pkrImpl->range.end); + + Standalone result = wait(pkrImpl->getRange(ryw, KeyRangeRef(startKey, endKey))); + // TODO : KeySelector::setKey has byte limit according to the knobs, customize it if needed + if (ks->offset < 1) { + if (result.size() >= 1 - ks->offset) { + ks->setKey(KeyRef(ks->arena(), result[result.size() - (1 - ks->offset)].key)); + ks->offset = 1; + } else { + ks->setKey(KeyRef(ks->arena(), result[0].key)); + ks->offset += result.size(); + } + } else { + if (result.size() >= ks->offset) { + ks->setKey(KeyRef(ks->arena(), result[ks->offset - 1].key)); + ks->offset = 1; + } else { + ks->setKey(KeyRef(ks->arena(), keyAfter(result[result.size() - 1].key))); + ks->offset -= result.size(); + } + } + TraceEvent("NormalizeKeySelector") + .detail("NormalizedKey", ks->getKey()) + .detail("NormalizedOffset", ks->offset) + .detail("SpecialKeyRangeStart", pkrImpl->range.begin) + .detail("SpecialKeyRangeEnd", pkrImpl->range.end); + return Void(); +} + +ACTOR Future> SpecialKeySpace::getRangeAggregationActor( + SpecialKeySpace* pks, Reference ryw, KeySelector begin, KeySelector end, + GetRangeLimits limits, bool reverse) { + // This function handles ranges which cover more than one keyrange and aggregates all results + // KeySelector, GetRangeLimits and reverse are all handled here + + // make sure orEqual == false + begin.removeOrEqual(begin.arena()); + end.removeOrEqual(end.arena()); + + // make sure offset == 1 + state RangeMap::Iterator iter = + pks->impls.rangeContaining(begin.getKey()); + while (begin.offset != 1 && iter != pks->impls.ranges().begin() && iter != pks->impls.ranges().end()) { + if (iter->value() != nullptr) wait(iter->value()->normalizeKeySelectorActor(iter->value(), ryw, &begin)); + begin.offset < 1 ? --iter : ++iter; + } + if (begin.offset != 1) { + // The Key Selector points to key outside the whole special key space + TraceEvent(SevError, "IllegalBeginKeySelector") + .detail("TerminateKey", begin.getKey()) + .detail("TerminateOffset", begin.offset); + } + iter = pks->impls.rangeContaining(end.getKey()); + while (end.offset != 1 && iter != pks->impls.ranges().begin() && iter != pks->impls.ranges().end()) { + if (iter->value() != nullptr) wait(iter->value()->normalizeKeySelectorActor(iter->value(), ryw, &end)); + end.offset < 1 ? --iter : ++iter; + } + if (end.offset != 1) { + // The Key Selector points to key outside the whole special key space + TraceEvent(SevError, "IllegalEndKeySelector") + .detail("TerminateKey", end.getKey()) + .detail("TerminateOffset", end.offset); + } + // return if range inverted + if (begin.offset >= end.offset && begin.getKey() >= end.getKey()) { + TEST(true); + return Standalone(); + } + state Standalone result; + state RangeMap::Ranges ranges = + pks->impls.intersectingRanges(KeyRangeRef(begin.getKey(), end.getKey())); + // 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(); + if (reverse) { + while (iter != ranges.begin()) { + --iter; + if (iter->value() == nullptr) continue; + KeyRangeRef kr = iter->range(); + KeyRef keyStart = kr.contains(begin.getKey()) ? begin.getKey() : kr.begin; + KeyRef keyEnd = kr.contains(end.getKey()) ? end.getKey() : kr.end; + Standalone pairs = wait(iter->value()->getRange(ryw, KeyRangeRef(keyStart, keyEnd))); + // limits handler + for (int i = pairs.size() - 1; i >= 0; --i) { + result.push_back_deep(result.arena(), pairs[i]); + // TODO : the behavior here is even the last kv makes bytes larger than specified, + // it is still returned and set limits.bytes to zero + limits.decrement(pairs[i]); + if (limits.isReached()) return result; + } + } + } else { + for (iter = ranges.begin(); iter != ranges.end(); ++iter) { + if (iter->value() == nullptr) continue; + KeyRangeRef kr = iter->range(); + KeyRef keyStart = kr.contains(begin.getKey()) ? begin.getKey() : kr.begin; + KeyRef keyEnd = kr.contains(end.getKey()) ? end.getKey() : kr.end; + Standalone pairs = wait(iter->value()->getRange(ryw, KeyRangeRef(keyStart, keyEnd))); + // limits handler + for (const KeyValueRef& kv : pairs) { + result.push_back_deep(result.arena(), kv); + // TODO : behavior here is even the last kv makes bytes larger than specified, + // it is still returned and set limits.bytes to zero + limits.decrement(kv); + if (limits.isReached()) return result; + } + } + } + return result; +} + +Future> SpecialKeySpace::getRange(Reference ryw, + KeySelector begin, KeySelector end, GetRangeLimits limits, + bool snapshot, bool reverse) { + // validate limits here + if (!limits.isValid()) return range_limits_invalid(); + if (limits.isReached()) { + TEST(true); // read limit 0 + return Standalone(); + } + // ignore snapshot, which is not used + return getRangeAggregationActor(this, ryw, begin, end, limits, reverse); +} + +ACTOR Future> SpecialKeySpace::getActor(SpecialKeySpace* pks, Reference ryw, + KeyRef key) { + // use getRange to workaround this + Standalone result = wait(pks->getRange( + ryw, KeySelector(firstGreaterOrEqual(key)), KeySelector(firstGreaterOrEqual(keyAfter(key))), GetRangeLimits())); + ASSERT(result.size() <= 1); + if (result.size()) { + return Optional(result[0].value); + } else { + return Optional(); + } +} + +Future> SpecialKeySpace::get(Reference ryw, const Key& key, bool snapshot) { + // ignore snapshot, which is not used + return getActor(this, ryw, key); +} + +class SpecialKeyRangeTestImpl : public SpecialKeyRangeBaseImpl { +public: + explicit SpecialKeyRangeTestImpl(KeyRef start, KeyRef end, const std::string& prefix, int size) + : SpecialKeyRangeBaseImpl(start, end), prefix(prefix), size(size) { + ASSERT(size > 0); + for (int i = 0; i < size; ++i) { + kvs.push_back_deep(kvs.arena(), + KeyValueRef(getKeyForIndex(i), deterministicRandom()->randomAlphaNumeric(16))); + } + } + + KeyValueRef getKeyValueForIndex(int idx) { return kvs[idx]; } + + Key getKeyForIndex(int idx) { return Key(prefix + format("%010d", idx)).withPrefix(range.begin); } + int getSize() { return size; } + virtual Future> getRange(Reference ryw, + KeyRangeRef kr) const override { + int startIndex = 0, endIndex = size; + while (startIndex < size && kvs[startIndex].key < kr.begin) ++startIndex; + while (endIndex > startIndex && kvs[endIndex - 1].key >= kr.end) --endIndex; + if (startIndex == endIndex) + return Standalone(); + else + return Standalone(RangeResultRef(kvs.slice(startIndex, endIndex), false)); + } + +private: + Standalone> kvs; + std::string prefix; + int size; +}; + +TEST_CASE("/fdbclient/SpecialKeySpace/Unittest") { + SpecialKeySpace pks(LiteralStringRef("\xff\xff"), LiteralStringRef("\xff\xff\xff")); + SpecialKeyRangeTestImpl pkr1(LiteralStringRef("\xff\xff/cat/"), LiteralStringRef("\xff\xff/cat/\xff"), "small", 10); + SpecialKeyRangeTestImpl pkr2(LiteralStringRef("\xff\xff/dog/"), LiteralStringRef("\xff\xff/dog/\xff"), "medium", + 100); + SpecialKeyRangeTestImpl pkr3(LiteralStringRef("\xff\xff/pig/"), LiteralStringRef("\xff\xff/pig/\xff"), "large", + 1000); + pks.registerKeyRange(pkr1.getKeyRange(), &pkr1); + pks.registerKeyRange(pkr2.getKeyRange(), &pkr2); + pks.registerKeyRange(pkr3.getKeyRange(), &pkr3); + auto nullRef = Reference(); + // get + { + auto resultFuture = pks.get(nullRef, LiteralStringRef("\xff\xff/cat/small0000000009")); + ASSERT(resultFuture.isReady()); + auto result = resultFuture.getValue().get(); + ASSERT(result == pkr1.getKeyValueForIndex(9).value); + auto emptyFuture = pks.get(nullRef, LiteralStringRef("\xff\xff/cat/small0000000010")); + ASSERT(emptyFuture.isReady()); + auto emptyResult = emptyFuture.getValue(); + ASSERT(!emptyResult.present()); + } + // general getRange + { + KeySelector start = KeySelectorRef(LiteralStringRef("\xff\xff/elepant"), false, -9); + KeySelector end = KeySelectorRef(LiteralStringRef("\xff\xff/frog"), false, +11); + auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits()); + ASSERT(resultFuture.isReady()); + auto result = resultFuture.getValue(); + ASSERT(result.size() == 20); + ASSERT(result[0].key == pkr2.getKeyForIndex(90)); + ASSERT(result[result.size() - 1].key == pkr3.getKeyForIndex(9)); + } + // KeySelector points outside + { + KeySelector start = KeySelectorRef(pkr3.getKeyForIndex(999), true, -1110); + KeySelector end = KeySelectorRef(pkr1.getKeyForIndex(0), false, +1112); + auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits()); + ASSERT(resultFuture.isReady()); + auto result = resultFuture.getValue(); + ASSERT(result.size() == 1110); + ASSERT(result[0].key == pkr1.getKeyForIndex(0)); + ASSERT(result[result.size() - 1].key == pkr3.getKeyForIndex(999)); + } + // GetRangeLimits with row limit + { + KeySelector start = KeySelectorRef(pkr2.getKeyForIndex(0), true, 0); + KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(0), false, 0); + auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits(2)); + ASSERT(resultFuture.isReady()); + auto result = resultFuture.getValue(); + ASSERT(result.size() == 2); + ASSERT(result[0].key == pkr2.getKeyForIndex(0)); + ASSERT(result[1].key == pkr2.getKeyForIndex(1)); + } + // GetRangeLimits with byte limit + { + KeySelector start = KeySelectorRef(pkr2.getKeyForIndex(0), true, 0); + KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(0), false, 0); + auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits(10, 100)); + ASSERT(resultFuture.isReady()); + auto result = resultFuture.getValue(); + int bytes = 0; + for (int i = 0; i < result.size() - 1; ++i) bytes += 8 + pkr2.getKeyValueForIndex(i).expectedSize(); + ASSERT(bytes < 100); + ASSERT(bytes + 8 + pkr2.getKeyValueForIndex(result.size()).expectedSize() >= 100); + } + // reverse test with overlapping key range + { + KeySelector start = KeySelectorRef(pkr2.getKeyForIndex(0), true, 0); + KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(999), true, +1); + auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits(1100), false, true); + ASSERT(resultFuture.isReady()); + auto result = resultFuture.getValue(); + for (int i = 0; i < pkr3.getSize(); ++i) ASSERT(result[i] == pkr3.getKeyValueForIndex(pkr3.getSize() - 1 - i)); + for (int i = 0; i < pkr2.getSize(); ++i) + ASSERT(result[i + pkr3.getSize()] == pkr2.getKeyValueForIndex(pkr2.getSize() - 1 - i)); + } + return Void(); +} diff --git a/fdbclient/SpecialKeySpace.actor.h b/fdbclient/SpecialKeySpace.actor.h new file mode 100644 index 0000000000..8dc717aa9f --- /dev/null +++ b/fdbclient/SpecialKeySpace.actor.h @@ -0,0 +1,72 @@ +#pragma once + +#if defined(NO_INTELLISENSE) && !defined(FDBCLIENT_SPECIALKEYSPACE_ACTOR_G_H) +#define FDBCLIENT_SPECIALKEYSPACE_ACTOR_G_H +#include "fdbclient/SpecialKeySpace.actor.g.h" +#elif !defined(FDBCLIENT_SPECIALKEYSPACE_ACTOR_H) +#define FDBCLIENT_SPECIALKEYSPACE_ACTOR_H + +#include "flow/flow.h" +#include "flow/Arena.h" +#include "fdbclient/FDBTypes.h" +#include "fdbclient/KeyRangeMap.h" +#include "fdbclient/ReadYourWrites.h" +#include "flow/actorcompiler.h" // This must be the last #include. + +class SpecialKeyRangeBaseImpl { +public: + // TO DISCUSS : do we need this general getRange interface here? + // Since a keyRange doesn't have any knowledge about other keyRanges, parameters like KeySelector, + // GetRangeLimits should be handled together in SpecialKeySpace + // Thus, having this general interface looks unnessary. + // virtual Future> getRange(ReadYourWritesTransaction* ryw, KeySelector begin, + // KeySelector end, GetRangeLimits limits, bool snapshot = false, bool reverse = false) const = 0; + + // Each derived class only needs to implement this simple version of getRange + virtual Future> getRange(Reference ryw, + KeyRangeRef kr) const = 0; + + explicit SpecialKeyRangeBaseImpl(KeyRef start, KeyRef end) { + range = KeyRangeRef(range.arena(), KeyRangeRef(start, end)); + } + KeyRangeRef getKeyRange() const { return range; } + ACTOR Future normalizeKeySelectorActor(const SpecialKeyRangeBaseImpl* pkrImpl, + Reference ryw, KeySelector* ks); + +protected: + KeyRange range; // underlying key range for this function +}; + +class SpecialKeySpace { +public: + Future> get(Reference ryw, const Key& key, bool snapshot = false); + + Future> getRange(Reference ryw, KeySelector begin, + KeySelector end, GetRangeLimits limits, bool snapshot = false, + bool reverse = false); + + SpecialKeySpace(KeyRef spaceStartKey = Key(), KeyRef spaceEndKey = allKeys.end) { + // Default value is nullptr, begin of KeyRangeMap is Key() + impls = KeyRangeMap(nullptr, spaceEndKey); + range = KeyRangeRef(spaceStartKey, spaceEndKey); + } + void registerKeyRange(const KeyRangeRef& kr, SpecialKeyRangeBaseImpl* impl) { + // range check + ASSERT(kr.begin >= range.begin && kr.end <= range.end); + impls.insert(kr, impl); + } + +private: + ACTOR Future> getActor(SpecialKeySpace* pks, Reference ryw, KeyRef key); + + ACTOR Future> getRangeAggregationActor(SpecialKeySpace* pks, + Reference ryw, + KeySelector begin, KeySelector end, + GetRangeLimits limits, bool reverse); + + KeyRangeMap impls; + KeyRange range; +}; + +#include "flow/unactorcompiler.h" +#endif From 9862aa8bed51640a9028e7216e86abf52fbc23e2 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Wed, 4 Mar 2020 11:15:32 -0800 Subject: [PATCH 0811/1604] Add support for setting knobs in fdbcli --- fdbcli/fdbcli.actor.cpp | 50 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/fdbcli/fdbcli.actor.cpp b/fdbcli/fdbcli.actor.cpp index cf76fe7ee4..0697fe821d 100644 --- a/fdbcli/fdbcli.actor.cpp +++ b/fdbcli/fdbcli.actor.cpp @@ -69,7 +69,8 @@ enum { OPT_NO_STATUS, OPT_STATUS_FROM_JSON, OPT_VERSION, - OPT_TRACE_FORMAT + OPT_TRACE_FORMAT, + OPT_KNOB }; CSimpleOpt::SOption g_rgOptions[] = { { OPT_CONNFILE, "-C", SO_REQ_SEP }, @@ -87,12 +88,13 @@ CSimpleOpt::SOption g_rgOptions[] = { { OPT_CONNFILE, "-C", SO_REQ_SEP }, { OPT_VERSION, "--version", SO_NONE }, { OPT_VERSION, "-v", SO_NONE }, { OPT_TRACE_FORMAT, "--trace_format", SO_REQ_SEP }, + { OPT_KNOB, "--knob_", SO_REQ_SEP }, #ifndef TLS_DISABLED TLS_OPTION_FLAGS #endif - SO_END_OF_OPTIONS }; + SO_END_OF_OPTIONS }; void printAtCol(const char* text, int col) { const char* iter = text; @@ -423,6 +425,8 @@ static void printProgramUsage(const char* name) { #ifndef TLS_DISABLED TLS_HELP #endif + " --knob_KNOBNAME KNOBVALUE\n" + " Changes a knob option. KNOBNAME should be lowercase.\n" " -v, --version Print FoundationDB CLI version information and exit.\n" " -h, --help Display this help and exit.\n"); } @@ -2444,6 +2448,8 @@ struct CLIOptions { std::string tlsCAPath; std::string tlsPassword; + std::vector> knobs; + CLIOptions( int argc, char* argv[] ) : trace(false), exit_timeout(0), @@ -2467,9 +2473,37 @@ struct CLIOptions { } if (exit_timeout && !exec.present()) { fprintf(stderr, "ERROR: --timeout may only be specified with --exec\n"); - exit_code = 1; + exit_code = FDB_EXIT_ERROR; return; } + + delete FLOW_KNOBS; + FlowKnobs* flowKnobs = new FlowKnobs(true); + FLOW_KNOBS = flowKnobs; + + delete CLIENT_KNOBS; + ClientKnobs* clientKnobs = new ClientKnobs(true); + CLIENT_KNOBS = clientKnobs; + + for(auto k=knobs.begin(); k!=knobs.end(); ++k) { + try { + if (!flowKnobs->setKnob( k->first, k->second ) && + !clientKnobs->setKnob( k->first, k->second )) + { + fprintf(stderr, "ERROR: Unrecognized knob option '%s'\n", k->first.c_str()); + exit_code = FDB_EXIT_ERROR; + } + } catch (Error& e) { + if (e.code() == error_code_invalid_option_value) { + fprintf(stderr, "ERROR: Invalid value '%s' for knob option '%s'\n", k->second.c_str(), k->first.c_str()); + exit_code = FDB_EXIT_ERROR; + } + else { + fprintf(stderr, "ERROR: Failed to set knob option '%s': %s\n", k->first.c_str(), e.what()); + exit_code = FDB_EXIT_ERROR; + } + } + } } int processArg(CSimpleOpt& args) { @@ -2536,6 +2570,16 @@ struct CLIOptions { } traceFormat = args.OptionArg(); break; + case OPT_KNOB: { + std::string syn = args.OptionSyntax(); + if (!StringRef(syn).startsWith(LiteralStringRef("--knob_"))) { + fprintf(stderr, "ERROR: unable to parse knob option '%s'\n", syn.c_str()); + return FDB_EXIT_ERROR; + } + syn = syn.substr(7); + knobs.push_back( std::make_pair( syn, args.OptionArg() ) ); + break; + } case OPT_VERSION: printVersion(); return FDB_EXIT_SUCCESS; From 181ca3fce07e0dd72455185bd26d2ec6e2bc7330 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Wed, 4 Mar 2020 11:18:00 -0800 Subject: [PATCH 0812/1604] Add release note. --- documentation/sphinx/source/release-notes.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/documentation/sphinx/source/release-notes.rst b/documentation/sphinx/source/release-notes.rst index 4bb82d8dcd..73250b270b 100644 --- a/documentation/sphinx/source/release-notes.rst +++ b/documentation/sphinx/source/release-notes.rst @@ -2,6 +2,14 @@ Release Notes ############# +6.2.18 +====== + +Features +-------- + +* Add support for setting knobs in fdbcli. `(PR #2773) `_. + 6.2.17 ====== From 3a98c691b68bd3704fcc082910b1393a1cb29c66 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Wed, 4 Mar 2020 11:42:19 -0800 Subject: [PATCH 0813/1604] Update comments --- fdbserver/SkipList.cpp | 4 +- .../workloads/ReportConflictingKeys.actor.cpp | 39 ++++++++++--------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/fdbserver/SkipList.cpp b/fdbserver/SkipList.cpp index 9d949a4c84..33fe40e696 100644 --- a/fdbserver/SkipList.cpp +++ b/fdbserver/SkipList.cpp @@ -608,8 +608,8 @@ private: bool* result; int state; int indexInTx; - VectorRef* conflictingKeyRange; // null if report_conflicting_keys is not enabled. - Arena* cKRArena; // null if report_conflicting_keys is not enabled. + VectorRef* conflictingKeyRange; // nullptr if report_conflicting_keys is not enabled. + Arena* cKRArena; // nullptr if report_conflicting_keys is not enabled. void init( const ReadConflictRange& r, Node* header, bool* tCS, int indexInTx, VectorRef* cKR, Arena* cKRArena) { this->start.init( r.begin, header ); diff --git a/fdbserver/workloads/ReportConflictingKeys.actor.cpp b/fdbserver/workloads/ReportConflictingKeys.actor.cpp index 4891debdb2..1d978ca8da 100644 --- a/fdbserver/workloads/ReportConflictingKeys.actor.cpp +++ b/fdbserver/workloads/ReportConflictingKeys.actor.cpp @@ -47,7 +47,7 @@ struct ReportConflictingKeysWorkload : TestWorkload { getOption(options, LiteralStringRef("keyPrefix"), LiteralStringRef("ReportConflictingKeysWorkload")) .toString()); keyBytes = getOption(options, LiteralStringRef("keyBytes"), 16); - + readConflictRangeCount = getOption(options, LiteralStringRef("readConflictRangeCountPerTx"), 1); writeConflictRangeCount = getOption(options, LiteralStringRef("writeConflictRangeCountPerTx"), 1); // modeled by geometric distribution: (1 - prob) / prob = mean @@ -59,10 +59,9 @@ struct ReportConflictingKeysWorkload : TestWorkload { // used for generating keyPrefix keyPrefixBytes = getOption(options, LiteralStringRef("keyPrefixBytes"), 0); if (keyPrefixBytes) { - prefixCount = 255 * std::round(std::exp2(8*(keyPrefixBytes-1))); + prefixCount = 255 * std::round(std::exp2(8 * (keyPrefixBytes - 1))); ASSERT(keyPrefixBytes + 16 <= keyBytes); - } - else { + } else { ASSERT(keyPrefix.size() + 16 <= keyBytes); // make sure the string format is valid } nodeCountPerPrefix = getOption(options, LiteralStringRef("nodeCountPerPrefix"), 100); @@ -106,12 +105,13 @@ struct ReportConflictingKeysWorkload : TestWorkload { double p = (double)n / nodeCountPerPrefix; int paddingLen = keyBytes - 16 - keyPrefixBytes; // left padding by zero - return StringRef(format("%0*llx", paddingLen, *(uint64_t*)&p)).withPrefix( prefixIdx >= 0 ? keyPrefixForIndex( prefixIdx) : keyPrefix); + return StringRef(format("%0*llx", paddingLen, *(uint64_t*)&p)) + .withPrefix(prefixIdx >= 0 ? keyPrefixForIndex(prefixIdx) : keyPrefix); } Key keyPrefixForIndex(uint64_t n) { Key prefix = makeString(keyPrefixBytes); - uint8_t * head = mutateString(prefix); + uint8_t* head = mutateString(prefix); memset(head, 0, keyPrefixBytes); int offset = keyPrefixBytes - 1; while (n) { @@ -182,29 +182,30 @@ struct ReportConflictingKeysWorkload : TestWorkload { if (!self->skipCorrectnessCheck && self->reportConflictingKeys && isConflict) { const KeyRef conflictingKeysPreifx = LiteralStringRef("\xff\xff/transaction/conflicting_keys/"); state KeyRange ckr = KeyRangeRef(LiteralStringRef("").withPrefix(conflictingKeysPreifx), - LiteralStringRef("\xff").withPrefix(conflictingKeysPreifx)); - // The getRange here using the special key prefix "\xff\xff/transaction/conflicting_keys/" happens locally - // Thus, the error handling is not needed here - Future> conflictingKeyRangesFuture = tr.getRange(ckr, readConflictRanges.size() * 2); + LiteralStringRef("\xff").withPrefix(conflictingKeysPreifx)); + // The getRange here using the special key prefix "\xff\xff/transaction/conflicting_keys/" happens + // locally Thus, the error handling is not needed here + Future> conflictingKeyRangesFuture = + tr.getRange(ckr, readConflictRanges.size() * 2); ASSERT(conflictingKeyRangesFuture.isReady()); const Standalone conflictingKeyRanges = conflictingKeyRangesFuture.get(); - ASSERT( conflictingKeyRanges.size() && ( conflictingKeyRanges.size() % 2 == 0 ) ); + ASSERT(conflictingKeyRanges.size() && (conflictingKeyRanges.size() % 2 == 0)); for (int i = 0; i < conflictingKeyRanges.size(); i += 2) { KeyValueRef startKeyWithPreifx = conflictingKeyRanges[i]; ASSERT(startKeyWithPreifx.value == conflictingKeysTrue); - KeyValueRef endKeyWithPrefix = conflictingKeyRanges[i+1]; - ASSERT(endKeyWithPrefix.value == conflictingKeysFalse); + KeyValueRef endKeyWithPrefix = conflictingKeyRanges[i + 1]; + ASSERT(endKeyWithPrefix.value == conflictingKeysFalse); // Remove the prefix of returning keys Key startKey = startKeyWithPreifx.key.removePrefix(conflictingKeysPreifx); Key endKey = endKeyWithPrefix.key.removePrefix(conflictingKeysPreifx); KeyRangeRef kr = KeyRangeRef(startKey, endKey); if (!std::any_of(readConflictRanges.begin(), readConflictRanges.end(), [&kr](KeyRange rCR) { - // Read_conflict_range remains same in the resolver. - // Thus, the returned keyrange is either the original read_conflict_range or merged - // by several overlapped ones In either case, it contains at least one original - // read_conflict_range - return kr.contains(rCR); - })) { + // Read_conflict_range remains same in the resolver. + // Thus, the returned keyrange is either the original read_conflict_range or merged + // by several overlapped ones In either case, it contains at least one original + // read_conflict_range + return kr.contains(rCR); + })) { ++self->invalidReports; TraceEvent(SevError, "TestFailure").detail("Reason", "InvalidKeyRangeReturned"); } From c63909c18ccc686141a69ddac69ba3dc1791b8e9 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Wed, 4 Mar 2020 11:44:14 -0800 Subject: [PATCH 0814/1604] clang-format --- fdbserver/ConflictSet.h | 5 +-- fdbserver/SkipList.cpp | 68 +++++++++++++++++++++-------------------- 2 files changed, 38 insertions(+), 35 deletions(-) diff --git a/fdbserver/ConflictSet.h b/fdbserver/ConflictSet.h index 6b2fffaf08..369ad01701 100644 --- a/fdbserver/ConflictSet.h +++ b/fdbserver/ConflictSet.h @@ -33,7 +33,8 @@ void clearConflictSet(ConflictSet*, Version); void destroyConflictSet(ConflictSet*); struct ConflictBatch { - explicit ConflictBatch( ConflictSet*, std::map< int, VectorRef< int > >* conflictingKeyRangeMap = nullptr, Arena* resolveBatchReplyArena = nullptr); + explicit ConflictBatch(ConflictSet*, std::map>* conflictingKeyRangeMap = nullptr, + Arena* resolveBatchReplyArena = nullptr); ~ConflictBatch(); enum TransactionCommitResult { @@ -55,7 +56,7 @@ private: std::vector> combinedWriteConflictRanges; std::vector combinedReadConflictRanges; bool* transactionConflictStatus; - std::map< int, VectorRef< int > >* conflictingKeyRangeMap; + std::map>* conflictingKeyRangeMap; Arena* resolveBatchReplyArena; void checkIntraBatchConflicts(); diff --git a/fdbserver/SkipList.cpp b/fdbserver/SkipList.cpp index 33fe40e696..5f6520f1f4 100644 --- a/fdbserver/SkipList.cpp +++ b/fdbserver/SkipList.cpp @@ -73,12 +73,12 @@ struct ReadConflictRange { int indexInTx; VectorRef* conflictingKeyRange; Arena* cKRArena; - - ReadConflictRange( StringRef begin, StringRef end, Version version, int transaction, int indexInTx, VectorRef * cKR = nullptr, Arena* cKRArena = nullptr) - : begin(begin), end(end), version(version), transaction(transaction), indexInTx(indexInTx), conflictingKeyRange(cKR), cKRArena(cKRArena) - { - } - bool operator<(const ReadConflictRange& rhs) const { return compare(begin, rhs.begin)<0; } + + ReadConflictRange(StringRef begin, StringRef end, Version version, int transaction, int indexInTx, + VectorRef* cKR = nullptr, Arena* cKRArena = nullptr) + : begin(begin), end(end), version(version), transaction(transaction), indexInTx(indexInTx), + conflictingKeyRange(cKR), cKRArena(cKRArena) {} + bool operator<(const ReadConflictRange& rhs) const { return compare(begin, rhs.begin) < 0; } }; struct KeyInfo { @@ -293,10 +293,10 @@ private: int nPointers, valueLength; }; - static force_inline bool less( const uint8_t* a, int aLen, const uint8_t* b, int bLen ) { - int c = memcmp(a,b,min(aLen,bLen)); - if (c<0) return true; - if (c>0) return false; + static force_inline bool less(const uint8_t* a, int aLen, const uint8_t* b, int bLen) { + int c = memcmp(a, b, min(aLen, bLen)); + if (c < 0) return true; + if (c > 0) return false; return aLen < bLen; } @@ -419,10 +419,11 @@ public: CheckMax inProgress[M]; if (!count) return; - int started = min(M,count); - for(int i=0; i* conflictingKeyRange; // nullptr if report_conflicting_keys is not enabled. Arena* cKRArena; // nullptr if report_conflicting_keys is not enabled. - void init( const ReadConflictRange& r, Node* header, bool* tCS, int indexInTx, VectorRef* cKR, Arena* cKRArena) { - this->start.init( r.begin, header ); - this->end.init( r.end, header ); + void init(const ReadConflictRange& r, Node* header, bool* tCS, int indexInTx, VectorRef* cKR, + Arena* cKRArena) { + this->start.init(r.begin, header); + this->end.init(r.end, header); this->version = r.version; this->indexInTx = indexInTx; this->cKRArena = cKRArena; - result = &tCS[ r.transaction ]; + result = &tCS[r.transaction]; conflictingKeyRange = cKR; this->state = 0; } bool noConflict() { return true; } - bool conflict() { + bool conflict() { *result = true; - if(conflictingKeyRange != nullptr) - conflictingKeyRange->push_back(*cKRArena, indexInTx); + if (conflictingKeyRange != nullptr) conflictingKeyRange->push_back(*cKRArena, indexInTx); return true; } @@ -745,10 +746,10 @@ void destroyConflictSet(ConflictSet* cs) { delete cs; } -ConflictBatch::ConflictBatch( ConflictSet* cs, std::map< int, VectorRef< int > >* conflictingKeyRangeMap, Arena* resolveBatchReplyArena ) - : cs(cs), transactionCount(0), conflictingKeyRangeMap(conflictingKeyRangeMap),resolveBatchReplyArena(resolveBatchReplyArena) -{ -} +ConflictBatch::ConflictBatch(ConflictSet* cs, std::map>* conflictingKeyRangeMap, + Arena* resolveBatchReplyArena) + : cs(cs), transactionCount(0), conflictingKeyRangeMap(conflictingKeyRangeMap), + resolveBatchReplyArena(resolveBatchReplyArena) {} ConflictBatch::~ConflictBatch() {} @@ -779,8 +780,9 @@ void ConflictBatch::addTransaction(const CommitTransactionRef& tr) { points.emplace_back(range.begin, true, false, t, &info->readRanges[r].first); points.emplace_back(range.end, false, false, t, &info->readRanges[r].second); combinedReadConflictRanges.emplace_back(range.begin, range.end, tr.read_snapshot, t, r, - tr.report_conflicting_keys ? &(*conflictingKeyRangeMap)[t] : nullptr, - tr.report_conflicting_keys ? resolveBatchReplyArena : nullptr); + tr.report_conflicting_keys ? &(*conflictingKeyRangeMap)[t] + : nullptr, + tr.report_conflicting_keys ? resolveBatchReplyArena : nullptr); } for (int r = 0; r < tr.write_conflict_ranges.size(); r++) { const KeyRangeRef& range = tr.write_conflict_ranges[r]; @@ -817,9 +819,9 @@ void ConflictBatch::checkIntraBatchConflicts() { const TransactionInfo& tr = *transactionInfo[t]; if (transactionConflictStatus[t]) continue; bool conflict = tr.tooOld; - for(int i=0; i Date: Wed, 4 Mar 2020 13:44:20 -0800 Subject: [PATCH 0815/1604] updat comments --- fdbclient/SpecialKeySpace.actor.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fdbclient/SpecialKeySpace.actor.cpp b/fdbclient/SpecialKeySpace.actor.cpp index a2e02f1bc8..8c71c31740 100644 --- a/fdbclient/SpecialKeySpace.actor.cpp +++ b/fdbclient/SpecialKeySpace.actor.cpp @@ -114,8 +114,8 @@ ACTOR Future> SpecialKeySpace::getRangeAggregationAct // limits handler for (int i = pairs.size() - 1; i >= 0; --i) { result.push_back_deep(result.arena(), pairs[i]); - // TODO : the behavior here is even the last kv makes bytes larger than specified, - // it is still returned and set limits.bytes to zero + // Note : behavior here is even the last k-v pair makes total bytes larger than specified, it is still returned + // In other words, the total size of the returned value (less the last entry) will be less than byteLimit limits.decrement(pairs[i]); if (limits.isReached()) return result; } @@ -130,8 +130,8 @@ ACTOR Future> SpecialKeySpace::getRangeAggregationAct // limits handler for (const KeyValueRef& kv : pairs) { result.push_back_deep(result.arena(), kv); - // TODO : behavior here is even the last kv makes bytes larger than specified, - // it is still returned and set limits.bytes to zero + // Note : behavior here is even the last k-v pari makes total bytes larger than specified, it is still returned + // In other words, the total size of the returned value (less the last entry) will be less than byteLimit limits.decrement(kv); if (limits.isReached()) return result; } From 6296465e079cb172b9cfb2c464f98588b135fc06 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 4 Mar 2020 14:07:32 -0800 Subject: [PATCH 0816/1604] Make the DD priority associated with populating a remote region lower than machine failures --- fdbserver/DataDistribution.actor.cpp | 9 ++++++--- fdbserver/DataDistributionQueue.actor.cpp | 10 ++++++---- fdbserver/Knobs.cpp | 1 + fdbserver/Knobs.h | 1 + fdbserver/Status.actor.cpp | 15 ++++++++------- 5 files changed, 22 insertions(+), 14 deletions(-) diff --git a/fdbserver/DataDistribution.actor.cpp b/fdbserver/DataDistribution.actor.cpp index 3d1e8ecdfc..ef16359a74 100644 --- a/fdbserver/DataDistribution.actor.cpp +++ b/fdbserver/DataDistribution.actor.cpp @@ -2870,7 +2870,9 @@ ACTOR Future teamTracker(DDTeamCollection* self, Reference tea lastWrongConfiguration = anyWrongConfiguration; state int lastPriority = team->getPriority(); - if( serversLeft < self->configuration.storageTeamSize ) { + if(team->size() == 0) { + team->setPriority( SERVER_KNOBS->PRIORITY_POPULATE_REGION ); + } else if( serversLeft < self->configuration.storageTeamSize ) { if( serversLeft == 0 ) team->setPriority( SERVER_KNOBS->PRIORITY_TEAM_0_LEFT ); else if( serversLeft == 1 ) @@ -2887,10 +2889,11 @@ ACTOR Future teamTracker(DDTeamCollection* self, Reference tea team->setPriority( SERVER_KNOBS->PRIORITY_TEAM_UNHEALTHY ); } } - else if( anyUndesired ) + else if( anyUndesired ) { team->setPriority( SERVER_KNOBS->PRIORITY_TEAM_CONTAINS_UNDESIRED_SERVER ); - else + } else { team->setPriority( SERVER_KNOBS->PRIORITY_TEAM_HEALTHY ); + } if(lastPriority != team->getPriority()) { self->priority_teams[lastPriority]--; diff --git a/fdbserver/DataDistributionQueue.actor.cpp b/fdbserver/DataDistributionQueue.actor.cpp index c534cda824..43626ea426 100644 --- a/fdbserver/DataDistributionQueue.actor.cpp +++ b/fdbserver/DataDistributionQueue.actor.cpp @@ -57,7 +57,8 @@ struct RelocateData { rs.priority == SERVER_KNOBS->PRIORITY_TEAM_REDUNDANT), interval("QueuedRelocation") {} static bool isHealthPriority(int priority) { - return priority == SERVER_KNOBS->PRIORITY_TEAM_UNHEALTHY || + return priority == SERVER_KNOBS->PRIORITY_POPULATE_REGION || + priority == SERVER_KNOBS->PRIORITY_TEAM_UNHEALTHY || priority == SERVER_KNOBS->PRIORITY_TEAM_2_LEFT || priority == SERVER_KNOBS->PRIORITY_TEAM_1_LEFT || priority == SERVER_KNOBS->PRIORITY_TEAM_0_LEFT || @@ -394,7 +395,7 @@ struct DDQueueData { // ensure a team remover will not start before the previous one finishes removing a team and move away data // NOTE: split and merge shard have higher priority. If they have to wait for unhealthyRelocations = 0, // deadlock may happen: split/merge shard waits for unhealthyRelocations, while blocks team_redundant. - if (healthPriority == SERVER_KNOBS->PRIORITY_TEAM_UNHEALTHY || healthPriority == SERVER_KNOBS->PRIORITY_TEAM_2_LEFT || + if (healthPriority == SERVER_KNOBS->PRIORITY_POPULATE_REGION || healthPriority == SERVER_KNOBS->PRIORITY_TEAM_UNHEALTHY || healthPriority == SERVER_KNOBS->PRIORITY_TEAM_2_LEFT || healthPriority == SERVER_KNOBS->PRIORITY_TEAM_1_LEFT || healthPriority == SERVER_KNOBS->PRIORITY_TEAM_0_LEFT || healthPriority == SERVER_KNOBS->PRIORITY_TEAM_REDUNDANT) { unhealthyRelocations++; rawProcessingUnhealthy->set(true); @@ -402,7 +403,7 @@ struct DDQueueData { priority_relocations[priority]++; } void finishRelocation(int priority, int healthPriority) { - if (healthPriority == SERVER_KNOBS->PRIORITY_TEAM_UNHEALTHY || healthPriority == SERVER_KNOBS->PRIORITY_TEAM_2_LEFT || + if (healthPriority == SERVER_KNOBS->PRIORITY_POPULATE_REGION || healthPriority == SERVER_KNOBS->PRIORITY_TEAM_UNHEALTHY || healthPriority == SERVER_KNOBS->PRIORITY_TEAM_2_LEFT || healthPriority == SERVER_KNOBS->PRIORITY_TEAM_1_LEFT || healthPriority == SERVER_KNOBS->PRIORITY_TEAM_0_LEFT || healthPriority == SERVER_KNOBS->PRIORITY_TEAM_REDUNDANT) { unhealthyRelocations--; ASSERT(unhealthyRelocations >= 0); @@ -927,7 +928,7 @@ ACTOR Future dataDistributionRelocator( DDQueueData *self, RelocateData rd while( tciIndex < self->teamCollections.size() ) { double inflightPenalty = SERVER_KNOBS->INFLIGHT_PENALTY_HEALTHY; if(rd.healthPriority == SERVER_KNOBS->PRIORITY_TEAM_UNHEALTHY || rd.healthPriority == SERVER_KNOBS->PRIORITY_TEAM_2_LEFT) inflightPenalty = SERVER_KNOBS->INFLIGHT_PENALTY_UNHEALTHY; - if(rd.healthPriority == SERVER_KNOBS->PRIORITY_TEAM_1_LEFT || rd.healthPriority == SERVER_KNOBS->PRIORITY_TEAM_0_LEFT) inflightPenalty = SERVER_KNOBS->INFLIGHT_PENALTY_ONE_LEFT; + if(rd.healthPriority == SERVER_KNOBS->PRIORITY_POPULATE_REGION || rd.healthPriority == SERVER_KNOBS->PRIORITY_TEAM_1_LEFT || rd.healthPriority == SERVER_KNOBS->PRIORITY_TEAM_0_LEFT) inflightPenalty = SERVER_KNOBS->INFLIGHT_PENALTY_ONE_LEFT; auto req = GetTeamRequest(rd.wantsNewServers, rd.priority == SERVER_KNOBS->PRIORITY_REBALANCE_UNDERUTILIZED_TEAM, true, false, inflightPenalty); req.completeSources = rd.completeSources; @@ -1497,6 +1498,7 @@ ACTOR Future dataDistributionQueue( .detail( "PriorityTeamContainsUndesiredServer", self.priority_relocations[SERVER_KNOBS->PRIORITY_TEAM_CONTAINS_UNDESIRED_SERVER] ) .detail( "PriorityTeamRedundant", self.priority_relocations[SERVER_KNOBS->PRIORITY_TEAM_REDUNDANT] ) .detail( "PriorityMergeShard", self.priority_relocations[SERVER_KNOBS->PRIORITY_MERGE_SHARD] ) + .detail( "PriorityPopulateRegion", self.priority_relocations[SERVER_KNOBS->PRIORITY_POPULATE_REGION] ) .detail( "PriorityTeamUnhealthy", self.priority_relocations[SERVER_KNOBS->PRIORITY_TEAM_UNHEALTHY] ) .detail( "PriorityTeam2Left", self.priority_relocations[SERVER_KNOBS->PRIORITY_TEAM_2_LEFT] ) .detail( "PriorityTeam1Left", self.priority_relocations[SERVER_KNOBS->PRIORITY_TEAM_1_LEFT] ) diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index 2ce0aac021..04118e7e75 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -112,6 +112,7 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs, bool isSimula init( PRIORITY_TEAM_CONTAINS_UNDESIRED_SERVER, 150 ); init( PRIORITY_TEAM_REDUNDANT, 200 ); init( PRIORITY_MERGE_SHARD, 340 ); + init( PRIORITY_POPULATE_REGION, 600 ); init( PRIORITY_TEAM_UNHEALTHY, 700 ); init( PRIORITY_TEAM_2_LEFT, 709 ); init( PRIORITY_TEAM_1_LEFT, 800 ); diff --git a/fdbserver/Knobs.h b/fdbserver/Knobs.h index c5c41fc58f..06fb5af3a0 100644 --- a/fdbserver/Knobs.h +++ b/fdbserver/Knobs.h @@ -117,6 +117,7 @@ public: int PRIORITY_TEAM_CONTAINS_UNDESIRED_SERVER; int PRIORITY_TEAM_REDUNDANT; int PRIORITY_MERGE_SHARD; + int PRIORITY_POPULATE_REGION; int PRIORITY_TEAM_UNHEALTHY; int PRIORITY_TEAM_2_LEFT; int PRIORITY_TEAM_1_LEFT; diff --git a/fdbserver/Status.actor.cpp b/fdbserver/Status.actor.cpp index 835848ce0f..d1cab6de22 100644 --- a/fdbserver/Status.actor.cpp +++ b/fdbserver/Status.actor.cpp @@ -1430,29 +1430,30 @@ ACTOR static Future dataStatusFetcher(WorkerDetails ddWorker, stateSectionObj["description"] = "No replicas remain of some data"; stateSectionObj["min_replicas_remaining"] = 0; replicas = 0; - } - else if (highestPriority >= SERVER_KNOBS->PRIORITY_TEAM_1_LEFT) { + } else if (highestPriority >= SERVER_KNOBS->PRIORITY_TEAM_1_LEFT) { stateSectionObj["healthy"] = false; stateSectionObj["name"] = "healing"; stateSectionObj["description"] = "Only one replica remains of some data"; stateSectionObj["min_replicas_remaining"] = 1; replicas = 1; - } - else if (highestPriority >= SERVER_KNOBS->PRIORITY_TEAM_2_LEFT) { + } else if (highestPriority >= SERVER_KNOBS->PRIORITY_TEAM_2_LEFT) { stateSectionObj["healthy"] = false; stateSectionObj["name"] = "healing"; stateSectionObj["description"] = "Only two replicas remain of some data"; stateSectionObj["min_replicas_remaining"] = 2; replicas = 2; - } - else if (highestPriority >= SERVER_KNOBS->PRIORITY_TEAM_UNHEALTHY) { + } else if (highestPriority >= SERVER_KNOBS->PRIORITY_TEAM_UNHEALTHY) { stateSectionObj["healthy"] = false; stateSectionObj["name"] = "healing"; stateSectionObj["description"] = "Restoring replication factor"; + } else if (highestPriority >= SERVER_KNOBS->PRIORITY_POPULATE_REGION) { + stateSectionObj["healthy"] = true; + stateSectionObj["name"] = "healthy_populating_region"; + stateSectionObj["description"] = "Populating remote region"; } else if (highestPriority >= SERVER_KNOBS->PRIORITY_MERGE_SHARD) { stateSectionObj["healthy"] = true; stateSectionObj["name"] = "healthy_repartitioning"; - stateSectionObj["description"] = "Repartitioning."; + stateSectionObj["description"] = "Repartitioning"; } else if (highestPriority >= SERVER_KNOBS->PRIORITY_TEAM_REDUNDANT) { stateSectionObj["healthy"] = true; stateSectionObj["name"] = "optimizing_team_collections"; From 125bd131987f96adc3f9a821b4b052800e98f924 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 4 Mar 2020 14:17:17 -0800 Subject: [PATCH 0817/1604] fix: in multi-region configurations, the data distribution queue could start too much work, expecting that the remote region would contribute to the read workload --- fdbserver/DataDistribution.actor.cpp | 2 +- fdbserver/DataDistribution.actor.h | 1 + fdbserver/DataDistributionQueue.actor.cpp | 30 +++++++++++------------ 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/fdbserver/DataDistribution.actor.cpp b/fdbserver/DataDistribution.actor.cpp index 3d1e8ecdfc..6078565d3f 100644 --- a/fdbserver/DataDistribution.actor.cpp +++ b/fdbserver/DataDistribution.actor.cpp @@ -4279,7 +4279,7 @@ ACTOR Future dataDistribution(Reference self) actors.push_back( pollMoveKeysLock(cx, lock) ); actors.push_back( reportErrorsExcept( dataDistributionTracker( initData, cx, output, shardsAffectedByTeamFailure, getShardMetrics, getAverageShardBytes.getFuture(), readyToStart, anyZeroHealthyTeams, self->ddId ), "DDTracker", self->ddId, &normalDDQueueErrors() ) ); - actors.push_back( reportErrorsExcept( dataDistributionQueue( cx, output, input.getFuture(), getShardMetrics, processingUnhealthy, tcis, shardsAffectedByTeamFailure, lock, getAverageShardBytes, self->ddId, storageTeamSize, &lastLimited ), "DDQueue", self->ddId, &normalDDQueueErrors() ) ); + actors.push_back( reportErrorsExcept( dataDistributionQueue( cx, output, input.getFuture(), getShardMetrics, processingUnhealthy, tcis, shardsAffectedByTeamFailure, lock, getAverageShardBytes, self->ddId, storageTeamSize, configuration.storageTeamSize, &lastLimited ), "DDQueue", self->ddId, &normalDDQueueErrors() ) ); vector teamCollectionsPtrs; Reference primaryTeamCollection( new DDTeamCollection(cx, self->ddId, lock, output, shardsAffectedByTeamFailure, configuration, primaryDcId, configuration.usableRegions > 1 ? remoteDcIds : std::vector>(), readyToStart.getFuture(), zeroHealthyTeams[0], true, processingUnhealthy) ); diff --git a/fdbserver/DataDistribution.actor.h b/fdbserver/DataDistribution.actor.h index 005c1c56c2..c52c953e20 100644 --- a/fdbserver/DataDistribution.actor.h +++ b/fdbserver/DataDistribution.actor.h @@ -204,6 +204,7 @@ Future dataDistributionQueue( PromiseStream> const& getAverageShardBytes, UID const& distributorId, int const& teamSize, + int const& singleRegionTeamSize, double* const& lastLimited); //Holds the permitted size and IO Bounds for a shard diff --git a/fdbserver/DataDistributionQueue.actor.cpp b/fdbserver/DataDistributionQueue.actor.cpp index c534cda824..8540bde61d 100644 --- a/fdbserver/DataDistributionQueue.actor.cpp +++ b/fdbserver/DataDistributionQueue.actor.cpp @@ -285,29 +285,27 @@ struct Busyness { }; // find the "workFactor" for this, were it launched now -int getWorkFactor( RelocateData const& relocation ) { - // Avoid the divide by 0! - ASSERT( relocation.src.size() ); - +int getWorkFactor( RelocateData const& relocation, int singleRegionTeamSize ) { if( relocation.healthPriority == SERVER_KNOBS->PRIORITY_TEAM_1_LEFT || relocation.healthPriority == SERVER_KNOBS->PRIORITY_TEAM_0_LEFT ) return WORK_FULL_UTILIZATION / SERVER_KNOBS->RELOCATION_PARALLELISM_PER_SOURCE_SERVER; else if( relocation.healthPriority == SERVER_KNOBS->PRIORITY_TEAM_2_LEFT ) return WORK_FULL_UTILIZATION / 2 / SERVER_KNOBS->RELOCATION_PARALLELISM_PER_SOURCE_SERVER; else // for now we assume that any message at a lower priority can best be assumed to have a full team left for work - return WORK_FULL_UTILIZATION / relocation.src.size() / SERVER_KNOBS->RELOCATION_PARALLELISM_PER_SOURCE_SERVER; + return WORK_FULL_UTILIZATION / singleRegionTeamSize / SERVER_KNOBS->RELOCATION_PARALLELISM_PER_SOURCE_SERVER; } // Data movement's resource control: Do not overload source servers used for the RelocateData // return true if servers are not too busy to launch the relocation -bool canLaunch( RelocateData & relocation, int teamSize, std::map & busymap, +bool canLaunch( RelocateData & relocation, int teamSize, int singleRegionTeamSize, std::map & busymap, std::vector cancellableRelocations ) { // assert this has not already been launched ASSERT( relocation.workFactor == 0 ); ASSERT( relocation.src.size() != 0 ); + ASSERT( teamSize >= singleRegionTeamSize ); // find the "workFactor" for this, were it launched now - int workFactor = getWorkFactor( relocation ); - int neededServers = std::max( 1, (int)relocation.src.size() - teamSize + 1 ); + int workFactor = getWorkFactor( relocation, singleRegionTeamSize ); + int neededServers = std::min( relocation.src.size(), teamSize - singleRegionTeamSize + 1 ); // see if each of the SS can launch this task for( int i = 0; i < relocation.src.size(); i++ ) { // For each source server for this relocation, copy and modify its busyness to reflect work that WOULD be cancelled @@ -328,9 +326,9 @@ bool canLaunch( RelocateData & relocation, int teamSize, std::map } // update busyness for each server -void launch( RelocateData & relocation, std::map & busymap ) { +void launch( RelocateData & relocation, std::map & busymap, int singleRegionTeamSize ) { // if we are here this means that we can launch and should adjust all the work the servers can do - relocation.workFactor = getWorkFactor( relocation ); + relocation.workFactor = getWorkFactor( relocation, singleRegionTeamSize ); for( int i = 0; i < relocation.src.size(); i++ ) busymap[ relocation.src[i] ].addWork( relocation.priority, relocation.workFactor ); } @@ -359,6 +357,7 @@ struct DDQueueData { int queuedRelocations; int64_t bytesWritten; int teamSize; + int singleRegionTeamSize; std::map busymap; @@ -415,10 +414,10 @@ struct DDQueueData { DDQueueData( UID mid, MoveKeysLock lock, Database cx, std::vector teamCollections, Reference sABTF, PromiseStream> getAverageShardBytes, - int teamSize, PromiseStream output, FutureStream input, PromiseStream getShardMetrics, double* lastLimited ) : + int teamSize, int singleRegionTeamSize, PromiseStream output, FutureStream input, PromiseStream getShardMetrics, double* lastLimited ) : activeRelocations( 0 ), queuedRelocations( 0 ), bytesWritten ( 0 ), teamCollections( teamCollections ), shardsAffectedByTeamFailure( sABTF ), getAverageShardBytes( getAverageShardBytes ), distributorId( mid ), lock( lock ), - cx( cx ), teamSize( teamSize ), output( output ), input( input ), getShardMetrics( getShardMetrics ), startMoveKeysParallelismLock( SERVER_KNOBS->DD_MOVE_KEYS_PARALLELISM ), + cx( cx ), teamSize( teamSize ), singleRegionTeamSize( singleRegionTeamSize ), output( output ), input( input ), getShardMetrics( getShardMetrics ), startMoveKeysParallelismLock( SERVER_KNOBS->DD_MOVE_KEYS_PARALLELISM ), finishMoveKeysParallelismLock( SERVER_KNOBS->DD_MOVE_KEYS_PARALLELISM ), lastLimited(lastLimited), suppressIntervals(0), lastInterval(0), unhealthyRelocations(0), rawProcessingUnhealthy( new AsyncVar(false) ) {} @@ -815,7 +814,7 @@ struct DDQueueData { // Data movement avoids overloading source servers in moving data. // SOMEDAY: the list of source servers may be outdated since they were fetched when the work was put in the queue // FIXME: we need spare capacity even when we're just going to be cancelling work via TEAM_HEALTHY - if( !canLaunch( rd, teamSize, busymap, cancellableRelocations ) ) { + if( !canLaunch( rd, teamSize, singleRegionTeamSize, busymap, cancellableRelocations ) ) { //logRelocation( rd, "SkippingQueuedRelocation" ); continue; } @@ -853,7 +852,7 @@ struct DDQueueData { RelocateData& rrs = inFlight.rangeContaining(ranges[r].begin)->value(); rrs.keys = ranges[r]; - launch( rrs, busymap ); + launch( rrs, busymap, singleRegionTeamSize ); activeRelocations++; startRelocation(rrs.priority, rrs.healthPriority); inFlightActors.insert( rrs.keys, dataDistributionRelocator( this, rrs ) ); @@ -1396,9 +1395,10 @@ ACTOR Future dataDistributionQueue( PromiseStream> getAverageShardBytes, UID distributorId, int teamSize, + int singleRegionTeamSize, double* lastLimited) { - state DDQueueData self( distributorId, lock, cx, teamCollections, shardsAffectedByTeamFailure, getAverageShardBytes, teamSize, output, input, getShardMetrics, lastLimited ); + state DDQueueData self( distributorId, lock, cx, teamCollections, shardsAffectedByTeamFailure, getAverageShardBytes, teamSize, singleRegionTeamSize, output, input, getShardMetrics, lastLimited ); state std::set serversToLaunchFrom; state KeyRange keysToLaunchFrom; state RelocateData launchData; From 820957025fd1d470edf271f7902e1bd3dd91d10d Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 4 Mar 2020 14:24:57 -0800 Subject: [PATCH 0818/1604] accept connections in batches of 20 to improve performance --- fdbrpc/FlowTransport.actor.cpp | 6 +++++- flow/Knobs.cpp | 2 +- flow/Knobs.h | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/fdbrpc/FlowTransport.actor.cpp b/fdbrpc/FlowTransport.actor.cpp index 83d40b1753..a2baab3b3d 100644 --- a/fdbrpc/FlowTransport.actor.cpp +++ b/fdbrpc/FlowTransport.actor.cpp @@ -988,6 +988,7 @@ ACTOR static Future connectionIncoming( TransportData* self, Reference listen( TransportData* self, NetworkAddress listenAddr ) { state ActorCollectionNoErrors incoming; // Actors monitoring incoming connections that haven't yet been associated with a peer state Reference listener = INetworkConnections::net()->listen( listenAddr ); + state int64_t connectionCount = 0; try { loop { Reference conn = wait( listener->accept() ); @@ -997,7 +998,10 @@ ACTOR static Future listen( TransportData* self, NetworkAddress listenAddr .detail("ListenAddress", listenAddr.toString()); incoming.add( connectionIncoming(self, conn) ); } - wait(delay(0) || delay(FLOW_KNOBS->CONNECTION_ACCEPT_DELAY, TaskPriority::WriteSocket)); + connectionCount++; + if( connectionCount%(FLOW_KNOBS->ACCEPT_BATCH_SIZE) == 0 ) { + wait(delay(0, TaskPriority::AcceptSocket)); + } } } catch (Error& e) { TraceEvent(SevError, "ListenError").error(e); diff --git a/flow/Knobs.cpp b/flow/Knobs.cpp index aa714551a0..751a8cd05b 100644 --- a/flow/Knobs.cpp +++ b/flow/Knobs.cpp @@ -67,7 +67,7 @@ FlowKnobs::FlowKnobs(bool randomize, bool isSimulated) { init( MAX_RECONNECTION_TIME, 0.5 ); init( RECONNECTION_TIME_GROWTH_RATE, 1.2 ); init( RECONNECTION_RESET_TIME, 5.0 ); - init( CONNECTION_ACCEPT_DELAY, 0.5 ); + init( ACCEPT_BATCH_SIZE, 20 ); init( USE_OBJECT_SERIALIZER, 1 ); init( TOO_MANY_CONNECTIONS_CLOSED_RESET_DELAY, 5.0 ); init( TOO_MANY_CONNECTIONS_CLOSED_TIMEOUT, 20.0 ); diff --git a/flow/Knobs.h b/flow/Knobs.h index 358fc82be0..a3bdd1572f 100644 --- a/flow/Knobs.h +++ b/flow/Knobs.h @@ -87,7 +87,7 @@ public: double MAX_RECONNECTION_TIME; double RECONNECTION_TIME_GROWTH_RATE; double RECONNECTION_RESET_TIME; - double CONNECTION_ACCEPT_DELAY; + int ACCEPT_BATCH_SIZE; int USE_OBJECT_SERIALIZER; int TLS_CERT_REFRESH_DELAY_SECONDS; From da579faf62bb9c70223ee971b2a896a87a16b03c Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 4 Mar 2020 14:25:30 -0800 Subject: [PATCH 0819/1604] add missing task priority --- flow/network.h | 1 + 1 file changed, 1 insertion(+) diff --git a/flow/network.h b/flow/network.h index 127d765bba..bbefb0d146 100644 --- a/flow/network.h +++ b/flow/network.h @@ -44,6 +44,7 @@ enum class TaskPriority { DiskIOComplete = 9150, LoadBalancedEndpoint = 9000, ReadSocket = 9000, + AcceptSocket = 8950, Handshake = 8900, CoordinationReply = 8810, Coordination = 8800, From 35a1ac648255415e7039298ae847d6a720d57666 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 4 Mar 2020 14:26:01 -0800 Subject: [PATCH 0820/1604] prepare net2 for new versions of boost --- flow/Net2.actor.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index be6aecedd5..23bd3f724e 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -451,12 +451,13 @@ private: }; class Listener : public IListener, ReferenceCounted { + boost::asio::io_context& io_service; NetworkAddress listenAddress; tcp::acceptor acceptor; public: - Listener( boost::asio::io_service& io_service, NetworkAddress listenAddress ) - : listenAddress(listenAddress), acceptor( io_service, tcpEndpoint( listenAddress ) ) + Listener( boost::asio::io_context& io_service, NetworkAddress listenAddress ) + : io_service(io_service), listenAddress(listenAddress), acceptor( io_service, tcpEndpoint( listenAddress ) ) { platform::setCloseOnExec(acceptor.native_handle()); } @@ -473,7 +474,7 @@ public: private: ACTOR static Future> doAccept( Listener* self ) { - state Reference conn( new Connection( self->acceptor.get_io_service() ) ); + state Reference conn( new Connection( self->io_service ) ); state tcp::acceptor::endpoint_type peer_endpoint; try { BindPromise p("N2_AcceptError", UID()); @@ -785,13 +786,14 @@ private: }; class SSLListener : public IListener, ReferenceCounted { + boost::asio::io_context& io_service; NetworkAddress listenAddress; tcp::acceptor acceptor; boost::asio::ssl::context* context; public: - SSLListener( boost::asio::io_service& io_service, boost::asio::ssl::context* context, NetworkAddress listenAddress ) - : listenAddress(listenAddress), acceptor( io_service, tcpEndpoint( listenAddress ) ), context(context) + SSLListener( boost::asio::io_context& io_service, boost::asio::ssl::context* context, NetworkAddress listenAddress ) + : io_service(io_service), listenAddress(listenAddress), acceptor( io_service, tcpEndpoint( listenAddress ) ), context(context) { platform::setCloseOnExec(acceptor.native_handle()); } @@ -808,7 +810,7 @@ public: private: ACTOR static Future> doAccept( SSLListener* self ) { - state Reference conn( new SSLConnection( self->acceptor.get_io_service(), *self->context) ); + state Reference conn( new SSLConnection( self->io_service, *self->context) ); state tcp::acceptor::endpoint_type peer_endpoint; try { BindPromise p("N2_AcceptError", UID()); @@ -861,7 +863,7 @@ Net2::Net2(bool useThreadPool, bool useMetrics, Reference tlsPolicy, tlsPolicy(tlsPolicy), tlsParams(tlsParams) #ifndef TLS_DISABLED - ,sslContext(boost::asio::ssl::context(boost::asio::ssl::context::tlsv12)) + ,sslContext(boost::asio::ssl::context(boost::asio::ssl::context::tls)) #endif { From 7cbabca124475eedac9e645f0f1f376893106879 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 4 Mar 2020 15:06:22 -0800 Subject: [PATCH 0821/1604] remove printing to stderr from initTLS because that could cause problems on clients --- flow/Net2.actor.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index 23bd3f724e..420289b0a7 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -954,8 +954,7 @@ void Net2::initTLS() { sslContext.add_certificate_authority(boost::asio::buffer(cert.data(), cert.size())); } catch (Error& e) { - fprintf(stderr, "Error reading CA file %s: %s\n", tlsParams.tlsCAPath.c_str(), e.what()); - TraceEvent("Net2TLSReadCAError").error(e); + TraceEvent("Net2TLSReadCAError").error(e).detail("CAPath", tlsParams.tlsCAPath); throw tls_error(); } } @@ -978,7 +977,6 @@ void Net2::initTLS() { } } } catch(boost::system::system_error e) { - fprintf(stderr, "Error initializing TLS: %s\n", e.what()); TraceEvent("Net2TLSInitError").detail("Message", e.what()); throw tls_error(); } From b3c3f8aa5f5f52db5196cb1d6a272b2a722c4183 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Wed, 4 Mar 2020 15:35:51 -0800 Subject: [PATCH 0822/1604] Update flow/genericactors.actor.h Pass by reference --- flow/genericactors.actor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/genericactors.actor.h b/flow/genericactors.actor.h index e2f9eda32f..6fdb646fb7 100644 --- a/flow/genericactors.actor.h +++ b/flow/genericactors.actor.h @@ -669,7 +669,7 @@ class ReferencedObject : NonCopyable, public ReferenceCounted> from(V v) { + static Reference> from(V const& v) { return Reference>(new ReferencedObject(v)); } From 58e621eca184ae5a94feebc2b27f135b9570d7d9 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Wed, 4 Mar 2020 15:50:04 -0800 Subject: [PATCH 0823/1604] Invalid knobs or knob values are treated as warnings rather than errors. Apply this change to backup as well. --- documentation/sphinx/source/release-notes.rst | 5 +++++ fdbbackup/backup.actor.cpp | 11 +++++++---- fdbcli/fdbcli.actor.cpp | 9 +++++---- fdbserver/fdbserver.actor.cpp | 4 +++- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/documentation/sphinx/source/release-notes.rst b/documentation/sphinx/source/release-notes.rst index 73250b270b..67b54a7867 100644 --- a/documentation/sphinx/source/release-notes.rst +++ b/documentation/sphinx/source/release-notes.rst @@ -10,6 +10,11 @@ Features * Add support for setting knobs in fdbcli. `(PR #2773) `_. +Other Changes +------------- + +* Setting invalid knobs in backup and DR binaries is now a warning instead of an error and will not result in the application being terminated. `(PR #2773) `_. + 6.2.17 ====== diff --git a/fdbbackup/backup.actor.cpp b/fdbbackup/backup.actor.cpp index 2ea44f1a99..e523de965e 100644 --- a/fdbbackup/backup.actor.cpp +++ b/fdbbackup/backup.actor.cpp @@ -3196,14 +3196,17 @@ int main(int argc, char* argv[]) { if (!flowKnobs->setKnob( k->first, k->second ) && !clientKnobs->setKnob( k->first, k->second )) { - fprintf(stderr, "Unrecognized knob option '%s'\n", k->first.c_str()); - return FDB_EXIT_ERROR; + fprintf(stderr, "WARNING: Unrecognized knob option '%s'\n", k->first.c_str()); + TraceEvent(SevWarnAlways, "UnrecognizedKnobOption").detail("Knob", printable(k->first)); } } catch (Error& e) { if (e.code() == error_code_invalid_option_value) { - fprintf(stderr, "Invalid value '%s' for option '%s'\n", k->second.c_str(), k->first.c_str()); - return FDB_EXIT_ERROR; + fprintf(stderr, "WARNING: Invalid value '%s' for knob option '%s'\n", k->second.c_str(), k->first.c_str()); + TraceEvent(SevWarnAlways, "InvalidKnobValue").detail("Knob", printable(k->first)).detail("Value", printable(k->second)); } + + fprintf(stderr, "ERROR: Failed to set knob option '%s': %s\n", k->first.c_str(), e.what()); + TraceEvent(SevError, "FailedToSetKnob").detail("Knob", printable(k->first)).detail("Value", printable(k->second)).error(e); throw; } } diff --git a/fdbcli/fdbcli.actor.cpp b/fdbcli/fdbcli.actor.cpp index 0697fe821d..15319d1431 100644 --- a/fdbcli/fdbcli.actor.cpp +++ b/fdbcli/fdbcli.actor.cpp @@ -2490,16 +2490,17 @@ struct CLIOptions { if (!flowKnobs->setKnob( k->first, k->second ) && !clientKnobs->setKnob( k->first, k->second )) { - fprintf(stderr, "ERROR: Unrecognized knob option '%s'\n", k->first.c_str()); - exit_code = FDB_EXIT_ERROR; + fprintf(stderr, "WARNING: Unrecognized knob option '%s'\n", k->first.c_str()); + TraceEvent(SevWarnAlways, "UnrecognizedKnobOption").detail("Knob", printable(k->first)); } } catch (Error& e) { if (e.code() == error_code_invalid_option_value) { - fprintf(stderr, "ERROR: Invalid value '%s' for knob option '%s'\n", k->second.c_str(), k->first.c_str()); - exit_code = FDB_EXIT_ERROR; + fprintf(stderr, "WARNING: Invalid value '%s' for knob option '%s'\n", k->second.c_str(), k->first.c_str()); + TraceEvent(SevWarnAlways, "InvalidKnobValue").detail("Knob", printable(k->first)).detail("Value", printable(k->second)); } else { fprintf(stderr, "ERROR: Failed to set knob option '%s': %s\n", k->first.c_str(), e.what()); + TraceEvent(SevError, "FailedToSetKnob").detail("Knob", printable(k->first)).detail("Value", printable(k->second)).error(e); exit_code = FDB_EXIT_ERROR; } } diff --git a/fdbserver/fdbserver.actor.cpp b/fdbserver/fdbserver.actor.cpp index 9f43cd2bde..70b0cc7346 100644 --- a/fdbserver/fdbserver.actor.cpp +++ b/fdbserver/fdbserver.actor.cpp @@ -1476,9 +1476,11 @@ int main(int argc, char* argv[]) { } } catch (Error& e) { if (e.code() == error_code_invalid_option_value) { - fprintf(stderr, "WARNING: Invalid value '%s' for option '%s'\n", k->second.c_str(), k->first.c_str()); + fprintf(stderr, "WARNING: Invalid value '%s' for knob option '%s'\n", k->second.c_str(), k->first.c_str()); TraceEvent(SevWarnAlways, "InvalidKnobValue").detail("Knob", printable(k->first)).detail("Value", printable(k->second)); } else { + fprintf(stderr, "ERROR: Failed to set knob option '%s': %s\n", k->first.c_str(), e.what()); + TraceEvent(SevError, "FailedToSetKnob").detail("Knob", printable(k->first)).detail("Value", printable(k->second)).error(e); throw; } } From cdcb81686688336a819b8603123f129aa49dd8ad Mon Sep 17 00:00:00 2001 From: Evan Tschannen <36455792+etschannen@users.noreply.github.com> Date: Wed, 4 Mar 2020 16:08:45 -0800 Subject: [PATCH 0824/1604] Update fdbbackup/backup.actor.cpp --- fdbbackup/backup.actor.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fdbbackup/backup.actor.cpp b/fdbbackup/backup.actor.cpp index e523de965e..afc27f7681 100644 --- a/fdbbackup/backup.actor.cpp +++ b/fdbbackup/backup.actor.cpp @@ -3204,10 +3204,11 @@ int main(int argc, char* argv[]) { fprintf(stderr, "WARNING: Invalid value '%s' for knob option '%s'\n", k->second.c_str(), k->first.c_str()); TraceEvent(SevWarnAlways, "InvalidKnobValue").detail("Knob", printable(k->first)).detail("Value", printable(k->second)); } - - fprintf(stderr, "ERROR: Failed to set knob option '%s': %s\n", k->first.c_str(), e.what()); - TraceEvent(SevError, "FailedToSetKnob").detail("Knob", printable(k->first)).detail("Value", printable(k->second)).error(e); - throw; + else { + fprintf(stderr, "ERROR: Failed to set knob option '%s': %s\n", k->first.c_str(), e.what()); + TraceEvent(SevError, "FailedToSetKnob").detail("Knob", printable(k->first)).detail("Value", printable(k->second)).error(e); + throw; + } } } From 976c2fc7a834aee4100cc489c91435f81cdb6257 Mon Sep 17 00:00:00 2001 From: Evan Tschannen <36455792+etschannen@users.noreply.github.com> Date: Wed, 4 Mar 2020 16:13:59 -0800 Subject: [PATCH 0825/1604] Update fdbrpc/FlowTransport.actor.cpp Co-Authored-By: Alex Miller <35046903+alexmiller-apple@users.noreply.github.com> --- fdbrpc/FlowTransport.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbrpc/FlowTransport.actor.cpp b/fdbrpc/FlowTransport.actor.cpp index a2baab3b3d..5041489d67 100644 --- a/fdbrpc/FlowTransport.actor.cpp +++ b/fdbrpc/FlowTransport.actor.cpp @@ -988,7 +988,7 @@ ACTOR static Future connectionIncoming( TransportData* self, Reference listen( TransportData* self, NetworkAddress listenAddr ) { state ActorCollectionNoErrors incoming; // Actors monitoring incoming connections that haven't yet been associated with a peer state Reference listener = INetworkConnections::net()->listen( listenAddr ); - state int64_t connectionCount = 0; + state uint64_t connectionCount = 0; try { loop { Reference conn = wait( listener->accept() ); From 39610d15f8151e3fc7101acedbcc40fe41142ae2 Mon Sep 17 00:00:00 2001 From: Xin Dong Date: Wed, 4 Mar 2020 16:14:38 -0800 Subject: [PATCH 0826/1604] Revert this change since it somehow introduced a random crash detected on circus --- fdbclient/Schemas.cpp | 8 +---- fdbserver/Knobs.cpp | 2 -- fdbserver/Knobs.h | 2 -- fdbserver/WorkerInterface.actor.h | 4 +-- fdbserver/worker.actor.cpp | 51 ++------------------------- flow/FileTraceLogWriter.cpp | 19 ++--------- flow/FileTraceLogWriter.h | 3 +- flow/Trace.cpp | 57 ++----------------------------- flow/Trace.h | 15 -------- 9 files changed, 9 insertions(+), 152 deletions(-) diff --git a/fdbclient/Schemas.cpp b/fdbclient/Schemas.cpp index f4cb486960..c4253bd0af 100644 --- a/fdbclient/Schemas.cpp +++ b/fdbclient/Schemas.cpp @@ -162,9 +162,6 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema( "$enum":[ "file_open_error", "incorrect_cluster_file_contents", - "trace_log_file_write_error", - "trace_log_could_not_create_file", - "trace_log_writer_thread_unresponsive", "process_error", "io_error", "io_timeout", @@ -402,10 +399,7 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema( { "name":{ "$enum":[ - "incorrect_cluster_file_contents", - "trace_log_file_write_error", - "trace_log_could_not_create_file", - "trace_log_writer_thread_unresponsive" + "incorrect_cluster_file_contents" ] }, "description":"Cluster file contents do not match current cluster connection string. Verify cluster file is writable and has not been overwritten externally." diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index e5bb0c972b..09c54dc9d4 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -514,8 +514,6 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs, bool isSimula init( DEGRADED_RESET_INTERVAL, 24*60*60 ); if ( randomize && BUGGIFY ) DEGRADED_RESET_INTERVAL = 10; init( DEGRADED_WARNING_LIMIT, 1 ); init( DEGRADED_WARNING_RESET_DELAY, 7*24*60*60 ); - init( TRACE_LOG_FLUSH_FAILURE_CHECK_INTERVAL_SECONDS, 10 ); - init( TRACE_LOG_PING_TIMEOUT_SECONDS, 5.0 ); // Test harness init( WORKER_POLL_DELAY, 1.0 ); diff --git a/fdbserver/Knobs.h b/fdbserver/Knobs.h index 337673a332..f7f62432a3 100644 --- a/fdbserver/Knobs.h +++ b/fdbserver/Knobs.h @@ -453,8 +453,6 @@ public: double DEGRADED_RESET_INTERVAL; double DEGRADED_WARNING_LIMIT; double DEGRADED_WARNING_RESET_DELAY; - int64_t TRACE_LOG_FLUSH_FAILURE_CHECK_INTERVAL_SECONDS; - double TRACE_LOG_PING_TIMEOUT_SECONDS; // Test harness double WORKER_POLL_DELAY; diff --git a/fdbserver/WorkerInterface.actor.h b/fdbserver/WorkerInterface.actor.h index c09cc5899a..4fe0f9c5f9 100644 --- a/fdbserver/WorkerInterface.actor.h +++ b/fdbserver/WorkerInterface.actor.h @@ -497,9 +497,7 @@ ACTOR Future tLog(IKeyValueStore* persistentData, IDiskQueue* persistentQu ACTOR Future monitorServerDBInfo(Reference>> ccInterface, Reference ccf, LocalityData locality, - Reference> dbInfo, - Optional>>> issues = - Optional>>>()); + Reference> dbInfo); ACTOR Future resolver(ResolverInterface proxy, InitializeResolverRequest initReq, Reference> db); ACTOR Future logRouter(TLogInterface interf, InitializeLogRouterRequest req, diff --git a/fdbserver/worker.actor.cpp b/fdbserver/worker.actor.cpp index 6460959ac9..dead8f2b8f 100644 --- a/fdbserver/worker.actor.cpp +++ b/fdbserver/worker.actor.cpp @@ -41,7 +41,6 @@ #include "fdbclient/MonitorLeader.h" #include "fdbclient/ClientWorkerInterface.h" #include "flow/Profiler.h" -#include "flow/ThreadHelper.actor.h" #ifdef __linux__ #include @@ -747,45 +746,9 @@ ACTOR Future workerSnapCreate(WorkerSnapRequest snapReq, StringRef snapFol return Void(); } -ACTOR Future monitorTraceLogIssues(Optional>>> issues) { - state bool pingTimeout = false; - loop { - wait(delay(SERVER_KNOBS->TRACE_LOG_FLUSH_FAILURE_CHECK_INTERVAL_SECONDS)); - ThreadFuture f(new ThreadSingleAssignmentVar); - Reference> callback = - Reference>(new CompletionCallback(f)); - callback->self = callback; - f.callOrSetAsCallback(callback.getPtr(), callback->userParam, 0); - pingTraceLogWriterThread(f); - try { - wait(timeoutError(callback->promise.getFuture(), SERVER_KNOBS->TRACE_LOG_PING_TIMEOUT_SECONDS)); - } catch (Error& e) { - if (e.code() == error_code_timed_out) { - pingTimeout = true; - } else { - throw; - } - } - if (issues.present()) { - std::set _issues; - retriveTraceLogIssues(_issues); - if (pingTimeout) { - // Ping trace log writer thread timeout. - _issues.insert("trace_log_writer_thread_unresponsive"); - pingTimeout = false; - } - issues.get()->set(_issues); - } - } -} - -// TODO: `issues` is right now only updated by `monitorTraceLogIssues` and thus is being `set` on every update. -// It could be changed to `insert` and `trigger` later if we want to use it as a generic way for the caller of this -// function to report issues to cluster controller. ACTOR Future monitorServerDBInfo(Reference>> ccInterface, Reference connFile, LocalityData locality, - Reference> dbInfo, - Optional>>> issues) { + Reference> dbInfo) { // Initially most of the serverDBInfo is not known, but we know our locality right away ServerDBInfo localInfo; localInfo.myLocality = locality; @@ -796,12 +759,6 @@ ACTOR Future monitorServerDBInfo(Referenceget().id; - if (issues.present()) { - for (auto const& i : issues.get()->get()) { - req.issues.push_back_deep(req.issues.arena(), i); - } - } - ClusterConnectionString fileConnectionString; if (connFile && !connFile->fileContentsUpToDate(fileConnectionString)) { req.issues.push_back_deep(req.issues.arena(), LiteralStringRef("incorrect_cluster_file_contents")); @@ -845,7 +802,6 @@ ACTOR Future monitorServerDBInfo(Referenceget().present()) TraceEvent("GotCCInterfaceChange").detail("CCID", ccInterface->get().get().id()).detail("CCMachine", ccInterface->get().get().getWorkers.getEndpoint().getPrimaryAddress()); } - when(wait(issues.present() ? issues.get()->onChange() : Never())) {} } } } @@ -914,8 +870,6 @@ ACTOR Future workerServer( state WorkerInterface interf( locality ); interf.initEndpoints(); - state Reference>> issues(new AsyncVar>()); - folder = abspath(folder); if(metricsPrefix.size() > 0) { @@ -935,8 +889,7 @@ ACTOR Future workerServer( errorForwarders.add( resetAfter(degraded, SERVER_KNOBS->DEGRADED_RESET_INTERVAL, false, SERVER_KNOBS->DEGRADED_WARNING_LIMIT, SERVER_KNOBS->DEGRADED_WARNING_RESET_DELAY, "DegradedReset")); errorForwarders.add( loadedPonger( interf.debugPing.getFuture() ) ); errorForwarders.add( waitFailureServer( interf.waitFailure.getFuture() ) ); - errorForwarders.add(monitorTraceLogIssues(issues)); - errorForwarders.add(monitorServerDBInfo(ccInterface, connFile, locality, dbInfo, issues)); + errorForwarders.add(monitorServerDBInfo(ccInterface, connFile, locality, dbInfo)); errorForwarders.add( testerServerCore( interf.testerInterface, connFile, dbInfo, locality ) ); errorForwarders.add(monitorHighMemory(memoryProfileThreshold)); diff --git a/flow/FileTraceLogWriter.cpp b/flow/FileTraceLogWriter.cpp index 3e4d0bdcd4..6fd5775db1 100644 --- a/flow/FileTraceLogWriter.cpp +++ b/flow/FileTraceLogWriter.cpp @@ -49,10 +49,9 @@ #include FileTraceLogWriter::FileTraceLogWriter(std::string directory, std::string processName, std::string basename, - std::string extension, uint64_t maxLogsSize, std::function onError, - Reference issues) + std::string extension, uint64_t maxLogsSize, std::function onError) : directory(directory), processName(processName), basename(basename), extension(extension), maxLogsSize(maxLogsSize), - traceFileFD(-1), index(0), onError(onError), issues(issues) {} + traceFileFD(-1), index(0), onError(onError) {} void FileTraceLogWriter::addref() { ReferenceCounted::addref(); @@ -74,7 +73,6 @@ void FileTraceLogWriter::lastError(int err) { void FileTraceLogWriter::write(const std::string& str) { auto ptr = str.c_str(); int remaining = str.size(); - bool needsResolve = false; while ( remaining ) { int ret = __write( traceFileFD, ptr, remaining ); @@ -82,14 +80,7 @@ void FileTraceLogWriter::write(const std::string& str) { lastError(0); remaining -= ret; ptr += ret; - if (needsResolve) { - issues->resolveIssue("trace_log_file_write_error"); - needsResolve = false; - } } else { - issues->addIssue("trace_log_file_write_error"); - needsResolve = true; - fprintf(stderr, "Unexpected error [%d] when flushing trace log.\n", errno); lastError(errno); threadSleep(0.1); } @@ -98,7 +89,6 @@ void FileTraceLogWriter::write(const std::string& str) { void FileTraceLogWriter::open() { cleanupTraceFiles(); - bool needsResolve = false; ++index; @@ -123,8 +113,6 @@ void FileTraceLogWriter::open() { } else { fprintf(stderr, "ERROR: could not create trace log file `%s' (%d: %s)\n", finalname.c_str(), errno, strerror(errno)); - issues->addIssue("trace_log_could_not_create_file"); - needsResolve = true; int errorNum = errno; onMainThreadVoid([finalname, errorNum]{ @@ -137,9 +125,6 @@ void FileTraceLogWriter::open() { } } onMainThreadVoid([]{ latestEventCache.clear("TraceFileOpenError"); }, NULL); - if (needsResolve) { - issues->resolveIssue("trace_log_could_not_create_file"); - } lastError(0); } diff --git a/flow/FileTraceLogWriter.h b/flow/FileTraceLogWriter.h index 1a7d86a840..3396486757 100644 --- a/flow/FileTraceLogWriter.h +++ b/flow/FileTraceLogWriter.h @@ -38,13 +38,12 @@ private: uint64_t maxLogsSize; int traceFileFD; uint32_t index; - Reference issues; std::function onError; public: FileTraceLogWriter(std::string directory, std::string processName, std::string basename, std::string extension, - uint64_t maxLogsSize, std::function onError, Reference issues); + uint64_t maxLogsSize, std::function onError); void addref(); void delref(); diff --git a/flow/Trace.cpp b/flow/Trace.cpp index 9a1a525621..5a29e74713 100644 --- a/flow/Trace.cpp +++ b/flow/Trace.cpp @@ -29,7 +29,6 @@ #include #include #include -#include #include "flow/IThreadPool.h" #include "flow/ThreadHelper.actor.h" @@ -221,35 +220,6 @@ public: } }; - struct IssuesList : ITraceLogIssuesReporter, ThreadSafeReferenceCounted { - IssuesList(){}; - void addIssue(std::string issue) override { - MutexHolder h(mutex); - issues.insert(issue); - } - - void retrieveIssues(std::set& out) override { - MutexHolder h(mutex); - for (auto const& i : issues) { - out.insert(i); - } - } - - void resolveIssue(std::string issue) override { - MutexHolder h(mutex); - issues.erase(issue); - } - - void addref() { ThreadSafeReferenceCounted::addref(); } - void delref() { ThreadSafeReferenceCounted::delref(); } - - private: - Mutex mutex; - std::set issues; - }; - - Reference issues; - Reference barriers; struct WriterThread : IThreadPoolReceiver { @@ -310,19 +280,11 @@ public: logWriter->sync(); } } - - struct Ping : TypedAction { - ThreadFuture p; - - explicit Ping(ThreadFuture p) : p(p){}; - virtual double getTimeEstimate() { return 0; } - }; - void action(Ping& a) { ((ThreadSingleAssignmentVar*)a.p.getPtr())->send(Void()); } }; TraceLog() : bufferLength(0), loggedLength(0), opened(false), preopenOverflowCount(0), barriers(new BarrierList), - logTraceEventMetrics(false), formatter(new XmlTraceLogFormatter()), issues(new IssuesList) {} + logTraceEventMetrics(false), formatter(new XmlTraceLogFormatter()) {} bool isOpen() const { return opened; } @@ -337,7 +299,7 @@ public: basename = format("%s/%s.%s.%s", directory.c_str(), processName.c_str(), timestamp.c_str(), deterministicRandom()->randomAlphaNumeric(6).c_str()); logWriter = Reference(new FileTraceLogWriter(directory, processName, basename, formatter->getExtension(), maxLogsSize, - [this]() { barriers->triggerAll(); }, issues)); + [this]() { barriers->triggerAll(); })); if ( g_network->isSimulated() ) writer = Reference(new DummyThreadPool()); @@ -535,13 +497,6 @@ public: } } - void pingWriterThread(ThreadFuture& p) { - auto a = new WriterThread::Ping(p); - writer->post(a); - } - - void retriveTraceLogIssues(std::set& out) { return issues->retrieveIssues(out); } - ~TraceLog() { close(); if (writer) writer->addref(); // FIXME: We are not shutting down the writer thread at all, because the ThreadPool shutdown mechanism is blocking (necessarily waits for current work items to finish) and we might not be able to finish everything. @@ -777,14 +732,6 @@ TraceEvent& TraceEvent::operator=(TraceEvent &&ev) { return *this; } -void retriveTraceLogIssues(std::set& out) { - return g_traceLog.retriveTraceLogIssues(out); -} - -void pingTraceLogWriterThread(ThreadFuture& p) { - return g_traceLog.pingWriterThread(p); -} - TraceEvent::TraceEvent( const char* type, UID id ) : id(id), type(type), severity(SevInfo), initialized(false), enabled(true), logged(false) { g_trace_depth++; setMaxFieldLength(0); diff --git a/flow/Trace.h b/flow/Trace.h index fed251077e..385cd81bee 100644 --- a/flow/Trace.h +++ b/flow/Trace.h @@ -27,7 +27,6 @@ #include #include #include -#include #include #include "flow/IRandom.h" #include "flow/Error.h" @@ -529,16 +528,6 @@ struct ITraceLogFormatter { virtual void delref() = 0; }; -struct ITraceLogIssuesReporter { - virtual void addIssue(std::string issue) = 0; - virtual void resolveIssue(std::string issue) = 0; - - virtual void retrieveIssues(std::set& out) = 0; - - virtual void addref() = 0; - virtual void delref() = 0; -}; - struct TraceInterval { TraceInterval( const char* type ) : count(-1), type(type), severity(SevInfo) {} @@ -597,10 +586,6 @@ bool validateTraceClockSource(std::string source); void addTraceRole(std::string role); void removeTraceRole(std::string role); -void retriveTraceLogIssues(std::set& out); -template -struct ThreadFuture; -void pingTraceLogWriterThread(ThreadFuture& p); enum trace_clock_t { TRACE_CLOCK_NOW, TRACE_CLOCK_REALTIME }; extern std::atomic g_trace_clock; From 6d6f184e2f96934d681254ea650aeb0ab4ad60be Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 4 Mar 2020 16:23:49 -0800 Subject: [PATCH 0827/1604] added a knob which reverts the new queue behavior --- fdbserver/DataDistributionQueue.actor.cpp | 3 +++ fdbserver/Knobs.cpp | 1 + fdbserver/Knobs.h | 1 + 3 files changed, 5 insertions(+) diff --git a/fdbserver/DataDistributionQueue.actor.cpp b/fdbserver/DataDistributionQueue.actor.cpp index 8540bde61d..b33349b2b2 100644 --- a/fdbserver/DataDistributionQueue.actor.cpp +++ b/fdbserver/DataDistributionQueue.actor.cpp @@ -306,6 +306,9 @@ bool canLaunch( RelocateData & relocation, int teamSize, int singleRegionTeamSiz // find the "workFactor" for this, were it launched now int workFactor = getWorkFactor( relocation, singleRegionTeamSize ); int neededServers = std::min( relocation.src.size(), teamSize - singleRegionTeamSize + 1 ); + if(SERVER_KNOBS->USE_OLD_NEEDED_SERVERS) { + neededServers = std::max( 1, (int)relocation.src.size() - teamSize + 1 ); + } // see if each of the SS can launch this task for( int i = 0; i < relocation.src.size(); i++ ) { // For each source server for this relocation, copy and modify its busyness to reflect work that WOULD be cancelled diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index 2ce0aac021..783f8f319f 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -104,6 +104,7 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs, bool isSimula init( INFLIGHT_PENALTY_HEALTHY, 1.0 ); init( INFLIGHT_PENALTY_UNHEALTHY, 500.0 ); init( INFLIGHT_PENALTY_ONE_LEFT, 1000.0 ); + init( USE_OLD_NEEDED_SERVERS, false ); init( PRIORITY_RECOVER_MOVE, 110 ); init( PRIORITY_REBALANCE_UNDERUTILIZED_TEAM, 120 ); diff --git a/fdbserver/Knobs.h b/fdbserver/Knobs.h index c5c41fc58f..fc54a7b065 100644 --- a/fdbserver/Knobs.h +++ b/fdbserver/Knobs.h @@ -104,6 +104,7 @@ public: double INFLIGHT_PENALTY_REDUNDANT; double INFLIGHT_PENALTY_UNHEALTHY; double INFLIGHT_PENALTY_ONE_LEFT; + bool USE_OLD_NEEDED_SERVERS; // Higher priorities are executed first // Priority/100 is the "priority group"/"superpriority". Priority inversion From b353ea1fd1e765b6fb20e3e6bfee3b5bbc231f86 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 4 Mar 2020 17:40:59 -0800 Subject: [PATCH 0828/1604] updated documentation --- documentation/sphinx/source/downloads.rst | 24 +++++++++---------- documentation/sphinx/source/release-notes.rst | 14 ++++++++++- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/documentation/sphinx/source/downloads.rst b/documentation/sphinx/source/downloads.rst index 6659b6a27f..c9084089ab 100644 --- a/documentation/sphinx/source/downloads.rst +++ b/documentation/sphinx/source/downloads.rst @@ -10,38 +10,38 @@ macOS The macOS installation package is supported on macOS 10.7+. It includes the client and (optionally) the server. -* `FoundationDB-6.2.17.pkg `_ +* `FoundationDB-6.2.18.pkg `_ Ubuntu ------ The Ubuntu packages are supported on 64-bit Ubuntu 12.04+, but beware of the Linux kernel bug in Ubuntu 12.x. -* `foundationdb-clients-6.2.17-1_amd64.deb `_ -* `foundationdb-server-6.2.17-1_amd64.deb `_ (depends on the clients package) +* `foundationdb-clients-6.2.18-1_amd64.deb `_ +* `foundationdb-server-6.2.18-1_amd64.deb `_ (depends on the clients package) RHEL/CentOS EL6 --------------- The RHEL/CentOS EL6 packages are supported on 64-bit RHEL/CentOS 6.x. -* `foundationdb-clients-6.2.17-1.el6.x86_64.rpm `_ -* `foundationdb-server-6.2.17-1.el6.x86_64.rpm `_ (depends on the clients package) +* `foundationdb-clients-6.2.18-1.el6.x86_64.rpm `_ +* `foundationdb-server-6.2.18-1.el6.x86_64.rpm `_ (depends on the clients package) RHEL/CentOS EL7 --------------- The RHEL/CentOS EL7 packages are supported on 64-bit RHEL/CentOS 7.x. -* `foundationdb-clients-6.2.17-1.el7.x86_64.rpm `_ -* `foundationdb-server-6.2.17-1.el7.x86_64.rpm `_ (depends on the clients package) +* `foundationdb-clients-6.2.18-1.el7.x86_64.rpm `_ +* `foundationdb-server-6.2.18-1.el7.x86_64.rpm `_ (depends on the clients package) Windows ------- The Windows installer is supported on 64-bit Windows XP and later. It includes the client and (optionally) the server. -* `foundationdb-6.2.17-x64.msi `_ +* `foundationdb-6.2.18-x64.msi `_ API Language Bindings ===================== @@ -58,18 +58,18 @@ On macOS and Windows, the FoundationDB Python API bindings are installed as part If you need to use the FoundationDB Python API from other Python installations or paths, download the Python package: -* `foundationdb-6.2.17.tar.gz `_ +* `foundationdb-6.2.18.tar.gz `_ Ruby 1.9.3/2.0.0+ ----------------- -* `fdb-6.2.17.gem `_ +* `fdb-6.2.18.gem `_ Java 8+ ------- -* `fdb-java-6.2.17.jar `_ -* `fdb-java-6.2.17-javadoc.jar `_ +* `fdb-java-6.2.18.jar `_ +* `fdb-java-6.2.18-javadoc.jar `_ Go 1.11+ -------- diff --git a/documentation/sphinx/source/release-notes.rst b/documentation/sphinx/source/release-notes.rst index 67b54a7867..cc632dab6f 100644 --- a/documentation/sphinx/source/release-notes.rst +++ b/documentation/sphinx/source/release-notes.rst @@ -5,6 +5,18 @@ Release Notes 6.2.18 ====== +Fixes +----- + +* When configuring a cluster to usable_regions=2, data distribution would not react to machine failures while copying data to the remote region. `(PR #2774) `_. +* When a cluster is configured with usable_regions=2, data distribution could push a cluster into saturation by relocating too many shards simulatenously. `(PR #2776) `_. +* Backup could not establish TLS connections (broken in 6.2.16). `(PR #2775) `_. + +Performance +----------- + +* Improved the efficiency of establishing large numbers of network connections. `(PR #2777) `_. + Features -------- @@ -21,7 +33,7 @@ Other Changes Fixes ----- -* Restored the ability to set TLS configuration using environment variables. `(PR #2755) `_. +* Restored the ability to set TLS configuration using environment variables (broken in 6.2.16). `(PR #2755) `_. 6.2.16 ====== From b3ea9d5896df7f1fab4095f69965c0ed776670aa Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 4 Mar 2020 18:45:26 -0800 Subject: [PATCH 0829/1604] Do not allow the cluster controller to mark any process as failed within 30 seconds of startup --- fdbserver/ClusterController.actor.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fdbserver/ClusterController.actor.cpp b/fdbserver/ClusterController.actor.cpp index b7823b4155..05b2e801e3 100644 --- a/fdbserver/ClusterController.actor.cpp +++ b/fdbserver/ClusterController.actor.cpp @@ -1807,7 +1807,8 @@ ACTOR Future failureDetectionServer( UID uniqueID, ClusterControllerData* //TraceEvent("FailureDetectionPoll", uniqueID).detail("PivotDelay", pivotDelay).detail("Clients", currentStatus.size()); //TraceEvent("FailureDetectionAcceptableDelay").detail("Delay", acceptableDelay1000); - bool tooManyLogGenerations = std::max(self->db.unfinishedRecoveries, self->db.logGenerations) > CLIENT_KNOBS->FAILURE_MAX_GENERATIONS; + bool tooManyLogGenerations = (std::max(self->db.unfinishedRecoveries, self->db.logGenerations) > CLIENT_KNOBS->FAILURE_MAX_GENERATIONS) || + (now() - self->startTime < CLIENT_KNOBS->FAILURE_EMERGENCY_DELAY); for(auto it = currentStatus.begin(); it != currentStatus.end(); ) { double delay = t - it->second.lastRequestTime; From 45fb098ce0bceb544f4708126c75e41b5079671c Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 4 Mar 2020 18:47:16 -0800 Subject: [PATCH 0830/1604] updated release notes --- documentation/sphinx/source/release-notes.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/sphinx/source/release-notes.rst b/documentation/sphinx/source/release-notes.rst index cc632dab6f..d6a577bcf4 100644 --- a/documentation/sphinx/source/release-notes.rst +++ b/documentation/sphinx/source/release-notes.rst @@ -10,6 +10,7 @@ Fixes * When configuring a cluster to usable_regions=2, data distribution would not react to machine failures while copying data to the remote region. `(PR #2774) `_. * When a cluster is configured with usable_regions=2, data distribution could push a cluster into saturation by relocating too many shards simulatenously. `(PR #2776) `_. +* Do not allow the cluster controller to mark any process as failed within 30 seconds of startup. `(PR #2780) `_. * Backup could not establish TLS connections (broken in 6.2.16). `(PR #2775) `_. Performance From f3ac2c9180170305f33a294519e3d89cdc1ec773 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 4 Mar 2020 18:49:21 -0800 Subject: [PATCH 0831/1604] renamed a variable --- fdbserver/ClusterController.actor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fdbserver/ClusterController.actor.cpp b/fdbserver/ClusterController.actor.cpp index 05b2e801e3..5cbc6ebb8a 100644 --- a/fdbserver/ClusterController.actor.cpp +++ b/fdbserver/ClusterController.actor.cpp @@ -1807,12 +1807,12 @@ ACTOR Future failureDetectionServer( UID uniqueID, ClusterControllerData* //TraceEvent("FailureDetectionPoll", uniqueID).detail("PivotDelay", pivotDelay).detail("Clients", currentStatus.size()); //TraceEvent("FailureDetectionAcceptableDelay").detail("Delay", acceptableDelay1000); - bool tooManyLogGenerations = (std::max(self->db.unfinishedRecoveries, self->db.logGenerations) > CLIENT_KNOBS->FAILURE_MAX_GENERATIONS) || + bool useEmergencyDelay = (std::max(self->db.unfinishedRecoveries, self->db.logGenerations) > CLIENT_KNOBS->FAILURE_MAX_GENERATIONS) || (now() - self->startTime < CLIENT_KNOBS->FAILURE_EMERGENCY_DELAY); for(auto it = currentStatus.begin(); it != currentStatus.end(); ) { double delay = t - it->second.lastRequestTime; - if ( it->first != g_network->getLocalAddresses() && ( tooManyLogGenerations ? + if ( it->first != g_network->getLocalAddresses() && ( useEmergencyDelay ? ( delay > CLIENT_KNOBS->FAILURE_EMERGENCY_DELAY ) : ( delay > pivotDelay * 2 + FLOW_KNOBS->SERVER_REQUEST_INTERVAL + CLIENT_KNOBS->FAILURE_MIN_DELAY || delay > CLIENT_KNOBS->FAILURE_MAX_DELAY ) ) ) { //printf("Failure Detection Server: Status of '%s' is now '%s' after %f sec\n", it->first.toString().c_str(), "Failed", now() - it->second.lastRequestTime); From 9b5ef3416ef0504c30e39295d449cb1dabf5fffc Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Wed, 4 Mar 2020 20:14:47 -0800 Subject: [PATCH 0832/1604] Refactor TLSParams into TLSConfig + LoadedTLSConfig The idea being that we keep around a TLSConfig that the configuration that the user has provided, and then when we want to intialize an SSL context, we ask the TLSConfig to load all certificates and return us a LoadedTLSConfig that is a concrete set of certificate bytes in memory. initTLS now just takes the in-memory bytes and applies them to the ssl context. This is a large refactor to lead up into certificate refeshing, where we will periodically check for changes to the certificates, and then re-load them and apply them to a new SSL context. --- bindings/flow/tester/Tester.actor.cpp | 5 +- fdbbackup/backup.actor.cpp | 13 +- fdbcli/fdbcli.actor.cpp | 13 +- fdbclient/NativeAPI.actor.cpp | 43 +-- fdbrpc/sim2.actor.cpp | 3 +- fdbserver/fdbserver.actor.cpp | 40 +-- flow/CMakeLists.txt | 10 +- flow/Net2.actor.cpp | 203 +++++++++----- flow/{TLSPolicy.cpp => TLSConfig.actor.cpp} | 186 +++++++++++- flow/TLSConfig.actor.h | 295 ++++++++++++++++++++ flow/TLSPolicy.h | 145 ---------- flow/flow.vcxproj | 6 +- flow/genericactors.actor.h | 30 ++ flow/network.h | 4 +- 14 files changed, 688 insertions(+), 308 deletions(-) rename flow/{TLSPolicy.cpp => TLSConfig.actor.cpp} (74%) create mode 100644 flow/TLSConfig.actor.h delete mode 100644 flow/TLSPolicy.h diff --git a/bindings/flow/tester/Tester.actor.cpp b/bindings/flow/tester/Tester.actor.cpp index 52d193320e..10ca75d404 100644 --- a/bindings/flow/tester/Tester.actor.cpp +++ b/bindings/flow/tester/Tester.actor.cpp @@ -28,6 +28,7 @@ #include "bindings/flow/FDBLoanerTypes.h" #include "fdbrpc/fdbrpc.h" #include "flow/DeterministicRandom.h" +#include "flow/TLSConfig.actor.h" #include "flow/actorcompiler.h" // This must be the last #include. // Otherwise we have to type setupNetwork(), FDB::open(), etc. @@ -1748,7 +1749,7 @@ ACTOR void startTest(std::string clusterFilename, StringRef prefix, int apiVersi populateOpsThatCreateDirectories(); // FIXME // This is "our" network - g_network = newNet2(false); + g_network = newNet2(TLSConfig()); ASSERT(!API::isAPIVersionSelected()); try { @@ -1791,7 +1792,7 @@ ACTOR void startTest(std::string clusterFilename, StringRef prefix, int apiVersi ACTOR void _test_versionstamp() { try { - g_network = newNet2(false); + g_network = newNet2(TLSConfig()); API *fdb = FDB::API::selectAPIVersion(620); diff --git a/fdbbackup/backup.actor.cpp b/fdbbackup/backup.actor.cpp index 2ea44f1a99..022e12349c 100644 --- a/fdbbackup/backup.actor.cpp +++ b/fdbbackup/backup.actor.cpp @@ -27,6 +27,7 @@ #include "flow/IRandom.h" #include "flow/genericactors.actor.h" #include "flow/SignalSafeUnwind.h" +#include "flow/TLSConfig.actor.h" #include "fdbclient/FDBTypes.h" #include "fdbclient/BackupAgent.actor.h" @@ -3071,22 +3072,22 @@ int main(int argc, char* argv[]) { blobCredentials.push_back(args->OptionArg()); break; #ifndef TLS_DISABLED - case TLSParams::OPT_TLS_PLUGIN: + case TLSConfig::OPT_TLS_PLUGIN: args->OptionArg(); break; - case TLSParams::OPT_TLS_CERTIFICATES: + case TLSConfig::OPT_TLS_CERTIFICATES: tlsCertPath = args->OptionArg(); break; - case TLSParams::OPT_TLS_PASSWORD: + case TLSConfig::OPT_TLS_PASSWORD: tlsPassword = args->OptionArg(); break; - case TLSParams::OPT_TLS_CA_FILE: + case TLSConfig::OPT_TLS_CA_FILE: tlsCAPath = args->OptionArg(); break; - case TLSParams::OPT_TLS_KEY: + case TLSConfig::OPT_TLS_KEY: tlsKeyPath = args->OptionArg(); break; - case TLSParams::OPT_TLS_VERIFY_PEERS: + case TLSConfig::OPT_TLS_VERIFY_PEERS: tlsVerifyPeers = args->OptionArg(); break; #endif diff --git a/fdbcli/fdbcli.actor.cpp b/fdbcli/fdbcli.actor.cpp index cf76fe7ee4..ee16f9a6a3 100644 --- a/fdbcli/fdbcli.actor.cpp +++ b/fdbcli/fdbcli.actor.cpp @@ -35,6 +35,7 @@ #include "flow/SignalSafeUnwind.h" #include "fdbrpc/Platform.h" +#include "flow/TLSConfig.actor.h" #include "flow/SimpleOpt.h" #include "fdbcli/FlowLineNoise.h" @@ -2506,22 +2507,22 @@ struct CLIOptions { #ifndef TLS_DISABLED // TLS Options - case TLSParams::OPT_TLS_PLUGIN: + case TLSConfig::OPT_TLS_PLUGIN: args.OptionArg(); break; - case TLSParams::OPT_TLS_CERTIFICATES: + case TLSConfig::OPT_TLS_CERTIFICATES: tlsCertPath = args.OptionArg(); break; - case TLSParams::OPT_TLS_CA_FILE: + case TLSConfig::OPT_TLS_CA_FILE: tlsCAPath = args.OptionArg(); break; - case TLSParams::OPT_TLS_KEY: + case TLSConfig::OPT_TLS_KEY: tlsKeyPath = args.OptionArg(); break; - case TLSParams::OPT_TLS_PASSWORD: + case TLSConfig::OPT_TLS_PASSWORD: tlsPassword = args.OptionArg(); break; - case TLSParams::OPT_TLS_VERIFY_PEERS: + case TLSConfig::OPT_TLS_VERIFY_PEERS: tlsVerifyPeers = args.OptionArg(); break; #endif diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index e30390cf22..4312cfee62 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -43,7 +43,7 @@ #include "flow/Knobs.h" #include "flow/Platform.h" #include "flow/SystemMonitor.h" -#include "flow/TLSPolicy.h" +#include "flow/TLSConfig.actor.h" #include "flow/UnitTest.h" #if defined(CMAKE_BUILD) || !defined(WIN32) @@ -67,16 +67,7 @@ using std::min; using std::pair; NetworkOptions networkOptions; -TLSParams tlsParams; -static Reference tlsPolicy; - -static void initTLSPolicy() { -#ifndef TLS_DISABLED - if (!tlsPolicy) { - tlsPolicy = Reference(new TLSPolicy(TLSPolicy::Is::CLIENT)); - } -#endif -} +TLSConfig tlsConfig(TLSEndpointType::CLIENT); static const Key CLIENT_LATENCY_INFO_PREFIX = LiteralStringRef("client_latency/"); static const Key CLIENT_LATENCY_INFO_CTR_PREFIX = LiteralStringRef("client_latency_counter/"); @@ -892,48 +883,40 @@ void setNetworkOption(FDBNetworkOptions::Option option, Optional valu break; case FDBNetworkOptions::TLS_CERT_PATH: validateOptionValue(value, true); - tlsParams.tlsCertBytes = ""; - tlsParams.tlsCertPath = value.get().toString(); + tlsConfig.setCertificatePath(value.get().toString()); break; case FDBNetworkOptions::TLS_CERT_BYTES: { validateOptionValue(value, true); - tlsParams.tlsCertPath = ""; - tlsParams.tlsCertBytes = value.get().toString(); + tlsConfig.setCertificateBytes(value.get().toString()); break; } case FDBNetworkOptions::TLS_CA_PATH: { validateOptionValue(value, true); - tlsParams.tlsCABytes = ""; - tlsParams.tlsCAPath = value.get().toString(); + tlsConfig.setCAPath(value.get().toString()); break; } case FDBNetworkOptions::TLS_CA_BYTES: { validateOptionValue(value, true); - tlsParams.tlsCAPath = ""; - tlsParams.tlsCABytes = value.get().toString(); + tlsConfig.setCABytes(value.get().toString()); break; } case FDBNetworkOptions::TLS_PASSWORD: validateOptionValue(value, true); - tlsParams.tlsPassword = value.get().toString(); + tlsConfig.setPassword(value.get().toString()); break; case FDBNetworkOptions::TLS_KEY_PATH: validateOptionValue(value, true); - tlsParams.tlsKeyBytes = ""; - tlsParams.tlsKeyPath = value.get().toString(); + tlsConfig.setKeyPath(value.get().toString()); break; case FDBNetworkOptions::TLS_KEY_BYTES: { validateOptionValue(value, true); - tlsParams.tlsKeyPath = ""; - tlsParams.tlsKeyBytes = value.get().toString(); + tlsConfig.setKeyBytes(value.get().toString()); break; } case FDBNetworkOptions::TLS_VERIFY_PEERS: validateOptionValue(value, true); - initTLSPolicy(); -#ifndef TLS_DISABLED - tlsPolicy->set_verify_peers({ value.get().toString() }); -#endif + tlsConfig.clearVerifyPeers(); + tlsConfig.addVerifyPeers( value.get().toString() ); break; case FDBNetworkOptions::CLIENT_BUGGIFY_ENABLE: enableBuggify(true, BuggifyType::Client); @@ -991,9 +974,7 @@ void setupNetwork(uint64_t transportId, bool useMetrics) { if (!networkOptions.logClientInfo.present()) networkOptions.logClientInfo = true; - initTLSPolicy(); - - g_network = newNet2(false, useMetrics || networkOptions.traceDirectory.present(), tlsPolicy, tlsParams); + g_network = newNet2(tlsConfig, false, useMetrics || networkOptions.traceDirectory.present()); FlowTransport::createInstance(true, transportId); Net2FileSystem::newFileSystem(); } diff --git a/fdbrpc/sim2.actor.cpp b/fdbrpc/sim2.actor.cpp index 9d0e516899..58f4b3fd5f 100644 --- a/fdbrpc/sim2.actor.cpp +++ b/fdbrpc/sim2.actor.cpp @@ -30,6 +30,7 @@ #include "fdbrpc/TraceFileIO.h" #include "flow/FaultInjection.h" #include "flow/network.h" +#include "flow/TLSConfig.actor.h" #include "fdbrpc/Net2FileSystem.h" #include "fdbrpc/Replication.h" #include "fdbrpc/ReplicationUtils.h" @@ -1599,7 +1600,7 @@ public: Sim2() : time(0.0), timerTime(0.0), taskCount(0), yielded(false), yield_limit(0), currentTaskID(TaskPriority::Zero) { // Not letting currentProcess be NULL eliminates some annoying special cases currentProcess = new ProcessInfo("NoMachine", LocalityData(Optional>(), StringRef(), StringRef(), StringRef()), ProcessClass(), {NetworkAddress()}, this, "", ""); - g_network = net2 = newNet2(false, true); + g_network = net2 = newNet2(TLSConfig(), false, true); Net2FileSystem::newFileSystem(); check_yield(TaskPriority::Zero); } diff --git a/fdbserver/fdbserver.actor.cpp b/fdbserver/fdbserver.actor.cpp index 9f43cd2bde..56d06171ff 100644 --- a/fdbserver/fdbserver.actor.cpp +++ b/fdbserver/fdbserver.actor.cpp @@ -57,7 +57,7 @@ #include "fdbrpc/AsyncFileCached.actor.h" #include "fdbserver/CoroFlow.h" #include "flow/SignalSafeUnwind.h" -#include "flow/TLSPolicy.h" +#include "flow/TLSConfig.actor.h" #if defined(CMAKE_BUILD) || !defined(WIN32) #include "versions.h" #endif @@ -961,8 +961,7 @@ int main(int argc, char* argv[]) { int minTesterCount = 1; bool testOnServers = false; - Reference tlsPolicy = Reference(new TLSPolicy(TLSPolicy::Is::SERVER)); - TLSParams tlsParams; + TLSConfig tlsConfig(TLSEndpointType::SERVER); std::vector tlsVerifyPeers; double fileIoTimeout = 0.0; bool fileIoWarnOnly = false; @@ -1331,23 +1330,23 @@ int main(int argc, char* argv[]) { whitelistBinPaths = args.OptionArg(); break; #ifndef TLS_DISABLED - case TLSParams::OPT_TLS_PLUGIN: + case TLSConfig::OPT_TLS_PLUGIN: args.OptionArg(); break; - case TLSParams::OPT_TLS_CERTIFICATES: - tlsParams.tlsCertPath = args.OptionArg(); + case TLSConfig::OPT_TLS_CERTIFICATES: + tlsConfig.setCertificatePath(args.OptionArg()); break; - case TLSParams::OPT_TLS_PASSWORD: - tlsParams.tlsPassword = args.OptionArg(); + case TLSConfig::OPT_TLS_PASSWORD: + tlsConfig.setPassword(args.OptionArg()); break; - case TLSParams::OPT_TLS_CA_FILE: - tlsParams.tlsCAPath = args.OptionArg(); + case TLSConfig::OPT_TLS_CA_FILE: + tlsConfig.setCAPath(args.OptionArg()); break; - case TLSParams::OPT_TLS_KEY: - tlsParams.tlsKeyPath = args.OptionArg(); + case TLSConfig::OPT_TLS_KEY: + tlsConfig.setKeyPath(args.OptionArg()); break; - case TLSParams::OPT_TLS_VERIFY_PEERS: - tlsVerifyPeers.push_back(args.OptionArg()); + case TLSConfig::OPT_TLS_VERIFY_PEERS: + tlsConfig.addVerifyPeers(args.OptionArg()); break; #endif } @@ -1551,18 +1550,7 @@ int main(int argc, char* argv[]) { startNewSimulator(); openTraceFile(NetworkAddress(), rollsize, maxLogsSize, logFolder, "trace", logGroup); } else { -#ifndef TLS_DISABLED - if ( tlsVerifyPeers.size() ) { - try { - tlsPolicy->set_verify_peers( tlsVerifyPeers ); - } catch( Error &e ) { - fprintf(stderr, "ERROR: The format of the --tls_verify_peers option is incorrect.\n"); - printHelpTeaser(argv[0]); - flushAndExit(FDB_EXIT_ERROR); - } - } -#endif - g_network = newNet2(useThreadPool, true, tlsPolicy, tlsParams); + g_network = newNet2(tlsConfig, useThreadPool, true); FlowTransport::createInstance(false, 1); const bool expectsPublicAddress = (role == FDBD || role == NetworkTestServer || role == Restore); diff --git a/flow/CMakeLists.txt b/flow/CMakeLists.txt index f102ee3a71..adc6cc0406 100644 --- a/flow/CMakeLists.txt +++ b/flow/CMakeLists.txt @@ -52,6 +52,8 @@ set(FLOW_SRCS SystemMonitor.h TDMetric.actor.h TDMetric.cpp + TLSConfig.actor.cpp + TLSConfig.actor.h ThreadHelper.actor.h ThreadHelper.cpp ThreadPrimitives.cpp @@ -59,24 +61,22 @@ set(FLOW_SRCS ThreadSafeQueue.h Trace.cpp Trace.h - TLSPolicy.h - TLSPolicy.cpp UnitTest.cpp UnitTest.h - XmlTraceLogFormatter.h XmlTraceLogFormatter.cpp + XmlTraceLogFormatter.h actorcompiler.h error_definitions.h - flat_buffers.h flat_buffers.cpp + flat_buffers.h flow.cpp flow.h genericactors.actor.cpp genericactors.actor.h network.cpp network.h - serialize.h serialize.cpp + serialize.h stacktrace.amalgamation.cpp stacktrace.h version.cpp) diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index be6aecedd5..9209b5910c 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -37,7 +37,7 @@ #include "flow/AsioReactor.h" #include "flow/Profiler.h" #include "flow/ProtocolVersion.h" -#include "flow/TLSPolicy.h" +#include "flow/TLSConfig.actor.h" #ifdef WIN32 #include @@ -111,7 +111,7 @@ thread_local INetwork* thread_network = 0; class Net2 sealed : public INetwork, public INetworkConnections { public: - Net2(bool useThreadPool, bool useMetrics, Reference tlsPolicy, const TLSParams& tlsParams); + Net2(const TLSConfig& tlsConfig, bool useThreadPool, bool useMetrics); void initTLS(); void run(); void initMetrics(); @@ -159,12 +159,12 @@ public: #ifndef TLS_DISABLED boost::asio::ssl::context sslContext; #endif - Reference tlsPolicy; - TLSParams tlsParams; + TLSConfig tlsConfig; + std::string tlsPassword; bool tlsInitialized; std::string get_password() const { - return tlsParams.tlsPassword; + return tlsPassword; } INetworkConnections *network; // initially this, but can be changed @@ -847,7 +847,7 @@ bool insecurely_always_accept(bool _1, boost::asio::ssl::verify_context& _2) { } #endif -Net2::Net2(bool useThreadPool, bool useMetrics, Reference tlsPolicy, const TLSParams& tlsParams) +Net2::Net2(const TLSConfig& tlsConfig, bool useThreadPool, bool useMetrics) : useThreadPool(useThreadPool), network(this), reactor(this), @@ -858,8 +858,7 @@ Net2::Net2(bool useThreadPool, bool useMetrics, Reference tlsPolicy, lastMinTaskID(TaskPriority::Zero), numYields(0), tlsInitialized(false), - tlsPolicy(tlsPolicy), - tlsParams(tlsParams) + tlsConfig(tlsConfig) #ifndef TLS_DISABLED ,sslContext(boost::asio::ssl::context(boost::asio::ssl::context::tlsv12)) #endif @@ -888,92 +887,142 @@ Net2::Net2(bool useThreadPool, bool useMetrics, Reference tlsPolicy, } +/* +ACTOR static Future watchFileForChanges( std::string filename, AsyncVar> *contents_var ) { + state std::time_t lastModTime = wait(IAsyncFileSystem::filesystem()->lastWriteTime(filename)); + loop { + wait(delay(FLOW_KNOBS->TLS_CERT_REFRESH_DELAY_SECONDS)); + std::time_t modtime = wait(IAsyncFileSystem::filesystem()->lastWriteTime(filename)); + if (lastModTime != modtime) { + lastModTime = modtime; + ErrorOr> contents = wait(readEntireFile(filename)); + if (contents.present()) { + contents_var->set(contents.get()); + } + } + } +} + +ACTOR static Future reloadConfigurationOnChange( TLSOptions::PolicyInfo *pci, Reference plugin, AsyncVar> *realVerifyPeersPolicy, AsyncVar> *realNoVerifyPeersPolicy ) { + if (FLOW_KNOBS->TLS_CERT_REFRESH_DELAY_SECONDS <= 0) { + return Void(); + return Void(); + } + loop { + // Early in bootup, the filesystem might not be initialized yet. Wait until it is. + if (IAsyncFileSystem::filesystem() != nullptr) { + break; + } + wait(delay(1.0)); + } + state int mismatches = 0; + state AsyncVar> ca_var; + state AsyncVar> key_var; + state AsyncVar> cert_var; + state std::vector> lifetimes; + if (!pci->ca_path.empty()) lifetimes.push_back(watchFileForChanges(pci->ca_path, &ca_var)); + if (!pci->key_path.empty()) lifetimes.push_back(watchFileForChanges(pci->key_path, &key_var)); + if (!pci->cert_path.empty()) lifetimes.push_back(watchFileForChanges(pci->cert_path, &cert_var)); + loop { + state Future ca_changed = ca_var.onChange(); + state Future key_changed = key_var.onChange(); + state Future cert_changed = cert_var.onChange(); + wait( ca_changed || key_changed || cert_changed ); + if (ca_changed.isReady()) { + TraceEvent(SevInfo, "TLSRefreshCAChanged").detail("path", pci->ca_path).detail("length", ca_var.get().size()); + pci->ca_contents = ca_var.get(); + } + if (key_changed.isReady()) { + TraceEvent(SevInfo, "TLSRefreshKeyChanged").detail("path", pci->key_path).detail("length", key_var.get().size()); + pci->key_contents = key_var.get(); + } + if (cert_changed.isReady()) { + TraceEvent(SevInfo, "TLSRefreshCertChanged").detail("path", pci->cert_path).detail("length", cert_var.get().size()); + pci->cert_contents = cert_var.get(); + } + bool rc = true; + Reference verifypeers = Reference(plugin->create_policy()); + Reference noverifypeers = Reference(plugin->create_policy()); + loop { + // Don't actually loop. We're just using loop/break as a `goto err`. + // This loop always ends with an unconditional break. + rc = verifypeers->set_ca_data(pci->ca_contents.begin(), pci->ca_contents.size()); + if (!rc) break; + rc = verifypeers->set_key_data(pci->key_contents.begin(), pci->key_contents.size(), pci->keyPassword.c_str()); + if (!rc) break; + rc = verifypeers->set_cert_data(pci->cert_contents.begin(), pci->cert_contents.size()); + if (!rc) break; + { + std::unique_ptr verify_peers_arr(new const uint8_t*[pci->verify_peers.size()]); + std::unique_ptr verify_peers_len(new int[pci->verify_peers.size()]); + for (int i = 0; i < pci->verify_peers.size(); i++) { + verify_peers_arr[i] = (const uint8_t *)&pci->verify_peers[i][0]; + verify_peers_len[i] = pci->verify_peers[i].size(); + } + rc = verifypeers->set_verify_peers(pci->verify_peers.size(), verify_peers_arr.get(), verify_peers_len.get()); + if (!rc) break; + } + rc = noverifypeers->set_ca_data(pci->ca_contents.begin(), pci->ca_contents.size()); + if (!rc) break; + rc = noverifypeers->set_key_data(pci->key_contents.begin(), pci->key_contents.size(), pci->keyPassword.c_str()); + if (!rc) break; + rc = noverifypeers->set_cert_data(pci->cert_contents.begin(), pci->cert_contents.size()); + if (!rc) break; + break; + } + + if (rc) { + TraceEvent(SevInfo, "TLSCertificateRefreshSucceeded"); + realVerifyPeersPolicy->set(verifypeers); + realNoVerifyPeersPolicy->set(noverifypeers); + mismatches = 0; + } else { + // Some files didn't match up, they should in the future, and we'll retry then. + mismatches++; + TraceEvent(SevWarn, "TLSCertificateRefreshMismatch").detail("mismatches", mismatches); + } + } +} +*/ + void Net2::initTLS() { if(tlsInitialized) { return; } #ifndef TLS_DISABLED try { - const char *defaultCertFileName = "fdb.pem"; - - if( tlsPolicy && !tlsPolicy->rules.size() ) { - std::string verify_peers; - if (platform::getEnvironmentVar("FDB_TLS_VERIFY_PEERS", verify_peers)) { - tlsPolicy->set_verify_peers({ verify_peers }); - } else { - tlsPolicy->set_verify_peers({ std::string("Check.Valid=1")}); - } - } + LoadedTLSConfig loaded = tlsConfig.loadSync(); sslContext.set_options(boost::asio::ssl::context::default_workarounds); sslContext.set_verify_mode(boost::asio::ssl::context::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert); - if (tlsPolicy) { - Reference policy = tlsPolicy; - sslContext.set_verify_callback([policy](bool preverified, boost::asio::ssl::verify_context& ctx) { - return policy->verify_peer(preverified, ctx.native_handle()); - }); + + if (loaded.isTLSEnabled()) { + Reference tlsPolicy = Reference(new TLSPolicy(loaded.getEndpointType())); + tlsPolicy->set_verify_peers({ loaded.getVerifyPeers() }); + + sslContext.set_verify_callback([policy=tlsPolicy](bool preverified, boost::asio::ssl::verify_context& ctx) { + return policy->verify_peer(preverified, ctx.native_handle()); + }); } else { sslContext.set_verify_callback(boost::bind(&insecurely_always_accept, _1, _2)); } - if ( !tlsParams.tlsPassword.size() ) { - platform::getEnvironmentVar( "FDB_TLS_PASSWORD", tlsParams.tlsPassword ); - } + tlsPassword = loaded.getPassword(); sslContext.set_password_callback(std::bind(&Net2::get_password, this)); - if ( tlsParams.tlsCertBytes.size() ) { - sslContext.use_certificate_chain(boost::asio::buffer(tlsParams.tlsCertBytes.data(), tlsParams.tlsCertBytes.size())); - } - else { - if ( !tlsParams.tlsCertPath.size() ) { - if ( !platform::getEnvironmentVar( "FDB_TLS_CERTIFICATE_FILE", tlsParams.tlsCertPath ) ) { - if( fileExists(defaultCertFileName) ) { - tlsParams.tlsCertPath = defaultCertFileName; - } else if( fileExists( joinPath(platform::getDefaultConfigPath(), defaultCertFileName) ) ) { - tlsParams.tlsCertPath = joinPath(platform::getDefaultConfigPath(), defaultCertFileName); - } - } - } - if ( tlsParams.tlsCertPath.size() ) { - sslContext.use_certificate_chain_file(tlsParams.tlsCertPath); - } + const std::string& certBytes = loaded.getCertificateBytes(); + if ( certBytes.size() ) { + sslContext.use_certificate_chain(boost::asio::buffer(certBytes.data(), certBytes.size())); } - if ( tlsParams.tlsCABytes.size() ) { - sslContext.add_certificate_authority(boost::asio::buffer(tlsParams.tlsCABytes.data(), tlsParams.tlsCABytes.size())); - } - else { - if ( !tlsParams.tlsCAPath.size() ) { - platform::getEnvironmentVar("FDB_TLS_CA_FILE", tlsParams.tlsCAPath); - } - if ( tlsParams.tlsCAPath.size() ) { - try { - std::string cert = readFileBytes(tlsParams.tlsCAPath, FLOW_KNOBS->CERT_FILE_MAX_SIZE); - sslContext.add_certificate_authority(boost::asio::buffer(cert.data(), cert.size())); - } - catch (Error& e) { - fprintf(stderr, "Error reading CA file %s: %s\n", tlsParams.tlsCAPath.c_str(), e.what()); - TraceEvent("Net2TLSReadCAError").error(e); - throw tls_error(); - } - } + const std::string& CABytes = loaded.getCABytes(); + if ( CABytes.size() ) { + sslContext.add_certificate_authority(boost::asio::buffer(CABytes.data(), CABytes.size())); } - if (tlsParams.tlsKeyBytes.size()) { - sslContext.use_private_key(boost::asio::buffer(tlsParams.tlsKeyBytes.data(), tlsParams.tlsKeyBytes.size()), boost::asio::ssl::context::pem); - } else { - if (!tlsParams.tlsKeyPath.size()) { - if(!platform::getEnvironmentVar( "FDB_TLS_KEY_FILE", tlsParams.tlsKeyPath)) { - if( fileExists(defaultCertFileName) ) { - tlsParams.tlsKeyPath = defaultCertFileName; - } else if( fileExists( joinPath(platform::getDefaultConfigPath(), defaultCertFileName) ) ) { - tlsParams.tlsKeyPath = joinPath(platform::getDefaultConfigPath(), defaultCertFileName); - } - } - } - if (tlsParams.tlsKeyPath.size()) { - sslContext.use_private_key_file(tlsParams.tlsKeyPath, boost::asio::ssl::context::pem); - } + const std::string& keyBytes = loaded.getKeyBytes(); + if (keyBytes.size()) { + sslContext.use_private_key(boost::asio::buffer(keyBytes.data(), keyBytes.size()), boost::asio::ssl::context::pem); } } catch(boost::system::system_error e) { fprintf(stderr, "Error initializing TLS: %s\n", e.what()); @@ -1522,9 +1571,9 @@ void ASIOReactor::wake() { } // namespace net2 -INetwork* newNet2(bool useThreadPool, bool useMetrics, Reference policy, const TLSParams& tlsParams) { +INetwork* newNet2(const TLSConfig& tlsConfig, bool useThreadPool, bool useMetrics) { try { - N2::g_net2 = new N2::Net2(useThreadPool, useMetrics, policy, tlsParams); + N2::g_net2 = new N2::Net2(tlsConfig, useThreadPool, useMetrics); } catch(boost::system::system_error e) { TraceEvent("Net2InitError").detail("Message", e.what()); diff --git a/flow/TLSPolicy.cpp b/flow/TLSConfig.actor.cpp similarity index 74% rename from flow/TLSPolicy.cpp rename to flow/TLSConfig.actor.cpp index cc83a24629..28685e88ad 100644 --- a/flow/TLSPolicy.cpp +++ b/flow/TLSConfig.actor.cpp @@ -1,5 +1,5 @@ /* - * TLSPolicy.cpp + * TLSConfig.actor.cpp * * This source file is part of the FoundationDB open source project * @@ -18,8 +18,11 @@ * limitations under the License. */ -#include "flow/TLSPolicy.h" +#define PRIVATE_EXCEPT_FOR_TLSCONFIG_CPP public +#include "flow/TLSConfig.actor.h" +#undef PRIVATE_EXCEPT_FOR_TLSCONFIG_CPP +// To force typeinfo to only be emitted once. TLSPolicy::~TLSPolicy() {} #ifndef TLS_DISABLED @@ -39,18 +42,191 @@ TLSPolicy::~TLSPolicy() {} #include #include #include +#include + +// This include breaks module dependencies, but we need to do async file reads. +// So either we include fdbrpc here, or this file is moved to fdbrpc/, and then +// Net2, which depends on us, includes fdbrpc/. +// +// Either way, the only way to break this dependency cycle is to move all of +// AsyncFile to flow/ +#include "fdbrpc/IAsyncFile.h" +#include "flow/Platform.h" #include "flow/FastRef.h" #include "flow/Trace.h" +#include "flow/genericactors.actor.h" +#include "flow/actorcompiler.h" // This must be the last #include. + + +std::vector LoadedTLSConfig::getVerifyPeers() const { + if (tlsVerifyPeers.size()) { + return tlsVerifyPeers; + } + + std::string envVerifyPeers; + if (platform::getEnvironmentVar("FDB_TLS_VERIFY_PEERS", envVerifyPeers)) { + return {envVerifyPeers}; + } + + return {"Check.Valid=1"}; +} + +std::string LoadedTLSConfig::getPassword() const { + if (tlsPassword.size()) { + return tlsPassword; + } + + std::string envPassword; + platform::getEnvironmentVar("FDB_TLS_PASSWORD", envPassword); + return envPassword; +} + +std::string TLSConfig::getCertificatePathSync() const { + if (tlsCertPath.size()) { + return tlsCertPath; + } + + std::string envCertPath; + if (platform::getEnvironmentVar("FDB_TLS_CERTIFICATE_FILE", envCertPath)) { + return envCertPath; + } + + const char *defaultCertFileName = "fdb.pem"; + if( fileExists(defaultCertFileName) ) { + return defaultCertFileName; + } + + if( fileExists( joinPath(platform::getDefaultConfigPath(), defaultCertFileName) ) ) { + return joinPath(platform::getDefaultConfigPath(), defaultCertFileName); + } + + return std::string(); +} + +std::string TLSConfig::getKeyPathSync() const { + if (tlsKeyPath.size()) { + return tlsKeyPath; + } + + std::string envKeyPath; + if (platform::getEnvironmentVar("FDB_TLS_KEY_FILE", envKeyPath)) { + return envKeyPath; + } + + const char *defaultCertFileName = "fdb.pem"; + if( fileExists(defaultCertFileName) ) { + return defaultCertFileName; + } + + if( fileExists( joinPath(platform::getDefaultConfigPath(), defaultCertFileName) ) ) { + return joinPath(platform::getDefaultConfigPath(), defaultCertFileName); + } + + return std::string(); +} + +std::string TLSConfig::getCAPathSync() const { + if (tlsCAPath.size()) { + return tlsCAPath; + } + + std::string envCAPath; + platform::getEnvironmentVar("FDB_TLS_CA_FILE", envCAPath); + return envCAPath; +} + +LoadedTLSConfig TLSConfig::loadSync() const { + LoadedTLSConfig loaded; + + const std::string certPath = getCertificatePathSync(); + if (certPath.size()) { + loaded.tlsCertBytes = readFileBytes( certPath, FLOW_KNOBS->CERT_FILE_MAX_SIZE ); + } else { + loaded.tlsCertBytes = tlsCertBytes; + } + + const std::string keyPath = getKeyPathSync(); + if (keyPath.size()) { + loaded.tlsKeyBytes = readFileBytes( keyPath, FLOW_KNOBS->CERT_FILE_MAX_SIZE ); + } else { + loaded.tlsKeyBytes = tlsKeyBytes; + } + + const std::string CAPath = getCAPathSync(); + if (CAPath.size()) { + loaded.tlsCABytes = readFileBytes( CAPath, FLOW_KNOBS->CERT_FILE_MAX_SIZE ); + } else { + loaded.tlsCABytes = tlsCABytes; + } + + loaded.tlsPassword = tlsPassword; + loaded.tlsVerifyPeers = tlsVerifyPeers; + loaded.endpointType = endpointType; + + return loaded; +} + +// And now do the same thing, but async... + +ACTOR static Future readEntireFile( std::string filename, std::string* destination ) { + state Reference file = wait(IAsyncFileSystem::filesystem()->open(filename, IAsyncFile::OPEN_READONLY | IAsyncFile::OPEN_UNCACHED, 0)); + state int64_t filesize = wait(file->size()); + if (filesize > FLOW_KNOBS->CERT_FILE_MAX_SIZE) { + throw tls_error(); + } + destination->resize(filesize); + int rc = wait(file->read(const_cast(destination->c_str()), filesize, 0)); + if (rc != filesize) { + // File modified during read, probably. The mtime should change, and thus we'll be called again. + throw tls_error(); + } + return Void(); +} + +ACTOR Future TLSConfig::loadAsync(const TLSConfig* self) { + state LoadedTLSConfig loaded; + state std::vector> reads; + + const std::string& certPath = self->getCertificatePathSync(); + if (certPath.size()) { + reads.push_back( readEntireFile( certPath, &loaded.tlsCertBytes ) ); + } else { + loaded.tlsCertBytes = self->tlsCertBytes; + } + + const std::string& keyPath = self->getKeyPathSync(); + if (keyPath.size()) { + reads.push_back( readEntireFile( keyPath, &loaded.tlsKeyBytes ) ); + } else { + loaded.tlsKeyBytes = self->tlsKeyBytes; + } + + const std::string& CAPath = self->getCAPathSync(); + if (CAPath.size()) { + reads.push_back( readEntireFile( CAPath, &loaded.tlsCABytes ) ); + } else { + loaded.tlsCABytes = self->tlsKeyBytes; + } + + wait(waitForAll(reads)); + + loaded.tlsPassword = self->tlsPassword; + loaded.tlsVerifyPeers = self->tlsVerifyPeers; + loaded.endpointType = self->endpointType; + + return loaded; +} + +void ConfigureSSLContext( boost::asio::ssl::context *context, const LoadedTLSConfig& config ) { + +} std::string TLSPolicy::ErrorString(boost::system::error_code e) { char* str = ERR_error_string(e.value(), NULL); return std::string(str); } -// To force typeinfo to only be emitted once. - - std::string TLSPolicy::toString() const { std::stringstream ss; ss << "TLSPolicy{ Rules=["; diff --git a/flow/TLSConfig.actor.h b/flow/TLSConfig.actor.h new file mode 100644 index 0000000000..e750c3de93 --- /dev/null +++ b/flow/TLSConfig.actor.h @@ -0,0 +1,295 @@ +/* + * TLSConfig.actor.h + * + * This source file is part of the FoundationDB open source project + * + * Copyright 2013-2020 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. + */ + +// When actually compiled (NO_INTELLISENSE), include the generated version of this file. In intellisense use the source version. +#if defined(NO_INTELLISENSE) && !defined(FLOW_TLS_CONFIG_ACTOR_G_H) + #define FLOW_TLS_CONFIG_ACTOR_G_H + #include "flow/TLSConfig.actor.g.h" +#elif !defined(FLOW_TLS_CONFIG_ACTOR_H) + #define FLOW_TLS_CONFIG_ACTOR_H + +#pragma once + +#include +#include +#include +#include +#include "flow/FastRef.h" +#include "flow/Knobs.h" +#include "flow/flow.h" + +#ifndef TLS_DISABLED + +#include +typedef int NID; + +enum class MatchType { + EXACT, + PREFIX, + SUFFIX, +}; + +enum class X509Location { + // This NID is located within a X509_NAME + NAME, + // This NID is an X509 extension, and should be parsed accordingly + EXTENSION, +}; + +struct Criteria { + Criteria( const std::string& s ) + : criteria(s), match_type(MatchType::EXACT), location(X509Location::NAME) {} + Criteria( const std::string& s, MatchType mt ) + : criteria(s), match_type(mt), location(X509Location::NAME) {} + Criteria( const std::string& s, X509Location loc) + : criteria(s), match_type(MatchType::EXACT), location(loc) {} + Criteria( const std::string& s, MatchType mt, X509Location loc) + : criteria(s), match_type(mt), location(loc) {} + + std::string criteria; + MatchType match_type; + X509Location location; + + bool operator==(const Criteria& c) const { + return criteria == c.criteria && match_type == c.match_type && location == c.location; + } +}; +#endif + +#include "flow/actorcompiler.h" // This must be the last #include. + +enum class TLSEndpointType { + UNSET = 0, + CLIENT, + SERVER +}; + +class TLSConfig; +template class LoadAsyncActorState; +// TODO: Remove this once this code is merged with master/to-be 7.0 and actors can access private variables. +#ifndef PRIVATE_EXCEPT_FOR_TLSCONFIG_CPP +#define PRIVATE_EXCEPT_FOR_TLSCONFIG_CPP private +#endif + +class LoadedTLSConfig { +public: + std::string getCertificateBytes() const { + return tlsCertBytes; + } + + std::string getKeyBytes() const { + return tlsKeyBytes; + } + + std::string getCABytes() const { + return tlsCABytes; + } + + // Return the explicitly set verify peers string. + // If no verify peers string was set, return the environment setting + // If no environment setting exists, return "Check.Valid=1" + std::vector getVerifyPeers() const; + + // Return the explicitly set password. + // If no password was set, return the environment setting + // If no environment setting exists, return an empty string + std::string getPassword() const; + + TLSEndpointType getEndpointType() const { + return endpointType; + } + + bool isTLSEnabled() const { + return endpointType != TLSEndpointType::UNSET; + } + +PRIVATE_EXCEPT_FOR_TLSCONFIG_CPP: + std::string tlsCertBytes, tlsKeyBytes, tlsCABytes; + std::string tlsPassword; + std::vector tlsVerifyPeers; + TLSEndpointType endpointType = TLSEndpointType::UNSET; + + friend class TLSConfig; + template + friend class LoadAsyncActorState; +}; + +class TLSConfig { +public: + enum { OPT_TLS = 100000, OPT_TLS_PLUGIN, OPT_TLS_CERTIFICATES, OPT_TLS_KEY, OPT_TLS_VERIFY_PEERS, OPT_TLS_CA_FILE, OPT_TLS_PASSWORD }; + + TLSConfig() = default; + explicit TLSConfig( TLSEndpointType endpointType ) + : endpointType( endpointType ) { + } + + void setCertificatePath( const std::string& path ) { + tlsCertPath = path; + tlsCertBytes = ""; + } + + void setCertificateBytes( const std::string& bytes ) { + tlsCertBytes = bytes; + tlsCertPath = ""; + } + + void setKeyPath( const std::string& path ) { + tlsKeyPath = path; + tlsKeyBytes = ""; + } + + void setKeyBytes( const std::string& bytes ) { + tlsKeyBytes = bytes; + tlsKeyPath = ""; + } + + void setCAPath( const std::string& path ) { + tlsCAPath = path; + tlsCABytes = ""; + } + + void setCABytes( const std::string& bytes ) { + tlsCABytes = bytes; + tlsCAPath = ""; + } + + void setPassword( const std::string& password ) { + tlsPassword = password; + } + + void clearVerifyPeers() { + tlsVerifyPeers.clear(); + } + + void addVerifyPeers( const std::string& verifyPeers ) { + tlsVerifyPeers.push_back( verifyPeers ); + } + + // Load all specified certificates into memory, and return an object that + // allows access to them. + // If self has any certificates by path, they will be *synchronously* loaded from disk. + LoadedTLSConfig loadSync() const; + + // Load all specified certificates into memory, and return an object that + // allows access to them. + // If self has any certificates by path, they will be *asynchronously* loaded from disk. + Future loadAsync() const { + return loadAsync(this); + } + +PRIVATE_EXCEPT_FOR_TLSCONFIG_CPP: + // Return the explicitly set path. + // If one was not set, return the path from the environment. + // (Cert and Key only) If neither exist, check for fdb.pem in cwd + // (Cert and Key only) If fdb.pem doesn't exist, check for it in default config dir + // Otherwise return the empty string. + // Theoretically, fileExists() can block, so these functions are labelled as synchronous + // TODO: make an easy to use Future fileExists, and port lots of code over to it. + std::string getCertificatePathSync() const; + std::string getKeyPathSync() const; + std::string getCAPathSync() const; + + ACTOR static Future loadAsync(const TLSConfig* self); + template + friend class LoadAsyncActorState; + + std::string tlsCertPath, tlsKeyPath, tlsCAPath; + std::string tlsCertBytes, tlsKeyBytes, tlsCABytes; + std::string tlsPassword; + std::vector tlsVerifyPeers; + TLSEndpointType endpointType = TLSEndpointType::UNSET; +}; + +namespace boost { + namespace asio { + namespace ssl { + struct context; + } + } +} +void ConfigureSSLContext( boost::asio::ssl::context *context, const LoadedTLSConfig& config ); + +class TLSPolicy : ReferenceCounted { +public: + + TLSPolicy(TLSEndpointType client) : is_client(client == TLSEndpointType::CLIENT) {} + virtual ~TLSPolicy(); + + virtual void addref() { ReferenceCounted::addref(); } + virtual void delref() { ReferenceCounted::delref(); } + +#ifndef TLS_DISABLED + static std::string ErrorString(boost::system::error_code e); + + void set_verify_peers(std::vector verify_peers); + bool verify_peer(bool preverified, X509_STORE_CTX* store_ctx); + + std::string toString() const; + + struct Rule { + explicit Rule(std::string input); + + std::string toString() const; + + std::map< NID, Criteria > subject_criteria; + std::map< NID, Criteria > issuer_criteria; + std::map< NID, Criteria > root_criteria; + + bool verify_cert = true; + bool verify_time = true; + }; + + std::vector rules; +#endif + bool is_client; +}; + +#define TLS_PLUGIN_FLAG "--tls_plugin" +#define TLS_CERTIFICATE_FILE_FLAG "--tls_certificate_file" +#define TLS_KEY_FILE_FLAG "--tls_key_file" +#define TLS_VERIFY_PEERS_FLAG "--tls_verify_peers" +#define TLS_CA_FILE_FLAG "--tls_ca_file" +#define TLS_PASSWORD_FLAG "--tls_password" + +#define TLS_OPTION_FLAGS \ + { TLSConfig::OPT_TLS_PLUGIN, TLS_PLUGIN_FLAG, SO_REQ_SEP }, \ + { TLSConfig::OPT_TLS_CERTIFICATES, TLS_CERTIFICATE_FILE_FLAG, SO_REQ_SEP }, \ + { TLSConfig::OPT_TLS_KEY, TLS_KEY_FILE_FLAG, SO_REQ_SEP }, \ + { TLSConfig::OPT_TLS_VERIFY_PEERS, TLS_VERIFY_PEERS_FLAG, SO_REQ_SEP }, \ + { TLSConfig::OPT_TLS_PASSWORD, TLS_PASSWORD_FLAG, SO_REQ_SEP }, \ + { TLSConfig::OPT_TLS_CA_FILE, TLS_CA_FILE_FLAG, SO_REQ_SEP }, + +#define TLS_HELP \ + " " TLS_CERTIFICATE_FILE_FLAG " CERTFILE\n" \ + " The path of a file containing the TLS certificate and CA\n" \ + " chain.\n" \ + " " TLS_CA_FILE_FLAG " CERTAUTHFILE\n" \ + " The path of a file containing the CA certificates chain.\n" \ + " " TLS_KEY_FILE_FLAG " KEYFILE\n" \ + " The path of a file containing the private key corresponding\n" \ + " to the TLS certificate.\n" \ + " " TLS_PASSWORD_FLAG " PASSCODE\n" \ + " The passphrase of encrypted private key\n" \ + " " TLS_VERIFY_PEERS_FLAG " CONSTRAINTS\n" \ + " The constraints by which to validate TLS peers. The contents\n" \ + " and format of CONSTRAINTS are plugin-specific.\n" + +#include "flow/unactorcompiler.h" +#endif diff --git a/flow/TLSPolicy.h b/flow/TLSPolicy.h deleted file mode 100644 index 9a0ddfcfa9..0000000000 --- a/flow/TLSPolicy.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * TLSPolicy.h - * - * This source file is part of the FoundationDB open source project - * - * Copyright 2013-2020 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 _FLOW_TLSPOLICY_H_ -#define _FLOW_TLSPOLICY_H_ -#pragma once - -#include -#include -#include -#include -#include "flow/FastRef.h" - -#ifndef TLS_DISABLED - -#include -typedef int NID; - -enum class MatchType { - EXACT, - PREFIX, - SUFFIX, -}; - -enum class X509Location { - // This NID is located within a X509_NAME - NAME, - // This NID is an X509 extension, and should be parsed accordingly - EXTENSION, -}; - -struct Criteria { - Criteria( const std::string& s ) - : criteria(s), match_type(MatchType::EXACT), location(X509Location::NAME) {} - Criteria( const std::string& s, MatchType mt ) - : criteria(s), match_type(mt), location(X509Location::NAME) {} - Criteria( const std::string& s, X509Location loc) - : criteria(s), match_type(MatchType::EXACT), location(loc) {} - Criteria( const std::string& s, MatchType mt, X509Location loc) - : criteria(s), match_type(mt), location(loc) {} - - std::string criteria; - MatchType match_type; - X509Location location; - - bool operator==(const Criteria& c) const { - return criteria == c.criteria && match_type == c.match_type && location == c.location; - } -}; -#endif - -struct TLSParams { - enum { OPT_TLS = 100000, OPT_TLS_PLUGIN, OPT_TLS_CERTIFICATES, OPT_TLS_KEY, OPT_TLS_VERIFY_PEERS, OPT_TLS_CA_FILE, OPT_TLS_PASSWORD }; - - std::string tlsCertPath, tlsKeyPath, tlsCAPath, tlsPassword; - std::string tlsCertBytes, tlsKeyBytes, tlsCABytes; -}; - -class TLSPolicy : ReferenceCounted { -public: - enum class Is { - CLIENT, - SERVER - }; - - TLSPolicy(Is client) : is_client(client == Is::CLIENT) {} - virtual ~TLSPolicy(); - - virtual void addref() { ReferenceCounted::addref(); } - virtual void delref() { ReferenceCounted::delref(); } - -#ifndef TLS_DISABLED - static std::string ErrorString(boost::system::error_code e); - - void set_verify_peers(std::vector verify_peers); - bool verify_peer(bool preverified, X509_STORE_CTX* store_ctx); - - std::string toString() const; - - struct Rule { - explicit Rule(std::string input); - - std::string toString() const; - - std::map< NID, Criteria > subject_criteria; - std::map< NID, Criteria > issuer_criteria; - std::map< NID, Criteria > root_criteria; - - bool verify_cert = true; - bool verify_time = true; - }; - - std::vector rules; -#endif - bool is_client; -}; - -#define TLS_PLUGIN_FLAG "--tls_plugin" -#define TLS_CERTIFICATE_FILE_FLAG "--tls_certificate_file" -#define TLS_KEY_FILE_FLAG "--tls_key_file" -#define TLS_VERIFY_PEERS_FLAG "--tls_verify_peers" -#define TLS_CA_FILE_FLAG "--tls_ca_file" -#define TLS_PASSWORD_FLAG "--tls_password" - -#define TLS_OPTION_FLAGS \ - { TLSParams::OPT_TLS_PLUGIN, TLS_PLUGIN_FLAG, SO_REQ_SEP }, \ - { TLSParams::OPT_TLS_CERTIFICATES, TLS_CERTIFICATE_FILE_FLAG, SO_REQ_SEP }, \ - { TLSParams::OPT_TLS_KEY, TLS_KEY_FILE_FLAG, SO_REQ_SEP }, \ - { TLSParams::OPT_TLS_VERIFY_PEERS, TLS_VERIFY_PEERS_FLAG, SO_REQ_SEP }, \ - { TLSParams::OPT_TLS_PASSWORD, TLS_PASSWORD_FLAG, SO_REQ_SEP }, \ - { TLSParams::OPT_TLS_CA_FILE, TLS_CA_FILE_FLAG, SO_REQ_SEP }, - -#define TLS_HELP \ - " " TLS_CERTIFICATE_FILE_FLAG " CERTFILE\n" \ - " The path of a file containing the TLS certificate and CA\n" \ - " chain.\n" \ - " " TLS_CA_FILE_FLAG " CERTAUTHFILE\n" \ - " The path of a file containing the CA certificates chain.\n" \ - " " TLS_KEY_FILE_FLAG " KEYFILE\n" \ - " The path of a file containing the private key corresponding\n" \ - " to the TLS certificate.\n" \ - " " TLS_PASSWORD_FLAG " PASSCODE\n" \ - " The passphrase of encrypted private key\n" \ - " " TLS_VERIFY_PEERS_FLAG " CONSTRAINTS\n" \ - " The constraints by which to validate TLS peers. The contents\n" \ - " and format of CONSTRAINTS are plugin-specific.\n" - -#endif diff --git a/flow/flow.vcxproj b/flow/flow.vcxproj index 3fb6b0a517..1002079cc6 100644 --- a/flow/flow.vcxproj +++ b/flow/flow.vcxproj @@ -50,7 +50,7 @@ - + @@ -94,7 +94,9 @@ - + + false + diff --git a/flow/genericactors.actor.h b/flow/genericactors.actor.h index 7365220e97..e8b03db412 100644 --- a/flow/genericactors.actor.h +++ b/flow/genericactors.actor.h @@ -647,6 +647,36 @@ protected: } }; +template +class ReferencedObject : NonCopyable, public ReferenceCounted> { + public: + ReferencedObject() : value() {} + ReferencedObject(V const& v) : value(v) {} + ReferencedObject(ReferencedObject&& r) : value(std::move(r.value)) {} + void operator=(ReferencedObject&& r) { + value = std::move(r.value); + } + + V const& get() const { + return value; + } + + V& mutate() const { + return value; + } + + void set(V const& v) { + value = v; + } + + static Reference> from(V const& v) { + return Reference>(new ReferencedObject(v)); + } + + private: + V value; +}; + template class AsyncVar : NonCopyable, public ReferenceCounted> { public: diff --git a/flow/network.h b/flow/network.h index 127d765bba..c15c0de6b0 100644 --- a/flow/network.h +++ b/flow/network.h @@ -32,7 +32,6 @@ #endif #include "flow/serialize.h" #include "flow/IRandom.h" -#include "flow/TLSPolicy.h" enum class TaskPriority { Max = 1000000, @@ -406,9 +405,10 @@ typedef void* flowGlobalType; typedef NetworkAddress (*NetworkAddressFuncPtr)(); typedef NetworkAddressList (*NetworkAddressesFuncPtr)(); +class TLSConfig; class INetwork; extern INetwork* g_network; -extern INetwork* newNet2(bool useThreadPool = false, bool useMetrics = false, Reference policy = Reference(), const TLSParams& tlsParams = TLSParams()); +extern INetwork* newNet2(const TLSConfig& tlsConfig, bool useThreadPool = false, bool useMetrics = false); class INetwork { public: From f657ca069e872450c72d5424eaa1036f5618b284 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Wed, 4 Mar 2020 23:51:21 -0800 Subject: [PATCH 0833/1604] Fix bindings build breakage, because I hadn't built bindings. --- bindings/flow/fdb_flow.actor.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bindings/flow/fdb_flow.actor.cpp b/bindings/flow/fdb_flow.actor.cpp index dc37e28b23..a6ddadba8a 100644 --- a/bindings/flow/fdb_flow.actor.cpp +++ b/bindings/flow/fdb_flow.actor.cpp @@ -25,6 +25,7 @@ #include "flow/DeterministicRandom.h" #include "flow/SystemMonitor.h" +#include "flow/TLSConfig.actor.h" #include "flow/actorcompiler.h" // This must be the last #include. using namespace FDB; @@ -82,7 +83,7 @@ void fdb_flow_test() { fdb->setupNetwork(); startThread(networkThread, fdb); - g_network = newNet2(false); + g_network = newNet2(TLSConfig()); openTraceFile(NetworkAddress(), 1000000, 1000000, "."); systemMonitor(); From 6d878003438647b1edd67e4f3db44152d75bce03 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Thu, 5 Mar 2020 08:20:55 -0800 Subject: [PATCH 0834/1604] Clarify fdbcli knob release note to say that the knobs being set apply to the behavior of fdbcli. --- documentation/sphinx/source/release-notes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/sphinx/source/release-notes.rst b/documentation/sphinx/source/release-notes.rst index d6a577bcf4..09b57620c7 100644 --- a/documentation/sphinx/source/release-notes.rst +++ b/documentation/sphinx/source/release-notes.rst @@ -21,7 +21,7 @@ Performance Features -------- -* Add support for setting knobs in fdbcli. `(PR #2773) `_. +* Add support for setting knobs to modify the behavior of fdbcli. `(PR #2773) `_. Other Changes ------------- From effb6d2d49cbc1fc3bbb36cb7ebea6ccb9171321 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Thu, 5 Mar 2020 10:49:21 -0800 Subject: [PATCH 0835/1604] Add ResolverMetrics trace event --- fdbrpc/PerfMetric.h | 26 ------------ fdbserver/Resolver.actor.cpp | 79 ++++++++++++++++++++++++++++------- fdbserver/SkipList.cpp | 8 ++-- fdbserver/fdbserver.actor.cpp | 2 - 4 files changed, 70 insertions(+), 45 deletions(-) diff --git a/fdbrpc/PerfMetric.h b/fdbrpc/PerfMetric.h index c5e07ff5ea..300f34a12a 100644 --- a/fdbrpc/PerfMetric.h +++ b/fdbrpc/PerfMetric.h @@ -81,30 +81,4 @@ private: double value; }; -struct GlobalCounters { - vector ints; - vector doubles; - - PerfDoubleCounter conflictTime; - PerfIntCounter conflictBatches; - PerfIntCounter conflictKeys; - PerfIntCounter conflictTransactions; - - GlobalCounters() : - conflictTime("Conflict detection time", doubles), - conflictBatches("Conflict batches", ints), - conflictKeys("Conflict keys", ints), - conflictTransactions("Conflict transactions", ints) - { - } - void clear() { - for(int i=0; iclear(); - for(int i=0; iclear(); - } -}; - -extern GlobalCounters g_counters; - #endif diff --git a/fdbserver/Resolver.actor.cpp b/fdbserver/Resolver.actor.cpp index 41834bb163..43b873874e 100644 --- a/fdbserver/Resolver.actor.cpp +++ b/fdbserver/Resolver.actor.cpp @@ -43,14 +43,6 @@ struct ProxyRequestsInfo { namespace{ struct Resolver : ReferenceCounted { - Resolver( UID dbgid, int proxyCount, int resolverCount ) - : dbgid(dbgid), proxyCount(proxyCount), resolverCount(resolverCount), version(-1), conflictSet( newConflictSet() ), iopsSample( SERVER_KNOBS->KEY_BYTES_PER_SAMPLE ), debugMinRecentStateVersion(0) - { - } - ~Resolver() { - destroyConflictSet( conflictSet ); - } - UID dbgid; int proxyCount, resolverCount; NotifiedVersion version; @@ -65,6 +57,45 @@ struct Resolver : ReferenceCounted { TransientStorageMetricSample iopsSample; Version debugMinRecentStateVersion; + + CounterCollection cc; + Counter resolveBatchIn; + Counter resolveBatchStart; + Counter resolvedTransactions; + Counter resolvedBytes; + Counter resolvedReadConflictRanges; + Counter resolvedWriteConflictRanges; + Counter transactionsAccepted; + Counter transactionsTooOld; + Counter transactionsConflicted; + Counter resolvedStateTransactions; + Counter resolvedStateMutations; + Counter resolvedStateBytes; + Counter resolveBatchOut; + Counter metricsRequests; + Counter splitRequests; + + Future logger; + + Resolver( UID dbgid, int proxyCount, int resolverCount ) + : dbgid(dbgid), proxyCount(proxyCount), resolverCount(resolverCount), version(-1), conflictSet( newConflictSet() ), iopsSample( SERVER_KNOBS->KEY_BYTES_PER_SAMPLE ), debugMinRecentStateVersion(0), + cc("Resolver", dbgid.toString()), + resolveBatchIn("ResolveBatchIn", cc), resolveBatchStart("ResolveBatchStart", cc), resolvedTransactions("ResolvedTransactions", cc), resolvedBytes("ResolvedBytes", cc), + resolvedReadConflictRanges("ResolvedReadConflictRanges", cc), resolvedWriteConflictRanges("ResolvedWriteConflictRanges", cc), transactionsAccepted("TransactionsAccepted", cc), + transactionsTooOld("TransactionsTooOld", cc), transactionsConflicted("TransactionsConflicted", cc), resolvedStateTransactions("ResolvedStateTransactions", cc), + resolvedStateMutations("ResolvedStateMutations", cc), resolvedStateBytes("ResolvedStateBytes", cc), resolveBatchOut("ResolveBatchOut", cc), metricsRequests("MetricsRequests", cc), + splitRequests("SplitRequests", cc) + { + specialCounter(cc, "Version", [this](){ return this->version.get(); }); + specialCounter(cc, "NeededVersion", [this](){ return this->neededVersion.get(); }); + specialCounter(cc, "TotalStateBytes", [this](){ return this->totalStateBytes.get(); }); + + logger = traceCounters("ResolverMetrics", dbgid, SERVER_KNOBS->WORKER_LOGGING_INTERVAL, &cc, "ResolverMetrics"); + } + ~Resolver() { + destroyConflictSet( conflictSet ); + } + }; } @@ -78,6 +109,8 @@ ACTOR Future resolveBatch( state NetworkAddress proxyAddress = req.prevVersion >= 0 ? req.reply.getEndpoint().getPrimaryAddress() : NetworkAddress(); state ProxyRequestsInfo &proxyInfo = self->proxyInfoMap[proxyAddress]; + ++self->resolveBatchIn; + if(req.debugID.present()) { debugID = nondeterministicRandom()->randomUniqueID(); g_traceBatch.addAttach("CommitAttachID", req.debugID.get().first(), debugID.get().first()); @@ -120,6 +153,10 @@ ACTOR Future resolveBatch( } if (self->version.get() == req.prevVersion) { // Not a duplicate (check relies on no waiting between here and self->version.set() below!) + ++self->resolveBatchStart; + self->resolvedTransactions += req.transactions.size(); + self->resolvedBytes += req.transactions.expectedSize(); + if(proxyInfo.lastVersion > 0) { proxyInfo.outstandingBatches.erase(proxyInfo.outstandingBatches.begin(), proxyInfo.outstandingBatches.upper_bound(req.lastReceivedVersion)); } @@ -140,6 +177,8 @@ ACTOR Future resolveBatch( int keys = 0; for(int t=0; tresolvedReadConflictRanges += req.transactions[t].read_conflict_ranges.size(); + self->resolvedWriteConflictRanges += req.transactions[t].write_conflict_ranges.size(); keys += req.transactions[t].write_conflict_ranges.size()*2 + req.transactions[t].read_conflict_ranges.size()*2; if(self->resolverCount > 1) { @@ -150,29 +189,37 @@ ACTOR Future resolveBatch( } } conflictBatch.detectConflicts( req.version, req.version - SERVER_KNOBS->MAX_WRITE_TRANSACTION_LIFE_VERSIONS, commitList, &tooOldList); - g_counters.conflictTime += timer() - tstart; - ++g_counters.conflictBatches; - g_counters.conflictTransactions += req.transactions.size(); - g_counters.conflictKeys += keys; ResolveTransactionBatchReply &reply = proxyInfo.outstandingBatches[req.version]; reply.debugID = req.debugID; reply.committed.resize( reply.arena, req.transactions.size() ); - for(int c=0; ctransactionsAccepted += commitList.size(); + self->transactionsTooOld += tooOldList.size(); + self->transactionsConflicted += req.transactions.size() - commitList.size() - tooOldList.size(); ASSERT(req.prevVersion >= 0 || req.txnStateTransactions.size() == 0); // The master's request should not have any state transactions auto& stateTransactions = self->recentStateTransactions[ req.version ]; + int64_t stateMutations = 0; int64_t stateBytes = 0; for(int t : req.txnStateTransactions) { + stateMutations += req.transactions[t].mutations.size(); stateBytes += req.transactions[t].mutations.expectedSize(); stateTransactions.push_back_deep(stateTransactions.arena(), StateTransactionRef(reply.committed[t] == ConflictBatch::TransactionCommitted, req.transactions[t].mutations)); } + self->resolvedStateTransactions += req.txnStateTransactions.size(); + self->resolvedStateMutations += stateMutations; + self->resolvedStateBytes += stateBytes; + if(stateBytes > 0) self->recentStateTransactionSizes.push_back(std::make_pair(req.version, stateBytes)); @@ -255,6 +302,8 @@ ACTOR Future resolveBatch( req.reply.send(Never()); } + ++self->resolveBatchOut; + return Void(); } @@ -273,9 +322,11 @@ ACTOR Future resolverCore( actors.add( resolveBatch(self, batch) ); } when ( ResolutionMetricsRequest req = waitNext( resolver.metrics.getFuture() ) ) { + ++self->metricsRequests; req.reply.send(self->iopsSample.getEstimate(allKeys)); } when ( ResolutionSplitRequest req = waitNext( resolver.split.getFuture() ) ) { + ++self->splitRequests; ResolutionSplitReply rep; rep.key = self->iopsSample.splitEstimate(req.range, req.offset, req.front); rep.used = self->iopsSample.getEstimate(req.front ? KeyRangeRef(req.range.begin, rep.key) : KeyRangeRef(rep.key, req.range.end)); diff --git a/fdbserver/SkipList.cpp b/fdbserver/SkipList.cpp index 91ae97d2df..7ade79016d 100644 --- a/fdbserver/SkipList.cpp +++ b/fdbserver/SkipList.cpp @@ -1181,10 +1181,12 @@ void ConflictBatch::detectConflicts(Version now, Version newOldestVersion, std:: for (int i = 0; i < transactionCount; i++) { - if (!transactionConflictStatus[i]) - nonConflicting.push_back( i ); - if (tooOldTransactions && transactionInfo[i]->tooOld) + if (tooOldTransactions && transactionInfo[i]->tooOld) { tooOldTransactions->push_back(i); + } + else if (!transactionConflictStatus[i]) { + nonConflicting.push_back( i ); + } } delete[] transactionConflictStatus; diff --git a/fdbserver/fdbserver.actor.cpp b/fdbserver/fdbserver.actor.cpp index 70b0cc7346..0fe05c9317 100644 --- a/fdbserver/fdbserver.actor.cpp +++ b/fdbserver/fdbserver.actor.cpp @@ -174,8 +174,6 @@ CSimpleOpt::SOption g_rgOptions[] = { SO_END_OF_OPTIONS }; -GlobalCounters g_counters; - extern void dsltest(); extern void pingtest(); extern void copyTest(); From 7fb8c3c08074acde966ac5400f8d6bde178db827 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Thu, 5 Mar 2020 11:38:30 -0800 Subject: [PATCH 0836/1604] Remove unused variable. --- fdbserver/Resolver.actor.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/fdbserver/Resolver.actor.cpp b/fdbserver/Resolver.actor.cpp index 43b873874e..a4850815d6 100644 --- a/fdbserver/Resolver.actor.cpp +++ b/fdbserver/Resolver.actor.cpp @@ -172,7 +172,6 @@ ACTOR Future resolveBatch( // Detect conflicts double expire = now() + SERVER_KNOBS->SAMPLE_EXPIRATION_TIME; - double tstart = timer(); ConflictBatch conflictBatch( self->conflictSet ); int keys = 0; for(int t=0; t Date: Thu, 5 Mar 2020 14:00:44 -0800 Subject: [PATCH 0837/1604] Add more metrics to the TransactionMetrics event --- fdbclient/DatabaseContext.h | 22 ++++ fdbclient/NativeAPI.actor.cpp | 204 ++++++++++++++++++++++++---------- 2 files changed, 167 insertions(+), 59 deletions(-) diff --git a/fdbclient/DatabaseContext.h b/fdbclient/DatabaseContext.h index cad13bc059..a0d2ea6414 100644 --- a/fdbclient/DatabaseContext.h +++ b/fdbclient/DatabaseContext.h @@ -160,12 +160,34 @@ public: CounterCollection cc; Counter transactionReadVersions; + Counter transactionReadVersionsCompleted; + Counter transactionReadVersionBatches; + Counter transactionBatchReadVersions; + Counter transactionDefaultReadVersions; + Counter transactionImmediateReadVersions; + Counter transactionBatchReadVersionsCompleted; + Counter transactionDefaultReadVersionsCompleted; + Counter transactionImmediateReadVersionsCompleted; Counter transactionLogicalReads; Counter transactionPhysicalReads; + Counter transactionPhysicalReadsCompleted; + Counter transactionGetKeyRequests; + Counter transactionGetValueRequests; + Counter transactionGetRangeRequests; + Counter transactionWatchRequests; + Counter transactionGetAddressesForKeyRequests; + Counter transactionBytesRead; + Counter transactionKeysRead; + Counter transactionMetadataVersionReads; Counter transactionCommittedMutations; Counter transactionCommittedMutationBytes; + Counter transactionSetMutations; + Counter transactionClearMutations; + Counter transactionAtomicMutations; Counter transactionsCommitStarted; Counter transactionsCommitCompleted; + Counter transactionKeyServerLocationRequests; + Counter transactionKeyServerLocationRequestsCompleted; Counter transactionsTooOld; Counter transactionsFutureVersions; Counter transactionsNotCommitted; diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index b1a51ae009..caea3af384 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -532,14 +532,20 @@ DatabaseContext::DatabaseContext( Reference>> connectionFile, Reference> clientInfo, Future clientInfoMonitor, TaskPriority taskID, LocalityData const& clientLocality, bool enableLocalityLoadBalance, bool lockAware, bool internal, int apiVersion, bool switchable ) : connectionFile(connectionFile),clientInfo(clientInfo), clientInfoMonitor(clientInfoMonitor), taskID(taskID), clientLocality(clientLocality), enableLocalityLoadBalance(enableLocalityLoadBalance), - lockAware(lockAware), apiVersion(apiVersion), switchable(switchable), provisional(false), cc("TransactionMetrics"), - transactionReadVersions("ReadVersions", cc), transactionLogicalReads("LogicalUncachedReads", cc), transactionPhysicalReads("PhysicalReadRequests", cc), - transactionCommittedMutations("CommittedMutations", cc), transactionCommittedMutationBytes("CommittedMutationBytes", cc), transactionsCommitStarted("CommitStarted", cc), - transactionsCommitCompleted("CommitCompleted", cc), transactionsTooOld("TooOld", cc), transactionsFutureVersions("FutureVersions", cc), - transactionsNotCommitted("NotCommitted", cc), transactionsMaybeCommitted("MaybeCommitted", cc), transactionsResourceConstrained("ResourceConstrained", cc), - transactionsProcessBehind("ProcessBehind", cc), outstandingWatches(0), - latencies(1000), readLatencies(1000), commitLatencies(1000), GRVLatencies(1000), mutationsPerCommit(1000), bytesPerCommit(1000), mvCacheInsertLocation(0), - healthMetricsLastUpdated(0), detailedHealthMetricsLastUpdated(0), internal(internal) + lockAware(lockAware), apiVersion(apiVersion), switchable(switchable), provisional(false), cc("TransactionMetrics"), transactionReadVersions("ReadVersions", cc), + transactionReadVersionsCompleted("ReadVersionsCompleted", cc), transactionReadVersionBatches("ReadVersionBatches", cc), transactionBatchReadVersions("BatchPriorityReadVersions", cc), + transactionDefaultReadVersions("DefaultPriorityReadVersions", cc), transactionImmediateReadVersions("ImmediatePriorityReadVersions", cc), + transactionBatchReadVersionsCompleted("BatchPriorityReadVersionsComplete", cc), transactionDefaultReadVersionsCompleted("DefaultPriorityReadVersionsComplete", cc), + transactionImmediateReadVersionsCompleted("ImmediatePriroityReadVersionsComplete", cc), transactionLogicalReads("LogicalUncachedReads", cc), transactionPhysicalReads("PhysicalReadRequests", cc), + transactionPhysicalReadsCompleted("PhysicalReadRequestsCompleted", cc), transactionGetKeyRequests("GetKeyRequests", cc), transactionGetValueRequests("GetValueRequests", cc), + transactionGetRangeRequests("GetRangeRequests", cc), transactionWatchRequests("WatchRequests", cc), transactionGetAddressesForKeyRequests("GetAddressesForKeyRequests", cc), + transactionBytesRead("BytesRead", cc), transactionKeysRead("KeysRead", cc), transactionMetadataVersionReads("MetadataVersionReads", cc), transactionCommittedMutations("CommittedMutations", cc), + transactionCommittedMutationBytes("CommittedMutationBytes", cc), transactionSetMutations("SetMutations", cc), transactionClearMutations("ClearMutations", cc), + transactionAtomicMutations("AtomicMutations", cc), transactionsCommitStarted("CommitStarted", cc), transactionsCommitCompleted("CommitCompleted", cc), + transactionKeyServerLocationRequests("KeyServerLocationRequests", cc), transactionKeyServerLocationRequestsCompleted("KeyServerLocationRequestsCompleted", cc), transactionsTooOld("TooOld", cc), + transactionsFutureVersions("FutureVersions", cc), transactionsNotCommitted("NotCommitted", cc), transactionsMaybeCommitted("MaybeCommitted", cc), + transactionsResourceConstrained("ResourceConstrained", cc), transactionsProcessBehind("ProcessBehind", cc), outstandingWatches(0), latencies(1000), readLatencies(1000), commitLatencies(1000), + GRVLatencies(1000), mutationsPerCommit(1000), bytesPerCommit(1000), mvCacheInsertLocation(0), healthMetricsLastUpdated(0), detailedHealthMetricsLastUpdated(0), internal(internal) { dbId = deterministicRandom()->randomUniqueID(); connected = clientInfo->get().proxies.size() ? Void() : clientInfo->onChange(); @@ -561,12 +567,19 @@ DatabaseContext::DatabaseContext( clientStatusUpdater.actor = clientStatusUpdateActor(this); } -DatabaseContext::DatabaseContext( const Error &err ) : deferredError(err), cc("TransactionMetrics"), - transactionReadVersions("ReadVersions", cc), transactionLogicalReads("LogicalUncachedReads", cc), transactionPhysicalReads("PhysicalReadRequests", cc), - transactionCommittedMutations("CommittedMutations", cc), transactionCommittedMutationBytes("CommittedMutationBytes", cc), transactionsCommitStarted("CommitStarted", cc), - transactionsCommitCompleted("CommitCompleted", cc), transactionsTooOld("TooOld", cc), transactionsFutureVersions("FutureVersions", cc), - transactionsNotCommitted("NotCommitted", cc), transactionsMaybeCommitted("MaybeCommitted", cc), transactionsResourceConstrained("ResourceConstrained", cc), - transactionsProcessBehind("ProcessBehind", cc), latencies(1000), readLatencies(1000), commitLatencies(1000), +DatabaseContext::DatabaseContext( const Error &err ) : deferredError(err), cc("TransactionMetrics"), transactionReadVersions("ReadVersions", cc), + transactionReadVersionsCompleted("ReadVersionsCompleted", cc), transactionReadVersionBatches("ReadVersionBatches", cc), transactionBatchReadVersions("BatchPriorityReadVersions", cc), + transactionDefaultReadVersions("DefaultPriorityReadVersions", cc), transactionImmediateReadVersions("ImmediatePriorityReadVersions", cc), + transactionBatchReadVersionsCompleted("BatchPriorityReadVersionsComplete", cc), transactionDefaultReadVersionsCompleted("DefaultPriorityReadVersionsComplete", cc), + transactionImmediateReadVersionsCompleted("ImmediatePriroityReadVersionsComplete", cc), transactionLogicalReads("LogicalUncachedReads", cc), transactionPhysicalReads("PhysicalReadRequests", cc), + transactionPhysicalReadsCompleted("PhysicalReadRequestsCompleted", cc), transactionGetKeyRequests("GetKeyRequests", cc), transactionGetValueRequests("GetValueRequests", cc), + transactionGetRangeRequests("GetRangeRequests", cc), transactionWatchRequests("WatchRequests", cc), transactionGetAddressesForKeyRequests("GetAddressesForKeyRequests", cc), + transactionBytesRead("BytesRead", cc), transactionKeysRead("KeysRead", cc), transactionMetadataVersionReads("MetadataVersionReads", cc), transactionCommittedMutations("CommittedMutations", cc), + transactionCommittedMutationBytes("CommittedMutationBytes", cc), transactionSetMutations("SetMutations", cc), transactionClearMutations("ClearMutations", cc), + transactionAtomicMutations("AtomicMutations", cc), transactionsCommitStarted("CommitStarted", cc), transactionsCommitCompleted("CommitCompleted", cc), + transactionKeyServerLocationRequests("KeyServerLocationRequests", cc), transactionKeyServerLocationRequestsCompleted("KeyServerLocationRequestsCompleted", cc), transactionsTooOld("TooOld", cc), + transactionsFutureVersions("FutureVersions", cc), transactionsNotCommitted("NotCommitted", cc), transactionsMaybeCommitted("MaybeCommitted", cc), + transactionsResourceConstrained("ResourceConstrained", cc), transactionsProcessBehind("ProcessBehind", cc), latencies(1000), readLatencies(1000), commitLatencies(1000), GRVLatencies(1000), mutationsPerCommit(1000), bytesPerCommit(1000), internal(false) {} @@ -1184,9 +1197,11 @@ ACTOR Future< pair> > getKeyLocation_internal( g_traceBatch.addEvent("TransactionDebug", info.debugID.get().first(), "NativeAPI.getKeyLocation.Before"); loop { + ++cx->transactionKeyServerLocationRequests; choose { when ( wait( cx->onMasterProxiesChanged() ) ) {} when ( GetKeyServerLocationsReply rep = wait( loadBalance( cx->getMasterProxies(info.useProvisionalProxies), &MasterProxyInterface::getKeyServersLocations, GetKeyServerLocationsRequest(key, Optional(), 100, isBackward, key.arena()), TaskPriority::DefaultPromiseEndpoint ) ) ) { + ++cx->transactionKeyServerLocationRequestsCompleted; if( info.debugID.present() ) g_traceBatch.addEvent("TransactionDebug", info.debugID.get().first(), "NativeAPI.getKeyLocation.After"); ASSERT( rep.results.size() == 1 ); @@ -1221,9 +1236,11 @@ ACTOR Future< vector< pair> > > getKeyRangeLoca g_traceBatch.addEvent("TransactionDebug", info.debugID.get().first(), "NativeAPI.getKeyLocations.Before"); loop { + ++cx->transactionKeyServerLocationRequests; choose { when ( wait( cx->onMasterProxiesChanged() ) ) {} when ( GetKeyServerLocationsReply _rep = wait( loadBalance( cx->getMasterProxies(info.useProvisionalProxies), &MasterProxyInterface::getKeyServersLocations, GetKeyServerLocationsRequest(keys.begin, keys.end, limit, reverse, keys.arena()), TaskPriority::DefaultPromiseEndpoint ) ) ) { + ++cx->transactionKeyServerLocationRequestsCompleted; state GetKeyServerLocationsReply rep = _rep; if( info.debugID.present() ) g_traceBatch.addEvent("TransactionDebug", info.debugID.get().first(), "NativeAPI.getKeyLocations.After"); @@ -1321,8 +1338,6 @@ ACTOR Future> getValue( Future version, Key key, Databa state uint64_t startTime; state double startTimeD; try { - //GetValueReply r = wait( deterministicRandom()->randomChoice( ssi->get() ).getValue.getReply( GetValueRequest(key,ver) ) ); - //return r.value; if( info.debugID.present() ) { getValueID = nondeterministicRandom()->randomUniqueID(); @@ -1339,19 +1354,26 @@ ACTOR Future> getValue( Future version, Key key, Databa startTimeD = now(); ++cx->transactionPhysicalReads; - if (CLIENT_BUGGIFY) { - throw deterministicRandom()->randomChoice( - std::vector{ transaction_too_old(), future_version() }); - } state GetValueReply reply; - choose { - when(wait(cx->connectionFileChanged())) { throw transaction_too_old(); } - when(GetValueReply _reply = - wait(loadBalance(ssi.second, &StorageServerInterface::getValue, - GetValueRequest(key, ver, getValueID), TaskPriority::DefaultPromiseEndpoint, false, - cx->enableLocalityLoadBalance ? &cx->queueModel : nullptr))) { - reply = _reply; + try { + if (CLIENT_BUGGIFY) { + throw deterministicRandom()->randomChoice( + std::vector{ transaction_too_old(), future_version() }); } + choose { + when(wait(cx->connectionFileChanged())) { throw transaction_too_old(); } + when(GetValueReply _reply = + wait(loadBalance(ssi.second, &StorageServerInterface::getValue, + GetValueRequest(key, ver, getValueID), TaskPriority::DefaultPromiseEndpoint, false, + cx->enableLocalityLoadBalance ? &cx->queueModel : nullptr))) { + reply = _reply; + } + } + ++cx->transactionPhysicalReadsCompleted; + } + catch(Error&) { + ++cx->transactionPhysicalReadsCompleted; + throw; } double latency = now() - startTimeD; @@ -1370,6 +1392,9 @@ ACTOR Future> getValue( Future version, Key key, Databa .detail("ReqVersion", ver) .detail("ReplySize", reply.value.present() ? reply.value.get().size() : -1);*/ } + + cx->transactionBytesRead += reply.value.present() ? reply.value.get().size() : 0; + ++cx->transactionKeysRead; return reply.value; } catch (Error& e) { cx->getValueCompleted->latency = timer_int() - startTime; @@ -1417,14 +1442,20 @@ ACTOR Future getKey( Database cx, KeySelector k, Future version, T g_traceBatch.addEvent("TransactionDebug", info.debugID.get().first(), "NativeAPI.getKey.Before"); //.detail("StartKey", k.getKey()).detail("Offset",k.offset).detail("OrEqual",k.orEqual); ++cx->transactionPhysicalReads; state GetKeyReply reply; - choose { - when(wait(cx->connectionFileChanged())) { throw transaction_too_old(); } - when(GetKeyReply _reply = - wait(loadBalance(ssi.second, &StorageServerInterface::getKey, GetKeyRequest(k, version.get()), - TaskPriority::DefaultPromiseEndpoint, false, - cx->enableLocalityLoadBalance ? &cx->queueModel : nullptr))) { - reply = _reply; + try { + choose { + when(wait(cx->connectionFileChanged())) { throw transaction_too_old(); } + when(GetKeyReply _reply = + wait(loadBalance(ssi.second, &StorageServerInterface::getKey, GetKeyRequest(k, version.get()), + TaskPriority::DefaultPromiseEndpoint, false, + cx->enableLocalityLoadBalance ? &cx->queueModel : nullptr))) { + reply = _reply; + } } + ++cx->transactionPhysicalReadsCompleted; + } catch(Error&) { + ++cx->transactionPhysicalReadsCompleted; + throw; } if( info.debugID.present() ) g_traceBatch.addEvent("TransactionDebug", info.debugID.get().first(), "NativeAPI.getKey.After"); //.detail("NextKey",reply.sel.key).detail("Offset", reply.sel.offset).detail("OrEqual", k.orEqual); @@ -1603,14 +1634,20 @@ ACTOR Future> getExactRange( Database cx, Version ver } ++cx->transactionPhysicalReads; state GetKeyValuesReply rep; - choose { - when(wait(cx->connectionFileChanged())) { throw transaction_too_old(); } - when(GetKeyValuesReply _rep = - wait(loadBalance(locations[shard].second, &StorageServerInterface::getKeyValues, req, - TaskPriority::DefaultPromiseEndpoint, false, - cx->enableLocalityLoadBalance ? &cx->queueModel : nullptr))) { - rep = _rep; + try { + choose { + when(wait(cx->connectionFileChanged())) { throw transaction_too_old(); } + when(GetKeyValuesReply _rep = + wait(loadBalance(locations[shard].second, &StorageServerInterface::getKeyValues, req, + TaskPriority::DefaultPromiseEndpoint, false, + cx->enableLocalityLoadBalance ? &cx->queueModel : nullptr))) { + rep = _rep; + } } + ++cx->transactionPhysicalReadsCompleted; + } catch(Error&) { + ++cx->transactionPhysicalReadsCompleted; + throw; } if( info.debugID.present() ) g_traceBatch.addEvent("TransactionDebug", info.debugID.get().first(), "NativeAPI.getExactRange.After"); @@ -1759,14 +1796,19 @@ ACTOR Future> getRangeFallback( Database cx, Version return r; } -void getRangeFinished(Reference trLogInfo, double startTime, KeySelector begin, KeySelector end, bool snapshot, +void getRangeFinished(Database cx, Reference trLogInfo, double startTime, KeySelector begin, KeySelector end, bool snapshot, Promise> conflictRange, bool reverse, Standalone result) { + int64_t bytes = 0; + for(const KeyValueRef &kv : result) { + bytes += kv.key.size() + kv.value.size(); + } + + cx->transactionBytesRead += bytes; + cx->transactionKeysRead += result.size(); + if( trLogInfo ) { - int rangeSize = 0; - for (const KeyValueRef &kv : result.contents()) - rangeSize += kv.key.size() + kv.value.size(); - trLogInfo->addLog(FdbClientLogEvents::EventGetRange(startTime, now()-startTime, rangeSize, begin.getKey(), end.getKey())); + trLogInfo->addLog(FdbClientLogEvents::EventGetRange(startTime, now()-startTime, bytes, begin.getKey(), end.getKey())); } if( !snapshot ) { @@ -1832,7 +1874,7 @@ ACTOR Future> getRange( Database cx, Reference> getRange( Database cx, ReferencetransactionPhysicalReads; - if (CLIENT_BUGGIFY) { - throw deterministicRandom()->randomChoice(std::vector{ - transaction_too_old(), future_version() - }); + ++cx->transactionGetRangeRequests; + state GetKeyValuesReply rep; + try { + if (CLIENT_BUGGIFY) { + throw deterministicRandom()->randomChoice(std::vector{ + transaction_too_old(), future_version() + }); + } + GetKeyValuesReply _rep = wait( loadBalance(beginServer.second, &StorageServerInterface::getKeyValues, req, TaskPriority::DefaultPromiseEndpoint, false, cx->enableLocalityLoadBalance ? &cx->queueModel : NULL ) ); + rep = _rep; + ++cx->transactionPhysicalReadsCompleted; + } catch(Error&) { + ++cx->transactionPhysicalReadsCompleted; + throw; } - GetKeyValuesReply rep = wait( loadBalance(beginServer.second, &StorageServerInterface::getKeyValues, req, TaskPriority::DefaultPromiseEndpoint, false, cx->enableLocalityLoadBalance ? &cx->queueModel : NULL ) ); if( info.debugID.present() ) { g_traceBatch.addEvent("TransactionDebug", info.debugID.get().first(), "NativeAPI.getRange.After");//.detail("SizeOf", rep.data.size()); @@ -1925,7 +1976,7 @@ ACTOR Future> getRange( Database cx, Reference std::max(1, originalLimits.minRows) ) { output.more = true; output.resize(output.arena(), deterministicRandom()->randomInt(std::max(1,originalLimits.minRows),output.size())); - getRangeFinished(trLogInfo, startTime, originalBegin, originalEnd, snapshot, conflictRange, reverse, output); + getRangeFinished(cx, trLogInfo, startTime, originalBegin, originalEnd, snapshot, conflictRange, reverse, output); return output; } @@ -1934,7 +1985,7 @@ ACTOR Future> getRange( Database cx, Reference> getRange( Database cx, Reference> getRange( Database cx, Reference result = wait( getRangeFallback(cx, version, originalBegin, originalEnd, originalLimits, reverse, info ) ); - getRangeFinished(trLogInfo, startTime, originalBegin, originalEnd, snapshot, conflictRange, reverse, result); + getRangeFinished(cx, trLogInfo, startTime, originalBegin, originalEnd, snapshot, conflictRange, reverse, result); return result; } @@ -1989,7 +2040,7 @@ ACTOR Future> getRange( Database cx, Reference result = wait( getRangeFallback(cx, version, originalBegin, originalEnd, originalLimits, reverse, info ) ); - getRangeFinished(trLogInfo, startTime, originalBegin, originalEnd, snapshot, conflictRange, reverse, result); + getRangeFinished(cx, trLogInfo, startTime, originalBegin, originalEnd, snapshot, conflictRange, reverse, result); return result; } @@ -2067,6 +2118,7 @@ void Transaction::setVersion( Version v ) { Future> Transaction::get( const Key& key, bool snapshot ) { ++cx->transactionLogicalReads; + ++cx->transactionGetValueRequests; //ASSERT (key < allKeys.end); //There are no keys in the database with size greater than KEY_SIZE_LIMIT @@ -2082,6 +2134,7 @@ Future> Transaction::get( const Key& key, bool snapshot ) { tr.transaction.read_conflict_ranges.push_back(tr.arena, singleKeyRange(key, tr.arena)); if(key == metadataVersionKey) { + ++cx->transactionMetadataVersionReads; if(!ver.isReady() || metadataVersion.isSet()) { return metadataVersion.getFuture(); } else { @@ -2165,6 +2218,7 @@ Future Transaction::getRawReadVersion() { } Future< Void > Transaction::watch( Reference watch ) { + ++cx->transactionWatchRequests; return ::watch(watch, cx, this); } @@ -2201,6 +2255,7 @@ ACTOR Future>> getAddressesForKeyActor(Key key Future< Standalone< VectorRef< const char*>>> Transaction::getAddressesForKey( const Key& key ) { ++cx->transactionLogicalReads; + ++cx->transactionGetAddressesForKeyRequests; auto ver = getReadVersion(); return getAddressesForKeyActor(key, ver, cx, info, options); @@ -2224,6 +2279,7 @@ ACTOR Future< Key > getKeyAndConflictRange( Future< Key > Transaction::getKey( const KeySelector& key, bool snapshot ) { ++cx->transactionLogicalReads; + ++cx->transactionGetKeyRequests; if( snapshot ) return ::getKey(cx, key, getReadVersion(), info); @@ -2240,6 +2296,7 @@ Future< Standalone > Transaction::getRange( bool reverse ) { ++cx->transactionLogicalReads; + ++cx->transactionGetRangeRequests; if( limits.isReached() ) return Standalone(); @@ -2316,7 +2373,7 @@ void Transaction::makeSelfConflicting() { } void Transaction::set( const KeyRef& key, const ValueRef& value, bool addConflictRange ) { - + ++cx->transactionSetMutations; if(key.size() > (key.startsWith(systemKeys.begin) ? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT : CLIENT_KNOBS->KEY_SIZE_LIMIT)) throw key_too_large(); if(value.size() > CLIENT_KNOBS->VALUE_SIZE_LIMIT) @@ -2334,6 +2391,7 @@ void Transaction::set( const KeyRef& key, const ValueRef& value, bool addConflic } void Transaction::atomicOp(const KeyRef& key, const ValueRef& operand, MutationRef::Type operationType, bool addConflictRange) { + ++cx->transactionAtomicMutations; if(key.size() > (key.startsWith(systemKeys.begin) ? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT : CLIENT_KNOBS->KEY_SIZE_LIMIT)) throw key_too_large(); if(operand.size() > CLIENT_KNOBS->VALUE_SIZE_LIMIT) @@ -2360,6 +2418,7 @@ void Transaction::atomicOp(const KeyRef& key, const ValueRef& operand, MutationR } void Transaction::clear( const KeyRangeRef& range, bool addConflictRange ) { + ++cx->transactionClearMutations; auto &req = tr; auto &t = req.transaction; @@ -2382,7 +2441,7 @@ void Transaction::clear( const KeyRangeRef& range, bool addConflictRange ) { t.write_conflict_ranges.push_back( req.arena, r ); } void Transaction::clear( const KeyRef& key, bool addConflictRange ) { - + ++cx->transactionClearMutations; //There aren't any keys in the database with size larger than KEY_SIZE_LIMIT if(key.size() > (key.startsWith(systemKeys.begin) ? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT : CLIENT_KNOBS->KEY_SIZE_LIMIT)) return; @@ -3017,6 +3076,7 @@ void Transaction::setOption( FDBTransactionOptions::Option option, Optional getConsistentReadVersion( DatabaseContext *cx, uint32_t transactionCount, uint32_t flags, Optional debugID ) { try { + ++cx->transactionReadVersionBatches; if( debugID.present() ) g_traceBatch.addEvent("TransactionDebug", debugID.get().first(), "NativeAPI.getConsistentReadVersion.Before"); loop { @@ -3107,6 +3167,20 @@ ACTOR Future extractReadVersion(DatabaseContext* cx, uint32_t flags, Re if(rep.locked && !lockAware) throw database_locked(); + ++cx->transactionReadVersionsCompleted; + if((flags & GetReadVersionRequest::PRIORITY_SYSTEM_IMMEDIATE) == GetReadVersionRequest::PRIORITY_SYSTEM_IMMEDIATE) { + ++cx->transactionImmediateReadVersionsCompleted; + } + else if((flags & GetReadVersionRequest::PRIORITY_DEFAULT) == GetReadVersionRequest::PRIORITY_DEFAULT) { + ++cx->transactionDefaultReadVersionsCompleted; + } + else if((flags & GetReadVersionRequest::PRIORITY_BATCH) == GetReadVersionRequest::PRIORITY_BATCH) { + ++cx->transactionBatchReadVersionsCompleted; + } + else { + ASSERT(false); + } + if(rep.version > cx->metadataVersionCache[cx->mvCacheInsertLocation].first) { cx->mvCacheInsertLocation = (cx->mvCacheInsertLocation + 1)%cx->metadataVersionCache.size(); cx->metadataVersionCache[cx->mvCacheInsertLocation] = std::make_pair(rep.version, rep.metadataVersion); @@ -3120,6 +3194,18 @@ Future Transaction::getReadVersion(uint32_t flags) { if (!readVersion.isValid()) { ++cx->transactionReadVersions; flags |= options.getReadVersionFlags; + if((flags & GetReadVersionRequest::PRIORITY_SYSTEM_IMMEDIATE) == GetReadVersionRequest::PRIORITY_SYSTEM_IMMEDIATE) { + ++cx->transactionImmediateReadVersions; + } + else if((flags & GetReadVersionRequest::PRIORITY_DEFAULT) == GetReadVersionRequest::PRIORITY_DEFAULT) { + ++cx->transactionDefaultReadVersions; + } + else if((flags & GetReadVersionRequest::PRIORITY_BATCH) == GetReadVersionRequest::PRIORITY_BATCH) { + ++cx->transactionBatchReadVersions; + } + else { + ASSERT(false); + } auto& batcher = cx->versionBatcher[ flags ]; if (!batcher.actor.isValid()) { From fd8d569b912e1e8bdece41b17c6920ebaa99de7c Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Thu, 5 Mar 2020 14:42:07 -0800 Subject: [PATCH 0838/1604] Fix a few typos. --- fdbclient/NativeAPI.actor.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index caea3af384..dfe6b4f9ed 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -535,8 +535,8 @@ DatabaseContext::DatabaseContext( lockAware(lockAware), apiVersion(apiVersion), switchable(switchable), provisional(false), cc("TransactionMetrics"), transactionReadVersions("ReadVersions", cc), transactionReadVersionsCompleted("ReadVersionsCompleted", cc), transactionReadVersionBatches("ReadVersionBatches", cc), transactionBatchReadVersions("BatchPriorityReadVersions", cc), transactionDefaultReadVersions("DefaultPriorityReadVersions", cc), transactionImmediateReadVersions("ImmediatePriorityReadVersions", cc), - transactionBatchReadVersionsCompleted("BatchPriorityReadVersionsComplete", cc), transactionDefaultReadVersionsCompleted("DefaultPriorityReadVersionsComplete", cc), - transactionImmediateReadVersionsCompleted("ImmediatePriroityReadVersionsComplete", cc), transactionLogicalReads("LogicalUncachedReads", cc), transactionPhysicalReads("PhysicalReadRequests", cc), + transactionBatchReadVersionsCompleted("BatchPriorityReadVersionsCompleted", cc), transactionDefaultReadVersionsCompleted("DefaultPriorityReadVersionsCompleted", cc), + transactionImmediateReadVersionsCompleted("ImmediatePriorityReadVersionsCompleted", cc), transactionLogicalReads("LogicalUncachedReads", cc), transactionPhysicalReads("PhysicalReadRequests", cc), transactionPhysicalReadsCompleted("PhysicalReadRequestsCompleted", cc), transactionGetKeyRequests("GetKeyRequests", cc), transactionGetValueRequests("GetValueRequests", cc), transactionGetRangeRequests("GetRangeRequests", cc), transactionWatchRequests("WatchRequests", cc), transactionGetAddressesForKeyRequests("GetAddressesForKeyRequests", cc), transactionBytesRead("BytesRead", cc), transactionKeysRead("KeysRead", cc), transactionMetadataVersionReads("MetadataVersionReads", cc), transactionCommittedMutations("CommittedMutations", cc), @@ -570,8 +570,8 @@ DatabaseContext::DatabaseContext( DatabaseContext::DatabaseContext( const Error &err ) : deferredError(err), cc("TransactionMetrics"), transactionReadVersions("ReadVersions", cc), transactionReadVersionsCompleted("ReadVersionsCompleted", cc), transactionReadVersionBatches("ReadVersionBatches", cc), transactionBatchReadVersions("BatchPriorityReadVersions", cc), transactionDefaultReadVersions("DefaultPriorityReadVersions", cc), transactionImmediateReadVersions("ImmediatePriorityReadVersions", cc), - transactionBatchReadVersionsCompleted("BatchPriorityReadVersionsComplete", cc), transactionDefaultReadVersionsCompleted("DefaultPriorityReadVersionsComplete", cc), - transactionImmediateReadVersionsCompleted("ImmediatePriroityReadVersionsComplete", cc), transactionLogicalReads("LogicalUncachedReads", cc), transactionPhysicalReads("PhysicalReadRequests", cc), + transactionBatchReadVersionsCompleted("BatchPriorityReadVersionsCompleted", cc), transactionDefaultReadVersionsCompleted("DefaultPriorityReadVersionsCompleted", cc), + transactionImmediateReadVersionsCompleted("ImmediatePriorityReadVersionsCompleted", cc), transactionLogicalReads("LogicalUncachedReads", cc), transactionPhysicalReads("PhysicalReadRequests", cc), transactionPhysicalReadsCompleted("PhysicalReadRequestsCompleted", cc), transactionGetKeyRequests("GetKeyRequests", cc), transactionGetValueRequests("GetValueRequests", cc), transactionGetRangeRequests("GetRangeRequests", cc), transactionWatchRequests("WatchRequests", cc), transactionGetAddressesForKeyRequests("GetAddressesForKeyRequests", cc), transactionBytesRead("BytesRead", cc), transactionKeysRead("KeysRead", cc), transactionMetadataVersionReads("MetadataVersionReads", cc), transactionCommittedMutations("CommittedMutations", cc), From 2d95a1e64dd0d6b086f895f35f76d7c42d55db9f Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Thu, 5 Mar 2020 17:25:33 -0800 Subject: [PATCH 0839/1604] Implement certificate refreshing --- flow/Net2.actor.cpp | 231 +++++++++++++++---------------------- flow/TLSConfig.actor.h | 11 +- flow/genericactors.actor.h | 11 +- 3 files changed, 107 insertions(+), 146 deletions(-) diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index 1780279d3f..ed0b10c118 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -38,6 +38,10 @@ #include "flow/Profiler.h" #include "flow/ProtocolVersion.h" #include "flow/TLSConfig.actor.h" +#include "flow/genericactors.actor.h" + +// See the comment in TLSConfig.actor.h for the explanation of why this module breaking include was done. +#include "fdbrpc/IAsyncFile.h" #ifdef WIN32 #include @@ -157,16 +161,12 @@ public: ASIOReactor reactor; #ifndef TLS_DISABLED - boost::asio::ssl::context sslContext; + AsyncVar>> sslContextVar; #endif TLSConfig tlsConfig; - std::string tlsPassword; + Future backgroundCertRefresh; bool tlsInitialized; - std::string get_password() const { - return tlsPassword; - } - INetworkConnections *network; // initially this, but can be changed int64_t tsc_begin, tsc_end; @@ -505,13 +505,13 @@ public: closeSocket(); } - explicit SSLConnection( boost::asio::io_service& io_service, boost::asio::ssl::context& context ) - : id(nondeterministicRandom()->randomUniqueID()), socket(io_service), ssl_sock(socket, context) + explicit SSLConnection( boost::asio::io_service& io_service, Reference> context ) + : id(nondeterministicRandom()->randomUniqueID()), socket(io_service), ssl_sock(socket, context->mutate()), sslContext(context) { } // This is not part of the IConnection interface, because it is wrapped by INetwork::connect() - ACTOR static Future> connect( boost::asio::io_service* ios, boost::asio::ssl::context* context, NetworkAddress addr ) { + ACTOR static Future> connect( boost::asio::io_service* ios, Reference> context, NetworkAddress addr ) { std::pair peerIP = std::make_pair(addr.ip, addr.port); auto iter(g_network->networkInfo.serverTLSConnectionThrottler.find(peerIP)); if(iter != g_network->networkInfo.serverTLSConnectionThrottler.end()) { @@ -526,7 +526,7 @@ public: } } - state Reference self( new SSLConnection(*ios, *context) ); + state Reference self( new SSLConnection(*ios, context) ); self->peer_address = addr; try { @@ -729,6 +729,7 @@ private: tcp::socket socket; ssl_socket ssl_sock; NetworkAddress peer_address; + Reference> sslContext; struct SendBufferIterator { typedef boost::asio::const_buffer value_type; @@ -789,11 +790,11 @@ class SSLListener : public IListener, ReferenceCounted { boost::asio::io_context& io_service; NetworkAddress listenAddress; tcp::acceptor acceptor; - boost::asio::ssl::context* context; + AsyncVar>> *contextVar; public: - SSLListener( boost::asio::io_context& io_service, boost::asio::ssl::context* context, NetworkAddress listenAddress ) - : io_service(io_service), listenAddress(listenAddress), acceptor( io_service, tcpEndpoint( listenAddress ) ), context(context) + SSLListener( boost::asio::io_context& io_service, AsyncVar>>* contextVar, NetworkAddress listenAddress ) + : io_service(io_service), listenAddress(listenAddress), acceptor( io_service, tcpEndpoint( listenAddress ) ), contextVar(contextVar) { platform::setCloseOnExec(acceptor.native_handle()); } @@ -810,7 +811,7 @@ public: private: ACTOR static Future> doAccept( SSLListener* self ) { - state Reference conn( new SSLConnection( self->io_service, *self->context) ); + state Reference conn( new SSLConnection( self->io_service, self->contextVar->get() ) ); state tcp::acceptor::endpoint_type peer_endpoint; try { BindPromise p("N2_AcceptError", UID()); @@ -862,7 +863,7 @@ Net2::Net2(const TLSConfig& tlsConfig, bool useThreadPool, bool useMetrics) tlsInitialized(false), tlsConfig(tlsConfig) #ifndef TLS_DISABLED - ,sslContext(boost::asio::ssl::context(boost::asio::ssl::context::tls)) + ,sslContextVar({ReferencedObject::from(boost::asio::ssl::context(boost::asio::ssl::context::tls))}) #endif { @@ -889,103 +890,92 @@ Net2::Net2(const TLSConfig& tlsConfig, bool useThreadPool, bool useMetrics) } -/* -ACTOR static Future watchFileForChanges( std::string filename, AsyncVar> *contents_var ) { +void ConfigureSSLContext( const LoadedTLSConfig& loaded, boost::asio::ssl::context* context ) { + context->set_options(boost::asio::ssl::context::default_workarounds); + context->set_verify_mode(boost::asio::ssl::context::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert); + + if (loaded.isTLSEnabled()) { + Reference tlsPolicy = Reference(new TLSPolicy(loaded.getEndpointType())); + tlsPolicy->set_verify_peers({ loaded.getVerifyPeers() }); + + context->set_verify_callback([policy=tlsPolicy](bool preverified, boost::asio::ssl::verify_context& ctx) { + return policy->verify_peer(preverified, ctx.native_handle()); + }); + } else { + context->set_verify_callback(boost::bind(&insecurely_always_accept, _1, _2)); + } + + context->set_password_callback( + [password=loaded.getPassword()](size_t, boost::asio::ssl::context::password_purpose) { + return password; + }); + + const std::string& certBytes = loaded.getCertificateBytes(); + if ( certBytes.size() ) { + context->use_certificate_chain(boost::asio::buffer(certBytes.data(), certBytes.size())); + } + + const std::string& CABytes = loaded.getCABytes(); + if ( CABytes.size() ) { + context->add_certificate_authority(boost::asio::buffer(CABytes.data(), CABytes.size())); + } + + const std::string& keyBytes = loaded.getKeyBytes(); + if (keyBytes.size()) { + context->use_private_key(boost::asio::buffer(keyBytes.data(), keyBytes.size()), boost::asio::ssl::context::pem); + } +} + +ACTOR static Future watchFileForChanges( std::string filename, AsyncTrigger* fileChanged ) { + if (filename == "") { + return Never(); + } state std::time_t lastModTime = wait(IAsyncFileSystem::filesystem()->lastWriteTime(filename)); loop { wait(delay(FLOW_KNOBS->TLS_CERT_REFRESH_DELAY_SECONDS)); std::time_t modtime = wait(IAsyncFileSystem::filesystem()->lastWriteTime(filename)); if (lastModTime != modtime) { lastModTime = modtime; - ErrorOr> contents = wait(readEntireFile(filename)); - if (contents.present()) { - contents_var->set(contents.get()); - } + fileChanged->trigger(); } } } -ACTOR static Future reloadConfigurationOnChange( TLSOptions::PolicyInfo *pci, Reference plugin, AsyncVar> *realVerifyPeersPolicy, AsyncVar> *realNoVerifyPeersPolicy ) { - if (FLOW_KNOBS->TLS_CERT_REFRESH_DELAY_SECONDS <= 0) { - return Void(); - return Void(); - } - loop { - // Early in bootup, the filesystem might not be initialized yet. Wait until it is. - if (IAsyncFileSystem::filesystem() != nullptr) { - break; - } - wait(delay(1.0)); - } - state int mismatches = 0; - state AsyncVar> ca_var; - state AsyncVar> key_var; - state AsyncVar> cert_var; - state std::vector> lifetimes; - if (!pci->ca_path.empty()) lifetimes.push_back(watchFileForChanges(pci->ca_path, &ca_var)); - if (!pci->key_path.empty()) lifetimes.push_back(watchFileForChanges(pci->key_path, &key_var)); - if (!pci->cert_path.empty()) lifetimes.push_back(watchFileForChanges(pci->cert_path, &cert_var)); - loop { - state Future ca_changed = ca_var.onChange(); - state Future key_changed = key_var.onChange(); - state Future cert_changed = cert_var.onChange(); - wait( ca_changed || key_changed || cert_changed ); - if (ca_changed.isReady()) { - TraceEvent(SevInfo, "TLSRefreshCAChanged").detail("path", pci->ca_path).detail("length", ca_var.get().size()); - pci->ca_contents = ca_var.get(); - } - if (key_changed.isReady()) { - TraceEvent(SevInfo, "TLSRefreshKeyChanged").detail("path", pci->key_path).detail("length", key_var.get().size()); - pci->key_contents = key_var.get(); - } - if (cert_changed.isReady()) { - TraceEvent(SevInfo, "TLSRefreshCertChanged").detail("path", pci->cert_path).detail("length", cert_var.get().size()); - pci->cert_contents = cert_var.get(); - } - bool rc = true; - Reference verifypeers = Reference(plugin->create_policy()); - Reference noverifypeers = Reference(plugin->create_policy()); - loop { - // Don't actually loop. We're just using loop/break as a `goto err`. - // This loop always ends with an unconditional break. - rc = verifypeers->set_ca_data(pci->ca_contents.begin(), pci->ca_contents.size()); - if (!rc) break; - rc = verifypeers->set_key_data(pci->key_contents.begin(), pci->key_contents.size(), pci->keyPassword.c_str()); - if (!rc) break; - rc = verifypeers->set_cert_data(pci->cert_contents.begin(), pci->cert_contents.size()); - if (!rc) break; - { - std::unique_ptr verify_peers_arr(new const uint8_t*[pci->verify_peers.size()]); - std::unique_ptr verify_peers_len(new int[pci->verify_peers.size()]); - for (int i = 0; i < pci->verify_peers.size(); i++) { - verify_peers_arr[i] = (const uint8_t *)&pci->verify_peers[i][0]; - verify_peers_len[i] = pci->verify_peers[i].size(); - } - rc = verifypeers->set_verify_peers(pci->verify_peers.size(), verify_peers_arr.get(), verify_peers_len.get()); - if (!rc) break; - } - rc = noverifypeers->set_ca_data(pci->ca_contents.begin(), pci->ca_contents.size()); - if (!rc) break; - rc = noverifypeers->set_key_data(pci->key_contents.begin(), pci->key_contents.size(), pci->keyPassword.c_str()); - if (!rc) break; - rc = noverifypeers->set_cert_data(pci->cert_contents.begin(), pci->cert_contents.size()); - if (!rc) break; - break; - } +ACTOR static Future reloadCertificatesOnChange( TLSConfig config, AsyncVar>>* contextVar ) { + if (FLOW_KNOBS->TLS_CERT_REFRESH_DELAY_SECONDS <= 0) { + return Void(); + } + loop { + // Early in bootup, the filesystem might not be initialized yet. Wait until it is. + if (IAsyncFileSystem::filesystem() != nullptr) { + break; + } + wait(delay(1.0)); + } + state int mismatches = 0; + state AsyncTrigger fileChanged; + state std::vector> lifetimes; + lifetimes.push_back(watchFileForChanges(config.getCertificatePathSync(), &fileChanged)); + lifetimes.push_back(watchFileForChanges(config.getKeyPathSync(), &fileChanged)); + lifetimes.push_back(watchFileForChanges(config.getCAPathSync(), &fileChanged)); + loop { + wait( fileChanged.onTrigger() ); + TraceEvent("TLSCertificateRefreshBegin"); - if (rc) { - TraceEvent(SevInfo, "TLSCertificateRefreshSucceeded"); - realVerifyPeersPolicy->set(verifypeers); - realNoVerifyPeersPolicy->set(noverifypeers); - mismatches = 0; - } else { - // Some files didn't match up, they should in the future, and we'll retry then. - mismatches++; - TraceEvent(SevWarn, "TLSCertificateRefreshMismatch").detail("mismatches", mismatches); - } - } + try { + LoadedTLSConfig loaded = wait( config.loadAsync() ); + boost::asio::ssl::context context(boost::asio::ssl::context::tls); + ConfigureSSLContext(loaded, &context); + TraceEvent(SevInfo, "TLSCertificateRefreshSucceeded"); + mismatches = 0; + contextVar->set(ReferencedObject::from(std::move(context))); + } catch (Error &e) { + // Some files didn't match up, they should in the future, and we'll retry then. + mismatches++; + TraceEvent(SevWarn, "TLSCertificateRefreshMismatch").detail("mismatches", mismatches); + } + } } -*/ void Net2::initTLS() { if(tlsInitialized) { @@ -993,39 +983,10 @@ void Net2::initTLS() { } #ifndef TLS_DISABLED try { - LoadedTLSConfig loaded = tlsConfig.loadSync(); - - sslContext.set_options(boost::asio::ssl::context::default_workarounds); - sslContext.set_verify_mode(boost::asio::ssl::context::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert); - - if (loaded.isTLSEnabled()) { - Reference tlsPolicy = Reference(new TLSPolicy(loaded.getEndpointType())); - tlsPolicy->set_verify_peers({ loaded.getVerifyPeers() }); - - sslContext.set_verify_callback([policy=tlsPolicy](bool preverified, boost::asio::ssl::verify_context& ctx) { - return policy->verify_peer(preverified, ctx.native_handle()); - }); - } else { - sslContext.set_verify_callback(boost::bind(&insecurely_always_accept, _1, _2)); - } - - tlsPassword = loaded.getPassword(); - sslContext.set_password_callback(std::bind(&Net2::get_password, this)); - - const std::string& certBytes = loaded.getCertificateBytes(); - if ( certBytes.size() ) { - sslContext.use_certificate_chain(boost::asio::buffer(certBytes.data(), certBytes.size())); - } - - const std::string& CABytes = loaded.getCABytes(); - if ( CABytes.size() ) { - sslContext.add_certificate_authority(boost::asio::buffer(CABytes.data(), CABytes.size())); - } - - const std::string& keyBytes = loaded.getKeyBytes(); - if (keyBytes.size()) { - sslContext.use_private_key(boost::asio::buffer(keyBytes.data(), keyBytes.size()), boost::asio::ssl::context::pem); - } + boost::asio::ssl::context newContext(boost::asio::ssl::context::tls); + ConfigureSSLContext( tlsConfig.loadSync(), &newContext ); + sslContextVar.set(ReferencedObject::from(std::move(newContext))); + backgroundCertRefresh = reloadCertificatesOnChange( tlsConfig, &sslContextVar ); } catch(boost::system::system_error e) { TraceEvent("Net2TLSInitError").detail("Message", e.what()); throw tls_error(); @@ -1392,7 +1353,7 @@ Future< Reference > Net2::connect( NetworkAddress toAddr, std::stri #ifndef TLS_DISABLED initTLS(); if ( toAddr.isTLS() ) { - return SSLConnection::connect(&this->reactor.ios, &this->sslContext, toAddr); + return SSLConnection::connect(&this->reactor.ios, this->sslContextVar.get(), toAddr); } #endif @@ -1472,7 +1433,7 @@ Reference Net2::listen( NetworkAddress localAddr ) { #ifndef TLS_DISABLED initTLS(); if ( localAddr.isTLS() ) { - return Reference(new SSLListener( reactor.ios, &this->sslContext, localAddr )); + return Reference(new SSLListener( reactor.ios, &this->sslContextVar, localAddr )); } #endif return Reference( new Listener( reactor.ios, localAddr ) ); diff --git a/flow/TLSConfig.actor.h b/flow/TLSConfig.actor.h index e750c3de93..667a6f2822 100644 --- a/flow/TLSConfig.actor.h +++ b/flow/TLSConfig.actor.h @@ -194,7 +194,6 @@ public: return loadAsync(this); } -PRIVATE_EXCEPT_FOR_TLSCONFIG_CPP: // Return the explicitly set path. // If one was not set, return the path from the environment. // (Cert and Key only) If neither exist, check for fdb.pem in cwd @@ -206,6 +205,7 @@ PRIVATE_EXCEPT_FOR_TLSCONFIG_CPP: std::string getKeyPathSync() const; std::string getCAPathSync() const; +PRIVATE_EXCEPT_FOR_TLSCONFIG_CPP: ACTOR static Future loadAsync(const TLSConfig* self); template friend class LoadAsyncActorState; @@ -217,15 +217,6 @@ PRIVATE_EXCEPT_FOR_TLSCONFIG_CPP: TLSEndpointType endpointType = TLSEndpointType::UNSET; }; -namespace boost { - namespace asio { - namespace ssl { - struct context; - } - } -} -void ConfigureSSLContext( boost::asio::ssl::context *context, const LoadedTLSConfig& config ); - class TLSPolicy : ReferenceCounted { public: diff --git a/flow/genericactors.actor.h b/flow/genericactors.actor.h index ae022b9ef7..14db6428ab 100644 --- a/flow/genericactors.actor.h +++ b/flow/genericactors.actor.h @@ -652,6 +652,7 @@ class ReferencedObject : NonCopyable, public ReferenceCounted> from(V const& v) { return Reference>(new ReferencedObject(v)); } + static Reference> from(V&& v) { + return Reference>(new ReferencedObject(std::move(v))); + } + private: V value; }; From ccef3f7d05cb9c250652b8f6c75f98d18ed87e02 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Thu, 5 Mar 2020 17:32:10 -0800 Subject: [PATCH 0840/1604] Attempt to fix TLS_DISABLED compiles. --- flow/Net2.actor.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index ed0b10c118..2080b7a336 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -890,6 +890,7 @@ Net2::Net2(const TLSConfig& tlsConfig, bool useThreadPool, bool useMetrics) } +#ifndef TLS_DISABLED void ConfigureSSLContext( const LoadedTLSConfig& loaded, boost::asio::ssl::context* context ) { context->set_options(boost::asio::ssl::context::default_workarounds); context->set_verify_mode(boost::asio::ssl::context::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert); @@ -976,6 +977,7 @@ ACTOR static Future reloadCertificatesOnChange( TLSConfig config, AsyncVar } } } +#endif void Net2::initTLS() { if(tlsInitialized) { From 112866684004ad4545aef8932955ef4d4b4ab252 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Thu, 5 Mar 2020 18:17:06 -0800 Subject: [PATCH 0841/1604] added additional logging on the log router --- fdbserver/DataDistribution.actor.cpp | 6 +++--- fdbserver/LogRouter.actor.cpp | 10 ++++++++-- fdbserver/LogSystem.h | 7 +++++++ fdbserver/LogSystemPeekCursor.actor.cpp | 24 ++++++++++++++++++++++++ fdbserver/Ratekeeper.actor.cpp | 2 +- fdbserver/worker.actor.cpp | 4 ++-- flow/Stats.actor.cpp | 2 +- flow/SystemMonitor.cpp | 2 +- flow/Trace.cpp | 2 +- flow/Trace.h | 12 +++++++++++- 10 files changed, 59 insertions(+), 12 deletions(-) diff --git a/fdbserver/DataDistribution.actor.cpp b/fdbserver/DataDistribution.actor.cpp index 11458b5750..035dfab078 100644 --- a/fdbserver/DataDistribution.actor.cpp +++ b/fdbserver/DataDistribution.actor.cpp @@ -3584,7 +3584,7 @@ ACTOR Future monitorStorageServerRecruitment(DDTeamCollection* self) { state bool recruiting = false; TraceEvent("StorageServerRecruitment", self->distributorId) .detail("State", "Idle") - .trackLatest(("StorageServerRecruitment_" + self->distributorId.toString()).c_str()); + .trackLatest("StorageServerRecruitment_" + self->distributorId.toString()); loop { if( !recruiting ) { while(self->recruitingStream.get() == 0) { @@ -3592,7 +3592,7 @@ ACTOR Future monitorStorageServerRecruitment(DDTeamCollection* self) { } TraceEvent("StorageServerRecruitment", self->distributorId) .detail("State", "Recruiting") - .trackLatest(("StorageServerRecruitment_" + self->distributorId.toString()).c_str()); + .trackLatest("StorageServerRecruitment_" + self->distributorId.toString()); recruiting = true; } else { loop { @@ -3603,7 +3603,7 @@ ACTOR Future monitorStorageServerRecruitment(DDTeamCollection* self) { } TraceEvent("StorageServerRecruitment", self->distributorId) .detail("State", "Idle") - .trackLatest(("StorageServerRecruitment_" + self->distributorId.toString()).c_str()); + .trackLatest("StorageServerRecruitment_" + self->distributorId.toString()); recruiting = false; } } diff --git a/fdbserver/LogRouter.actor.cpp b/fdbserver/LogRouter.actor.cpp index 46686ef677..c84b7e8956 100644 --- a/fdbserver/LogRouter.actor.cpp +++ b/fdbserver/LogRouter.actor.cpp @@ -95,6 +95,7 @@ struct LogRouterData { CounterCollection cc; Future logger; + Reference eventCacheHolder; std::vector> tag_data; //we only store data for the remote tag locality @@ -130,8 +131,11 @@ struct LogRouterData { } } + eventCacheHolder = Reference( new EventCacheHolder(dbgid.shortString() + ".PeekLocation") ); + specialCounter(cc, "Version", [this](){return this->version.get(); }); specialCounter(cc, "MinPopped", [this](){return this->minPopped.get(); }); + specialCounter(cc, "FetchedVersions", [this](){ return std::max(0, std::min(SERVER_KNOBS->MAX_READ_TRANSACTION_LIFE_VERSIONS, this->version.get() - this->minPopped.get())); }); specialCounter(cc, "MinKnownCommittedVersion", [this](){ return this->minKnownCommittedVersion; }); specialCounter(cc, "PoppedVersion", [this](){ return this->poppedVersion; }); logger = traceCounters("LogRouterMetrics", dbgid, SERVER_KNOBS->WORKER_LOGGING_INTERVAL, &cc, "LogRouterMetrics"); @@ -232,10 +236,12 @@ ACTOR Future pullAsyncData( LogRouterData *self ) { } when( wait( dbInfoChange ) ) { //FIXME: does this actually happen? if(r) tagPopped = std::max(tagPopped, r->popped()); - if( self->logSystem->get() ) + if( self->logSystem->get() ) { r = self->logSystem->get()->peekLogRouter( self->dbgid, tagAt, self->routerTag ); - else + TraceEvent("LogRouterPeekLocation", self->dbgid).detail("LogID", r->getPrimaryPeekLocation()).trackLatest(self->eventCacheHolder->trackingKey); + } else { r = Reference(); + } dbInfoChange = self->logSystem->onChange(); } } diff --git a/fdbserver/LogSystem.h b/fdbserver/LogSystem.h index 8a91172dd7..1cb52d6ba0 100644 --- a/fdbserver/LogSystem.h +++ b/fdbserver/LogSystem.h @@ -368,6 +368,8 @@ struct ILogSystem { virtual Version getMinKnownCommittedVersion() = 0; + virtual Optional getPrimaryPeekLocation() = 0; + virtual void addref() = 0; virtual void delref() = 0; @@ -414,6 +416,7 @@ struct ILogSystem { virtual const LogMessageVersion& version(); virtual Version popped(); virtual Version getMinKnownCommittedVersion(); + virtual Optional getPrimaryPeekLocation(); virtual void addref() { ReferenceCounted::addref(); @@ -463,6 +466,7 @@ struct ILogSystem { virtual const LogMessageVersion& version(); virtual Version popped(); virtual Version getMinKnownCommittedVersion(); + virtual Optional getPrimaryPeekLocation(); virtual void addref() { ReferenceCounted::addref(); @@ -509,6 +513,7 @@ struct ILogSystem { virtual const LogMessageVersion& version(); virtual Version popped(); virtual Version getMinKnownCommittedVersion(); + virtual Optional getPrimaryPeekLocation(); virtual void addref() { ReferenceCounted::addref(); @@ -543,6 +548,7 @@ struct ILogSystem { virtual const LogMessageVersion& version(); virtual Version popped(); virtual Version getMinKnownCommittedVersion(); + virtual Optional getPrimaryPeekLocation(); virtual void addref() { ReferenceCounted::addref(); @@ -614,6 +620,7 @@ struct ILogSystem { virtual const LogMessageVersion& version(); virtual Version popped(); virtual Version getMinKnownCommittedVersion(); + virtual Optional getPrimaryPeekLocation(); virtual void addref() { ReferenceCounted::addref(); diff --git a/fdbserver/LogSystemPeekCursor.actor.cpp b/fdbserver/LogSystemPeekCursor.actor.cpp index 3e38bf5e0d..7aa5d32e59 100644 --- a/fdbserver/LogSystemPeekCursor.actor.cpp +++ b/fdbserver/LogSystemPeekCursor.actor.cpp @@ -285,6 +285,8 @@ const LogMessageVersion& ILogSystem::ServerPeekCursor::version() { return messag Version ILogSystem::ServerPeekCursor::getMinKnownCommittedVersion() { return results.minKnownCommittedVersion; } +Optional ILogSystem::ServerPeekCursor::getPrimaryPeekLocation() { return interf->get().id(); } + Version ILogSystem::ServerPeekCursor::popped() { return poppedVersion; } ILogSystem::MergedPeekCursor::MergedPeekCursor( vector< Reference > const& serverCursors, Version begin ) @@ -515,6 +517,13 @@ Version ILogSystem::MergedPeekCursor::getMinKnownCommittedVersion() { return serverCursors[currentCursor]->getMinKnownCommittedVersion(); } +Optional ILogSystem::MergedPeekCursor::getPrimaryPeekLocation() { + if(bestServer >= 0) { + return serverCursors[bestServer]->getPrimaryPeekLocation(); + } + return Optional(); +} + Version ILogSystem::MergedPeekCursor::popped() { Version poppedVersion = 0; for (auto& c : serverCursors) @@ -818,6 +827,13 @@ Version ILogSystem::SetPeekCursor::getMinKnownCommittedVersion() { return serverCursors[currentSet][currentCursor]->getMinKnownCommittedVersion(); } +Optional ILogSystem::SetPeekCursor::getPrimaryPeekLocation() { + if(bestServer >= 0 && bestSet >= 0) { + return serverCursors[bestSet][bestServer]->getPrimaryPeekLocation(); + } + return Optional(); +} + Version ILogSystem::SetPeekCursor::popped() { Version poppedVersion = 0; for (auto& cursors : serverCursors) { @@ -912,6 +928,10 @@ Version ILogSystem::MultiCursor::getMinKnownCommittedVersion() { return cursors.back()->getMinKnownCommittedVersion(); } +Optional ILogSystem::MultiCursor::getPrimaryPeekLocation() { + return cursors.back()->getPrimaryPeekLocation(); +} + Version ILogSystem::MultiCursor::popped() { return std::max(poppedVersion, cursors.back()->popped()); } @@ -1153,6 +1173,10 @@ Version ILogSystem::BufferedCursor::getMinKnownCommittedVersion() { return minKnownCommittedVersion; } +Optional ILogSystem::BufferedCursor::getPrimaryPeekLocation() { + return Optional(); +} + Version ILogSystem::BufferedCursor::popped() { if(initialPoppedVersion == poppedVersion) { return 0; diff --git a/fdbserver/Ratekeeper.actor.cpp b/fdbserver/Ratekeeper.actor.cpp index a6de87e7a9..c696838cd0 100644 --- a/fdbserver/Ratekeeper.actor.cpp +++ b/fdbserver/Ratekeeper.actor.cpp @@ -684,7 +684,7 @@ void updateRate(RatekeeperData* self, RatekeeperLimits* limits) { .detail("LimitingStorageServerVersionLag", limitingVersionLag) .detail("WorstStorageServerDurabilityLag", worstDurabilityLag) .detail("LimitingStorageServerDurabilityLag", limitingDurabilityLag) - .trackLatest(name.c_str()); + .trackLatest(name); } } diff --git a/fdbserver/worker.actor.cpp b/fdbserver/worker.actor.cpp index dc5557c806..ce7cbc49be 100644 --- a/fdbserver/worker.actor.cpp +++ b/fdbserver/worker.actor.cpp @@ -631,7 +631,7 @@ void startRole(const Role &role, UID roleId, UID workerId, const std::mapfirst.c_str(), it->second); - ev.trackLatest( (roleId.shortString() + ".Role" ).c_str() ); + ev.trackLatest( roleId.shortString() + ".Role" ); // Update roles map, log Roles metrics g_roles.insert({role.roleName, roleId.shortString()}); @@ -649,7 +649,7 @@ void endRole(const Role &role, UID id, std::string reason, bool ok, Error e) { .detail("As", role.roleName) .detail("Reason", reason); - ev.trackLatest( (id.shortString() + ".Role").c_str() ); + ev.trackLatest( id.shortString() + ".Role" ); } if(!ok) { diff --git a/flow/Stats.actor.cpp b/flow/Stats.actor.cpp index 751130bc25..d621188277 100644 --- a/flow/Stats.actor.cpp +++ b/flow/Stats.actor.cpp @@ -91,7 +91,7 @@ ACTOR Future traceCounters(std::string traceEventName, UID traceEventID, d counters->logToTraceEvent(te); if (!trackLatestName.empty()) { - te.trackLatest(trackLatestName.c_str()); + te.trackLatest(trackLatestName); } last_interval = now(); diff --git a/flow/SystemMonitor.cpp b/flow/SystemMonitor.cpp index e523c9d6d1..cc525f098a 100644 --- a/flow/SystemMonitor.cpp +++ b/flow/SystemMonitor.cpp @@ -101,7 +101,7 @@ SystemStatistics customSystemMonitor(std::string eventName, StatisticsState *sta .detail("ConnectionsEstablished", (double) (netData.countConnEstablished - statState->networkState.countConnEstablished) / currentStats.elapsed) .detail("ConnectionsClosed", ((netData.countConnClosedWithError - statState->networkState.countConnClosedWithError) + (netData.countConnClosedWithoutError - statState->networkState.countConnClosedWithoutError)) / currentStats.elapsed) .detail("ConnectionErrors", (netData.countConnClosedWithError - statState->networkState.countConnClosedWithError) / currentStats.elapsed) - .trackLatest(eventName.c_str()); + .trackLatest(eventName); TraceEvent("MemoryMetrics") .DETAILALLOCATORMEMUSAGE(16) diff --git a/flow/Trace.cpp b/flow/Trace.cpp index 3a46381105..1b3f8199c0 100644 --- a/flow/Trace.cpp +++ b/flow/Trace.cpp @@ -880,7 +880,7 @@ TraceEvent& TraceEvent::detailfNoMetric( std::string&& key, const char* valueFor return *this; } -TraceEvent& TraceEvent::trackLatest( const char *trackingKey ){ +TraceEvent& TraceEvent::trackLatest(const std::string& trackingKey ){ ASSERT(!logged); this->trackingKey = trackingKey; ASSERT( this->trackingKey.size() != 0 && this->trackingKey[0] != '/' && this->trackingKey[0] != '\\'); diff --git a/flow/Trace.h b/flow/Trace.h index 7bc4415035..b0ab0a81aa 100644 --- a/flow/Trace.h +++ b/flow/Trace.h @@ -452,7 +452,7 @@ private: TraceEvent& detailImpl( std::string&& key, std::string&& value, bool writeEventMetricField=true ); public: TraceEvent& backtrace(const std::string& prefix = ""); - TraceEvent& trackLatest( const char* trackingKey ); + TraceEvent& trackLatest(const std::string& trackingKey ); TraceEvent& sample( double sampleRate, bool logSampleRate=true ); // Sets the maximum length a field can be before it gets truncated. A value of 0 uses the default, a negative value @@ -559,6 +559,16 @@ private: extern LatestEventCache latestEventCache; +struct EventCacheHolder : public ReferenceCounted { + std::string trackingKey; + + EventCacheHolder(const std::string& trackingKey) : trackingKey(trackingKey) {} + + ~EventCacheHolder() { + latestEventCache.clear(trackingKey); + } +}; + // Evil but potentially useful for verbose messages: #if CENABLED(0, NOT_IN_CLEAN) #define TRACE( t, m ) if (TraceEvent::isEnabled(t)) TraceEvent(t,m) From 39050308ffb91ec07de56d33fbd0463776275a61 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Thu, 5 Mar 2020 18:17:49 -0800 Subject: [PATCH 0842/1604] lower accept batch size just to be conservative with the change --- flow/Knobs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/Knobs.cpp b/flow/Knobs.cpp index 751a8cd05b..276857a966 100644 --- a/flow/Knobs.cpp +++ b/flow/Knobs.cpp @@ -67,7 +67,7 @@ FlowKnobs::FlowKnobs(bool randomize, bool isSimulated) { init( MAX_RECONNECTION_TIME, 0.5 ); init( RECONNECTION_TIME_GROWTH_RATE, 1.2 ); init( RECONNECTION_RESET_TIME, 5.0 ); - init( ACCEPT_BATCH_SIZE, 20 ); + init( ACCEPT_BATCH_SIZE, 10 ); init( USE_OBJECT_SERIALIZER, 1 ); init( TOO_MANY_CONNECTIONS_CLOSED_RESET_DELAY, 5.0 ); init( TOO_MANY_CONNECTIONS_CLOSED_TIMEOUT, 20.0 ); From 1076abdee5f19b49c71730b2bbb8f2aa80bcd9da Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Thu, 5 Mar 2020 19:09:08 -0800 Subject: [PATCH 0843/1604] fixed crash when interf was not created --- fdbserver/LogSystemPeekCursor.actor.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fdbserver/LogSystemPeekCursor.actor.cpp b/fdbserver/LogSystemPeekCursor.actor.cpp index 7aa5d32e59..c99dd8b492 100644 --- a/fdbserver/LogSystemPeekCursor.actor.cpp +++ b/fdbserver/LogSystemPeekCursor.actor.cpp @@ -285,7 +285,12 @@ const LogMessageVersion& ILogSystem::ServerPeekCursor::version() { return messag Version ILogSystem::ServerPeekCursor::getMinKnownCommittedVersion() { return results.minKnownCommittedVersion; } -Optional ILogSystem::ServerPeekCursor::getPrimaryPeekLocation() { return interf->get().id(); } +Optional ILogSystem::ServerPeekCursor::getPrimaryPeekLocation() { + if(interf) { + return interf->get().id(); + } + return Optional(); +} Version ILogSystem::ServerPeekCursor::popped() { return poppedVersion; } From ac52b6b4741ffb1a800e46258356cb8830ed2911 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Fri, 6 Mar 2020 02:33:16 -0800 Subject: [PATCH 0844/1604] Rework a bit of error and exception handling. I went back and dug through all of the "what functions can throw what types", and made sane decisions about them. boost errors are aggressively translated into FDB ones, whcih might result in multiple lines of logging about errors, but this is in infrequently run code, so it should be fine. --- flow/Net2.actor.cpp | 92 +++++++++++++++++++++++----------------- flow/TLSConfig.actor.cpp | 12 +----- 2 files changed, 54 insertions(+), 50 deletions(-) diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index 2080b7a336..89f68e3e50 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -892,38 +892,43 @@ Net2::Net2(const TLSConfig& tlsConfig, bool useThreadPool, bool useMetrics) #ifndef TLS_DISABLED void ConfigureSSLContext( const LoadedTLSConfig& loaded, boost::asio::ssl::context* context ) { - context->set_options(boost::asio::ssl::context::default_workarounds); - context->set_verify_mode(boost::asio::ssl::context::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert); + try { + context->set_options(boost::asio::ssl::context::default_workarounds); + context->set_verify_mode(boost::asio::ssl::context::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert); - if (loaded.isTLSEnabled()) { - Reference tlsPolicy = Reference(new TLSPolicy(loaded.getEndpointType())); - tlsPolicy->set_verify_peers({ loaded.getVerifyPeers() }); + if (loaded.isTLSEnabled()) { + Reference tlsPolicy = Reference(new TLSPolicy(loaded.getEndpointType())); + tlsPolicy->set_verify_peers({ loaded.getVerifyPeers() }); - context->set_verify_callback([policy=tlsPolicy](bool preverified, boost::asio::ssl::verify_context& ctx) { - return policy->verify_peer(preverified, ctx.native_handle()); + context->set_verify_callback([policy=tlsPolicy](bool preverified, boost::asio::ssl::verify_context& ctx) { + return policy->verify_peer(preverified, ctx.native_handle()); + }); + } else { + context->set_verify_callback(boost::bind(&insecurely_always_accept, _1, _2)); + } + + context->set_password_callback( + [password=loaded.getPassword()](size_t, boost::asio::ssl::context::password_purpose) { + return password; }); - } else { - context->set_verify_callback(boost::bind(&insecurely_always_accept, _1, _2)); - } - context->set_password_callback( - [password=loaded.getPassword()](size_t, boost::asio::ssl::context::password_purpose) { - return password; - }); + const std::string& certBytes = loaded.getCertificateBytes(); + if ( certBytes.size() ) { + context->use_certificate_chain(boost::asio::buffer(certBytes.data(), certBytes.size())); + } - const std::string& certBytes = loaded.getCertificateBytes(); - if ( certBytes.size() ) { - context->use_certificate_chain(boost::asio::buffer(certBytes.data(), certBytes.size())); - } + const std::string& CABytes = loaded.getCABytes(); + if ( CABytes.size() ) { + context->add_certificate_authority(boost::asio::buffer(CABytes.data(), CABytes.size())); + } - const std::string& CABytes = loaded.getCABytes(); - if ( CABytes.size() ) { - context->add_certificate_authority(boost::asio::buffer(CABytes.data(), CABytes.size())); - } - - const std::string& keyBytes = loaded.getKeyBytes(); - if (keyBytes.size()) { - context->use_private_key(boost::asio::buffer(keyBytes.data(), keyBytes.size()), boost::asio::ssl::context::pem); + const std::string& keyBytes = loaded.getKeyBytes(); + if (keyBytes.size()) { + context->use_private_key(boost::asio::buffer(keyBytes.data(), keyBytes.size()), boost::asio::ssl::context::pem); + } + } catch (boost::system::system_error& e) { + TraceEvent("TLSConfigureError").detail("What", e.what()).detail("Value", e.code().value()).detail("WhichMeans", TLSPolicy::ErrorString(e.code())); + throw tls_error(); } } @@ -934,10 +939,22 @@ ACTOR static Future watchFileForChanges( std::string filename, AsyncTrigge state std::time_t lastModTime = wait(IAsyncFileSystem::filesystem()->lastWriteTime(filename)); loop { wait(delay(FLOW_KNOBS->TLS_CERT_REFRESH_DELAY_SECONDS)); - std::time_t modtime = wait(IAsyncFileSystem::filesystem()->lastWriteTime(filename)); - if (lastModTime != modtime) { - lastModTime = modtime; - fileChanged->trigger(); + try { + std::time_t modtime = wait(IAsyncFileSystem::filesystem()->lastWriteTime(filename)); + if (lastModTime != modtime) { + lastModTime = modtime; + fileChanged->trigger(); + } + } catch (Error& e) { + if (e.code() == error_code_io_error) { + // EACCES, ELOOP, ENOENT all come out as io_error(), but are more of a system + // configuration issue than an FDB problem. If we managed to load valid + // certificates, then there's no point in crashing, but we should complain + // loudly. IAsyncFile will log the error, but not necessarily as a warning. + TraceEvent(SevWarnAlways, "TLSCertificateRefreshStatError").detail("File", filename); + } else { + throw; + } } } } @@ -973,7 +990,7 @@ ACTOR static Future reloadCertificatesOnChange( TLSConfig config, AsyncVar } catch (Error &e) { // Some files didn't match up, they should in the future, and we'll retry then. mismatches++; - TraceEvent(SevWarn, "TLSCertificateRefreshMismatch").detail("mismatches", mismatches); + TraceEvent(SevWarn, "TLSCertificateRefreshMismatch").error(e).detail("mismatches", mismatches); } } } @@ -984,15 +1001,10 @@ void Net2::initTLS() { return; } #ifndef TLS_DISABLED - try { - boost::asio::ssl::context newContext(boost::asio::ssl::context::tls); - ConfigureSSLContext( tlsConfig.loadSync(), &newContext ); - sslContextVar.set(ReferencedObject::from(std::move(newContext))); - backgroundCertRefresh = reloadCertificatesOnChange( tlsConfig, &sslContextVar ); - } catch(boost::system::system_error e) { - TraceEvent("Net2TLSInitError").detail("Message", e.what()); - throw tls_error(); - } + boost::asio::ssl::context newContext(boost::asio::ssl::context::tls); + ConfigureSSLContext( tlsConfig.loadSync(), &newContext ); + sslContextVar.set(ReferencedObject::from(std::move(newContext))); + backgroundCertRefresh = reloadCertificatesOnChange( tlsConfig, &sslContextVar ); #endif tlsInitialized = true; } diff --git a/flow/TLSConfig.actor.cpp b/flow/TLSConfig.actor.cpp index 28685e88ad..ea61317b80 100644 --- a/flow/TLSConfig.actor.cpp +++ b/flow/TLSConfig.actor.cpp @@ -173,14 +173,10 @@ ACTOR static Future readEntireFile( std::string filename, std::string* des state Reference file = wait(IAsyncFileSystem::filesystem()->open(filename, IAsyncFile::OPEN_READONLY | IAsyncFile::OPEN_UNCACHED, 0)); state int64_t filesize = wait(file->size()); if (filesize > FLOW_KNOBS->CERT_FILE_MAX_SIZE) { - throw tls_error(); + throw file_too_large(); } destination->resize(filesize); - int rc = wait(file->read(const_cast(destination->c_str()), filesize, 0)); - if (rc != filesize) { - // File modified during read, probably. The mtime should change, and thus we'll be called again. - throw tls_error(); - } + wait(file->read(const_cast(destination->c_str()), filesize, 0)); return Void(); } @@ -218,10 +214,6 @@ ACTOR Future TLSConfig::loadAsync(const TLSConfig* self) { return loaded; } -void ConfigureSSLContext( boost::asio::ssl::context *context, const LoadedTLSConfig& config ) { - -} - std::string TLSPolicy::ErrorString(boost::system::error_code e) { char* str = ERR_error_string(e.value(), NULL); return std::string(str); From faf9101ad446af7ca884d773b6d589f28c900bf1 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Fri, 6 Mar 2020 09:20:38 -0800 Subject: [PATCH 0845/1604] Update fdbserver/Resolver.actor.cpp Co-Authored-By: Evan Tschannen <36455792+etschannen@users.noreply.github.com> --- fdbserver/Resolver.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/Resolver.actor.cpp b/fdbserver/Resolver.actor.cpp index a4850815d6..c1079aa987 100644 --- a/fdbserver/Resolver.actor.cpp +++ b/fdbserver/Resolver.actor.cpp @@ -192,7 +192,7 @@ ACTOR Future resolveBatch( ResolveTransactionBatchReply &reply = proxyInfo.outstandingBatches[req.version]; reply.debugID = req.debugID; reply.committed.resize( reply.arena, req.transactions.size() ); - for(int c=0; c Date: Fri, 6 Mar 2020 10:15:04 -0800 Subject: [PATCH 0846/1604] Fix the build with success() Co-Authored-By: A.J. Beamon --- flow/TLSConfig.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/TLSConfig.actor.cpp b/flow/TLSConfig.actor.cpp index ea61317b80..51e2ac9c93 100644 --- a/flow/TLSConfig.actor.cpp +++ b/flow/TLSConfig.actor.cpp @@ -176,7 +176,7 @@ ACTOR static Future readEntireFile( std::string filename, std::string* des throw file_too_large(); } destination->resize(filesize); - wait(file->read(const_cast(destination->c_str()), filesize, 0)); + wait(success(file->read(const_cast(destination->c_str()), filesize, 0))); return Void(); } From 9b760fae2db4329dc1bfad3ca5da2aecd28acadf Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Fri, 6 Mar 2020 11:06:19 -0800 Subject: [PATCH 0847/1604] Rewrite all Errors into tls_errors if they happen as part of initializing TLS. --- flow/Net2.actor.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index 89f68e3e50..f3613e1b4f 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -1001,10 +1001,15 @@ void Net2::initTLS() { return; } #ifndef TLS_DISABLED - boost::asio::ssl::context newContext(boost::asio::ssl::context::tls); - ConfigureSSLContext( tlsConfig.loadSync(), &newContext ); - sslContextVar.set(ReferencedObject::from(std::move(newContext))); - backgroundCertRefresh = reloadCertificatesOnChange( tlsConfig, &sslContextVar ); + try { + boost::asio::ssl::context newContext(boost::asio::ssl::context::tls); + ConfigureSSLContext( tlsConfig.loadSync(), &newContext ); + sslContextVar.set(ReferencedObject::from(std::move(newContext))); + backgroundCertRefresh = reloadCertificatesOnChange( tlsConfig, &sslContextVar ); + } catch (Error& e) { + TraceEvent("Net2TLSInitError").error(e); + throw tls_error(); + } #endif tlsInitialized = true; } From 188d9b8239b2e7f91a3c84ee6ed8de42d70cd290 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Fri, 6 Mar 2020 11:09:17 -0800 Subject: [PATCH 0848/1604] Don't swallow actor cancellation in certificate refreshing. --- flow/Net2.actor.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index f3613e1b4f..011f335a4c 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -988,6 +988,9 @@ ACTOR static Future reloadCertificatesOnChange( TLSConfig config, AsyncVar mismatches = 0; contextVar->set(ReferencedObject::from(std::move(context))); } catch (Error &e) { + if (e.code() == error_code_actor_cancelled) { + throw; + } // Some files didn't match up, they should in the future, and we'll retry then. mismatches++; TraceEvent(SevWarn, "TLSCertificateRefreshMismatch").error(e).detail("mismatches", mismatches); From 15f1a75d4f104c7301f845d4fc7d5a6c3c470f3e Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Fri, 6 Mar 2020 11:16:10 -0800 Subject: [PATCH 0849/1604] updated documentation for 6.2.18 --- documentation/sphinx/source/mr-status-json-schemas.rst.inc | 2 ++ documentation/sphinx/source/release-notes.rst | 1 + fdbclient/Schemas.cpp | 2 ++ 3 files changed, 5 insertions(+) diff --git a/documentation/sphinx/source/mr-status-json-schemas.rst.inc b/documentation/sphinx/source/mr-status-json-schemas.rst.inc index 4613303753..c8d81f5c95 100644 --- a/documentation/sphinx/source/mr-status-json-schemas.rst.inc +++ b/documentation/sphinx/source/mr-status-json-schemas.rst.inc @@ -566,6 +566,7 @@ "missing_data", "healing", "optimizing_team_collections", + "healthy_populating_region", "healthy_repartitioning", "healthy_removing_server", "healthy_rebalancing", @@ -600,6 +601,7 @@ "missing_data", "healing", "optimizing_team_collections", + "healthy_populating_region", "healthy_repartitioning", "healthy_removing_server", "healthy_rebalancing", diff --git a/documentation/sphinx/source/release-notes.rst b/documentation/sphinx/source/release-notes.rst index 09b57620c7..3a479bbe76 100644 --- a/documentation/sphinx/source/release-notes.rst +++ b/documentation/sphinx/source/release-notes.rst @@ -12,6 +12,7 @@ Fixes * When a cluster is configured with usable_regions=2, data distribution could push a cluster into saturation by relocating too many shards simulatenously. `(PR #2776) `_. * Do not allow the cluster controller to mark any process as failed within 30 seconds of startup. `(PR #2780) `_. * Backup could not establish TLS connections (broken in 6.2.16). `(PR #2775) `_. +* Certificates were not refreshed automatically (broken in 6.2.16). `(PR #2781) `_. Performance ----------- diff --git a/fdbclient/Schemas.cpp b/fdbclient/Schemas.cpp index 264fb01952..51572cf015 100644 --- a/fdbclient/Schemas.cpp +++ b/fdbclient/Schemas.cpp @@ -594,6 +594,7 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema( "missing_data", "healing", "optimizing_team_collections", + "healthy_populating_region", "healthy_repartitioning", "healthy_removing_server", "healthy_rebalancing", @@ -628,6 +629,7 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema( "missing_data", "healing", "optimizing_team_collections", + "healthy_populating_region", "healthy_repartitioning", "healthy_removing_server", "healthy_rebalancing", From 40209191850566ef54931457d79cce84bdd9566f Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Fri, 6 Mar 2020 13:58:02 -0800 Subject: [PATCH 0850/1604] update version to 6.2.19 --- CMakeLists.txt | 2 +- versions.target | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 61a626b185..622ebb02d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ # limitations under the License. cmake_minimum_required(VERSION 3.12) project(foundationdb - VERSION 6.2.18 + VERSION 6.2.19 DESCRIPTION "FoundationDB is a scalable, fault-tolerant, ordered key-value store with full ACID transactions." HOMEPAGE_URL "http://www.foundationdb.org/" LANGUAGES C CXX ASM) diff --git a/versions.target b/versions.target index 6932c61ae2..495ee9d5cb 100644 --- a/versions.target +++ b/versions.target @@ -1,7 +1,7 @@ - 6.2.18 + 6.2.19 6.2 From f2cb743cfacc4dcb2da8c9d5e8607aebb3952cfd Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Fri, 6 Mar 2020 13:58:03 -0800 Subject: [PATCH 0851/1604] update installer WIX GUID following release --- packaging/msi/FDBInstaller.wxs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/msi/FDBInstaller.wxs b/packaging/msi/FDBInstaller.wxs index 286dd84f19..990cb2896b 100644 --- a/packaging/msi/FDBInstaller.wxs +++ b/packaging/msi/FDBInstaller.wxs @@ -32,7 +32,7 @@ Date: Fri, 6 Mar 2020 16:31:33 -0800 Subject: [PATCH 0852/1604] Ignore createDirectory error if directory already exists --- flow/Platform.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/flow/Platform.cpp b/flow/Platform.cpp index b1f70c6bc4..d4fff05def 100644 --- a/flow/Platform.cpp +++ b/flow/Platform.cpp @@ -1820,16 +1820,29 @@ bool createDirectory( std::string const& directory ) { if ( mkdir( directory.substr(0, sep).c_str(), 0755 ) != 0 ) { if (errno == EEXIST) continue; + auto mkdirErrno = errno; + + // check if directory already exists + // necessary due to old kernel bugs + struct stat s; + const char* dirname = directory.c_str(); + if (stat(dirname, &s) != -1 && S_ISDIR(s.st_mode)) { + TraceEvent("DirectoryAlreadyExists").detail("Directory", dirname).detail("IgnoredError", mkdirErrno); + continue; + } Error e; - if(errno == EACCES) { + if (mkdirErrno == EACCES) { e = file_not_writable(); - } - else { + } else { e = systemErrorCodeToError(); } - TraceEvent(SevError, "CreateDirectory").detail("Directory", directory).GetLastError().error(e); + TraceEvent(SevError, "CreateDirectory") + .detail("Directory", directory) + .detailf("UnixErrorCode", "%x", errno) + .detail("UnixError", strerror(mkdirErrno)) + .error(e); throw e; } createdDirectory(); From cac5a5b65fc5f3a9af6d6b6c93402224158ce626 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Fri, 6 Mar 2020 18:35:17 -0800 Subject: [PATCH 0853/1604] fixed compile error --- documentation/tutorial/tutorial.actor.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/documentation/tutorial/tutorial.actor.cpp b/documentation/tutorial/tutorial.actor.cpp index d0be6a3e2b..0d12e1c87d 100644 --- a/documentation/tutorial/tutorial.actor.cpp +++ b/documentation/tutorial/tutorial.actor.cpp @@ -24,6 +24,7 @@ #include "flow/DeterministicRandom.h" #include "fdbclient/NativeAPI.actor.h" #include "fdbclient/ReadYourWrites.h" +#include "flow/TLSConfig.actor.h" #include #include #include @@ -439,7 +440,7 @@ int main(int argc, char* argv[]) { toRun.push_back(actor->second); } platformInit(); - g_network = newNet2(false, true); + g_network = newNet2(TLSConfig(), false, true); NetworkAddress publicAddress = NetworkAddress::parse("0.0.0.0:0"); if (isServer) { publicAddress = NetworkAddress::parse("0.0.0.0:" + port); From ab33e688c8b0251cfbce633613d41e2889e62386 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Mon, 9 Mar 2020 10:45:57 -0700 Subject: [PATCH 0854/1604] comment --- fdbclient/SpecialKeySpace.actor.cpp | 1 + fdbserver/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/fdbclient/SpecialKeySpace.actor.cpp b/fdbclient/SpecialKeySpace.actor.cpp index 8c71c31740..2197c69a42 100644 --- a/fdbclient/SpecialKeySpace.actor.cpp +++ b/fdbclient/SpecialKeySpace.actor.cpp @@ -149,6 +149,7 @@ Future> SpecialKeySpace::getRange(Reference(); } + // TODO : transform limits like in NativeAPI.actor.cpp there // ignore snapshot, which is not used return getRangeAggregationActor(this, ryw, begin, end, limits, reverse); } diff --git a/fdbserver/CMakeLists.txt b/fdbserver/CMakeLists.txt index fedb6be995..b2812ef7fe 100644 --- a/fdbserver/CMakeLists.txt +++ b/fdbserver/CMakeLists.txt @@ -176,6 +176,7 @@ set(FDBSERVER_SRCS workloads/Sideband.actor.cpp workloads/SlowTaskWorkload.actor.cpp workloads/SnapTest.actor.cpp + workloads/SpecialKeySpaceCorrectness.actor.cpp workloads/StatusWorkload.actor.cpp workloads/Storefront.actor.cpp workloads/StreamingRead.actor.cpp From a7f1efc5436cc3502cfd98cab3bfe29298a0fa8e Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Mon, 9 Mar 2020 10:46:47 -0700 Subject: [PATCH 0855/1604] add test workload --- .../SpecialKeySpaceCorrectness.actor.cpp | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp diff --git a/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp b/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp new file mode 100644 index 0000000000..d3c2cc6baf --- /dev/null +++ b/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp @@ -0,0 +1,115 @@ +#include "fdbclient/NativeAPI.actor.h" +#include "fdbclient/ReadYourWrites.h" +#include "fdbclient/SpecialKeySpace.actor.h" +#include "fdbserver/TesterInterface.actor.h" +#include "fdbserver/workloads/workloads.actor.h" +#include "flow/actorcompiler.h" + +const KeyRef startSuffix = LiteralStringRef("/"); +const KeyRef endSuffix = LiteralStringRef("/\xff"); +class SPSCTestImpl : public SpecialKeyRangeBaseImpl { +public: + explicit SPSCTestImpl(KeyRef start, KeyRef end) : SpecialKeyRangeBaseImpl(start, end) {} + virtual Future> getRange(Reference ryw, KeyRangeRef kr) const { + ASSERT(range.contains(kr)); + auto resultFuture = ryw->getRange(kr, GetRangeLimits()); + ASSERT(resultFuture.isReady()); + return resultFuture.getValue(); + } +}; + +struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { + + int minKeysPerRange, maxKeysPerRange, rangeCount, keyBytes, valBytes; + double testDuration, absoluteRandomProb; + std::vector> clients; + + PerfIntCounter wrongResults, keysCount; + + Reference ryw; // used to store all populated data + std::vector impls; + + Standalone> keys; + + 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("valBytes"), 16); + testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); + absoluteRandomProb = getOption(options, LiteralStringRef("absoluteRandomProb"), 0.5); + } + + virtual std::string description() { return "SpecialKeySpaceCorrectness"; } + virtual Future setup(Database const& cx) { return _setup(cx, this); } + virtual Future start(Database const& cx) { return _start(cx, this); } + virtual Future check(Database const& cx) { return wrongResults.getValue() == 0; } + virtual void getMetrics(std::vector& m) {} + + // disable the default timeout setting + double getCheckTimeout() override { return std::numeric_limits::max(); } + + ACTOR Future _setup(Database cx, SpecialKeySpaceCorrectnessWorkload* self) { + self->ryw = Reference(new ReadYourWritesTransaction(cx)); + // generate key ranges + for (int i = 0; i < self->rangeCount; ++i) { + std::string baseKey = deterministicRandom()->randomAlphaNumeric(i + 1); + Key startKey(baseKey + "/"); + Key endKey(baseKey + "/\xff"); + self->keys.push_back_deep(self->keys.arena(), KeyRangeRef(startKey, endKey)); + self->impls.emplace_back(startKey, endKey); + cx->specialKeySpace->registerKeyRange(self->keys.back(), &self->impls.back()); + // generate keys in each key range + int keysInRange = deterministicRandom()->randomInt(self->minKeysPerRange, self->maxKeysPerRange + 1); + self->keysCount += keysInRange; + for (int j = 0; j < keysInRange; ++j) { + self->ryw->set(Key(deterministicRandom()->randomAlphaNumeric(self->keyBytes)).withPrefix(startKey), + Value(deterministicRandom()->randomAlphaNumeric(self->valBytes))); + } + } + return Void(); + } + ACTOR Future _start(Database cx, SpecialKeySpaceCorrectnessWorkload* self) { + wait(timeout(waitForAll(self->clients), self->testDuration, Void())); + return Void(); + } + + ACTOR Future getRangeCallActor(Database cx, SpecialKeySpaceCorrectnessWorkload* self) { + // loop { + // bool flag = deterministicRandom()->random01() < absoluteRandomProb; + // } + return Void(); + } + + KeySelector randomKeySelector() { + Key prefix; + if (deterministicRandom()->random01() < absoluteRandomProb) + prefix = Key(deterministicRandom()->randomAlphaNumeric(deterministicRandom()->randomInt(1, rangeCount + 1)) + "/"); + else + prefix = keys[deterministicRandom()->randomInt(1, rangeCount + 1)].begin; + Key suffix; + // TODO : add randomness to pickup existing keys + // if (deterministicRandom()->random01() < absoluteRandomProb) + suffix = Key(deterministicRandom()->randomAlphaNumeric(keyBytes)); + // return Key(deterministicRandom()->randomAlphaNumeric(keyBytes)).withPrefix(prefix); + // TODO : test corner case here if offset points out + int offset = deterministicRandom()->randomInt(-keysCount.getValue()-1, keysCount.getValue()+1); + bool orEqual = deterministicRandom()->random01() < 0.5; + return KeySelectorRef(suffix.withPrefix(prefix), orEqual, offset); + } + + GetRangeLimits randomLimits() { + if (deterministicRandom()->random01() < 0.5) + return GetRangeLimits(); + int rowLimits = deterministicRandom()->randomInt(1, keysCount.getValue()+1); + // TODO : add random bytes limit here + return GetRangeLimits(rowLimits); + } + + // void updateGetGetRangeParas(KeySelector& begin, KeySelector& end, GetRangeLimits& limits, bool& reverse) { + // reverse = deterministicRandom()->random01() < 0.5; + // } +}; + +WorkloadFactory SpecialKeySpaceCorrectnessFactory("SpecialKeySpaceCorrectness"); \ No newline at end of file From 027029cc9b120160bfc38678769caf461c28b0ab Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Mon, 2 Mar 2020 14:32:36 -0800 Subject: [PATCH 0856/1604] Remove offending overload? --- flow/serialize.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/flow/serialize.h b/flow/serialize.h index 00fdd07dc4..1adc93dfa1 100644 --- a/flow/serialize.h +++ b/flow/serialize.h @@ -82,20 +82,21 @@ inline typename Archive::READER& operator >> (Archive& ar, Item& item ) { return ar; } -template -void serializer(Archive& ar) {} - template typename Archive::WRITER& serializer(Archive& ar, const Item& item, const Items&... items) { save(ar, item); - serializer(ar, items...); + if constexpr (sizeof...(Items) > 0) { + serializer(ar, items...); + } return ar; } template typename Archive::READER& serializer(Archive& ar, Item& item, Items&... items) { load(ar, item); - serializer(ar, items...); + if constexpr (sizeof...(Items) > 0) { + serializer(ar, items...); + } return ar; } From 770ef6e726218d1fc1f583a366864d4f8a145d7f Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Tue, 10 Mar 2020 10:42:57 -0700 Subject: [PATCH 0857/1604] Add test --- flow/flat_buffers.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/flow/flat_buffers.cpp b/flow/flat_buffers.cpp index 1cb4b1099d..89fb058f98 100644 --- a/flow/flat_buffers.cpp +++ b/flow/flat_buffers.cpp @@ -488,6 +488,10 @@ TEST_CASE("/flow/FlatBuffers/Standalone") { // Meant to be run with valgrind or asan, to catch heap buffer overflows TEST_CASE("/flow/FlatBuffers/Void") { Standalone msg = ObjectWriter::toValue(Void(), Unversioned()); + // Manually verified to be a valid flatbuffers message. This is technically brittle since there are other valid + // encodings of this message, but our implementation is unlikely to change. + ASSERT(msg == LiteralStringRef("\x14\x00\x00\x00J\xad\x1e\x00\x00\x00\x04\x00\x04\x00\x06\x00\x08\x00\x04\x00\x06" + "\x00\x00\x00\x04\x00\x00\x00\x12\x00\x00\x00")); auto buffer = std::make_unique(msg.size()); // Make a heap allocation of precisely the right size, so // that asan or valgrind will catch any overflows memcpy(buffer.get(), msg.begin(), msg.size()); From 157d026ba2d0dab0b9afe6836906c04d04c9a8a9 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Tue, 10 Mar 2020 18:42:49 -0700 Subject: [PATCH 0858/1604] debugging --- fdbclient/NativeAPI.actor.cpp | 2 +- .../SpecialKeySpaceCorrectness.actor.cpp | 145 ++++++++++++------ 2 files changed, 99 insertions(+), 48 deletions(-) diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index 09dd1784ea..5d1340147e 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -531,7 +531,7 @@ DatabaseContext::DatabaseContext(Reference(LiteralStringRef("\xff\xff"), LiteralStringRef("\xff\xff\xff\xff")); + specialKeySpace = std::make_unique(LiteralStringRef(""), LiteralStringRef("\xff\xff\xff\xff")); } DatabaseContext::DatabaseContext(const Error& err) diff --git a/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp b/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp index d3c2cc6baf..2bbdd0e127 100644 --- a/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp +++ b/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp @@ -9,18 +9,19 @@ const KeyRef startSuffix = LiteralStringRef("/"); const KeyRef endSuffix = LiteralStringRef("/\xff"); class SPSCTestImpl : public SpecialKeyRangeBaseImpl { public: - explicit SPSCTestImpl(KeyRef start, KeyRef end) : SpecialKeyRangeBaseImpl(start, end) {} - virtual Future> getRange(Reference ryw, KeyRangeRef kr) const { + explicit SPSCTestImpl(KeyRef start, KeyRef end) : SpecialKeyRangeBaseImpl(start, end) {} + virtual Future> getRange(Reference ryw, + KeyRangeRef kr) const { ASSERT(range.contains(kr)); - auto resultFuture = ryw->getRange(kr, GetRangeLimits()); - ASSERT(resultFuture.isReady()); - return resultFuture.getValue(); + auto result = ryw->getRange(kr, GetRangeLimits()); + // ASSERT(resultFuture.isReady()); + return result; } }; struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { - int minKeysPerRange, maxKeysPerRange, rangeCount, keyBytes, valBytes; + int actorCount, minKeysPerRange, maxKeysPerRange, rangeCount, keyBytes, valBytes; double testDuration, absoluteRandomProb; std::vector> clients; @@ -31,14 +32,16 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { Standalone> keys; - SpecialKeySpaceCorrectnessWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), wrongResults("Wrong Results"), keysCount("Number of generated keys") { + 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("valBytes"), 16); + valBytes = getOption(options, LiteralStringRef("valueBytes"), 16); testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - absoluteRandomProb = getOption(options, LiteralStringRef("absoluteRandomProb"), 0.5); + actorCount = getOption( options, LiteralStringRef("actorCount"), 50 ); + absoluteRandomProb = getOption(options, LiteralStringRef("absoluteRandomProb"), 0.5); } virtual std::string description() { return "SpecialKeySpaceCorrectness"; } @@ -51,7 +54,27 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { double getCheckTimeout() override { return std::numeric_limits::max(); } ACTOR Future _setup(Database cx, SpecialKeySpaceCorrectnessWorkload* self) { - self->ryw = Reference(new ReadYourWritesTransaction(cx)); + // self->ryw = Reference(new ReadYourWritesTransaction(cx)); + // // generate key ranges + // for (int i = 0; i < self->rangeCount; ++i) { + // std::string baseKey = deterministicRandom()->randomAlphaNumeric(i + 1); + // Key startKey(baseKey + "/"); + // Key endKey(baseKey + "/\xff"); + // self->keys.push_back_deep(self->keys.arena(), KeyRangeRef(startKey, endKey)); + // self->impls.emplace_back(startKey, endKey); + // cx->specialKeySpace->registerKeyRange(self->keys.back(), &self->impls.back()); + // // generate keys in each key range + // int keysInRange = deterministicRandom()->randomInt(self->minKeysPerRange, self->maxKeysPerRange + 1); + // self->keysCount += keysInRange; + // for (int j = 0; j < keysInRange; ++j) { + // self->ryw->set(Key(deterministicRandom()->randomAlphaNumeric(self->keyBytes)).withPrefix(startKey), + // Value(deterministicRandom()->randomAlphaNumeric(self->valBytes))); + // } + // } + return Void(); + } + ACTOR Future _start(Database cx, SpecialKeySpaceCorrectnessWorkload* self) { + self->ryw = Reference(new ReadYourWritesTransaction(cx)); // generate key ranges for (int i = 0; i < self->rangeCount; ++i) { std::string baseKey = deterministicRandom()->randomAlphaNumeric(i + 1); @@ -62,54 +85,82 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { cx->specialKeySpace->registerKeyRange(self->keys.back(), &self->impls.back()); // generate keys in each key range int keysInRange = deterministicRandom()->randomInt(self->minKeysPerRange, self->maxKeysPerRange + 1); - self->keysCount += keysInRange; + self->keysCount += keysInRange; for (int j = 0; j < keysInRange; ++j) { self->ryw->set(Key(deterministicRandom()->randomAlphaNumeric(self->keyBytes)).withPrefix(startKey), Value(deterministicRandom()->randomAlphaNumeric(self->valBytes))); } } - return Void(); - } - ACTOR Future _start(Database cx, SpecialKeySpaceCorrectnessWorkload* self) { - wait(timeout(waitForAll(self->clients), self->testDuration, Void())); + + std::vector> clients; + for(int c = 0; c < self->actorCount; c++) { + clients.push_back(self->getRangeCallActor(cx, self)); + } + wait(timeout(waitForAll(clients), self->testDuration, Void())); return Void(); } - ACTOR Future getRangeCallActor(Database cx, SpecialKeySpaceCorrectnessWorkload* self) { - // loop { - // bool flag = deterministicRandom()->random01() < absoluteRandomProb; - // } - return Void(); - } + ACTOR Future getRangeCallActor(Database cx, SpecialKeySpaceCorrectnessWorkload* self) { + loop { + state bool reverse = deterministicRandom()->random01() < 0.5; + state GetRangeLimits limit = self->randomLimits(); + state KeySelector begin = self->randomKeySelector(); + state KeySelector end = self->randomKeySelector(); + KeyRange kr = KeyRangeRef(LiteralStringRef("test"), LiteralStringRef("test2")); + // state Standalone correctResult = wait(self->ryw->getRange(begin, end, limit, false, reverse)); + Standalone tempResult = wait(self->ryw->getRange(kr, 1)); + // ASSERT(correctResultFuture.isReady()); + // auto correctResult = correctResultFuture.getValue(); + // auto testResultFuture = cx->specialKeySpace->getRange(self->ryw, begin, end, limit, false, reverse); + // ASSERT(testResultFuture.isReady()); + // auto testResult = testResultFuture.getValue(); - KeySelector randomKeySelector() { - Key prefix; - if (deterministicRandom()->random01() < absoluteRandomProb) - prefix = Key(deterministicRandom()->randomAlphaNumeric(deterministicRandom()->randomInt(1, rangeCount + 1)) + "/"); - else - prefix = keys[deterministicRandom()->randomInt(1, rangeCount + 1)].begin; - Key suffix; - // TODO : add randomness to pickup existing keys - // if (deterministicRandom()->random01() < absoluteRandomProb) - suffix = Key(deterministicRandom()->randomAlphaNumeric(keyBytes)); - // return Key(deterministicRandom()->randomAlphaNumeric(keyBytes)).withPrefix(prefix); - // TODO : test corner case here if offset points out - int offset = deterministicRandom()->randomInt(-keysCount.getValue()-1, keysCount.getValue()+1); - bool orEqual = deterministicRandom()->random01() < 0.5; - return KeySelectorRef(suffix.withPrefix(prefix), orEqual, offset); - } + // // check the same + // if (!self->compareRangeResult(correctResult, testResult)) { + // // TODO : log here + // // TraceEvent("WrongGetRangeResult"). detail("KeySeleco") + // ++self->wrongResults; + // } + } + } - GetRangeLimits randomLimits() { - if (deterministicRandom()->random01() < 0.5) - return GetRangeLimits(); - int rowLimits = deterministicRandom()->randomInt(1, keysCount.getValue()+1); - // TODO : add random bytes limit here - return GetRangeLimits(rowLimits); - } + bool compareRangeResult(Standalone& res1, Standalone& res2) { + if (res1.size() != res2.size()) return false; + for (int i = 0; i < res1.size(); ++i) { + if (res1[i] != res2[i]) return false; + } + return true; + } - // void updateGetGetRangeParas(KeySelector& begin, KeySelector& end, GetRangeLimits& limits, bool& reverse) { - // reverse = deterministicRandom()->random01() < 0.5; - // } + KeySelector randomKeySelector() { + Key prefix; + if (deterministicRandom()->random01() < absoluteRandomProb) + prefix = Key( + deterministicRandom()->randomAlphaNumeric(deterministicRandom()->randomInt(1, rangeCount + 1)) + "/"); + else + prefix = keys[deterministicRandom()->randomInt(1, rangeCount + 1)].begin; + Key suffix; + // TODO : add randomness to pickup existing keys + // if (deterministicRandom()->random01() < absoluteRandomProb) + suffix = Key(deterministicRandom()->randomAlphaNumeric(keyBytes)); + // return Key(deterministicRandom()->randomAlphaNumeric(keyBytes)).withPrefix(prefix); + // TODO : test corner case here if offset points out + int offset = deterministicRandom()->randomInt(-keysCount.getValue() - 1, keysCount.getValue() + 1); + bool orEqual = deterministicRandom()->random01() < 0.5; + return KeySelectorRef(suffix.withPrefix(prefix), orEqual, offset); + } + + GetRangeLimits randomLimits() { + if (deterministicRandom()->random01() < 0.5) return GetRangeLimits(); + int rowLimits = deterministicRandom()->randomInt(1, keysCount.getValue() + 1); + // TODO : add random bytes limit here + // TODO : setRequestLimits in RYW + return GetRangeLimits(rowLimits); + } + + // void updateGetGetRangeParas(KeySelector& begin, KeySelector& end, GetRangeLimits& limits, bool& reverse) { + // reverse = deterministicRandom()->random01() < 0.5; + // } }; WorkloadFactory SpecialKeySpaceCorrectnessFactory("SpecialKeySpaceCorrectness"); \ No newline at end of file From 357ca88b4c6ae337df2ac557ba998a04a64c4060 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Tue, 10 Mar 2020 18:51:45 -0700 Subject: [PATCH 0859/1604] test spec file --- tests/SpecialKeySpaceCorrectness.txt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 tests/SpecialKeySpaceCorrectness.txt diff --git a/tests/SpecialKeySpaceCorrectness.txt b/tests/SpecialKeySpaceCorrectness.txt new file mode 100644 index 0000000000..6de079e86e --- /dev/null +++ b/tests/SpecialKeySpaceCorrectness.txt @@ -0,0 +1,5 @@ +testTitle=SpecialKeySpaceCorrectnessTest + testName=SpecialKeySpaceCorrectness + testDuration=10.0 + valueBytes=16 + keyBytes=16 From bd345f85dba05f29e208ac3453ef124a4b5bc118 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Tue, 10 Mar 2020 15:05:13 -0700 Subject: [PATCH 0860/1604] ConsistencyCheck:Fix failue due to address inconsistency between process and worker With TLS, a worker (or process) can have a TLS address and non-TLS address. When a process is created in simulation, the primary address is TLS by default. The non-TLS one is the TLS address port plus one. In a connection between two workers, if their primary addresses do not enable or disable TLS together, one worker will swap its primary address and secondary address so that the TLS config of the two endpoints can match. The swap can make the primary address no longer the TLS one that was created when the process is created. And the swap only happens for worker instead of process struct in simulation. This swap can cause worker->address != process->address. In checkForExtraDataStores actor, we use worker->address to check if a process is killable and use the process->address to kill the process. The inconsistency can cause simulation to kill a protected process that is not killable and leads to simulation failure. --- fdbclient/ManagementAPI.actor.cpp | 2 +- fdbclient/StorageServerInterface.h | 3 +- fdbrpc/FlowTransport.h | 5 ++- fdbrpc/sim2.actor.cpp | 22 ++++++++++--- fdbrpc/simulator.h | 6 ++++ fdbserver/LogSystemDiskQueueAdapter.actor.cpp | 4 +-- fdbserver/LogSystemPeekCursor.actor.cpp | 1 + fdbserver/OldTLogServer_6_0.actor.cpp | 3 +- fdbserver/TLogInterface.h | 2 +- .../workloads/ConsistencyCheck.actor.cpp | 32 ++++++++++++++++++- .../workloads/RemoveServersSafely.actor.cpp | 10 +++--- 11 files changed, 74 insertions(+), 16 deletions(-) diff --git a/fdbclient/ManagementAPI.actor.cpp b/fdbclient/ManagementAPI.actor.cpp index 9ac0d47102..3c5d858e7a 100644 --- a/fdbclient/ManagementAPI.actor.cpp +++ b/fdbclient/ManagementAPI.actor.cpp @@ -986,7 +986,7 @@ ACTOR Future changeQuorum( Database cx, ReferenceisSimulated()) { for(int i = 0; i < (desiredCoordinators.size()/2)+1; i++) { auto addresses = g_simulator.getProcessByAddress(desiredCoordinators[i])->addresses; - + g_simulator.protectedAddresses.insert(addresses.address); if(addresses.secondaryAddress.present()) { g_simulator.protectedAddresses.insert(addresses.secondaryAddress.get()); diff --git a/fdbclient/StorageServerInterface.h b/fdbclient/StorageServerInterface.h index a4dd2e2892..f54cce4d60 100644 --- a/fdbclient/StorageServerInterface.h +++ b/fdbclient/StorageServerInterface.h @@ -74,9 +74,10 @@ struct StorageServerInterface { explicit StorageServerInterface(UID uid) : uniqueID( uid ) {} StorageServerInterface() : uniqueID( deterministicRandom()->randomUniqueID() ) {} NetworkAddress address() const { return getValue.getEndpoint().getPrimaryAddress(); } + Optional secondaryAddress() const {return getValue.getEndpoint().addresses.secondaryAddress;} UID id() const { return uniqueID; } std::string toString() const { return id().shortString(); } - template + template void serialize( Ar& ar ) { // StorageServerInterface is persisted in the database and in the tLog's data structures, so changes here have to be // versioned carefully! diff --git a/fdbrpc/FlowTransport.h b/fdbrpc/FlowTransport.h index f3f3fc32a0..5c5ba3990f 100644 --- a/fdbrpc/FlowTransport.h +++ b/fdbrpc/FlowTransport.h @@ -45,8 +45,11 @@ public: void choosePrimaryAddress() { if(addresses.secondaryAddress.present() && !g_network->getLocalAddresses().secondaryAddress.present() && (addresses.address.isTLS() != g_network->getLocalAddresses().address.isTLS())) { + if (addresses.address.isTLS()) { + TraceEvent(SevWarn, "MXDEBUGChoosePrimaryAddressSwap").detail("PrimaryAddressWillBeTLS", addresses.secondaryAddress.get().isTLS()).backtrace(); + } std::swap(addresses.address, addresses.secondaryAddress.get()); - } + } } bool isValid() const { return token.isValid(); } diff --git a/fdbrpc/sim2.actor.cpp b/fdbrpc/sim2.actor.cpp index aa46a9f06e..639df59dfb 100644 --- a/fdbrpc/sim2.actor.cpp +++ b/fdbrpc/sim2.actor.cpp @@ -1029,7 +1029,7 @@ public: NetworkAddressList addresses; addresses.address = NetworkAddress(ip, port, true, sslEnabled); - if(listenPerProcess == 2) { + if(listenPerProcess == 2) { // listenPerProcess is only 1 or 2 addresses.secondaryAddress = NetworkAddress(ip, port+1, true, false); } @@ -1250,12 +1250,26 @@ public: TEST( kt == InjectFaults ); // Simulated machine was killed with faults if (kt == KillInstantly) { - TraceEvent(SevWarn, "FailMachine").detail("Name", machine->name).detail("Address", machine->address).detail("ZoneId", machine->locality.zoneId()).detail("Process", machine->toString()).detail("Rebooting", machine->rebooting).detail("Protected", protectedAddresses.count(machine->address)).backtrace(); + TraceEvent(SevWarn, "FailMachine") + .detail("Name", machine->name) + .detail("Address", machine->address) + .detail("ZoneId", machine->locality.zoneId()) + .detail("Process", machine->toString()) + .detail("Rebooting", machine->rebooting) + .detail("Protected", protectedAddresses.count(machine->address)) + .backtrace(); // This will remove all the "tracked" messages that came from the machine being killed latestEventCache.clear(); machine->failed = true; } else if (kt == InjectFaults) { - TraceEvent(SevWarn, "FaultMachine").detail("Name", machine->name).detail("Address", machine->address).detail("ZoneId", machine->locality.zoneId()).detail("Process", machine->toString()).detail("Rebooting", machine->rebooting).detail("Protected", protectedAddresses.count(machine->address)).backtrace(); + TraceEvent(SevWarn, "FaultMachine") + .detail("Name", machine->name) + .detail("Address", machine->address) + .detail("ZoneId", machine->locality.zoneId()) + .detail("Process", machine->toString()) + .detail("Rebooting", machine->rebooting) + .detail("Protected", protectedAddresses.count(machine->address)) + .backtrace(); should_inject_fault = simulator_should_inject_fault; machine->fault_injection_r = deterministicRandom()->randomUniqueID().first(); machine->fault_injection_p1 = 0.1; @@ -1290,7 +1304,7 @@ public: } } virtual void killProcess( ProcessInfo* machine, KillType kt ) { - TraceEvent("AttemptingKillProcess"); + TraceEvent("AttemptingKillProcess").detail("ProcessInfo", machine->toString()); if (kt < RebootAndDelete ) { killProcess_internal( machine, kt ); } diff --git a/fdbrpc/simulator.h b/fdbrpc/simulator.h index ee85a29466..d81e5763a1 100644 --- a/fdbrpc/simulator.h +++ b/fdbrpc/simulator.h @@ -81,6 +81,12 @@ public: bool isAvailable() const { return !isExcluded() && isReliable(); } bool isExcluded() const { return excluded; } bool isCleared() const { return cleared; } + std::string getReliableInfo() { + std::stringstream ss; + ss << "failed:" << failed << " fault_injection_p1:" << fault_injection_p1 + << " fault_injection_p2:" << fault_injection_p2; + return ss.str(); + } // Returns true if the class represents an acceptable worker bool isAvailableClass() const { diff --git a/fdbserver/LogSystemDiskQueueAdapter.actor.cpp b/fdbserver/LogSystemDiskQueueAdapter.actor.cpp index e1c01dc1bf..ec4727a09a 100644 --- a/fdbserver/LogSystemDiskQueueAdapter.actor.cpp +++ b/fdbserver/LogSystemDiskQueueAdapter.actor.cpp @@ -61,8 +61,8 @@ public: } } TraceEvent("PeekNextGetMore").detail("Total", self->totalRecoveredBytes).detail("Queue", self->recoveryQueue.size()).detail("Bytes", bytes).detail("Loc", self->recoveryLoc) - .detail("End", self->logSystem->getEnd()).detail("HasMessage", self->cursor->hasMessage()).detail("Version", self->cursor->version().version); - + .detail("End", self->logSystem->getEnd()).detail("HasMessage", self->cursor->hasMessage()).detail("Version", self->cursor->version().version); + if(self->cursor->popped() != 0 || (!self->hasDiscardedData && BUGGIFY_WITH_PROB(0.01))) { TEST(true); //disk adapter reset TraceEvent(SevWarnAlways, "DiskQueueAdapterReset").detail("Version", self->cursor->popped()); diff --git a/fdbserver/LogSystemPeekCursor.actor.cpp b/fdbserver/LogSystemPeekCursor.actor.cpp index 8a07cd1304..51880f5064 100644 --- a/fdbserver/LogSystemPeekCursor.actor.cpp +++ b/fdbserver/LogSystemPeekCursor.actor.cpp @@ -195,6 +195,7 @@ ACTOR Future serverPeekParallelGetMore( ILogSystem::ServerPeekCursor* self TraceEvent("PeekCursorTimedOut", self->randomID).error(e); // We *should* never get timed_out(), as it means the TLog got stuck while handling a parallel peek, // and thus we've likely just wasted 10min. + // timed_out() is sent by cleanupPeekTrackers as value PEEK_TRACKER_EXPIRATION_TIME ASSERT_WE_THINK(e.code() == error_code_operation_obsolete || SERVER_KNOBS->PEEK_TRACKER_EXPIRATION_TIME < 10); self->interfaceChanged = self->interf->onChange(); self->randomID = deterministicRandom()->randomUniqueID(); diff --git a/fdbserver/OldTLogServer_6_0.actor.cpp b/fdbserver/OldTLogServer_6_0.actor.cpp index 33e6a915c2..94f086b2a1 100644 --- a/fdbserver/OldTLogServer_6_0.actor.cpp +++ b/fdbserver/OldTLogServer_6_0.actor.cpp @@ -381,6 +381,7 @@ struct LogData : NonCopyable, public ReferenceCounted { Version queueCommittingVersion; Version knownCommittedVersion, durableKnownCommittedVersion, minKnownCommittedVersion; + // Track lastUpdate time for parallel peek and detect stall on tLogs struct PeekTrackerData { std::map>> sequence_version; double lastUpdate; @@ -500,7 +501,7 @@ struct LogData : NonCopyable, public ReferenceCounted { for ( auto it = peekTracker.begin(); it != peekTracker.end(); ++it ) { for(auto seq : it->second.sequence_version) { if(!seq.second.isSet()) { - seq.second.sendError(timed_out()); + seq.second.sendError(operation_obsolete()); } } } diff --git a/fdbserver/TLogInterface.h b/fdbserver/TLogInterface.h index 645244acdc..e89d2301b4 100644 --- a/fdbserver/TLogInterface.h +++ b/fdbserver/TLogInterface.h @@ -49,7 +49,6 @@ struct TLogInterface { RequestStream< struct TLogEnablePopRequest> enablePopRequest; RequestStream< struct TLogSnapRequest> snapRequest; - TLogInterface() {} explicit TLogInterface(const LocalityData& locality) : uniqueID( deterministicRandom()->randomUniqueID() ), locality(locality) { sharedTLogID = uniqueID; } TLogInterface(UID sharedTLogID, const LocalityData& locality) : uniqueID( deterministicRandom()->randomUniqueID() ), sharedTLogID(sharedTLogID), locality(locality) {} @@ -59,6 +58,7 @@ struct TLogInterface { std::string toString() const { return id().shortString(); } bool operator == ( TLogInterface const& r ) const { return id() == r.id(); } NetworkAddress address() const { return peekMessages.getEndpoint().getPrimaryAddress(); } + Optional secondaryAddress() const {return peekMessages.getEndpoint().addresses.secondaryAddress;} void initEndpoints() { getQueuingMetrics.getEndpoint( TaskPriority::TLogQueuingMetrics ); popMessages.getEndpoint( TaskPriority::TLogPop ); diff --git a/fdbserver/workloads/ConsistencyCheck.actor.cpp b/fdbserver/workloads/ConsistencyCheck.actor.cpp index 0ef7d86693..40aabfd09c 100644 --- a/fdbserver/workloads/ConsistencyCheck.actor.cpp +++ b/fdbserver/workloads/ConsistencyCheck.actor.cpp @@ -1175,14 +1175,24 @@ struct ConsistencyCheckWorkload : TestWorkload state std::vector::iterator itr; state bool foundExtraDataStore = false; + state std::vector protectedProcessesToKill; state std::map> statefulProcesses; for (const auto& ss : storageServers) { statefulProcesses[ss.address()].insert(ss.id()); + // Add both addresses so that we will not mistakenly trigger ConsistencyCheck_ExtraDataStore + if (ss.secondaryAddress().present()) { + statefulProcesses[ss.secondaryAddress().get()].insert(ss.id()); + } } for (const auto& log : logs) { statefulProcesses[log.address()].insert(log.id()); + if (log.secondaryAddress().present()) { + statefulProcesses[log.secondaryAddress().get()].insert(log.id()); + } } + // TODO: Add coordinators into stateful processes + // Why don't we add coordinator address into statefulProcesses? for(itr = workers.begin(); itr != workers.end(); ++itr) { ErrorOr>> stores = wait(itr->interf.diskStoreRequest.getReplyUnlessFailedFor(DiskStoreRequest(false), 2, 0)); @@ -1193,13 +1203,28 @@ struct ConsistencyCheckWorkload : TestWorkload } for (const auto& id : stores.get()) { + // if (statefulProcesses[itr->interf.address()].count(id)) { + // continue; + // } if(!statefulProcesses[itr->interf.address()].count(id)) { TraceEvent("ConsistencyCheck_ExtraDataStore").detail("Address", itr->interf.address()).detail("DataStoreID", id); if(g_network->isSimulated()) { //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 auto p = g_simulator.getProcessByAddress(itr->interf.address()); - TraceEvent("ConsistencyCheck_RebootProcess").detail("Address", itr->interf.address()).detail("DataStoreID", id).detail("Reliable", p->isReliable()); + // 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") + .detail("Address", itr->interf.address()) // worker's primary address (i.e., the first address) + .detail("ProcessAddress", p->address) + .detail("DataStoreID", id) + .detail("Protected", g_simulator.protectedAddresses.count(itr->interf.address())) + .detail("Reliable", p->isReliable()) + .detail("ReliableInfo", p->getReliableInfo()) + .detail("KillOrRebootProcess", p->address); + // if (g_simulator.protectedAddresses.count(machine->address)) { + // protectedProcessesToKill.push_back(p); + // continue; + // } if(p->isReliable()) { g_simulator.rebootProcess(p, ISimulator::RebootProcess); } else { @@ -1212,6 +1237,11 @@ struct ConsistencyCheckWorkload : TestWorkload } } + // kill or reboot protected process + // for () { + + // } + if(foundExtraDataStore) { self->testFailure("Extra data stores present on workers"); return false; diff --git a/fdbserver/workloads/RemoveServersSafely.actor.cpp b/fdbserver/workloads/RemoveServersSafely.actor.cpp index 1900ddeeaa..bda028d61b 100644 --- a/fdbserver/workloads/RemoveServersSafely.actor.cpp +++ b/fdbserver/workloads/RemoveServersSafely.actor.cpp @@ -113,14 +113,14 @@ struct RemoveServersSafelyWorkload : TestWorkload { toKill2.insert(processSet.begin(), processSet.end()); } - std::vector disableAddrs1; + // std::vector disableAddrs1; for( AddressExclusion ex : toKill1 ) { AddressExclusion machineIp(ex.ip); ASSERT(machine_ids.count(machineIp)); g_simulator.disableSwapToMachine(machine_ids[machineIp]); } - std::vector disableAddrs2; + // std::vector disableAddrs2; for( AddressExclusion ex : toKill2 ) { AddressExclusion machineIp(ex.ip); ASSERT(machine_ids.count(machineIp)); @@ -224,6 +224,8 @@ struct RemoveServersSafelyWorkload : TestWorkload { return procArray; } + // Return processes that are intersection of killAddrs and allServers and that are safe to kill together; + // killAddrs does not guarantee the addresses are safe to kill simultaneously. virtual std::vector protectServers(std::set const& killAddrs) { std::vector processes; @@ -309,7 +311,7 @@ struct RemoveServersSafelyWorkload : TestWorkload { TraceEvent("RemoveAndKill").detail("Step", "include all first").detail("KillTotal", toKill1.size()).detail("ToKill", describe(toKill1)).detail("ClusterAvailable", g_simulator.isAvailable()); wait( includeServers( cx, vector(1) ) ); self->includeAddresses(toKill1); - TraceEvent("RemoveAndKill").detail("Step", "included all first").detail("KillTotal", toKill1.size()).detail("ToKill", describe(toKill1)).detail("ClusterAvailable", g_simulator.isAvailable()); + //TraceEvent("RemoveAndKill").detail("Step", "included all first").detail("KillTotal", toKill1.size()).detail("ToKill", describe(toKill1)).detail("ClusterAvailable", g_simulator.isAvailable()); } // Get the list of protected servers @@ -335,7 +337,7 @@ struct RemoveServersSafelyWorkload : TestWorkload { TraceEvent("RemoveAndKill").detail("Step", "include all second").detail("KillTotal", toKill2.size()).detail("ToKill", describe(toKill2)).detail("ClusterAvailable", g_simulator.isAvailable()); wait( includeServers( cx, vector(1) ) ); self->includeAddresses(toKill2); - TraceEvent("RemoveAndKill").detail("Step", "included all second").detail("KillTotal", toKill2.size()).detail("ToKill", describe(toKill2)).detail("ClusterAvailable", g_simulator.isAvailable()); + //TraceEvent("RemoveAndKill").detail("Step", "included all second").detail("KillTotal", toKill2.size()).detail("ToKill", describe(toKill2)).detail("ClusterAvailable", g_simulator.isAvailable()); } return Void(); From e0d2eca7a886d7d3fec7e6966bb2edf3c3424c62 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Tue, 10 Mar 2020 23:38:30 -0700 Subject: [PATCH 0861/1604] checkForExtraDataStores:Add coordinators into stateful process list --- fdbserver/WorkerInterface.actor.h | 1 + fdbserver/workloads/ConsistencyCheck.actor.cpp | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/fdbserver/WorkerInterface.actor.h b/fdbserver/WorkerInterface.actor.h index 4fe0f9c5f9..ae2fea1c92 100644 --- a/fdbserver/WorkerInterface.actor.h +++ b/fdbserver/WorkerInterface.actor.h @@ -67,6 +67,7 @@ struct WorkerInterface { UID id() const { return tLog.getEndpoint().token; } NetworkAddress address() const { return tLog.getEndpoint().getPrimaryAddress(); } + Optional secondaryAddress() const { return tLog.getEndpoint().addresses.secondaryAddress; } WorkerInterface() {} WorkerInterface( const LocalityData& locality ) : locality( locality ) {} diff --git a/fdbserver/workloads/ConsistencyCheck.actor.cpp b/fdbserver/workloads/ConsistencyCheck.actor.cpp index 40aabfd09c..f07dd46f3c 100644 --- a/fdbserver/workloads/ConsistencyCheck.actor.cpp +++ b/fdbserver/workloads/ConsistencyCheck.actor.cpp @@ -1172,6 +1172,7 @@ struct ConsistencyCheckWorkload : TestWorkload state vector storageServers = wait( getStorageServers( cx ) ); auto& db = self->dbInfo->get(); state std::vector logs = db.logSystemConfig.allPresentLogs(); + state std::vector coordWorkers = wait(getCoordWorkers(cx, self->dbInfo)); state std::vector::iterator itr; state bool foundExtraDataStore = false; @@ -1191,8 +1192,13 @@ struct ConsistencyCheckWorkload : TestWorkload statefulProcesses[log.secondaryAddress().get()].insert(log.id()); } } - // TODO: Add coordinators into stateful processes - // Why don't we add coordinator address into statefulProcesses? + // Coordinators are also stateful processes + for (const auto& cWorker: coordWorkers) { + statefulProcesses[cWorker.address()].insert(cWorker.id()); + if (cWorker.secondaryAddress().present()) { + statefulProcesses[cWorker.secondaryAddress().get()].insert(cWorker.id()); + } + } for(itr = workers.begin(); itr != workers.end(); ++itr) { ErrorOr>> stores = wait(itr->interf.diskStoreRequest.getReplyUnlessFailedFor(DiskStoreRequest(false), 2, 0)); From d87ed92f785826f2791892077a45a9de9eae6c89 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Wed, 11 Mar 2020 09:59:11 -0700 Subject: [PATCH 0862/1604] checkForExtraDataStores:Fix compilation error --- fdbserver/workloads/ConsistencyCheck.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/workloads/ConsistencyCheck.actor.cpp b/fdbserver/workloads/ConsistencyCheck.actor.cpp index f07dd46f3c..5b3ce9b035 100644 --- a/fdbserver/workloads/ConsistencyCheck.actor.cpp +++ b/fdbserver/workloads/ConsistencyCheck.actor.cpp @@ -1170,9 +1170,9 @@ struct ConsistencyCheckWorkload : TestWorkload ACTOR Future checkForExtraDataStores(Database cx, ConsistencyCheckWorkload *self) { state vector workers = wait( getWorkers( self->dbInfo ) ); state vector storageServers = wait( getStorageServers( cx ) ); + state std::vector coordWorkers = wait(getCoordWorkers(cx, self->dbInfo)); auto& db = self->dbInfo->get(); state std::vector logs = db.logSystemConfig.allPresentLogs(); - state std::vector coordWorkers = wait(getCoordWorkers(cx, self->dbInfo)); state std::vector::iterator itr; state bool foundExtraDataStore = false; From 1a5b41157e362a953accf7db930589981a3efbf5 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Wed, 11 Mar 2020 11:17:34 -0700 Subject: [PATCH 0863/1604] add test for native transaction object --- fdbserver/workloads/ReportConflictingKeys.actor.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fdbserver/workloads/ReportConflictingKeys.actor.cpp b/fdbserver/workloads/ReportConflictingKeys.actor.cpp index 1d978ca8da..b5934b74d3 100644 --- a/fdbserver/workloads/ReportConflictingKeys.actor.cpp +++ b/fdbserver/workloads/ReportConflictingKeys.actor.cpp @@ -163,6 +163,10 @@ struct ReportConflictingKeysWorkload : TestWorkload { // used for throttling wait(poisson(&lastTime, delay)); if (self->reportConflictingKeys) tr.setOption(FDBTransactionOptions::REPORT_CONFLICTING_KEYS); + // If READ_YOUR_WRITES_DISABLE set, it behaves like native transaction object + // where overlapped conflict ranges are not merged. + if (deterministicRandom()->random01() < 0.5) + tr.setOption(FDBTransactionOptions::READ_YOUR_WRITES_DISABLE); self->addRandomReadConflictRange(&tr, readConflictRanges); self->addRandomWriteConflictRange(&tr); ++self->commits; From d1c56d3b57dcfeda2e35299b9d74728d8c8584e1 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Wed, 11 Mar 2020 12:25:50 -0700 Subject: [PATCH 0864/1604] add constant KeyRefs in SystemData --- fdbclient/SystemData.cpp | 3 +++ fdbclient/SystemData.h | 2 ++ 2 files changed, 5 insertions(+) diff --git a/fdbclient/SystemData.cpp b/fdbclient/SystemData.cpp index f7bf8112fc..5dcac85466 100644 --- a/fdbclient/SystemData.cpp +++ b/fdbclient/SystemData.cpp @@ -28,6 +28,7 @@ const KeyRangeRef systemKeys(systemKeysPrefix, LiteralStringRef("\xff\xff") ); const KeyRangeRef nonMetadataSystemKeys(LiteralStringRef("\xff\x02"), LiteralStringRef("\xff\x03")); 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\xff")); // keyServersKeys.contains(k) iff k.startsWith(keyServersPrefix) const KeyRangeRef keyServersKeys( LiteralStringRef("\xff/keyServers/"), LiteralStringRef("\xff/keyServers0") ); @@ -58,6 +59,8 @@ void decodeKeyServersValue( const ValueRef& value, vector& src, vector } } +const KeyRef conflictingKeysPrefix = LiteralStringRef("/transaction/conflicting_keys/"); +const KeyRef conflictingKeysAbsolutePrefix = conflictingKeysPrefix.withPrefix(specialKeys.begin); const ValueRef conflictingKeysTrue = LiteralStringRef("1"); const ValueRef conflictingKeysFalse = LiteralStringRef("0"); diff --git a/fdbclient/SystemData.h b/fdbclient/SystemData.h index edb8c08aa5..0af5f8fac3 100644 --- a/fdbclient/SystemData.h +++ b/fdbclient/SystemData.h @@ -36,6 +36,7 @@ extern const KeyRangeRef normalKeys; // '' to systemKeys.begin extern const KeyRangeRef systemKeys; // [FF] to [FF][FF] extern const KeyRangeRef nonMetadataSystemKeys; // [FF][00] to [FF][01] extern const KeyRangeRef allKeys; // '' to systemKeys.end +extern const KeyRangeRef specialKeys; // [FF][FF] to [FF][FF][FF][FF] extern const KeyRef afterAllKeys; // "\xff/keyServers/[[begin]]" := "[[vector, vector]]" @@ -64,6 +65,7 @@ const Key serverKeysPrefixFor( UID serverID ); UID serverKeysDecodeServer( const KeyRef& key ); bool serverHasKey( ValueRef storedValue ); +extern const KeyRef conflictingKeysPrefix, conflictingKeysAbsolutePrefix; extern const ValueRef conflictingKeysTrue, conflictingKeysFalse; extern const KeyRef cacheKeysPrefix; From bdabb8638e8d139b57a3181d286456a892471364 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Wed, 11 Mar 2020 12:40:40 -0700 Subject: [PATCH 0865/1604] Change prefix --- fdbclient/ReadYourWrites.actor.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/fdbclient/ReadYourWrites.actor.cpp b/fdbclient/ReadYourWrites.actor.cpp index 97f20dcfb9..769a70cc7b 100644 --- a/fdbclient/ReadYourWrites.actor.cpp +++ b/fdbclient/ReadYourWrites.actor.cpp @@ -1285,12 +1285,11 @@ Future< Standalone > ReadYourWritesTransaction::getRange( // prefix/ : '1' - any keys equal or larger than this key are (probably) conflicting keys // prefix/ : '0' - any keys equal or larger than this key are (definitely) not conflicting keys // Currently, the conflicting keyranges returned are original read_conflict_ranges. - const KeyRef conflictingKeysPrefix = LiteralStringRef("\xff\xff/transaction/conflicting_keys/"); - // TODO : This condition needs to be changed in the future when we have more special keys under "\xff\xff/transaction/" - if (begin.getKey().startsWith(conflictingKeysPrefix) && end.getKey().startsWith(conflictingKeysPrefix)) { - // Remove the special key prefix "\xff\xff/transaction/conflicting_keys/" - KeyRef beginConflictingKey = begin.getKey().removePrefix(conflictingKeysPrefix); - KeyRef endConflictingKey = end.getKey().removePrefix(conflictingKeysPrefix); + // TODO : This interface needs to be integrated into the framework that handles special keys' calls in the future + if (begin.getKey().startsWith(conflictingKeysAbsolutePrefix) && end.getKey().startsWith(conflictingKeysAbsolutePrefix)) { + // Remove the special key prefix "\xff\xff" + KeyRef beginConflictingKey = begin.getKey().removePrefix(specialKeys.begin); + KeyRef endConflictingKey = end.getKey().removePrefix(specialKeys.begin); // Check if the conflicting key range to be read is valid KeyRef maxKey = getMaxReadKey(); @@ -1309,7 +1308,7 @@ Future< Standalone > ReadYourWritesTransaction::getRange( Standalone resultWithPrefix; resultWithPrefix.reserve(resultWithPrefix.arena(), resultWithoutPrefix.size()); for (auto const & kv : resultWithoutPrefix) { - KeyValueRef kvWithPrefix(kv.key.withPrefix(conflictingKeysPrefix, resultWithPrefix.arena()), kv.value); + KeyValueRef kvWithPrefix(kv.key.withPrefix(specialKeys.begin, resultWithPrefix.arena()), kv.value); resultWithPrefix.push_back(resultWithPrefix.arena(), kvWithPrefix); } return resultWithPrefix; From 6ae60870fcabf50363d8c7c6e42d9b95d6637bb4 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Wed, 11 Mar 2020 13:20:40 -0700 Subject: [PATCH 0866/1604] use krmSetRange --- fdbclient/NativeAPI.actor.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index 415bbadf2e..b9782de43a 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -2752,22 +2752,26 @@ ACTOR static Future tryCommit( Database cx, Reference // We create an empty RYWTransaction and write all conflicting key/values to it. // Since it is RYWTr, we can call getRange on it with same parameters given to the original getRange. tr->info.conflictingKeysRYW = std::make_shared(tr->getDatabase()); + state Reference hackTr = + Reference(tr->info.conflictingKeysRYW.get()); + state Standalone> conflictingKRIndices = ci.conflictingKRIndices.get(); // To make the getRange call local, we need to explicitly set the read version here. // This version number 100 set here does nothing but prevent getting read version from the proxy tr->info.conflictingKeysRYW->setVersion(100); // Clear the whole key space, thus, RYWTr knows to only read keys locally tr->info.conflictingKeysRYW->clear(normalKeys); - // in case system keys are conflicting - tr->info.conflictingKeysRYW->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); - tr->info.conflictingKeysRYW->clear(systemKeys); - // merge duplicate indices - const auto cKRs = ci.conflictingKRIndices.get(); - std::set mergedIds(cKRs.begin(), cKRs.end()); + // initialize value + wait(krmSetRange(hackTr, conflictingKeysPrefix, normalKeys, conflictingKeysFalse)); + // drop duplicate indices and merge overlapped ranges + // Note: addReadConflictRange in native transaction object does not merge overlapped ranges + state std::set mergedIds(conflictingKRIndices.begin(), conflictingKRIndices.end()); for (auto const & rCRIndex : mergedIds) { - const KeyRangeRef & kr = req.transaction.read_conflict_ranges[rCRIndex]; - tr->info.conflictingKeysRYW->set(kr.begin, conflictingKeysTrue); - tr->info.conflictingKeysRYW->set(kr.end, conflictingKeysFalse); + const KeyRange kr = req.transaction.read_conflict_ranges[rCRIndex]; + // tr->info.conflictingKeysRYW->set(kr.begin, conflictingKeysTrue); + // tr->info.conflictingKeysRYW->set(kr.end, conflictingKeysFalse); + wait(krmSetRange(hackTr, conflictingKeysPrefix, kr, conflictingKeysTrue)); } + hackTr.extractPtr(); // Avoid the Reference to destroy the RYW object } if (info.debugID.present()) From 02ee4f4c466deb79cee3a323111cd6548da1bf12 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Wed, 11 Mar 2020 22:22:51 -0700 Subject: [PATCH 0867/1604] Update comments --- fdbserver/MasterProxyServer.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/MasterProxyServer.actor.cpp b/fdbserver/MasterProxyServer.actor.cpp index 782f6e0503..9948332f99 100644 --- a/fdbserver/MasterProxyServer.actor.cpp +++ b/fdbserver/MasterProxyServer.actor.cpp @@ -1143,7 +1143,7 @@ ACTOR Future commitBatch( trs[t].reply.sendError(transaction_too_old()); } else { - // If enable the option to report conflicting keys from resolvers, we union all conflicting key ranges here and send back through CommitID + // If enable the option to report conflicting keys from resolvers, we send back all keyranges' indices through CommitID if (trs[t].transaction.report_conflicting_keys) { Standalone> conflictingKRIndices; for (int resolverInd : transactionResolverMap[t]) { From 0094293d50b839fb891df4f782c10dfb7d1bb6e9 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Wed, 11 Mar 2020 23:11:49 -0700 Subject: [PATCH 0868/1604] add const vars --- fdbclient/SystemData.cpp | 2 +- fdbclient/SystemData.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/fdbclient/SystemData.cpp b/fdbclient/SystemData.cpp index 5dcac85466..065358082f 100644 --- a/fdbclient/SystemData.cpp +++ b/fdbclient/SystemData.cpp @@ -60,7 +60,7 @@ void decodeKeyServersValue( const ValueRef& value, vector& src, vector } const KeyRef conflictingKeysPrefix = LiteralStringRef("/transaction/conflicting_keys/"); -const KeyRef conflictingKeysAbsolutePrefix = conflictingKeysPrefix.withPrefix(specialKeys.begin); +const Key conflictingKeysAbsolutePrefix = conflictingKeysPrefix.withPrefix(specialKeys.begin); const ValueRef conflictingKeysTrue = LiteralStringRef("1"); const ValueRef conflictingKeysFalse = LiteralStringRef("0"); diff --git a/fdbclient/SystemData.h b/fdbclient/SystemData.h index 0af5f8fac3..6b9de0fbf1 100644 --- a/fdbclient/SystemData.h +++ b/fdbclient/SystemData.h @@ -65,7 +65,8 @@ const Key serverKeysPrefixFor( UID serverID ); UID serverKeysDecodeServer( const KeyRef& key ); bool serverHasKey( ValueRef storedValue ); -extern const KeyRef conflictingKeysPrefix, conflictingKeysAbsolutePrefix; +extern const KeyRef conflictingKeysPrefix; +extern const Key conflictingKeysAbsolutePrefix; extern const ValueRef conflictingKeysTrue, conflictingKeysFalse; extern const KeyRef cacheKeysPrefix; From c2f0c41c52562774f36ee5c1094aa84b3cacbe47 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Wed, 11 Mar 2020 23:12:38 -0700 Subject: [PATCH 0869/1604] use krmSetRange --- fdbclient/NativeAPI.actor.cpp | 3 ++- .../workloads/ReportConflictingKeys.actor.cpp | 20 +++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index b9782de43a..a869fc3636 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -2761,7 +2761,8 @@ ACTOR static Future tryCommit( Database cx, Reference // Clear the whole key space, thus, RYWTr knows to only read keys locally tr->info.conflictingKeysRYW->clear(normalKeys); // initialize value - wait(krmSetRange(hackTr, conflictingKeysPrefix, normalKeys, conflictingKeysFalse)); + // wait(krmSetRange(hackTr, conflictingKeysPrefix, normalKeys, conflictingKeysFalse)); + tr->info.conflictingKeysRYW->set(conflictingKeysPrefix, conflictingKeysFalse); // drop duplicate indices and merge overlapped ranges // Note: addReadConflictRange in native transaction object does not merge overlapped ranges state std::set mergedIds(conflictingKRIndices.begin(), conflictingKRIndices.end()); diff --git a/fdbserver/workloads/ReportConflictingKeys.actor.cpp b/fdbserver/workloads/ReportConflictingKeys.actor.cpp index b5934b74d3..ceee5e2fe0 100644 --- a/fdbserver/workloads/ReportConflictingKeys.actor.cpp +++ b/fdbserver/workloads/ReportConflictingKeys.actor.cpp @@ -184,24 +184,24 @@ struct ReportConflictingKeysWorkload : TestWorkload { wait(tr.onError(e)); // check API correctness if (!self->skipCorrectnessCheck && self->reportConflictingKeys && isConflict) { - const KeyRef conflictingKeysPreifx = LiteralStringRef("\xff\xff/transaction/conflicting_keys/"); - state KeyRange ckr = KeyRangeRef(LiteralStringRef("").withPrefix(conflictingKeysPreifx), - LiteralStringRef("\xff").withPrefix(conflictingKeysPreifx)); + // const KeyRef conflictingKeysPreifx = LiteralStringRef("\xff\xff/transaction/conflicting_keys/"); + state KeyRange ckr = KeyRangeRef(LiteralStringRef("").withPrefix(conflictingKeysAbsolutePrefix), + LiteralStringRef("\xff\xff").withPrefix(conflictingKeysAbsolutePrefix)); // The getRange here using the special key prefix "\xff\xff/transaction/conflicting_keys/" happens // locally Thus, the error handling is not needed here Future> conflictingKeyRangesFuture = - tr.getRange(ckr, readConflictRanges.size() * 2); + tr.getRange(ckr, readConflictRanges.size() * 2 + 1); ASSERT(conflictingKeyRangesFuture.isReady()); const Standalone conflictingKeyRanges = conflictingKeyRangesFuture.get(); - ASSERT(conflictingKeyRanges.size() && (conflictingKeyRanges.size() % 2 == 0)); - for (int i = 0; i < conflictingKeyRanges.size(); i += 2) { - KeyValueRef startKeyWithPreifx = conflictingKeyRanges[i]; - ASSERT(startKeyWithPreifx.value == conflictingKeysTrue); + ASSERT(conflictingKeyRanges.size() && (conflictingKeyRanges.size() % 2 == 1)); + for (int i = 1; i < conflictingKeyRanges.size(); i += 2) { + KeyValueRef startKeyWithPrefix = conflictingKeyRanges[i]; + ASSERT(startKeyWithPrefix.value == conflictingKeysTrue); KeyValueRef endKeyWithPrefix = conflictingKeyRanges[i + 1]; ASSERT(endKeyWithPrefix.value == conflictingKeysFalse); // Remove the prefix of returning keys - Key startKey = startKeyWithPreifx.key.removePrefix(conflictingKeysPreifx); - Key endKey = endKeyWithPrefix.key.removePrefix(conflictingKeysPreifx); + Key startKey = startKeyWithPrefix.key.removePrefix(conflictingKeysAbsolutePrefix); + Key endKey = endKeyWithPrefix.key.removePrefix(conflictingKeysAbsolutePrefix); KeyRangeRef kr = KeyRangeRef(startKey, endKey); if (!std::any_of(readConflictRanges.begin(), readConflictRanges.end(), [&kr](KeyRange rCR) { // Read_conflict_range remains same in the resolver. From 4e8cb0cb9653bc7e37caaabe68916bbb562b42bc Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Thu, 12 Mar 2020 09:53:00 -0700 Subject: [PATCH 0870/1604] add krmSetRangeCoalescing for RYWTr --- fdbclient/KeyRangeMap.actor.cpp | 67 +++++++++++++++++++++++++++++++++ fdbclient/KeyRangeMap.h | 1 + 2 files changed, 68 insertions(+) diff --git a/fdbclient/KeyRangeMap.actor.cpp b/fdbclient/KeyRangeMap.actor.cpp index d3cb36f833..5dc785b2cf 100644 --- a/fdbclient/KeyRangeMap.actor.cpp +++ b/fdbclient/KeyRangeMap.actor.cpp @@ -216,3 +216,70 @@ ACTOR Future krmSetRangeCoalescing( Transaction *tr, Key mapPrefix, KeyRan return Void(); } + +ACTOR Future krmSetRangeCoalescing( Reference tr, Key mapPrefix, KeyRange range, KeyRange maxRange, Value value ) { + ASSERT(maxRange.contains(range)); + + state KeyRange withPrefix = KeyRangeRef( mapPrefix.toString() + range.begin.toString(), mapPrefix.toString() + range.end.toString() ); + state KeyRange maxWithPrefix = KeyRangeRef( mapPrefix.toString() + maxRange.begin.toString(), mapPrefix.toString() + maxRange.end.toString() ); + + state vector>> keys; + keys.push_back(tr->getRange(lastLessThan(withPrefix.begin), firstGreaterOrEqual(withPrefix.begin), 1, true)); + keys.push_back(tr->getRange(lastLessOrEqual(withPrefix.end), firstGreaterThan(withPrefix.end) + 1, 2, true)); + wait(waitForAll(keys)); + + //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(""); + + state Key beginKey = withPrefix.begin; + if(beginValue == value) { + bool outsideRange = !hasBegin || beginRange[0].key < maxWithPrefix.begin; + beginKey = outsideRange ? maxWithPrefix.begin : beginRange[0].key; + } + + //Determine how far to extend this range at the end + auto endRange = keys[1].get(); + 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(""); + bool valueMatches = value == existingValue; + + KeyRange conflictRange = KeyRangeRef( hasBegin ? beginRange[0].key : mapPrefix, withPrefix.begin ); + if( !conflictRange.empty() ) + tr->addReadConflictRange( conflictRange ); + + conflictRange = KeyRangeRef( hasEnd ? endRange[0].key : mapPrefix, hasNext ? keyAfter(endRange.end()[-1].key) : strinc( mapPrefix ) ); + if( !conflictRange.empty() ) + tr->addReadConflictRange( conflictRange ); + + state Key endKey; + state Value endValue; + + //Case 1: Coalesce completely with the following range + if(hasNext && endRange.end()[-1].key <= maxWithPrefix.end && valueMatches) { + endKey = endRange.end()[-1].key; + endValue = endRange.end()[-1].value; + } + + //Case 2: Coalesce with the following range only up to the end of maxRange + else if(valueMatches) { + endKey = maxWithPrefix.end; + endValue = existingValue; + } + + //Case 3: Don't coalesce + else { + endKey = withPrefix.end; + endValue = existingValue; + } + + tr->clear(KeyRangeRef(beginKey, endKey)); + + ASSERT(value != endValue || endKey == maxWithPrefix.end); + tr->set(beginKey, value); + tr->set(endKey, endValue); + + return Void(); +} diff --git a/fdbclient/KeyRangeMap.h b/fdbclient/KeyRangeMap.h index aafd92cb69..f34badb988 100644 --- a/fdbclient/KeyRangeMap.h +++ b/fdbclient/KeyRangeMap.h @@ -103,6 +103,7 @@ void krmSetPreviouslyEmptyRange( struct CommitTransactionRef& tr, Arena& trArena Future krmSetRange( Transaction* const& tr, Key const& mapPrefix, KeyRange const& range, Value const& value ); Future krmSetRange( Reference const& tr, Key const& mapPrefix, KeyRange const& range, Value const& value ); Future krmSetRangeCoalescing( Transaction* const& tr, Key const& mapPrefix, KeyRange const& range, KeyRange const& maxRange, Value const& value ); +Future krmSetRangeCoalescing( Reference const& tr, Key const& mapPrefix, KeyRange const& range, KeyRange const& maxRange, Value const& value ); Standalone krmDecodeRanges( KeyRef mapPrefix, KeyRange keys, Standalone kv ); template From a9136f3f72557f90d9cfd0b40997385efe68b19d Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Thu, 12 Mar 2020 10:18:31 -0700 Subject: [PATCH 0871/1604] Add waitForUnreliableExtraStoreReboot to wait for extra store to reboot --- fdbrpc/FlowTransport.h | 6 +- fdbserver/worker.actor.cpp | 8 +- .../workloads/ConsistencyCheck.actor.cpp | 98 +++++++++++++++++-- 3 files changed, 99 insertions(+), 13 deletions(-) diff --git a/fdbrpc/FlowTransport.h b/fdbrpc/FlowTransport.h index 5c5ba3990f..b2cb7bd4fb 100644 --- a/fdbrpc/FlowTransport.h +++ b/fdbrpc/FlowTransport.h @@ -45,9 +45,9 @@ public: void choosePrimaryAddress() { if(addresses.secondaryAddress.present() && !g_network->getLocalAddresses().secondaryAddress.present() && (addresses.address.isTLS() != g_network->getLocalAddresses().address.isTLS())) { - if (addresses.address.isTLS()) { - TraceEvent(SevWarn, "MXDEBUGChoosePrimaryAddressSwap").detail("PrimaryAddressWillBeTLS", addresses.secondaryAddress.get().isTLS()).backtrace(); - } + // if (addresses.address.isTLS()) { + // TraceEvent(SevWarn, "MXDEBUGChoosePrimaryAddressSwap").detail("PrimaryAddressWillBeTLS", addresses.secondaryAddress.get().isTLS()).backtrace(); + // } std::swap(addresses.address, addresses.secondaryAddress.get()); } } diff --git a/fdbserver/worker.actor.cpp b/fdbserver/worker.actor.cpp index 9b8a5744ce..10534ea7f5 100644 --- a/fdbserver/worker.actor.cpp +++ b/fdbserver/worker.actor.cpp @@ -400,6 +400,8 @@ std::vector< DiskStore > getDiskStores( std::string folder ) { return result; } +// Register the worker interf to cluster controller (cc) and +// re-register the worker when key roles interface, e.g., cc, dd, ratekeeper, change. ACTOR Future registrationClient( Reference>> ccInterface, WorkerInterface interf, @@ -424,7 +426,7 @@ ACTOR Future registrationClient( Future registrationReply = ccInterface->get().present() ? brokenPromiseToNever( ccInterface->get().get().registerWorker.getReply(request) ) : Never(); choose { when ( RegisterWorkerReply reply = wait( registrationReply )) { - processClass = reply.processClass; + processClass = reply.processClass; asyncPriorityInfo->set( reply.priorityInfo ); if(!reply.storageCache.present()) { @@ -434,7 +436,7 @@ ACTOR Future registrationClient( StorageServerInterface recruited; recruited.locality = locality; recruited.initEndpoints(); - + std::map details; startRole( Role::STORAGE_CACHE, recruited.id(), interf.id(), details ); @@ -1127,7 +1129,7 @@ ACTOR Future workerServer( Future backupProcess = backupWorker(recruited, req, dbInfo); errorForwarders.add(forwardError(errors, Role::BACKUP, recruited.id(), backupProcess)); - TraceEvent("Backup_InitRequest", req.reqId).detail("BackupId", recruited.id()); + TraceEvent("BackupInitRequest", req.reqId).detail("BackupId", recruited.id()); InitializeBackupReply reply(recruited, req.backupEpoch); req.reply.send(reply); } diff --git a/fdbserver/workloads/ConsistencyCheck.actor.cpp b/fdbserver/workloads/ConsistencyCheck.actor.cpp index 5b3ce9b035..2ba01b1591 100644 --- a/fdbserver/workloads/ConsistencyCheck.actor.cpp +++ b/fdbserver/workloads/ConsistencyCheck.actor.cpp @@ -34,6 +34,9 @@ #include "fdbclient/ManagementAPI.actor.h" #include "flow/actorcompiler.h" // This must be the last #include. +//#define SevCCheckInfo SevVerbose +#define SevCCheckInfo SevInfo + struct ConsistencyCheckWorkload : TestWorkload { //Whether or not we should perform checks that will only pass if the database is in a quiescent state @@ -292,6 +295,7 @@ struct ConsistencyCheckWorkload : TestWorkload } wait(::success(self->checkForStorage(cx, configuration, self))); + wait(::success(self->waitForUnreliableExtraStoreReboot(cx, self))); wait(::success(self->checkForExtraDataStores(cx, self))); //Check that each machine is operating as its desired class @@ -1167,9 +1171,89 @@ struct ConsistencyCheckWorkload : TestWorkload return true; } + ACTOR Future waitForUnreliableExtraStoreReboot(Database cx, ConsistencyCheckWorkload *self) { + state int waitCount = 0; + loop { + state std::vector workers = wait( getWorkers( self->dbInfo ) ); + state std::vector storageServers = wait( getStorageServers( cx ) ); + state std::vector coordWorkers = wait(getCoordWorkers(cx, self->dbInfo)); + auto& db = self->dbInfo->get(); + state std::vector logs = db.logSystemConfig.allPresentLogs(); + + state std::vector::iterator itr; + state bool foundExtraDataStore = false; + state std::vector protectedProcessesToKill; + + state std::map> statefulProcesses; + for (const auto& ss : storageServers) { + statefulProcesses[ss.address()].insert(ss.id()); + // Add both addresses so that we will not mistakenly trigger ConsistencyCheck_ExtraDataStore + if (ss.secondaryAddress().present()) { + statefulProcesses[ss.secondaryAddress().get()].insert(ss.id()); + } + TraceEvent(SevCCheckInfo, "StatefulProcess").detail("StorageServer", ss.id()).detail("PrimaryAddress", ss.address().toString()).detail("SecondaryAddress", ss.secondaryAddress().present() ? ss.secondaryAddress().get().toString() : "Unset"); + } + for (const auto& log : logs) { + statefulProcesses[log.address()].insert(log.id()); + if (log.secondaryAddress().present()) { + statefulProcesses[log.secondaryAddress().get()].insert(log.id()); + } + TraceEvent(SevCCheckInfo, "StatefulProcess").detail("Log", log.id()).detail("PrimaryAddress", log.address().toString()).detail("SecondaryAddress", log.secondaryAddress().present() ? log.secondaryAddress().get().toString() : "Unset"); + } + // Coordinators are also stateful processes + for (const auto& cWorker : coordWorkers) { + statefulProcesses[cWorker.address()].insert(cWorker.id()); + if (cWorker.secondaryAddress().present()) { + statefulProcesses[cWorker.secondaryAddress().get()].insert(cWorker.id()); + } + TraceEvent(SevCCheckInfo, "StatefulProcess").detail("Coordinator", cWorker.id()).detail("PrimaryAddress", cWorker.address().toString()).detail("SecondaryAddress", cWorker.secondaryAddress().present() ? cWorker.secondaryAddress().get().toString() : "Unset"); + } + + // Wait for extra store process that is unreliable (i.e., in the process of rebooting) to finish; Otherwise, + // the test will try to kill the extra store process which may be protected. This causes failure. + state bool protectedExtraStoreUnreliable = false; + + for(itr = workers.begin(); itr != workers.end(); ++itr) { + ErrorOr>> stores = wait(itr->interf.diskStoreRequest.getReplyUnlessFailedFor(DiskStoreRequest(false), 2, 0)); + if(stores.isError()) { + TraceEvent("ConsistencyCheck_GetDataStoreFailure").error(stores.getError()).detail("Address", itr->interf.address()); + self->testFailure("Failed to get data stores"); + return false; + } + + TraceEvent(SevCCheckInfo, "CheckProtectedExtraStoreRebootProgress").detail("Worker", itr->interf.id().toString()).detail("PrimaryAddress", itr->interf.address().toString()).detail("SecondaryAddress", itr->interf.secondaryAddress().present() ? itr->interf.secondaryAddress().get().toString() : "Unset"); + for (const auto& id : stores.get()) { + if (statefulProcesses[itr->interf.address()].count(id)) { + continue; + } else { + if(g_network->isSimulated()) { + auto p = g_simulator.getProcessByAddress(itr->interf.address()); + if (g_simulator.protectedAddresses.count(p->address) && !p->isReliable()) { + protectedExtraStoreUnreliable = true; + break; + } + } + } + } + if (protectedExtraStoreUnreliable) { + break; + } + } + if (protectedExtraStoreUnreliable) { + wait(delay(10.0)); + waitCount++; + } + if (waitCount > 20) { + TraceEvent(SevError, "ProtectedExtraStoreUnreliableStuck").detail("ExpectedBehavior", "Extra store should be cleaned up after process reboot"); + break; + } + } + return waitCount <= 20; + } + ACTOR Future checkForExtraDataStores(Database cx, ConsistencyCheckWorkload *self) { - state vector workers = wait( getWorkers( self->dbInfo ) ); - state vector storageServers = wait( getStorageServers( cx ) ); + state std::vector workers = wait( getWorkers( self->dbInfo ) ); + state std::vector storageServers = wait( getStorageServers( cx ) ); state std::vector coordWorkers = wait(getCoordWorkers(cx, self->dbInfo)); auto& db = self->dbInfo->get(); state std::vector logs = db.logSystemConfig.allPresentLogs(); @@ -1185,19 +1269,22 @@ struct ConsistencyCheckWorkload : TestWorkload if (ss.secondaryAddress().present()) { statefulProcesses[ss.secondaryAddress().get()].insert(ss.id()); } + TraceEvent(SevCCheckInfo, "StatefulProcess").detail("StorageServer", ss.id()).detail("PrimaryAddress", ss.address().toString()).detail("SecondaryAddress", ss.secondaryAddress().present() ? ss.secondaryAddress().get().toString() : "Unset"); } for (const auto& log : logs) { statefulProcesses[log.address()].insert(log.id()); if (log.secondaryAddress().present()) { statefulProcesses[log.secondaryAddress().get()].insert(log.id()); } + TraceEvent(SevCCheckInfo, "StatefulProcess").detail("Log", log.id()).detail("PrimaryAddress", log.address().toString()).detail("SecondaryAddress", log.secondaryAddress().present() ? log.secondaryAddress().get().toString() : "Unset"); } // Coordinators are also stateful processes - for (const auto& cWorker: coordWorkers) { + for (const auto& cWorker : coordWorkers) { statefulProcesses[cWorker.address()].insert(cWorker.id()); if (cWorker.secondaryAddress().present()) { statefulProcesses[cWorker.secondaryAddress().get()].insert(cWorker.id()); } + TraceEvent(SevCCheckInfo, "StatefulProcess").detail("Coordinator", cWorker.id()).detail("PrimaryAddress", cWorker.address().toString()).detail("SecondaryAddress", cWorker.secondaryAddress().present() ? cWorker.secondaryAddress().get().toString() : "Unset"); } for(itr = workers.begin(); itr != workers.end(); ++itr) { @@ -1208,6 +1295,7 @@ struct ConsistencyCheckWorkload : TestWorkload return false; } + TraceEvent(SevCCheckInfo, "ConsistencyCheck_ExtraDataStore").detail("Worker", itr->interf.id().toString()).detail("PrimaryAddress", itr->interf.address().toString()).detail("SecondaryAddress", itr->interf.secondaryAddress().present() ? itr->interf.secondaryAddress().get().toString() : "Unset"); for (const auto& id : stores.get()) { // if (statefulProcesses[itr->interf.address()].count(id)) { // continue; @@ -1227,10 +1315,6 @@ struct ConsistencyCheckWorkload : TestWorkload .detail("Reliable", p->isReliable()) .detail("ReliableInfo", p->getReliableInfo()) .detail("KillOrRebootProcess", p->address); - // if (g_simulator.protectedAddresses.count(machine->address)) { - // protectedProcessesToKill.push_back(p); - // continue; - // } if(p->isReliable()) { g_simulator.rebootProcess(p, ISimulator::RebootProcess); } else { From 1759d5c8c4d4fd3cec756a4bc20b1d233747970c Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Thu, 12 Mar 2020 10:18:53 -0700 Subject: [PATCH 0872/1604] Apply clang-format --- fdbclient/StorageServerInterface.h | 4 +- fdbrpc/FlowTransport.h | 3 +- fdbrpc/sim2.actor.cpp | 2 +- fdbserver/LogSystemDiskQueueAdapter.actor.cpp | 10 ++- fdbserver/TLogInterface.h | 2 +- .../workloads/ConsistencyCheck.actor.cpp | 87 ++++++++++++++----- 6 files changed, 78 insertions(+), 30 deletions(-) diff --git a/fdbclient/StorageServerInterface.h b/fdbclient/StorageServerInterface.h index f54cce4d60..3ba0ea7562 100644 --- a/fdbclient/StorageServerInterface.h +++ b/fdbclient/StorageServerInterface.h @@ -74,11 +74,11 @@ struct StorageServerInterface { explicit StorageServerInterface(UID uid) : uniqueID( uid ) {} StorageServerInterface() : uniqueID( deterministicRandom()->randomUniqueID() ) {} NetworkAddress address() const { return getValue.getEndpoint().getPrimaryAddress(); } - Optional secondaryAddress() const {return getValue.getEndpoint().addresses.secondaryAddress;} + Optional secondaryAddress() const { return getValue.getEndpoint().addresses.secondaryAddress; } UID id() const { return uniqueID; } std::string toString() const { return id().shortString(); } template - void serialize( Ar& ar ) { + void serialize(Ar& ar) { // StorageServerInterface is persisted in the database and in the tLog's data structures, so changes here have to be // versioned carefully! diff --git a/fdbrpc/FlowTransport.h b/fdbrpc/FlowTransport.h index b2cb7bd4fb..1f76cf7337 100644 --- a/fdbrpc/FlowTransport.h +++ b/fdbrpc/FlowTransport.h @@ -46,7 +46,8 @@ public: void choosePrimaryAddress() { if(addresses.secondaryAddress.present() && !g_network->getLocalAddresses().secondaryAddress.present() && (addresses.address.isTLS() != g_network->getLocalAddresses().address.isTLS())) { // if (addresses.address.isTLS()) { - // TraceEvent(SevWarn, "MXDEBUGChoosePrimaryAddressSwap").detail("PrimaryAddressWillBeTLS", addresses.secondaryAddress.get().isTLS()).backtrace(); + // TraceEvent(SevWarn, "MXDEBUGChoosePrimaryAddressSwap").detail("PrimaryAddressWillBeTLS", + // addresses.secondaryAddress.get().isTLS()).backtrace(); // } std::swap(addresses.address, addresses.secondaryAddress.get()); } diff --git a/fdbrpc/sim2.actor.cpp b/fdbrpc/sim2.actor.cpp index 639df59dfb..55be35e171 100644 --- a/fdbrpc/sim2.actor.cpp +++ b/fdbrpc/sim2.actor.cpp @@ -1029,7 +1029,7 @@ public: NetworkAddressList addresses; addresses.address = NetworkAddress(ip, port, true, sslEnabled); - if(listenPerProcess == 2) { // listenPerProcess is only 1 or 2 + if (listenPerProcess == 2) { // listenPerProcess is only 1 or 2 addresses.secondaryAddress = NetworkAddress(ip, port+1, true, false); } diff --git a/fdbserver/LogSystemDiskQueueAdapter.actor.cpp b/fdbserver/LogSystemDiskQueueAdapter.actor.cpp index ec4727a09a..b8612e9033 100644 --- a/fdbserver/LogSystemDiskQueueAdapter.actor.cpp +++ b/fdbserver/LogSystemDiskQueueAdapter.actor.cpp @@ -60,8 +60,14 @@ public: } } } - TraceEvent("PeekNextGetMore").detail("Total", self->totalRecoveredBytes).detail("Queue", self->recoveryQueue.size()).detail("Bytes", bytes).detail("Loc", self->recoveryLoc) - .detail("End", self->logSystem->getEnd()).detail("HasMessage", self->cursor->hasMessage()).detail("Version", self->cursor->version().version); + TraceEvent("PeekNextGetMore") + .detail("Total", self->totalRecoveredBytes) + .detail("Queue", self->recoveryQueue.size()) + .detail("Bytes", bytes) + .detail("Loc", self->recoveryLoc) + .detail("End", self->logSystem->getEnd()) + .detail("HasMessage", self->cursor->hasMessage()) + .detail("Version", self->cursor->version().version); if(self->cursor->popped() != 0 || (!self->hasDiscardedData && BUGGIFY_WITH_PROB(0.01))) { TEST(true); //disk adapter reset diff --git a/fdbserver/TLogInterface.h b/fdbserver/TLogInterface.h index e89d2301b4..5440202cb9 100644 --- a/fdbserver/TLogInterface.h +++ b/fdbserver/TLogInterface.h @@ -58,7 +58,7 @@ struct TLogInterface { std::string toString() const { return id().shortString(); } bool operator == ( TLogInterface const& r ) const { return id() == r.id(); } NetworkAddress address() const { return peekMessages.getEndpoint().getPrimaryAddress(); } - Optional secondaryAddress() const {return peekMessages.getEndpoint().addresses.secondaryAddress;} + Optional secondaryAddress() const { return peekMessages.getEndpoint().addresses.secondaryAddress; } void initEndpoints() { getQueuingMetrics.getEndpoint( TaskPriority::TLogQueuingMetrics ); popMessages.getEndpoint( TaskPriority::TLogPop ); diff --git a/fdbserver/workloads/ConsistencyCheck.actor.cpp b/fdbserver/workloads/ConsistencyCheck.actor.cpp index 2ba01b1591..1c5795bd54 100644 --- a/fdbserver/workloads/ConsistencyCheck.actor.cpp +++ b/fdbserver/workloads/ConsistencyCheck.actor.cpp @@ -1171,11 +1171,11 @@ struct ConsistencyCheckWorkload : TestWorkload return true; } - ACTOR Future waitForUnreliableExtraStoreReboot(Database cx, ConsistencyCheckWorkload *self) { + ACTOR Future waitForUnreliableExtraStoreReboot(Database cx, ConsistencyCheckWorkload* self) { state int waitCount = 0; loop { - state std::vector workers = wait( getWorkers( self->dbInfo ) ); - state std::vector storageServers = wait( getStorageServers( cx ) ); + state std::vector workers = wait(getWorkers(self->dbInfo)); + state std::vector storageServers = wait(getStorageServers(cx)); state std::vector coordWorkers = wait(getCoordWorkers(cx, self->dbInfo)); auto& db = self->dbInfo->get(); state std::vector logs = db.logSystemConfig.allPresentLogs(); @@ -1191,14 +1191,22 @@ struct ConsistencyCheckWorkload : TestWorkload if (ss.secondaryAddress().present()) { statefulProcesses[ss.secondaryAddress().get()].insert(ss.id()); } - TraceEvent(SevCCheckInfo, "StatefulProcess").detail("StorageServer", ss.id()).detail("PrimaryAddress", ss.address().toString()).detail("SecondaryAddress", ss.secondaryAddress().present() ? ss.secondaryAddress().get().toString() : "Unset"); + TraceEvent(SevCCheckInfo, "StatefulProcess") + .detail("StorageServer", ss.id()) + .detail("PrimaryAddress", ss.address().toString()) + .detail("SecondaryAddress", + ss.secondaryAddress().present() ? ss.secondaryAddress().get().toString() : "Unset"); } for (const auto& log : logs) { statefulProcesses[log.address()].insert(log.id()); if (log.secondaryAddress().present()) { statefulProcesses[log.secondaryAddress().get()].insert(log.id()); } - TraceEvent(SevCCheckInfo, "StatefulProcess").detail("Log", log.id()).detail("PrimaryAddress", log.address().toString()).detail("SecondaryAddress", log.secondaryAddress().present() ? log.secondaryAddress().get().toString() : "Unset"); + TraceEvent(SevCCheckInfo, "StatefulProcess") + .detail("Log", log.id()) + .detail("PrimaryAddress", log.address().toString()) + .detail("SecondaryAddress", + log.secondaryAddress().present() ? log.secondaryAddress().get().toString() : "Unset"); } // Coordinators are also stateful processes for (const auto& cWorker : coordWorkers) { @@ -1206,27 +1214,40 @@ struct ConsistencyCheckWorkload : TestWorkload if (cWorker.secondaryAddress().present()) { statefulProcesses[cWorker.secondaryAddress().get()].insert(cWorker.id()); } - TraceEvent(SevCCheckInfo, "StatefulProcess").detail("Coordinator", cWorker.id()).detail("PrimaryAddress", cWorker.address().toString()).detail("SecondaryAddress", cWorker.secondaryAddress().present() ? cWorker.secondaryAddress().get().toString() : "Unset"); + TraceEvent(SevCCheckInfo, "StatefulProcess") + .detail("Coordinator", cWorker.id()) + .detail("PrimaryAddress", cWorker.address().toString()) + .detail("SecondaryAddress", cWorker.secondaryAddress().present() + ? cWorker.secondaryAddress().get().toString() + : "Unset"); } // Wait for extra store process that is unreliable (i.e., in the process of rebooting) to finish; Otherwise, // the test will try to kill the extra store process which may be protected. This causes failure. state bool protectedExtraStoreUnreliable = false; - for(itr = workers.begin(); itr != workers.end(); ++itr) { - ErrorOr>> stores = wait(itr->interf.diskStoreRequest.getReplyUnlessFailedFor(DiskStoreRequest(false), 2, 0)); - if(stores.isError()) { - TraceEvent("ConsistencyCheck_GetDataStoreFailure").error(stores.getError()).detail("Address", itr->interf.address()); + for (itr = workers.begin(); itr != workers.end(); ++itr) { + ErrorOr>> stores = + wait(itr->interf.diskStoreRequest.getReplyUnlessFailedFor(DiskStoreRequest(false), 2, 0)); + if (stores.isError()) { + TraceEvent("ConsistencyCheck_GetDataStoreFailure") + .error(stores.getError()) + .detail("Address", itr->interf.address()); self->testFailure("Failed to get data stores"); return false; } - TraceEvent(SevCCheckInfo, "CheckProtectedExtraStoreRebootProgress").detail("Worker", itr->interf.id().toString()).detail("PrimaryAddress", itr->interf.address().toString()).detail("SecondaryAddress", itr->interf.secondaryAddress().present() ? itr->interf.secondaryAddress().get().toString() : "Unset"); + TraceEvent(SevCCheckInfo, "CheckProtectedExtraStoreRebootProgress") + .detail("Worker", itr->interf.id().toString()) + .detail("PrimaryAddress", itr->interf.address().toString()) + .detail("SecondaryAddress", itr->interf.secondaryAddress().present() + ? itr->interf.secondaryAddress().get().toString() + : "Unset"); for (const auto& id : stores.get()) { if (statefulProcesses[itr->interf.address()].count(id)) { continue; - } else { - if(g_network->isSimulated()) { + } else { + if (g_network->isSimulated()) { auto p = g_simulator.getProcessByAddress(itr->interf.address()); if (g_simulator.protectedAddresses.count(p->address) && !p->isReliable()) { protectedExtraStoreUnreliable = true; @@ -1244,7 +1265,8 @@ struct ConsistencyCheckWorkload : TestWorkload waitCount++; } if (waitCount > 20) { - TraceEvent(SevError, "ProtectedExtraStoreUnreliableStuck").detail("ExpectedBehavior", "Extra store should be cleaned up after process reboot"); + TraceEvent(SevError, "ProtectedExtraStoreUnreliableStuck") + .detail("ExpectedBehavior", "Extra store should be cleaned up after process reboot"); break; } } @@ -1252,8 +1274,8 @@ struct ConsistencyCheckWorkload : TestWorkload } ACTOR Future checkForExtraDataStores(Database cx, ConsistencyCheckWorkload *self) { - state std::vector workers = wait( getWorkers( self->dbInfo ) ); - state std::vector storageServers = wait( getStorageServers( cx ) ); + state std::vector workers = wait(getWorkers(self->dbInfo)); + state std::vector storageServers = wait(getStorageServers(cx)); state std::vector coordWorkers = wait(getCoordWorkers(cx, self->dbInfo)); auto& db = self->dbInfo->get(); state std::vector logs = db.logSystemConfig.allPresentLogs(); @@ -1269,14 +1291,22 @@ struct ConsistencyCheckWorkload : TestWorkload if (ss.secondaryAddress().present()) { statefulProcesses[ss.secondaryAddress().get()].insert(ss.id()); } - TraceEvent(SevCCheckInfo, "StatefulProcess").detail("StorageServer", ss.id()).detail("PrimaryAddress", ss.address().toString()).detail("SecondaryAddress", ss.secondaryAddress().present() ? ss.secondaryAddress().get().toString() : "Unset"); + TraceEvent(SevCCheckInfo, "StatefulProcess") + .detail("StorageServer", ss.id()) + .detail("PrimaryAddress", ss.address().toString()) + .detail("SecondaryAddress", + ss.secondaryAddress().present() ? ss.secondaryAddress().get().toString() : "Unset"); } for (const auto& log : logs) { statefulProcesses[log.address()].insert(log.id()); if (log.secondaryAddress().present()) { statefulProcesses[log.secondaryAddress().get()].insert(log.id()); } - TraceEvent(SevCCheckInfo, "StatefulProcess").detail("Log", log.id()).detail("PrimaryAddress", log.address().toString()).detail("SecondaryAddress", log.secondaryAddress().present() ? log.secondaryAddress().get().toString() : "Unset"); + TraceEvent(SevCCheckInfo, "StatefulProcess") + .detail("Log", log.id()) + .detail("PrimaryAddress", log.address().toString()) + .detail("SecondaryAddress", + log.secondaryAddress().present() ? log.secondaryAddress().get().toString() : "Unset"); } // Coordinators are also stateful processes for (const auto& cWorker : coordWorkers) { @@ -1284,7 +1314,11 @@ struct ConsistencyCheckWorkload : TestWorkload if (cWorker.secondaryAddress().present()) { statefulProcesses[cWorker.secondaryAddress().get()].insert(cWorker.id()); } - TraceEvent(SevCCheckInfo, "StatefulProcess").detail("Coordinator", cWorker.id()).detail("PrimaryAddress", cWorker.address().toString()).detail("SecondaryAddress", cWorker.secondaryAddress().present() ? cWorker.secondaryAddress().get().toString() : "Unset"); + TraceEvent(SevCCheckInfo, "StatefulProcess") + .detail("Coordinator", cWorker.id()) + .detail("PrimaryAddress", cWorker.address().toString()) + .detail("SecondaryAddress", + cWorker.secondaryAddress().present() ? cWorker.secondaryAddress().get().toString() : "Unset"); } for(itr = workers.begin(); itr != workers.end(); ++itr) { @@ -1295,7 +1329,12 @@ struct ConsistencyCheckWorkload : TestWorkload return false; } - TraceEvent(SevCCheckInfo, "ConsistencyCheck_ExtraDataStore").detail("Worker", itr->interf.id().toString()).detail("PrimaryAddress", itr->interf.address().toString()).detail("SecondaryAddress", itr->interf.secondaryAddress().present() ? itr->interf.secondaryAddress().get().toString() : "Unset"); + TraceEvent(SevCCheckInfo, "ConsistencyCheck_ExtraDataStore") + .detail("Worker", itr->interf.id().toString()) + .detail("PrimaryAddress", itr->interf.address().toString()) + .detail("SecondaryAddress", itr->interf.secondaryAddress().present() + ? itr->interf.secondaryAddress().get().toString() + : "Unset"); for (const auto& id : stores.get()) { // if (statefulProcesses[itr->interf.address()].count(id)) { // continue; @@ -1306,10 +1345,12 @@ 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 auto 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. + // 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") - .detail("Address", itr->interf.address()) // worker's primary address (i.e., the first address) - .detail("ProcessAddress", p->address) + .detail("Address", + itr->interf.address()) // worker's primary address (i.e., the first address) + .detail("ProcessAddress", p->address) .detail("DataStoreID", id) .detail("Protected", g_simulator.protectedAddresses.count(itr->interf.address())) .detail("Reliable", p->isReliable()) From 8cdf918316be5b9b249d78d3b0df46cc20159acb Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Thu, 12 Mar 2020 11:06:53 -0700 Subject: [PATCH 0873/1604] Add logging when file identifiers don't match --- flow/ObjectSerializer.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/flow/ObjectSerializer.h b/flow/ObjectSerializer.h index 1d5b6e3684..fbeee1e67d 100644 --- a/flow/ObjectSerializer.h +++ b/flow/ObjectSerializer.h @@ -78,7 +78,10 @@ public: void deserialize(FileIdentifier file_identifier, Items&... items) { const uint8_t* data = static_cast(this)->data(); LoadContext context(static_cast(this)); - ASSERT(read_file_identifier(data) == file_identifier); + if(read_file_identifier(data) != file_identifier) { + TraceEvent(SevError, "MismatchedFileIdentifier").detail("Expected", file_identifier).detail("Read", read_file_identifier(data)); + ASSERT(false); + } load_members(data, context, items...); } From 2466749648176f074dd4337f3eb8d2c154c6f713 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Thu, 12 Mar 2020 11:17:49 -0700 Subject: [PATCH 0874/1604] Don't disallow allocation tracking when a trace event is open because we now have state trace events. Instead, only block allocation tracking while we are in the middle of allocation tracking already to prevent recursion. --- flow/Arena.cpp | 4 +++- flow/FastAlloc.cpp | 4 +++- flow/Trace.cpp | 7 +------ flow/Trace.h | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/flow/Arena.cpp b/flow/Arena.cpp index 7701c13e91..88837e102b 100644 --- a/flow/Arena.cpp +++ b/flow/Arena.cpp @@ -184,9 +184,11 @@ ArenaBlock* ArenaBlock::create(int dataSize, Reference& next) { b->bigSize = reqSize; b->bigUsed = sizeof(ArenaBlock); - if (FLOW_KNOBS && g_trace_depth == 0 && + if (FLOW_KNOBS && !g_tracing_allocation && nondeterministicRandom()->random01() < (reqSize / FLOW_KNOBS->HUGE_ARENA_LOGGING_BYTES)) { + g_tracing_allocation = true; hugeArenaSample(reqSize); + g_tracing_allocation = false; } g_hugeArenaMemory.fetch_add(reqSize); diff --git a/flow/FastAlloc.cpp b/flow/FastAlloc.cpp index ac5f4c79b1..8cac3948b4 100644 --- a/flow/FastAlloc.cpp +++ b/flow/FastAlloc.cpp @@ -445,8 +445,10 @@ void FastAllocator::getMagazine() { // FIXME: We should be able to allocate larger magazine sizes here if we // detect that the underlying system supports hugepages. Using hugepages // with smaller-than-2MiB magazine sizes strands memory. See issue #909. - if(FLOW_KNOBS && g_trace_depth == 0 && nondeterministicRandom()->random01() < (magazine_size * Size)/FLOW_KNOBS->FAST_ALLOC_LOGGING_BYTES) { + if(FLOW_KNOBS && !g_tracing_allocation && nondeterministicRandom()->random01() < (magazine_size * Size)/FLOW_KNOBS->FAST_ALLOC_LOGGING_BYTES) { + g_tracing_allocation = true; TraceEvent("GetMagazineSample").detail("Size", Size).backtrace(); + g_tracing_allocation = false; } block = (void **)::allocate(magazine_size * Size, false); #endif diff --git a/flow/Trace.cpp b/flow/Trace.cpp index 1b3f8199c0..9f6a77bc1f 100644 --- a/flow/Trace.cpp +++ b/flow/Trace.cpp @@ -43,7 +43,7 @@ #undef min #endif -thread_local int g_trace_depth = 0; +thread_local bool g_tracing_allocation = false; class DummyThreadPool : public IThreadPool, ReferenceCounted { public: @@ -698,14 +698,12 @@ TraceEvent& TraceEvent::operator=(TraceEvent &&ev) { } TraceEvent::TraceEvent( const char* type, UID id ) : id(id), type(type), severity(SevInfo), initialized(false), enabled(true), logged(false) { - g_trace_depth++; setMaxFieldLength(0); setMaxEventLength(0); } TraceEvent::TraceEvent( Severity severity, const char* type, UID id ) : id(id), type(type), severity(severity), initialized(false), logged(false), enabled(g_network == nullptr || FLOW_KNOBS->MIN_TRACE_SEVERITY <= severity) { - g_trace_depth++; setMaxFieldLength(0); setMaxEventLength(0); } @@ -715,7 +713,6 @@ TraceEvent::TraceEvent( TraceInterval& interval, UID id ) initialized(false), logged(false), enabled(g_network == nullptr || FLOW_KNOBS->MIN_TRACE_SEVERITY <= interval.severity) { - g_trace_depth++; setMaxFieldLength(0); setMaxEventLength(0); @@ -727,7 +724,6 @@ TraceEvent::TraceEvent( Severity severity, TraceInterval& interval, UID id ) initialized(false), logged(false), enabled(g_network == nullptr || FLOW_KNOBS->MIN_TRACE_SEVERITY <= severity) { - g_trace_depth++; setMaxFieldLength(0); setMaxEventLength(0); @@ -1014,7 +1010,6 @@ void TraceEvent::log() { TraceEvent(SevError, "TraceEventLoggingError").error(e,true); } delete tmpEventMetric; - g_trace_depth--; logged = true; } } diff --git a/flow/Trace.h b/flow/Trace.h index b0ab0a81aa..5d7bb242d1 100644 --- a/flow/Trace.h +++ b/flow/Trace.h @@ -43,7 +43,7 @@ inline int fastrand() { //inline static bool TRACE_SAMPLE() { return fastrand()<16; } inline static bool TRACE_SAMPLE() { return false; } -extern thread_local int g_trace_depth; +extern thread_local bool g_tracing_allocation; enum Severity { SevSample=1, From 555db50cd1b951568eb7840df32ab15b4cc9dd94 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Thu, 12 Mar 2020 11:22:03 -0700 Subject: [PATCH 0875/1604] Avoid calling into SABTF so frequently. Use a cheaper call that only checks that shards exist. --- fdbserver/DataDistribution.actor.cpp | 12 +++++++----- fdbserver/DataDistribution.actor.h | 1 + fdbserver/DataDistributionTracker.actor.cpp | 5 +++++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/fdbserver/DataDistribution.actor.cpp b/fdbserver/DataDistribution.actor.cpp index 035dfab078..76c9d62165 100644 --- a/fdbserver/DataDistribution.actor.cpp +++ b/fdbserver/DataDistribution.actor.cpp @@ -811,11 +811,12 @@ struct DDTeamCollection : ReferenceCounted { ASSERT( !bestOption.present() ); for( int i = 0; i < self->teams.size(); i++ ) { if (self->teams[i]->isHealthy() && - (!req.preferLowerUtilization || self->teams[i]->hasHealthyAvailableSpace(self->medianAvailableSpace)) && - (!req.teamMustHaveShards || self->shardsAffectedByTeamFailure->getShardsFor(ShardsAffectedByTeamFailure::Team(self->teams[i]->getServerIDs(), self->primary)).size() > 0)) + (!req.preferLowerUtilization || self->teams[i]->hasHealthyAvailableSpace(self->medianAvailableSpace))) { int64_t loadBytes = self->teams[i]->getLoadBytes(true, req.inflightPenalty); - if( !bestOption.present() || ( req.preferLowerUtilization && loadBytes < bestLoadBytes ) || ( !req.preferLowerUtilization && loadBytes > bestLoadBytes ) ) { + if((!bestOption.present() || (req.preferLowerUtilization && loadBytes < bestLoadBytes) || (!req.preferLowerUtilization && loadBytes > bestLoadBytes)) && + (!req.teamMustHaveShards || self->shardsAffectedByTeamFailure->hasShards(ShardsAffectedByTeamFailure::Team(self->teams[i]->getServerIDs(), self->primary)))) + { bestLoadBytes = loadBytes; bestOption = self->teams[i]; } @@ -828,8 +829,7 @@ struct DDTeamCollection : ReferenceCounted { Reference dest = deterministicRandom()->randomChoice(self->teams); bool ok = dest->isHealthy() && - (!req.preferLowerUtilization || dest->hasHealthyAvailableSpace(self->medianAvailableSpace)) && - (!req.teamMustHaveShards || self->shardsAffectedByTeamFailure->getShardsFor(ShardsAffectedByTeamFailure::Team(dest->getServerIDs(), self->primary)).size() > 0); + (!req.preferLowerUtilization || dest->hasHealthyAvailableSpace(self->medianAvailableSpace)); for(int i=0; ok && igetServerIDs() == dest->getServerIDs()) { @@ -838,6 +838,8 @@ struct DDTeamCollection : ReferenceCounted { } } + ok = ok && (!req.teamMustHaveShards || self->shardsAffectedByTeamFailure->hasShards(ShardsAffectedByTeamFailure::Team(dest->getServerIDs(), self->primary))); + if (ok) randomTeams.push_back( dest ); else diff --git a/fdbserver/DataDistribution.actor.h b/fdbserver/DataDistribution.actor.h index c52c953e20..29bcf09eb9 100644 --- a/fdbserver/DataDistribution.actor.h +++ b/fdbserver/DataDistribution.actor.h @@ -134,6 +134,7 @@ public: int getNumberOfShards( UID ssID ); vector getShardsFor( Team team ); + bool hasShards(Team team); //The first element of the pair is either the source for non-moving shards or the destination team for in-flight shards //The second element of the pair is all previous sources for in-flight shards diff --git a/fdbserver/DataDistributionTracker.actor.cpp b/fdbserver/DataDistributionTracker.actor.cpp index 220c4c3a25..215bbac656 100644 --- a/fdbserver/DataDistributionTracker.actor.cpp +++ b/fdbserver/DataDistributionTracker.actor.cpp @@ -784,6 +784,11 @@ vector ShardsAffectedByTeamFailure::getShardsFor( Team team ) { return r; } +bool ShardsAffectedByTeamFailure::hasShards(Team team) { + auto it = team_shards.lower_bound(std::pair(team, KeyRangeRef())); + return it != team_shards.end() && it->first == team; +} + int ShardsAffectedByTeamFailure::getNumberOfShards( UID ssID ) { return storageServerShards[ssID]; } From 6f90228a0be252bb590679fe0e971212355299a6 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Thu, 12 Mar 2020 11:31:36 -0700 Subject: [PATCH 0876/1604] change to krmSetRangeCoalescing --- fdbclient/NativeAPI.actor.cpp | 3 ++- fdbserver/workloads/ReportConflictingKeys.actor.cpp | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index a869fc3636..13c3825bef 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -2770,7 +2770,8 @@ ACTOR static Future tryCommit( Database cx, Reference const KeyRange kr = req.transaction.read_conflict_ranges[rCRIndex]; // tr->info.conflictingKeysRYW->set(kr.begin, conflictingKeysTrue); // tr->info.conflictingKeysRYW->set(kr.end, conflictingKeysFalse); - wait(krmSetRange(hackTr, conflictingKeysPrefix, kr, conflictingKeysTrue)); + // wait(krmSetRange(hackTr, conflictingKeysPrefix, kr, conflictingKeysTrue)); + wait(krmSetRangeCoalescing(hackTr, conflictingKeysPrefix, kr, allKeys, conflictingKeysTrue)); } hackTr.extractPtr(); // Avoid the Reference to destroy the RYW object } diff --git a/fdbserver/workloads/ReportConflictingKeys.actor.cpp b/fdbserver/workloads/ReportConflictingKeys.actor.cpp index ceee5e2fe0..06aa509063 100644 --- a/fdbserver/workloads/ReportConflictingKeys.actor.cpp +++ b/fdbserver/workloads/ReportConflictingKeys.actor.cpp @@ -184,17 +184,17 @@ struct ReportConflictingKeysWorkload : TestWorkload { wait(tr.onError(e)); // check API correctness if (!self->skipCorrectnessCheck && self->reportConflictingKeys && isConflict) { - // const KeyRef conflictingKeysPreifx = LiteralStringRef("\xff\xff/transaction/conflicting_keys/"); - state KeyRange ckr = KeyRangeRef(LiteralStringRef("").withPrefix(conflictingKeysAbsolutePrefix), + // \xff\xff/transaction/conflicting_keys is always false, we skip it here for simplicity + state KeyRange ckr = KeyRangeRef(keyAfter(LiteralStringRef("").withPrefix(conflictingKeysAbsolutePrefix)), LiteralStringRef("\xff\xff").withPrefix(conflictingKeysAbsolutePrefix)); // The getRange here using the special key prefix "\xff\xff/transaction/conflicting_keys/" happens // locally Thus, the error handling is not needed here Future> conflictingKeyRangesFuture = - tr.getRange(ckr, readConflictRanges.size() * 2 + 1); + tr.getRange(ckr, readConflictRanges.size() * 2); ASSERT(conflictingKeyRangesFuture.isReady()); const Standalone conflictingKeyRanges = conflictingKeyRangesFuture.get(); - ASSERT(conflictingKeyRanges.size() && (conflictingKeyRanges.size() % 2 == 1)); - for (int i = 1; i < conflictingKeyRanges.size(); i += 2) { + ASSERT(conflictingKeyRanges.size() && (conflictingKeyRanges.size() % 2 == 0)); + for (int i = 0; i < conflictingKeyRanges.size(); i += 2) { KeyValueRef startKeyWithPrefix = conflictingKeyRanges[i]; ASSERT(startKeyWithPrefix.value == conflictingKeysTrue); KeyValueRef endKeyWithPrefix = conflictingKeyRanges[i + 1]; From 6940d546f54f31452d50dfa820af1e99cb8fe06c Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Thu, 12 Mar 2020 12:27:53 -0700 Subject: [PATCH 0877/1604] Fix bug where status is truncated when a null byte is included. This is implemented by escaping unprintable characters. --- fdbclient/ReadYourWrites.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbclient/ReadYourWrites.actor.cpp b/fdbclient/ReadYourWrites.actor.cpp index b1d08dc8ee..fc2e9546b0 100644 --- a/fdbclient/ReadYourWrites.actor.cpp +++ b/fdbclient/ReadYourWrites.actor.cpp @@ -1156,7 +1156,7 @@ Future ReadYourWritesTransaction::getReadVersion() { Optional getValueFromJSON(StatusObject statusObj) { try { - Value output = StringRef(json_spirit::write_string(json_spirit::mValue(statusObj), json_spirit::Output_options::raw_utf8).c_str()); + Value output = StringRef(json_spirit::write_string(json_spirit::mValue(statusObj), json_spirit::Output_options::none).c_str()); return output; } catch (std::exception& e){ From f7198c4ba397653335ad8df2667cda8053679b47 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Thu, 12 Mar 2020 12:35:08 -0700 Subject: [PATCH 0878/1604] Use the std::string constructor of StringRef, which will use the length of string correctly. --- fdbclient/ReadYourWrites.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbclient/ReadYourWrites.actor.cpp b/fdbclient/ReadYourWrites.actor.cpp index fc2e9546b0..6de7f80946 100644 --- a/fdbclient/ReadYourWrites.actor.cpp +++ b/fdbclient/ReadYourWrites.actor.cpp @@ -1156,7 +1156,7 @@ Future ReadYourWritesTransaction::getReadVersion() { Optional getValueFromJSON(StatusObject statusObj) { try { - Value output = StringRef(json_spirit::write_string(json_spirit::mValue(statusObj), json_spirit::Output_options::none).c_str()); + Value output = StringRef(json_spirit::write_string(json_spirit::mValue(statusObj), json_spirit::Output_options::none)); return output; } catch (std::exception& e){ From 0ef09539a9dafa7ee4657d07bbecc4b3e687a6bb Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Thu, 12 Mar 2020 13:01:25 -0700 Subject: [PATCH 0879/1604] addressMap[normalizedAddress]->address may not equal to normalizedAddress --- fdbrpc/sim2.actor.cpp | 4 +++- fdbserver/workloads/ConsistencyCheck.actor.cpp | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/fdbrpc/sim2.actor.cpp b/fdbrpc/sim2.actor.cpp index 55be35e171..ab1eeec11a 100644 --- a/fdbrpc/sim2.actor.cpp +++ b/fdbrpc/sim2.actor.cpp @@ -1584,7 +1584,9 @@ public: virtual ProcessInfo* getProcessByAddress( NetworkAddress const& address ) { NetworkAddress normalizedAddress(address.ip, address.port, true, address.isTLS()); ASSERT( addressMap.count( normalizedAddress ) ); - return addressMap[ normalizedAddress ]; + // NOTE: addressMap[normalizedAddress]->address may not equal to normalizedAddress + // ASSERT_WE_THINK( addressMap[normalizedAddress]->address == normalizedAddress ); + return addressMap[normalizedAddress]; } virtual MachineInfo* getMachineByNetworkAddress(NetworkAddress const& address) { diff --git a/fdbserver/workloads/ConsistencyCheck.actor.cpp b/fdbserver/workloads/ConsistencyCheck.actor.cpp index 1c5795bd54..6c4a3f312b 100644 --- a/fdbserver/workloads/ConsistencyCheck.actor.cpp +++ b/fdbserver/workloads/ConsistencyCheck.actor.cpp @@ -1350,7 +1350,8 @@ struct ConsistencyCheckWorkload : TestWorkload TraceEvent("ConsistencyCheck_RebootProcess") .detail("Address", itr->interf.address()) // worker's primary address (i.e., the first address) - .detail("ProcessAddress", p->address) + .detail("ProcessPrimaryAddress", p->address) + .detail("ProcessAddresses", p->addresses.toString()) .detail("DataStoreID", id) .detail("Protected", g_simulator.protectedAddresses.count(itr->interf.address())) .detail("Reliable", p->isReliable()) From 5967ef5eab31442d9cebe4577eeda71d61513bd3 Mon Sep 17 00:00:00 2001 From: Xin Dong Date: Thu, 12 Mar 2020 14:34:19 -0700 Subject: [PATCH 0880/1604] Added back the changes that report trace log flush failures and fix the random crash --- fdbclient/Schemas.cpp | 8 +++- fdbserver/Knobs.cpp | 2 + fdbserver/Knobs.h | 2 + fdbserver/WorkerInterface.actor.h | 4 +- fdbserver/worker.actor.cpp | 52 ++++++++++++++++++++++++- flow/FileTraceLogWriter.cpp | 19 +++++++++- flow/FileTraceLogWriter.h | 3 +- flow/Trace.cpp | 63 ++++++++++++++++++++++++++++++- flow/Trace.h | 15 ++++++++ 9 files changed, 159 insertions(+), 9 deletions(-) diff --git a/fdbclient/Schemas.cpp b/fdbclient/Schemas.cpp index 829ca7acda..b88a082ec0 100644 --- a/fdbclient/Schemas.cpp +++ b/fdbclient/Schemas.cpp @@ -162,6 +162,9 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema( "$enum":[ "file_open_error", "incorrect_cluster_file_contents", + "trace_log_file_write_error", + "trace_log_could_not_create_file", + "trace_log_writer_thread_unresponsive", "process_error", "io_error", "io_timeout", @@ -399,7 +402,10 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema( { "name":{ "$enum":[ - "incorrect_cluster_file_contents" + "incorrect_cluster_file_contents", + "trace_log_file_write_error", + "trace_log_could_not_create_file", + "trace_log_writer_thread_unresponsive" ] }, "description":"Cluster file contents do not match current cluster connection string. Verify cluster file is writable and has not been overwritten externally." diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index 0aa7343427..1a7e51d2b0 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -517,6 +517,8 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs, bool isSimula init( DEGRADED_RESET_INTERVAL, 24*60*60 ); if ( randomize && BUGGIFY ) DEGRADED_RESET_INTERVAL = 10; init( DEGRADED_WARNING_LIMIT, 1 ); init( DEGRADED_WARNING_RESET_DELAY, 7*24*60*60 ); + init( TRACE_LOG_FLUSH_FAILURE_CHECK_INTERVAL_SECONDS, 10 ); + init( TRACE_LOG_PING_TIMEOUT_SECONDS, 5.0 ); // Test harness init( WORKER_POLL_DELAY, 1.0 ); diff --git a/fdbserver/Knobs.h b/fdbserver/Knobs.h index 1aead5f7d2..4740658abb 100644 --- a/fdbserver/Knobs.h +++ b/fdbserver/Knobs.h @@ -456,6 +456,8 @@ public: double DEGRADED_RESET_INTERVAL; double DEGRADED_WARNING_LIMIT; double DEGRADED_WARNING_RESET_DELAY; + int64_t TRACE_LOG_FLUSH_FAILURE_CHECK_INTERVAL_SECONDS; + double TRACE_LOG_PING_TIMEOUT_SECONDS; // Test harness double WORKER_POLL_DELAY; diff --git a/fdbserver/WorkerInterface.actor.h b/fdbserver/WorkerInterface.actor.h index 4fe0f9c5f9..c09cc5899a 100644 --- a/fdbserver/WorkerInterface.actor.h +++ b/fdbserver/WorkerInterface.actor.h @@ -497,7 +497,9 @@ ACTOR Future tLog(IKeyValueStore* persistentData, IDiskQueue* persistentQu ACTOR Future monitorServerDBInfo(Reference>> ccInterface, Reference ccf, LocalityData locality, - Reference> dbInfo); + Reference> dbInfo, + Optional>>> issues = + Optional>>>()); ACTOR Future resolver(ResolverInterface proxy, InitializeResolverRequest initReq, Reference> db); ACTOR Future logRouter(TLogInterface interf, InitializeLogRouterRequest req, diff --git a/fdbserver/worker.actor.cpp b/fdbserver/worker.actor.cpp index 9b8a5744ce..face2a17fd 100644 --- a/fdbserver/worker.actor.cpp +++ b/fdbserver/worker.actor.cpp @@ -41,6 +41,7 @@ #include "fdbclient/MonitorLeader.h" #include "fdbclient/ClientWorkerInterface.h" #include "flow/Profiler.h" +#include "flow/ThreadHelper.actor.h" #ifdef __linux__ #include @@ -746,9 +747,46 @@ ACTOR Future workerSnapCreate(WorkerSnapRequest snapReq, StringRef snapFol return Void(); } +ACTOR Future monitorTraceLogIssues(Optional>>> issues) { + state bool pingTimeout = false; + state ThreadFuture f; + state Reference> callback; + loop { + wait(delay(SERVER_KNOBS->TRACE_LOG_FLUSH_FAILURE_CHECK_INTERVAL_SECONDS)); + f = ThreadFuture(new ThreadSingleAssignmentVar); + callback = Reference>(new CompletionCallback(f)); + callback->self = callback; + f.callOrSetAsCallback(callback.getPtr(), callback->userParam, 0); + pingTraceLogWriterThread(f); + try { + wait(timeoutError(callback->promise.getFuture(), SERVER_KNOBS->TRACE_LOG_PING_TIMEOUT_SECONDS)); + } catch (Error& e) { + if (e.code() == error_code_timed_out) { + pingTimeout = true; + } else { + throw; + } + } + if (issues.present()) { + std::set _issues; + retriveTraceLogIssues(_issues); + if (pingTimeout) { + // Ping trace log writer thread timeout. + _issues.insert("trace_log_writer_thread_unresponsive"); + pingTimeout = false; + } + issues.get()->set(_issues); + } + } +} + +// TODO: `issues` is right now only updated by `monitorTraceLogIssues` and thus is being `set` on every update. +// It could be changed to `insert` and `trigger` later if we want to use it as a generic way for the caller of this +// function to report issues to cluster controller. ACTOR Future monitorServerDBInfo(Reference>> ccInterface, Reference connFile, LocalityData locality, - Reference> dbInfo) { + Reference> dbInfo, + Optional>>> issues) { // Initially most of the serverDBInfo is not known, but we know our locality right away ServerDBInfo localInfo; localInfo.myLocality = locality; @@ -759,6 +797,12 @@ ACTOR Future monitorServerDBInfo(Referenceget().id; + if (issues.present()) { + for (auto const& i : issues.get()->get()) { + req.issues.push_back_deep(req.issues.arena(), i); + } + } + ClusterConnectionString fileConnectionString; if (connFile && !connFile->fileContentsUpToDate(fileConnectionString)) { req.issues.push_back_deep(req.issues.arena(), LiteralStringRef("incorrect_cluster_file_contents")); @@ -802,6 +846,7 @@ ACTOR Future monitorServerDBInfo(Referenceget().present()) TraceEvent("GotCCInterfaceChange").detail("CCID", ccInterface->get().get().id()).detail("CCMachine", ccInterface->get().get().getWorkers.getEndpoint().getPrimaryAddress()); } + when(wait(issues.present() ? issues.get()->onChange() : Never())) {} } } } @@ -870,6 +915,8 @@ ACTOR Future workerServer( state WorkerInterface interf( locality ); interf.initEndpoints(); + state Reference>> issues(new AsyncVar>()); + folder = abspath(folder); if(metricsPrefix.size() > 0) { @@ -889,7 +936,8 @@ ACTOR Future workerServer( errorForwarders.add( resetAfter(degraded, SERVER_KNOBS->DEGRADED_RESET_INTERVAL, false, SERVER_KNOBS->DEGRADED_WARNING_LIMIT, SERVER_KNOBS->DEGRADED_WARNING_RESET_DELAY, "DegradedReset")); errorForwarders.add( loadedPonger( interf.debugPing.getFuture() ) ); errorForwarders.add( waitFailureServer( interf.waitFailure.getFuture() ) ); - errorForwarders.add(monitorServerDBInfo(ccInterface, connFile, locality, dbInfo)); + errorForwarders.add(monitorTraceLogIssues(issues)); + errorForwarders.add(monitorServerDBInfo(ccInterface, connFile, locality, dbInfo, issues)); errorForwarders.add( testerServerCore( interf.testerInterface, connFile, dbInfo, locality ) ); errorForwarders.add(monitorHighMemory(memoryProfileThreshold)); diff --git a/flow/FileTraceLogWriter.cpp b/flow/FileTraceLogWriter.cpp index 6fd5775db1..3e4d0bdcd4 100644 --- a/flow/FileTraceLogWriter.cpp +++ b/flow/FileTraceLogWriter.cpp @@ -49,9 +49,10 @@ #include FileTraceLogWriter::FileTraceLogWriter(std::string directory, std::string processName, std::string basename, - std::string extension, uint64_t maxLogsSize, std::function onError) + std::string extension, uint64_t maxLogsSize, std::function onError, + Reference issues) : directory(directory), processName(processName), basename(basename), extension(extension), maxLogsSize(maxLogsSize), - traceFileFD(-1), index(0), onError(onError) {} + traceFileFD(-1), index(0), onError(onError), issues(issues) {} void FileTraceLogWriter::addref() { ReferenceCounted::addref(); @@ -73,6 +74,7 @@ void FileTraceLogWriter::lastError(int err) { void FileTraceLogWriter::write(const std::string& str) { auto ptr = str.c_str(); int remaining = str.size(); + bool needsResolve = false; while ( remaining ) { int ret = __write( traceFileFD, ptr, remaining ); @@ -80,7 +82,14 @@ void FileTraceLogWriter::write(const std::string& str) { lastError(0); remaining -= ret; ptr += ret; + if (needsResolve) { + issues->resolveIssue("trace_log_file_write_error"); + needsResolve = false; + } } else { + issues->addIssue("trace_log_file_write_error"); + needsResolve = true; + fprintf(stderr, "Unexpected error [%d] when flushing trace log.\n", errno); lastError(errno); threadSleep(0.1); } @@ -89,6 +98,7 @@ void FileTraceLogWriter::write(const std::string& str) { void FileTraceLogWriter::open() { cleanupTraceFiles(); + bool needsResolve = false; ++index; @@ -113,6 +123,8 @@ void FileTraceLogWriter::open() { } else { fprintf(stderr, "ERROR: could not create trace log file `%s' (%d: %s)\n", finalname.c_str(), errno, strerror(errno)); + issues->addIssue("trace_log_could_not_create_file"); + needsResolve = true; int errorNum = errno; onMainThreadVoid([finalname, errorNum]{ @@ -125,6 +137,9 @@ void FileTraceLogWriter::open() { } } onMainThreadVoid([]{ latestEventCache.clear("TraceFileOpenError"); }, NULL); + if (needsResolve) { + issues->resolveIssue("trace_log_could_not_create_file"); + } lastError(0); } diff --git a/flow/FileTraceLogWriter.h b/flow/FileTraceLogWriter.h index 3396486757..1a7d86a840 100644 --- a/flow/FileTraceLogWriter.h +++ b/flow/FileTraceLogWriter.h @@ -38,12 +38,13 @@ private: uint64_t maxLogsSize; int traceFileFD; uint32_t index; + Reference issues; std::function onError; public: FileTraceLogWriter(std::string directory, std::string processName, std::string basename, std::string extension, - uint64_t maxLogsSize, std::function onError); + uint64_t maxLogsSize, std::function onError, Reference issues); void addref(); void delref(); diff --git a/flow/Trace.cpp b/flow/Trace.cpp index a23c3fa620..4cf1cedb37 100644 --- a/flow/Trace.cpp +++ b/flow/Trace.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include "flow/IThreadPool.h" #include "flow/ThreadHelper.actor.h" @@ -220,6 +221,35 @@ public: } }; + struct IssuesList : ITraceLogIssuesReporter, ThreadSafeReferenceCounted { + IssuesList(){}; + void addIssue(std::string issue) override { + MutexHolder h(mutex); + issues.insert(issue); + } + + void retrieveIssues(std::set& out) override { + MutexHolder h(mutex); + for (auto const& i : issues) { + out.insert(i); + } + } + + void resolveIssue(std::string issue) override { + MutexHolder h(mutex); + issues.erase(issue); + } + + void addref() { ThreadSafeReferenceCounted::addref(); } + void delref() { ThreadSafeReferenceCounted::delref(); } + + private: + Mutex mutex; + std::set issues; + }; + + Reference issues; + Reference barriers; struct WriterThread : IThreadPoolReceiver { @@ -280,11 +310,25 @@ public: logWriter->sync(); } } + + struct Ping : TypedAction { + ThreadFuture p; + + explicit Ping(ThreadFuture p) : p(p){}; + virtual double getTimeEstimate() { return 0; } + }; + void action(Ping& a) { + try { + ((ThreadSingleAssignmentVar*)a.p.getPtr())->send(Void()); + } catch (Error& e) { + TraceEvent(SevError, "PingActionFailed").error(e); + } + } }; TraceLog() : bufferLength(0), loggedLength(0), opened(false), preopenOverflowCount(0), barriers(new BarrierList), - logTraceEventMetrics(false), formatter(new XmlTraceLogFormatter()) {} + logTraceEventMetrics(false), formatter(new XmlTraceLogFormatter()), issues(new IssuesList) {} bool isOpen() const { return opened; } @@ -299,7 +343,7 @@ public: basename = format("%s/%s.%s.%s", directory.c_str(), processName.c_str(), timestamp.c_str(), deterministicRandom()->randomAlphaNumeric(6).c_str()); logWriter = Reference(new FileTraceLogWriter(directory, processName, basename, formatter->getExtension(), maxLogsSize, - [this]() { barriers->triggerAll(); })); + [this]() { barriers->triggerAll(); }, issues)); if ( g_network->isSimulated() ) writer = Reference(new DummyThreadPool()); @@ -497,6 +541,13 @@ public: } } + void pingWriterThread(ThreadFuture& p) { + auto a = new WriterThread::Ping(p); + writer->post(a); + } + + void retriveTraceLogIssues(std::set& out) { return issues->retrieveIssues(out); } + ~TraceLog() { close(); if (writer) writer->addref(); // FIXME: We are not shutting down the writer thread at all, because the ThreadPool shutdown mechanism is blocking (necessarily waits for current work items to finish) and we might not be able to finish everything. @@ -732,6 +783,14 @@ TraceEvent& TraceEvent::operator=(TraceEvent &&ev) { return *this; } +void retriveTraceLogIssues(std::set& out) { + return g_traceLog.retriveTraceLogIssues(out); +} + +void pingTraceLogWriterThread(ThreadFuture& p) { + return g_traceLog.pingWriterThread(p); +} + TraceEvent::TraceEvent( const char* type, UID id ) : id(id), type(type), severity(SevInfo), initialized(false), enabled(true), logged(false) { g_trace_depth++; setMaxFieldLength(0); diff --git a/flow/Trace.h b/flow/Trace.h index 37ac57f87b..c4c26b90a8 100644 --- a/flow/Trace.h +++ b/flow/Trace.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "flow/IRandom.h" #include "flow/Error.h" @@ -528,6 +529,16 @@ struct ITraceLogFormatter { virtual void delref() = 0; }; +struct ITraceLogIssuesReporter { + virtual void addIssue(std::string issue) = 0; + virtual void resolveIssue(std::string issue) = 0; + + virtual void retrieveIssues(std::set& out) = 0; + + virtual void addref() = 0; + virtual void delref() = 0; +}; + struct TraceInterval { TraceInterval( const char* type ) : count(-1), type(type), severity(SevInfo) {} @@ -596,6 +607,10 @@ bool validateTraceClockSource(std::string source); void addTraceRole(std::string role); void removeTraceRole(std::string role); +void retriveTraceLogIssues(std::set& out); +template +struct ThreadFuture; +void pingTraceLogWriterThread(ThreadFuture& p); enum trace_clock_t { TRACE_CLOCK_NOW, TRACE_CLOCK_REALTIME }; extern std::atomic g_trace_clock; From 0c558efcfee1a9b022a94dc866a4061fe954eba8 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Fri, 13 Mar 2020 00:11:53 -0700 Subject: [PATCH 0881/1604] Add a `tlsinfo` command to fdbcli that prints the certificate chain. This requires the certificate chain to load successfully, otherwise fdbcli will error out at an earlier point due to Net2 not being able to configure TLS. --- fdbcli/fdbcli.actor.cpp | 26 +++++++++++ fdbrpc/FlowTests.actor.cpp | 5 +++ fdbrpc/sim2.actor.cpp | 4 ++ flow/Net2.actor.cpp | 49 +------------------- flow/TLSConfig.actor.cpp | 91 +++++++++++++++++++++++++++++++++++--- flow/TLSConfig.actor.h | 8 ++++ flow/network.h | 3 ++ 7 files changed, 134 insertions(+), 52 deletions(-) diff --git a/fdbcli/fdbcli.actor.cpp b/fdbcli/fdbcli.actor.cpp index 896a3ffc6d..f4f7a094eb 100644 --- a/fdbcli/fdbcli.actor.cpp +++ b/fdbcli/fdbcli.actor.cpp @@ -404,6 +404,23 @@ static std::vector> parseLine(std::string& line, bool& er return ret; } +// This function has to be outside of cli(), because the actor compiler doesn't +// understand preprocessor macros. +bool loadAndPrintTLSCertificates() { +#ifndef TLS_DISABLED + try { + LoadedTLSConfig loaded = g_network->getTLSConfig().loadSync(); + loaded.print(stdout); + } catch (Error& e) { + printf("Please use --log and check the log file for more details on the error."); + } + return false; +#else + printf("This fdbcli was built with TLS disabled.\n"); + return true; +#endif +} + static void printProgramUsage(const char* name) { printf("FoundationDB CLI " FDB_VT_PACKAGE_NAME " (v" FDB_VT_VERSION ")\n" "usage: %s [OPTIONS]\n" @@ -557,6 +574,10 @@ void initHelp() { "consistencycheck [on|off]", "permits or prevents consistency checking", "Calling this command with `on' permits consistency check processes to run and `off' will halt their checking. Calling this command with no arguments will display if consistency checking is currently allowed.\n"); + helpMap["tlsinfo"] = CommandHelp( + "tlsinfo", + "prints a textual representation of the configured TLS Certificates", + "This prints the TLS certificate and the CA certificate, which is likely to be helpful in debugging verify_peers failures."); hiddenCommands.insert("expensive_data_check"); hiddenCommands.insert("datadistribution"); @@ -3134,6 +3155,11 @@ ACTOR Future cli(CLIOptions opt, LineNoise* plinenoise) { continue; } + if (tokencmp(tokens[0], "tlsinfo")) { + is_error = loadAndPrintTLSCertificates(); + continue; + } + if (tokencmp(tokens[0], "profile")) { if (tokens.size() == 1) { printf("ERROR: Usage: profile \n"); diff --git a/fdbrpc/FlowTests.actor.cpp b/fdbrpc/FlowTests.actor.cpp index dbfbf94c77..b6d38c8fe2 100644 --- a/fdbrpc/FlowTests.actor.cpp +++ b/fdbrpc/FlowTests.actor.cpp @@ -25,6 +25,7 @@ #include "flow/IThreadPool.h" #include "fdbrpc/fdbrpc.h" #include "fdbrpc/IAsyncFile.h" +#include "flow/TLSConfig.actor.h" #include "flow/actorcompiler.h" // This must be the last #include. void forceLinkFlowTests() {} @@ -202,6 +203,10 @@ struct YieldMockNetwork : INetwork, ReferenceCounted { virtual void run() { return baseNetwork->run(); } virtual void getDiskBytes(std::string const& directory, int64_t& free, int64_t& total) { return baseNetwork->getDiskBytes(directory,free,total); } virtual bool isAddressOnThisHost(NetworkAddress const& addr) { return baseNetwork->isAddressOnThisHost(addr); } + virtual const TLSConfig& getTLSConfig() { + static TLSConfig emptyConfig; + return emptyConfig; + } }; struct NonserializableThing {}; diff --git a/fdbrpc/sim2.actor.cpp b/fdbrpc/sim2.actor.cpp index 58f4b3fd5f..845a166a9f 100644 --- a/fdbrpc/sim2.actor.cpp +++ b/fdbrpc/sim2.actor.cpp @@ -865,6 +865,10 @@ public: } } } + virtual const TLSConfig& getTLSConfig() { + static TLSConfig emptyConfig; + return emptyConfig; + } virtual void stop() { isStopped = true; } virtual bool isSimulated() const { return true; } diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index 011f335a4c..70b572142d 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -156,6 +156,8 @@ public: virtual void setGlobal(size_t id, flowGlobalType v) { globals.resize(std::max(globals.size(),id+1)); globals[id] = v; } std::vector globals; + virtual const TLSConfig& getTLSConfig() { return tlsConfig; } + bool useThreadPool; //private: @@ -844,12 +846,6 @@ struct PromiseTask : public Task, public FastAllocated { // 5MB for loading files into memory -#ifndef TLS_DISABLED -bool insecurely_always_accept(bool _1, boost::asio::ssl::verify_context& _2) { - return true; -} -#endif - Net2::Net2(const TLSConfig& tlsConfig, bool useThreadPool, bool useMetrics) : useThreadPool(useThreadPool), network(this), @@ -891,47 +887,6 @@ Net2::Net2(const TLSConfig& tlsConfig, bool useThreadPool, bool useMetrics) } #ifndef TLS_DISABLED -void ConfigureSSLContext( const LoadedTLSConfig& loaded, boost::asio::ssl::context* context ) { - try { - context->set_options(boost::asio::ssl::context::default_workarounds); - context->set_verify_mode(boost::asio::ssl::context::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert); - - if (loaded.isTLSEnabled()) { - Reference tlsPolicy = Reference(new TLSPolicy(loaded.getEndpointType())); - tlsPolicy->set_verify_peers({ loaded.getVerifyPeers() }); - - context->set_verify_callback([policy=tlsPolicy](bool preverified, boost::asio::ssl::verify_context& ctx) { - return policy->verify_peer(preverified, ctx.native_handle()); - }); - } else { - context->set_verify_callback(boost::bind(&insecurely_always_accept, _1, _2)); - } - - context->set_password_callback( - [password=loaded.getPassword()](size_t, boost::asio::ssl::context::password_purpose) { - return password; - }); - - const std::string& certBytes = loaded.getCertificateBytes(); - if ( certBytes.size() ) { - context->use_certificate_chain(boost::asio::buffer(certBytes.data(), certBytes.size())); - } - - const std::string& CABytes = loaded.getCABytes(); - if ( CABytes.size() ) { - context->add_certificate_authority(boost::asio::buffer(CABytes.data(), CABytes.size())); - } - - const std::string& keyBytes = loaded.getKeyBytes(); - if (keyBytes.size()) { - context->use_private_key(boost::asio::buffer(keyBytes.data(), keyBytes.size()), boost::asio::ssl::context::pem); - } - } catch (boost::system::system_error& e) { - TraceEvent("TLSConfigureError").detail("What", e.what()).detail("Value", e.code().value()).detail("WhichMeans", TLSPolicy::ErrorString(e.code())); - throw tls_error(); - } -} - ACTOR static Future watchFileForChanges( std::string filename, AsyncTrigger* fileChanged ) { if (filename == "") { return Never(); diff --git a/flow/TLSConfig.actor.cpp b/flow/TLSConfig.actor.cpp index 51e2ac9c93..90b75d8bc1 100644 --- a/flow/TLSConfig.actor.cpp +++ b/flow/TLSConfig.actor.cpp @@ -25,7 +25,14 @@ // To force typeinfo to only be emitted once. TLSPolicy::~TLSPolicy() {} -#ifndef TLS_DISABLED +#ifdef TLS_DISABLED + +void LoadedTLSConfig::print(FILE *fp) { + fprintf(fp, "Cannot print LoadedTLSConfig. TLS support is not enabled.\n"); +} + +#else // TLS is enabled + #include #include #include @@ -63,7 +70,7 @@ std::vector LoadedTLSConfig::getVerifyPeers() const { if (tlsVerifyPeers.size()) { return tlsVerifyPeers; } - + std::string envVerifyPeers; if (platform::getEnvironmentVar("FDB_TLS_VERIFY_PEERS", envVerifyPeers)) { return {envVerifyPeers}; @@ -76,12 +83,86 @@ std::string LoadedTLSConfig::getPassword() const { if (tlsPassword.size()) { return tlsPassword; } - + std::string envPassword; platform::getEnvironmentVar("FDB_TLS_PASSWORD", envPassword); return envPassword; } +void LoadedTLSConfig::print(FILE* fp) { + int num_certs = 0; + boost::asio::ssl::context context(boost::asio::ssl::context::tls); + try { + ConfigureSSLContext(*this, &context); + } catch (Error& e) { + fprintf(fp, "There was an error in loading the certificate chain.\n"); + return; + } + + X509_STORE* store = SSL_CTX_get_cert_store(context.native_handle()); + X509_STORE_CTX* store_ctx = X509_STORE_CTX_new(); + X509* cert = SSL_CTX_get0_certificate(context.native_handle()); + X509_STORE_CTX_init(store_ctx, store, cert, NULL); + + X509_verify_cert(store_ctx); + STACK_OF(X509)* chain = X509_STORE_CTX_get0_chain(store_ctx); + + X509_print_fp(fp, cert); + + num_certs = sk_X509_num(chain); + if (num_certs) { + for ( int i = 0; i < num_certs; i++ ) { + printf("\n"); + X509* cert = sk_X509_value(chain, i); + X509_print_fp(fp, cert); + } + } + + X509_STORE_CTX_free(store_ctx); +} + +void ConfigureSSLContext( const LoadedTLSConfig& loaded, boost::asio::ssl::context* context ) { + try { + context->set_options(boost::asio::ssl::context::default_workarounds); + context->set_verify_mode(boost::asio::ssl::context::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert); + + if (loaded.isTLSEnabled()) { + Reference tlsPolicy = Reference(new TLSPolicy(loaded.getEndpointType())); + tlsPolicy->set_verify_peers({ loaded.getVerifyPeers() }); + + context->set_verify_callback([policy=tlsPolicy](bool preverified, boost::asio::ssl::verify_context& ctx) { + return policy->verify_peer(preverified, ctx.native_handle()); + }); + } else { + // Insecurely always except if TLS is not enabled. + context->set_verify_callback([](bool, boost::asio::ssl::verify_context&){ return true; }); + } + + context->set_password_callback( + [password=loaded.getPassword()](size_t, boost::asio::ssl::context::password_purpose) { + return password; + }); + + const std::string& CABytes = loaded.getCABytes(); + if ( CABytes.size() ) { + context->add_certificate_authority(boost::asio::buffer(CABytes.data(), CABytes.size())); + } + + const std::string& keyBytes = loaded.getKeyBytes(); + if (keyBytes.size()) { + context->use_private_key(boost::asio::buffer(keyBytes.data(), keyBytes.size()), boost::asio::ssl::context::pem); + } + + const std::string& certBytes = loaded.getCertificateBytes(); + if ( certBytes.size() ) { + context->use_certificate_chain(boost::asio::buffer(certBytes.data(), certBytes.size())); + } + } catch (boost::system::system_error& e) { + TraceEvent("TLSConfigureError").detail("What", e.what()).detail("Value", e.code().value()).detail("WhichMeans", TLSPolicy::ErrorString(e.code())); + throw tls_error(); + } +} + std::string TLSConfig::getCertificatePathSync() const { if (tlsCertPath.size()) { return tlsCertPath; @@ -96,7 +177,7 @@ std::string TLSConfig::getCertificatePathSync() const { if( fileExists(defaultCertFileName) ) { return defaultCertFileName; } - + if( fileExists( joinPath(platform::getDefaultConfigPath(), defaultCertFileName) ) ) { return joinPath(platform::getDefaultConfigPath(), defaultCertFileName); } @@ -118,7 +199,7 @@ std::string TLSConfig::getKeyPathSync() const { if( fileExists(defaultCertFileName) ) { return defaultCertFileName; } - + if( fileExists( joinPath(platform::getDefaultConfigPath(), defaultCertFileName) ) ) { return joinPath(platform::getDefaultConfigPath(), defaultCertFileName); } diff --git a/flow/TLSConfig.actor.h b/flow/TLSConfig.actor.h index 667a6f2822..6439bc293c 100644 --- a/flow/TLSConfig.actor.h +++ b/flow/TLSConfig.actor.h @@ -27,6 +27,7 @@ #pragma once +#include #include #include #include @@ -120,6 +121,8 @@ public: return endpointType != TLSEndpointType::UNSET; } + void print(FILE* fp); + PRIVATE_EXCEPT_FOR_TLSCONFIG_CPP: std::string tlsCertBytes, tlsKeyBytes, tlsCABytes; std::string tlsPassword; @@ -217,6 +220,11 @@ PRIVATE_EXCEPT_FOR_TLSCONFIG_CPP: TLSEndpointType endpointType = TLSEndpointType::UNSET; }; +#ifndef TLS_DISABLED +namespace boost { namespace asio { namespace ssl { struct context; }}} +void ConfigureSSLContext(const LoadedTLSConfig& loaded, boost::asio::ssl::context* context); +#endif + class TLSPolicy : ReferenceCounted { public: diff --git a/flow/network.h b/flow/network.h index f9234a4d9b..eaf0234425 100644 --- a/flow/network.h +++ b/flow/network.h @@ -485,6 +485,9 @@ public: virtual void initTLS() {} // TLS must be initialized before using the network + virtual const TLSConfig& getTLSConfig() = 0; + // Return the TLS Configuration + virtual void getDiskBytes( std::string const& directory, int64_t& free, int64_t& total) = 0; //Gets the number of free and total bytes available on the disk which contains directory From 7118759dfa9e00cd86b538603253384103d18579 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Fri, 13 Mar 2020 01:48:55 -0700 Subject: [PATCH 0882/1604] Delete code to test resolvers' performance, simplify the workload to only test correctness --- .../workloads/ReportConflictingKeys.actor.cpp | 86 ++++++------------- tests/fast/ReportConflictingKeys.txt | 8 +- 2 files changed, 27 insertions(+), 67 deletions(-) diff --git a/fdbserver/workloads/ReportConflictingKeys.actor.cpp b/fdbserver/workloads/ReportConflictingKeys.actor.cpp index 06aa509063..9afee15f54 100644 --- a/fdbserver/workloads/ReportConflictingKeys.actor.cpp +++ b/fdbserver/workloads/ReportConflictingKeys.actor.cpp @@ -31,9 +31,7 @@ struct ReportConflictingKeysWorkload : TestWorkload { double testDuration, transactionsPerSecond, addReadConflictRangeProb, addWriteConflictRangeProb; Key keyPrefix; - int nodeCountPerPrefix, actorCount, keyBytes, valueBytes, readConflictRangeCount, writeConflictRangeCount; - bool reportConflictingKeys, skipCorrectnessCheck; - uint64_t keyPrefixBytes, prefixCount; + int nodeCount, actorCount, keyBytes, valueBytes, readConflictRangeCount, writeConflictRangeCount; PerfIntCounter invalidReports, commits, conflicts, retries, xacts; @@ -41,8 +39,8 @@ struct ReportConflictingKeysWorkload : TestWorkload { : TestWorkload(wcx), invalidReports("InvalidReports"), conflicts("Conflicts"), retries("Retries"), commits("Commits"), xacts("Transactions") { testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); - transactionsPerSecond = getOption(options, LiteralStringRef("transactionsPerSecond"), 5000.0) / clientCount; - actorCount = getOption(options, LiteralStringRef("actorsPerClient"), transactionsPerSecond / 5); + // transactionsPerSecond = getOption(options, LiteralStringRef("transactionsPerSecond"), 5000.0) / clientCount; + actorCount = getOption(options, LiteralStringRef("actorsPerClient"), 1); keyPrefix = unprintable( getOption(options, LiteralStringRef("keyPrefix"), LiteralStringRef("ReportConflictingKeysWorkload")) .toString()); @@ -53,18 +51,8 @@ struct ReportConflictingKeysWorkload : TestWorkload { // modeled by geometric distribution: (1 - prob) / prob = mean addReadConflictRangeProb = readConflictRangeCount / (readConflictRangeCount + 1.0); addWriteConflictRangeProb = writeConflictRangeCount / (writeConflictRangeCount + 1.0); - // If true, store key ranges conflicting with other txs - reportConflictingKeys = getOption(options, LiteralStringRef("reportConflictingKeys"), false); - skipCorrectnessCheck = getOption(options, LiteralStringRef("skipCorrectnessCheck"), false); - // used for generating keyPrefix - keyPrefixBytes = getOption(options, LiteralStringRef("keyPrefixBytes"), 0); - if (keyPrefixBytes) { - prefixCount = 255 * std::round(std::exp2(8 * (keyPrefixBytes - 1))); - ASSERT(keyPrefixBytes + 16 <= keyBytes); - } else { - ASSERT(keyPrefix.size() + 16 <= keyBytes); // make sure the string format is valid - } - nodeCountPerPrefix = getOption(options, LiteralStringRef("nodeCountPerPrefix"), 100); + ASSERT(keyPrefix.size() + 16 <= keyBytes); // make sure the string format is valid + nodeCount = getOption(options, LiteralStringRef("nodeCount"), 100); } std::string description() override { return "ReportConflictingKeysWorkload"; } @@ -74,12 +62,8 @@ struct ReportConflictingKeysWorkload : TestWorkload { Future start(const Database& cx) override { return _start(cx->clone(), this); } ACTOR Future _start(Database cx, ReportConflictingKeysWorkload* self) { - std::vector> clients; - for (int c = 0; c < self->actorCount; ++c) { - clients.push_back(self->conflictingClient(cx, self, self->actorCount / self->transactionsPerSecond, c)); - } - - wait(timeout(waitForAll(clients), self->testDuration, Void())); + if (self->clientId == 0) + wait(timeout(self->conflictingClient(cx, self), self->testDuration, Void())); return Void(); } @@ -101,68 +85,48 @@ struct ReportConflictingKeysWorkload : TestWorkload { double getCheckTimeout() override { return std::numeric_limits::max(); } // Copied from tester.actor.cpp, added parameter to determine the key's length - Key keyForIndex(int prefixIdx, int n) { - double p = (double)n / nodeCountPerPrefix; - int paddingLen = keyBytes - 16 - keyPrefixBytes; + Key keyForIndex(int n) { + double p = (double)n / nodeCount; + int paddingLen = keyBytes - 16 - keyPrefix.size(); // left padding by zero return StringRef(format("%0*llx", paddingLen, *(uint64_t*)&p)) - .withPrefix(prefixIdx >= 0 ? keyPrefixForIndex(prefixIdx) : keyPrefix); - } - - Key keyPrefixForIndex(uint64_t n) { - Key prefix = makeString(keyPrefixBytes); - uint8_t* head = mutateString(prefix); - memset(head, 0, keyPrefixBytes); - int offset = keyPrefixBytes - 1; - while (n) { - *(head + offset) = static_cast(n % 256); - n /= 256; - offset -= 1; - } - return prefix; + .withPrefix(keyPrefix); } void addRandomReadConflictRange(ReadYourWritesTransaction* tr, std::vector& readConflictRanges) { - int startIdx, endIdx, startPrefixIdx, endPrefixIdx; + int startIdx, endIdx; Key startKey, endKey; while (deterministicRandom()->random01() < addReadConflictRangeProb) { - startPrefixIdx = keyPrefixBytes ? deterministicRandom()->randomInt(0, prefixCount) : -1; - endPrefixIdx = keyPrefixBytes ? deterministicRandom()->randomInt(startPrefixIdx, prefixCount) : -1; - startIdx = deterministicRandom()->randomInt(0, nodeCountPerPrefix); - endIdx = deterministicRandom()->randomInt(startPrefixIdx < endPrefixIdx ? 0 : startIdx, nodeCountPerPrefix); - startKey = keyForIndex(startPrefixIdx, startIdx); - endKey = keyForIndex(endPrefixIdx, endIdx); + startIdx = deterministicRandom()->randomInt(0, nodeCount); + endIdx = deterministicRandom()->randomInt(startIdx, nodeCount); + startKey = keyForIndex(startIdx); + endKey = keyForIndex(endIdx); tr->addReadConflictRange(KeyRangeRef(startKey, endKey)); readConflictRanges.push_back(KeyRangeRef(startKey, endKey)); } } void addRandomWriteConflictRange(ReadYourWritesTransaction* tr) { - int startIdx, endIdx, startPrefixIdx, endPrefixIdx; + int startIdx, endIdx; Key startKey, endKey; while (deterministicRandom()->random01() < addWriteConflictRangeProb) { - startPrefixIdx = keyPrefixBytes ? deterministicRandom()->randomInt(0, prefixCount) : -1; - endPrefixIdx = keyPrefixBytes ? deterministicRandom()->randomInt(startPrefixIdx, prefixCount) : -1; - startIdx = deterministicRandom()->randomInt(0, nodeCountPerPrefix); - endIdx = deterministicRandom()->randomInt(startPrefixIdx < endPrefixIdx ? 0 : startIdx, nodeCountPerPrefix); - startKey = keyForIndex(startPrefixIdx, startIdx); - endKey = keyForIndex(endPrefixIdx, endIdx); + startIdx = deterministicRandom()->randomInt(0, nodeCount); + endIdx = deterministicRandom()->randomInt(startIdx, nodeCount); + startKey = keyForIndex(startIdx); + endKey = keyForIndex(endIdx); tr->addWriteConflictRange(KeyRangeRef(startKey, endKey)); } } - ACTOR Future conflictingClient(Database cx, ReportConflictingKeysWorkload* self, double delay, - int actorIndex) { + ACTOR Future conflictingClient(Database cx, ReportConflictingKeysWorkload* self) { state ReadYourWritesTransaction tr(cx); - state double lastTime = now(); + state ReadYourWritesTransaction tr2(cx); state std::vector readConflictRanges; loop { try { - // used for throttling - wait(poisson(&lastTime, delay)); - if (self->reportConflictingKeys) tr.setOption(FDBTransactionOptions::REPORT_CONFLICTING_KEYS); + tr.setOption(FDBTransactionOptions::REPORT_CONFLICTING_KEYS); // If READ_YOUR_WRITES_DISABLE set, it behaves like native transaction object // where overlapped conflict ranges are not merged. if (deterministicRandom()->random01() < 0.5) @@ -183,7 +147,7 @@ struct ReportConflictingKeysWorkload : TestWorkload { } wait(tr.onError(e)); // check API correctness - if (!self->skipCorrectnessCheck && self->reportConflictingKeys && isConflict) { + if (isConflict) { // \xff\xff/transaction/conflicting_keys is always false, we skip it here for simplicity state KeyRange ckr = KeyRangeRef(keyAfter(LiteralStringRef("").withPrefix(conflictingKeysAbsolutePrefix)), LiteralStringRef("\xff\xff").withPrefix(conflictingKeysAbsolutePrefix)); diff --git a/tests/fast/ReportConflictingKeys.txt b/tests/fast/ReportConflictingKeys.txt index 9ee68a57fd..1010e2493a 100644 --- a/tests/fast/ReportConflictingKeys.txt +++ b/tests/fast/ReportConflictingKeys.txt @@ -1,12 +1,8 @@ testTitle=ReportConflictingKeysTest testName=ReportConflictingKeys testDuration=10.0 - transactionsPerSecond=100000 - nodeCountPerPrefix=10000 - actorsPerClient=256 + nodeCount=10000 keyPrefix=RCK keyBytes=64 readConflictRangeCountPerTx=1 - writeConflictRangeCountPerTx=1 - reportConflictingKeys=true - skipCorrectnessCheck=false \ No newline at end of file + writeConflictRangeCountPerTx=1 \ No newline at end of file From 75e2fffe5a07faed316fcbf28789864de970f08f Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Fri, 13 Mar 2020 02:24:37 -0700 Subject: [PATCH 0883/1604] Add a ProcessMetrics.TLSPolicyFailures metric This reports the number of policy failures over the past 5s interval. It also is step 1 towards getting this information into status json. --- flow/Net2.actor.cpp | 21 ++++++++++++++------- flow/SystemMonitor.cpp | 1 + flow/SystemMonitor.h | 2 ++ 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index 011f335a4c..c6d999f6bf 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -220,6 +220,7 @@ public: Int64MetricHandle countYieldCallsTrue; Int64MetricHandle countASIOEvents; Int64MetricHandle countSlowTaskSignals; + Int64MetricHandle countTLSPolicyFailures; Int64MetricHandle priorityMetric; DoubleMetricHandle countLaunchTime; DoubleMetricHandle countReactTime; @@ -891,7 +892,7 @@ Net2::Net2(const TLSConfig& tlsConfig, bool useThreadPool, bool useMetrics) } #ifndef TLS_DISABLED -void ConfigureSSLContext( const LoadedTLSConfig& loaded, boost::asio::ssl::context* context ) { +void ConfigureSSLContext( const LoadedTLSConfig& loaded, boost::asio::ssl::context* context, std::function onPolicyFailure ) { try { context->set_options(boost::asio::ssl::context::default_workarounds); context->set_verify_mode(boost::asio::ssl::context::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert); @@ -900,8 +901,12 @@ void ConfigureSSLContext( const LoadedTLSConfig& loaded, boost::asio::ssl::conte Reference tlsPolicy = Reference(new TLSPolicy(loaded.getEndpointType())); tlsPolicy->set_verify_peers({ loaded.getVerifyPeers() }); - context->set_verify_callback([policy=tlsPolicy](bool preverified, boost::asio::ssl::verify_context& ctx) { - return policy->verify_peer(preverified, ctx.native_handle()); + context->set_verify_callback([policy=tlsPolicy, onPolicyFailure](bool preverified, boost::asio::ssl::verify_context& ctx) { + bool success = policy->verify_peer(preverified, ctx.native_handle()); + if (!success) { + onPolicyFailure(); + } + return success; }); } else { context->set_verify_callback(boost::bind(&insecurely_always_accept, _1, _2)); @@ -959,7 +964,7 @@ ACTOR static Future watchFileForChanges( std::string filename, AsyncTrigge } } -ACTOR static Future reloadCertificatesOnChange( TLSConfig config, AsyncVar>>* contextVar ) { +ACTOR static Future reloadCertificatesOnChange( TLSConfig config, std::function onPolicyFailure, AsyncVar>>* contextVar ) { if (FLOW_KNOBS->TLS_CERT_REFRESH_DELAY_SECONDS <= 0) { return Void(); } @@ -983,7 +988,7 @@ ACTOR static Future reloadCertificatesOnChange( TLSConfig config, AsyncVar try { LoadedTLSConfig loaded = wait( config.loadAsync() ); boost::asio::ssl::context context(boost::asio::ssl::context::tls); - ConfigureSSLContext(loaded, &context); + ConfigureSSLContext(loaded, &context, onPolicyFailure); TraceEvent(SevInfo, "TLSCertificateRefreshSucceeded"); mismatches = 0; contextVar->set(ReferencedObject::from(std::move(context))); @@ -1006,9 +1011,10 @@ void Net2::initTLS() { #ifndef TLS_DISABLED try { boost::asio::ssl::context newContext(boost::asio::ssl::context::tls); - ConfigureSSLContext( tlsConfig.loadSync(), &newContext ); + auto onPolicyFailure = [this]() { this->countTLSPolicyFailures++; }; + ConfigureSSLContext( tlsConfig.loadSync(), &newContext, onPolicyFailure ); sslContextVar.set(ReferencedObject::from(std::move(newContext))); - backgroundCertRefresh = reloadCertificatesOnChange( tlsConfig, &sslContextVar ); + backgroundCertRefresh = reloadCertificatesOnChange( tlsConfig, onPolicyFailure, &sslContextVar ); } catch (Error& e) { TraceEvent("Net2TLSInitError").error(e); throw tls_error(); @@ -1044,6 +1050,7 @@ void Net2::initMetrics() { countASIOEvents.init(LiteralStringRef("Net2.CountASIOEvents")); countYieldCallsTrue.init(LiteralStringRef("Net2.CountYieldCallsTrue")); countSlowTaskSignals.init(LiteralStringRef("Net2.CountSlowTaskSignals")); + countTLSPolicyFailures.init(LiteralStringRef("Net2.CountTLSPolicyFailures")); priorityMetric.init(LiteralStringRef("Net2.Priority")); awakeMetric.init(LiteralStringRef("Net2.Awake")); slowTaskMetric.init(LiteralStringRef("Net2.SlowTask")); diff --git a/flow/SystemMonitor.cpp b/flow/SystemMonitor.cpp index cc525f098a..47e5c6497c 100644 --- a/flow/SystemMonitor.cpp +++ b/flow/SystemMonitor.cpp @@ -101,6 +101,7 @@ SystemStatistics customSystemMonitor(std::string eventName, StatisticsState *sta .detail("ConnectionsEstablished", (double) (netData.countConnEstablished - statState->networkState.countConnEstablished) / currentStats.elapsed) .detail("ConnectionsClosed", ((netData.countConnClosedWithError - statState->networkState.countConnClosedWithError) + (netData.countConnClosedWithoutError - statState->networkState.countConnClosedWithoutError)) / currentStats.elapsed) .detail("ConnectionErrors", (netData.countConnClosedWithError - statState->networkState.countConnClosedWithError) / currentStats.elapsed) + .detail("TLSPolicyFailures", (netData.countTLSPolicyFailures - statState->networkState.countTLSPolicyFailures)) .trackLatest(eventName); TraceEvent("MemoryMetrics") diff --git a/flow/SystemMonitor.h b/flow/SystemMonitor.h index ac1cb33817..e8c4ca2c59 100644 --- a/flow/SystemMonitor.h +++ b/flow/SystemMonitor.h @@ -80,6 +80,7 @@ struct NetworkData { int64_t countConnEstablished; int64_t countConnClosedWithError; int64_t countConnClosedWithoutError; + int64_t countTLSPolicyFailures; double countLaunchTime; double countReactTime; @@ -107,6 +108,7 @@ struct NetworkData { 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")); From d86a601b841996d592dd40493d4940a13255d743 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Fri, 13 Mar 2020 02:40:45 -0700 Subject: [PATCH 0884/1604] Add cluster.processes.id.network.tls_policy.hz to status. This allows monitoring of TLS policy failures, but one has to go scrape for TLSPolicyFailure trace events to figure out why they're happening. --- fdbclient/Schemas.cpp | 5 ++++- fdbserver/Status.actor.cpp | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/fdbclient/Schemas.cpp b/fdbclient/Schemas.cpp index 51572cf015..2bdf54e275 100644 --- a/fdbclient/Schemas.cpp +++ b/fdbclient/Schemas.cpp @@ -208,7 +208,10 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema( }, "megabits_received":{ "hz":0.0 - } + }, + "tls_policy_failures":{ + "hz":0 + }, }, "run_loop_busy":0.2 } diff --git a/fdbserver/Status.actor.cpp b/fdbserver/Status.actor.cpp index d1cab6de22..dcc7e0774e 100644 --- a/fdbserver/Status.actor.cpp +++ b/fdbserver/Status.actor.cpp @@ -773,6 +773,10 @@ ACTOR static Future processStatusFetcher( megabits_received.setKeyRawNumber("hz", processMetrics.getValue("MbpsReceived")); networkObj["megabits_received"] = megabits_received; + JsonBuilderObject tls_policy_failures; + tls_policy_failures.setKeyRawNumber("hz", processMetrics.getValue("TLSPolicyFailures")); + networkObj["tls_policy_failures"] = tls_policy_failures; + statusObj["network"] = networkObj; memoryObj.setKeyRawNumber("used_bytes", processMetrics.getValue("Memory")); From 04498cbc0e92ef334445c63fb75e40c503096829 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Fri, 13 Mar 2020 02:49:06 -0700 Subject: [PATCH 0885/1604] Make policy failures be reported as per 1s and not over 5s. --- fdbclient/Schemas.cpp | 2 +- flow/SystemMonitor.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fdbclient/Schemas.cpp b/fdbclient/Schemas.cpp index 2bdf54e275..44a2de1c2f 100644 --- a/fdbclient/Schemas.cpp +++ b/fdbclient/Schemas.cpp @@ -210,7 +210,7 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema( "hz":0.0 }, "tls_policy_failures":{ - "hz":0 + "hz":0.0 }, }, "run_loop_busy":0.2 diff --git a/flow/SystemMonitor.cpp b/flow/SystemMonitor.cpp index 47e5c6497c..c3b5cb8051 100644 --- a/flow/SystemMonitor.cpp +++ b/flow/SystemMonitor.cpp @@ -101,7 +101,7 @@ SystemStatistics customSystemMonitor(std::string eventName, StatisticsState *sta .detail("ConnectionsEstablished", (double) (netData.countConnEstablished - statState->networkState.countConnEstablished) / currentStats.elapsed) .detail("ConnectionsClosed", ((netData.countConnClosedWithError - statState->networkState.countConnClosedWithError) + (netData.countConnClosedWithoutError - statState->networkState.countConnClosedWithoutError)) / currentStats.elapsed) .detail("ConnectionErrors", (netData.countConnClosedWithError - statState->networkState.countConnClosedWithError) / currentStats.elapsed) - .detail("TLSPolicyFailures", (netData.countTLSPolicyFailures - statState->networkState.countTLSPolicyFailures)) + .detail("TLSPolicyFailures", (netData.countTLSPolicyFailures - statState->networkState.countTLSPolicyFailures) / currentStats.elapsed) .trackLatest(eventName); TraceEvent("MemoryMetrics") From 243c268d9dc271d1fb2300bc996658f1b66d5021 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Fri, 13 Mar 2020 10:17:49 -0700 Subject: [PATCH 0886/1604] Limit the amount of requests the proxy can queue up in memory --- fdbclient/MasterProxyInterface.h | 5 +- fdbclient/NativeAPI.actor.cpp | 3 + fdbrpc/fdbrpc.h | 1 + fdbserver/Knobs.cpp | 2 + fdbserver/Knobs.h | 2 + fdbserver/MasterProxyServer.actor.cpp | 135 +++++++++++++++----------- flow/flow.h | 1 + flow/network.h | 1 - 8 files changed, 91 insertions(+), 59 deletions(-) diff --git a/fdbclient/MasterProxyInterface.h b/fdbclient/MasterProxyInterface.h index f0ddbe314b..7333f588ed 100644 --- a/fdbclient/MasterProxyInterface.h +++ b/fdbclient/MasterProxyInterface.h @@ -67,10 +67,11 @@ struct MasterProxyInterface { } void initEndpoints() { - getConsistentReadVersion.getEndpoint(TaskPriority::ProxyGetConsistentReadVersion); + getConsistentReadVersion.getEndpoint(TaskPriority::ReadSocket); getRawCommittedVersion.getEndpoint(TaskPriority::ProxyGetRawCommittedVersion); - commit.getEndpoint(TaskPriority::ProxyCommitDispatcher); + commit.getEndpoint(TaskPriority::ReadSocket); getStorageServerRejoinInfo.getEndpoint(TaskPriority::ProxyStorageRejoin); + getKeyServersLocations.getEndpoint(TaskPriority::ReadSocket); //priority lowered to TaskPriority::DefaultEndpoint on the proxy //getKeyServersLocations.getEndpoint(TaskProxyGetKeyServersLocations); //do not increase the priority of these requests, because clients cans bring down the cluster with too many of these messages. } }; diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index 7dbf08cdc7..2d8a616ab2 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -3145,6 +3145,9 @@ ACTOR Future extractReadVersion(DatabaseContext* cx, uint32_t flags, Re cx->GRVLatencies.addSample(latency); if (trLogInfo) trLogInfo->addLog(FdbClientLogEvents::EventGetVersion_V2(startTime, latency, flags & GetReadVersionRequest::FLAG_PRIORITY_MASK)); + if (rep.version == 1 && rep.locked) { + throw proxy_memory_limit_exceeded(); + } if(rep.locked && !lockAware) throw database_locked(); diff --git a/fdbrpc/fdbrpc.h b/fdbrpc/fdbrpc.h index 65bbcb6df3..cd0cd93a6d 100644 --- a/fdbrpc/fdbrpc.h +++ b/fdbrpc/fdbrpc.h @@ -403,6 +403,7 @@ public: bool operator == (const RequestStream& rhs) const { return queue == rhs.queue; } bool isEmpty() const { return !queue->isReady(); } + uint32_t size() const { return queue->size(); } private: NetNotifiedQueue* queue; diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index d3b229f689..830cd8c99f 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -290,6 +290,8 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs, bool isSimula init( START_TRANSACTION_BATCH_QUEUE_CHECK_INTERVAL, 0.001 ); init( START_TRANSACTION_MAX_TRANSACTIONS_TO_START, 100000 ); init( START_TRANSACTION_MAX_REQUESTS_TO_START, 10000 ); + init( START_TRANSACTION_MAX_QUEUE_SIZE, 1e6 ); + init( KEY_LOCATION_MAX_QUEUE_SIZE, 1e6 ); init( COMMIT_TRANSACTION_BATCH_INTERVAL_FROM_IDLE, 0.0005 ); if( randomize && BUGGIFY ) COMMIT_TRANSACTION_BATCH_INTERVAL_FROM_IDLE = 0.005; init( COMMIT_TRANSACTION_BATCH_INTERVAL_MIN, 0.001 ); if( randomize && BUGGIFY ) COMMIT_TRANSACTION_BATCH_INTERVAL_MIN = 0.1; diff --git a/fdbserver/Knobs.h b/fdbserver/Knobs.h index 2473f78e0f..ce23cb79a9 100644 --- a/fdbserver/Knobs.h +++ b/fdbserver/Knobs.h @@ -239,6 +239,8 @@ public: double START_TRANSACTION_BATCH_QUEUE_CHECK_INTERVAL; double START_TRANSACTION_MAX_TRANSACTIONS_TO_START; int START_TRANSACTION_MAX_REQUESTS_TO_START; + int START_TRANSACTION_MAX_QUEUE_SIZE; + int KEY_LOCATION_MAX_QUEUE_SIZE; double COMMIT_TRANSACTION_BATCH_INTERVAL_FROM_IDLE; double COMMIT_TRANSACTION_BATCH_INTERVAL_MIN; diff --git a/fdbserver/MasterProxyServer.actor.cpp b/fdbserver/MasterProxyServer.actor.cpp index 7859b4a4e5..a990d41fd6 100644 --- a/fdbserver/MasterProxyServer.actor.cpp +++ b/fdbserver/MasterProxyServer.actor.cpp @@ -50,6 +50,7 @@ struct ProxyStats { CounterCollection cc; + Counter txnRequestIn, txnRequestOut, txnRequestErrors; Counter txnStartIn, txnStartOut, txnStartBatch; Counter txnSystemPriorityStartIn, txnSystemPriorityStartOut; Counter txnBatchPriorityStartIn, txnBatchPriorityStartOut; @@ -60,7 +61,7 @@ struct ProxyStats { Counter mutationBytes; Counter mutations; Counter conflictRanges; - Counter keyServerLocationRequests; + Counter keyServerLocationIn, keyServerLocationOut, keyServerLocationErrors; Version lastCommitVersionAssigned; LatencyBands commitLatencyBands; @@ -69,10 +70,10 @@ struct ProxyStats { Future logger; explicit ProxyStats(UID id, Version* pVersion, NotifiedVersion* pCommittedVersion, int64_t *commitBatchesMemBytesCountPtr) - : cc("ProxyStats", id.toString()), + : cc("ProxyStats", id.toString()), txnRequestIn("TxnRequestIn", cc), txnRequestOut("TxnRequestOut", cc), txnRequestErrors("TxnRequestErrors", cc), txnStartIn("TxnStartIn", cc), txnStartOut("TxnStartOut", cc), txnStartBatch("TxnStartBatch", cc), txnSystemPriorityStartIn("TxnSystemPriorityStartIn", cc), txnSystemPriorityStartOut("TxnSystemPriorityStartOut", cc), txnBatchPriorityStartIn("TxnBatchPriorityStartIn", cc), txnBatchPriorityStartOut("TxnBatchPriorityStartOut", cc), txnDefaultPriorityStartIn("TxnDefaultPriorityStartIn", cc), txnDefaultPriorityStartOut("TxnDefaultPriorityStartOut", cc), txnCommitIn("TxnCommitIn", cc), txnCommitVersionAssigned("TxnCommitVersionAssigned", cc), txnCommitResolving("TxnCommitResolving", cc), txnCommitResolved("TxnCommitResolved", cc), txnCommitOut("TxnCommitOut", cc), - txnCommitOutSuccess("TxnCommitOutSuccess", cc), txnConflicts("TxnConflicts", cc), commitBatchIn("CommitBatchIn", cc), commitBatchOut("CommitBatchOut", cc), mutationBytes("MutationBytes", cc), mutations("Mutations", cc), conflictRanges("ConflictRanges", cc), keyServerLocationRequests("KeyServerLocationRequests", cc), + txnCommitOutSuccess("TxnCommitOutSuccess", cc), txnConflicts("TxnConflicts", cc), commitBatchIn("CommitBatchIn", cc), commitBatchOut("CommitBatchOut", cc), mutationBytes("MutationBytes", cc), mutations("Mutations", cc), conflictRanges("ConflictRanges", cc), keyServerLocationIn("KeyServerLocationIn", cc), keyServerLocationOut("KeyServerLocationOut", cc), keyServerLocationErrors("KeyServerLocationErrors", cc), lastCommitVersionAssigned(0), commitLatencyBands("CommitLatencyMetrics", id, SERVER_KNOBS->STORAGE_LOGGING_DELAY), grvLatencyBands("GRVLatencyMetrics", id, SERVER_KNOBS->STORAGE_LOGGING_DELAY) { specialCounter(cc, "LastAssignedCommitVersion", [this](){return this->lastCommitVersionAssigned;}); @@ -144,22 +145,32 @@ ACTOR Future queueTransactionStartRequests( state int64_t counter = 0; loop choose{ when(GetReadVersionRequest req = waitNext(readVersionRequests)) { - if (req.debugID.present()) - g_traceBatch.addEvent("TransactionDebug", req.debugID.get().first(), "MasterProxyServer.queueTransactionStartRequests.Before"); + if( stats->txnRequestIn.getValue() - stats->txnRequestOut.getValue() > SERVER_KNOBS->START_TRANSACTION_MAX_QUEUE_SIZE ) { + ++stats->txnRequestErrors; + //FIXME: send an error instead of giving an unreadable version when the client can support the error: req.reply.sendError(proxy_memory_limit_exceeded()); + GetReadVersionReply rep; + rep.version = 1; + rep.locked = true; + req.reply.send(rep); + } else { + if (req.debugID.present()) + g_traceBatch.addEvent("TransactionDebug", req.debugID.get().first(), "MasterProxyServer.queueTransactionStartRequests.Before"); - stats->txnStartIn += req.transactionCount; - if (req.priority() >= GetReadVersionRequest::PRIORITY_SYSTEM_IMMEDIATE) - stats->txnSystemPriorityStartIn += req.transactionCount; - else if (req.priority() >= GetReadVersionRequest::PRIORITY_DEFAULT) - stats->txnDefaultPriorityStartIn += req.transactionCount; - else - stats->txnBatchPriorityStartIn += req.transactionCount; + ++stats->txnRequestIn; + stats->txnStartIn += req.transactionCount; + if (req.priority() >= GetReadVersionRequest::PRIORITY_SYSTEM_IMMEDIATE) + stats->txnSystemPriorityStartIn += req.transactionCount; + else if (req.priority() >= GetReadVersionRequest::PRIORITY_DEFAULT) + stats->txnDefaultPriorityStartIn += req.transactionCount; + else + stats->txnBatchPriorityStartIn += req.transactionCount; - if (transactionQueue->empty()) { - forwardPromise(GRVTimer, delayJittered(std::max(0.0, *GRVBatchTime - (now() - *lastGRVTime)), TaskPriority::ProxyGRVTimer)); + if (transactionQueue->empty()) { + forwardPromise(GRVTimer, delayJittered(std::max(0.0, *GRVBatchTime - (now() - *lastGRVTime)), TaskPriority::ProxyGRVTimer)); + } + + transactionQueue->push(std::make_pair(req, counter--)); } - - transactionQueue->push(std::make_pair(req, counter--)); } // dynamic batching monitors reply latencies when(double reply_latency = waitNext(replyTimes)) { @@ -1196,6 +1207,7 @@ ACTOR Future sendGrvReplies(Future replyFuture, std:: if(request.priority() >= GetReadVersionRequest::PRIORITY_DEFAULT) { stats->grvLatencyBands.addMeasurement(end - request.requestTime()); } + ++stats->txnRequestOut; request.reply.send(reply); } @@ -1325,55 +1337,64 @@ ACTOR static Future transactionStarter( } } -ACTOR static Future readRequestServer( MasterProxyInterface proxy, ProxyCommitData* commitData ) { - // Implement read-only parts of the proxy interface +ACTOR static Future doKeyServerLocationRequest( GetKeyServerLocationsRequest req, ProxyCommitData* commitData ) { // We can't respond to these requests until we have valid txnStateStore wait(commitData->validState.getFuture()); + wait(delay(0, TaskPriority::DefaultEndpoint)); - TraceEvent("ProxyReadyForReads", proxy.id()); - - loop { - GetKeyServerLocationsRequest req = waitNext(proxy.getKeyServersLocations.getFuture()); - ++commitData->stats.keyServerLocationRequests; - GetKeyServerLocationsReply rep; - if(!req.end.present()) { - auto r = req.reverse ? commitData->keyInfo.rangeContainingKeyBefore(req.begin) : commitData->keyInfo.rangeContaining(req.begin); + GetKeyServerLocationsReply rep; + if(!req.end.present()) { + auto r = req.reverse ? commitData->keyInfo.rangeContainingKeyBefore(req.begin) : commitData->keyInfo.rangeContaining(req.begin); + vector ssis; + ssis.reserve(r.value().src_info.size()); + for(auto& it : r.value().src_info) { + ssis.push_back(it->interf); + } + rep.results.push_back(std::make_pair(r.range(), ssis)); + } else if(!req.reverse) { + int count = 0; + for(auto r = commitData->keyInfo.rangeContaining(req.begin); r != commitData->keyInfo.ranges().end() && count < req.limit && r.begin() < req.end.get(); ++r) { vector ssis; ssis.reserve(r.value().src_info.size()); for(auto& it : r.value().src_info) { ssis.push_back(it->interf); } rep.results.push_back(std::make_pair(r.range(), ssis)); - } else if(!req.reverse) { - int count = 0; - for(auto r = commitData->keyInfo.rangeContaining(req.begin); r != commitData->keyInfo.ranges().end() && count < req.limit && r.begin() < req.end.get(); ++r) { - vector ssis; - ssis.reserve(r.value().src_info.size()); - for(auto& it : r.value().src_info) { - ssis.push_back(it->interf); - } - rep.results.push_back(std::make_pair(r.range(), ssis)); - count++; - } - } else { - int count = 0; - auto r = commitData->keyInfo.rangeContainingKeyBefore(req.end.get()); - while( count < req.limit && req.begin < r.end() ) { - vector ssis; - ssis.reserve(r.value().src_info.size()); - for(auto& it : r.value().src_info) { - ssis.push_back(it->interf); - } - rep.results.push_back(std::make_pair(r.range(), ssis)); - if(r == commitData->keyInfo.ranges().begin()) { - break; - } - count++; - --r; - } + count++; + } + } else { + int count = 0; + auto r = commitData->keyInfo.rangeContainingKeyBefore(req.end.get()); + while( count < req.limit && req.begin < r.end() ) { + vector ssis; + ssis.reserve(r.value().src_info.size()); + for(auto& it : r.value().src_info) { + ssis.push_back(it->interf); + } + rep.results.push_back(std::make_pair(r.range(), ssis)); + if(r == commitData->keyInfo.ranges().begin()) { + break; + } + count++; + --r; + } + } + req.reply.send(rep); + ++commitData->stats.keyServerLocationOut; + return Void(); +} + +ACTOR static Future readRequestServer( MasterProxyInterface proxy, PromiseStream> addActor, ProxyCommitData* commitData ) { + loop { + GetKeyServerLocationsRequest req = waitNext(proxy.getKeyServersLocations.getFuture()); + if(req.limit != CLIENT_KNOBS->STORAGE_METRICS_SHARD_LIMIT && //Always do data distribution requests + commitData->stats.keyServerLocationIn.getValue() - commitData->stats.keyServerLocationOut.getValue() > SERVER_KNOBS->KEY_LOCATION_MAX_QUEUE_SIZE) { + ++commitData->stats.keyServerLocationErrors; + req.reply.sendError(proxy_memory_limit_exceeded()); + } else { + ++commitData->stats.keyServerLocationIn; + addActor.send(doKeyServerLocationRequest(req, commitData)); } - req.reply.send(rep); - wait(yield()); } } @@ -1381,6 +1402,8 @@ ACTOR static Future rejoinServer( MasterProxyInterface proxy, ProxyCommitD // We can't respond to these requests until we have valid txnStateStore wait(commitData->validState.getFuture()); + TraceEvent("ProxyReadyForReads", proxy.id()); + loop { GetStorageServerRejoinInfoRequest req = waitNext(proxy.getStorageServerRejoinInfo.getFuture()); if (commitData->txnStateStore->readValue(serverListKeyFor(req.id)).get().present()) { @@ -1664,7 +1687,7 @@ ACTOR Future masterProxyServerCore( addActor.send(monitorRemoteCommitted(&commitData)); addActor.send(transactionStarter(proxy, commitData.db, addActor, &commitData, &healthMetricsReply, &detailedHealthMetricsReply)); - addActor.send(readRequestServer(proxy, &commitData)); + addActor.send(readRequestServer(proxy, addActor, &commitData)); addActor.send(rejoinServer(proxy, &commitData)); addActor.send(healthMetricsRequestServer(proxy, &healthMetricsReply, &detailedHealthMetricsReply)); diff --git a/flow/flow.h b/flow/flow.h index 1d32e8a520..577d8ac1aa 100644 --- a/flow/flow.h +++ b/flow/flow.h @@ -578,6 +578,7 @@ struct NotifiedQueue : private SingleCallback, FastAllocated bool isReady() const { return !queue.empty() || error.isValid(); } bool isError() const { return queue.empty() && error.isValid(); } // the *next* thing queued is an error + uint32_t size() const { return queue.size(); } T pop() { if (queue.empty()) { diff --git a/flow/network.h b/flow/network.h index f9234a4d9b..485b9acefd 100644 --- a/flow/network.h +++ b/flow/network.h @@ -56,7 +56,6 @@ enum class TaskPriority { ClusterController = 8650, MasterTLogRejoin = 8646, ProxyStorageRejoin = 8645, - ProxyCommitDispatcher = 8640, TLogQueuingMetrics = 8620, TLogPop = 8610, TLogPeekReply = 8600, From 4640edf5d6bb8f4a3819b6ebbc1f8c740ddd9e42 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Fri, 13 Mar 2020 10:24:52 -0700 Subject: [PATCH 0887/1604] do not recruit satellite tlogs when usable regions=1 --- fdbclient/DatabaseConfiguration.h | 2 +- fdbserver/ClusterController.actor.cpp | 8 ++++---- fdbserver/Status.actor.cpp | 2 +- fdbserver/TagPartitionedLogSystem.actor.cpp | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/fdbclient/DatabaseConfiguration.h b/fdbclient/DatabaseConfiguration.h index 0fdae09956..3f7482a564 100644 --- a/fdbclient/DatabaseConfiguration.h +++ b/fdbclient/DatabaseConfiguration.h @@ -107,7 +107,7 @@ struct DatabaseConfiguration { int expectedLogSets( Optional dcId ) const { int result = 1; - if(dcId.present() && getRegion(dcId.get()).satelliteTLogReplicationFactor > 0) { + if(dcId.present() && getRegion(dcId.get()).satelliteTLogReplicationFactor > 0 && usableRegions > 1) { result++; } diff --git a/fdbserver/ClusterController.actor.cpp b/fdbserver/ClusterController.actor.cpp index 5cbc6ebb8a..982de61074 100644 --- a/fdbserver/ClusterController.actor.cpp +++ b/fdbserver/ClusterController.actor.cpp @@ -683,7 +683,7 @@ public: } std::vector satelliteLogs; - if(region.satelliteTLogReplicationFactor > 0) { + if(region.satelliteTLogReplicationFactor > 0 && req.configuration.usableRegions > 1) { satelliteLogs = getWorkersForSatelliteLogs( req.configuration, region, remoteRegion, id_used, result.satelliteFallback ); for(int i = 0; i < satelliteLogs.size(); i++) { result.satelliteTLogs.push_back(satelliteLogs[i].interf); @@ -718,7 +718,7 @@ public: if( !goodRecruitmentTime.isReady() && ( RoleFitness(SERVER_KNOBS->EXPECTED_TLOG_FITNESS, req.configuration.getDesiredLogs(), ProcessClass::TLog).betterCount(RoleFitness(tlogs, ProcessClass::TLog)) || - ( region.satelliteTLogReplicationFactor > 0 && RoleFitness(SERVER_KNOBS->EXPECTED_TLOG_FITNESS, req.configuration.getDesiredSatelliteLogs(dcId), ProcessClass::TLog).betterCount(RoleFitness(satelliteLogs, ProcessClass::TLog)) ) || + ( region.satelliteTLogReplicationFactor > 0 && req.configuration.usableRegions > 1 && RoleFitness(SERVER_KNOBS->EXPECTED_TLOG_FITNESS, req.configuration.getDesiredSatelliteLogs(dcId), ProcessClass::TLog).betterCount(RoleFitness(satelliteLogs, ProcessClass::TLog)) ) || RoleFitness(SERVER_KNOBS->EXPECTED_PROXY_FITNESS, req.configuration.getDesiredProxies(), ProcessClass::Proxy).betterCount(RoleFitness(proxies, ProcessClass::Proxy)) || RoleFitness(SERVER_KNOBS->EXPECTED_RESOLVER_FITNESS, req.configuration.getDesiredResolvers(), ProcessClass::Resolver).betterCount(RoleFitness(resolvers, ProcessClass::Resolver)) ) ) { return operation_failed(); @@ -895,7 +895,7 @@ public: std::set> primaryDC; primaryDC.insert(regions[0].dcId); getWorkersForTlogs(db.config, db.config.tLogReplicationFactor, db.config.getDesiredLogs(), db.config.tLogPolicy, id_used, true, primaryDC); - if(regions[0].satelliteTLogReplicationFactor > 0) { + if(regions[0].satelliteTLogReplicationFactor > 0 && db.config.usableRegions > 1) { bool satelliteFallback = false; getWorkersForSatelliteLogs(db.config, regions[0], regions[1], id_used, satelliteFallback, true); } @@ -1068,7 +1068,7 @@ public: RoleFitness oldSatelliteTLogFit(satellite_tlogs, ProcessClass::TLog); bool newSatelliteFallback = false; - auto newSatelliteTLogs = region.satelliteTLogReplicationFactor > 0 ? getWorkersForSatelliteLogs(db.config, region, remoteRegion, id_used, newSatelliteFallback, true) : satellite_tlogs; + auto newSatelliteTLogs = (region.satelliteTLogReplicationFactor > 0 && db.config.usableRegions > 1) ? getWorkersForSatelliteLogs(db.config, region, remoteRegion, id_used, newSatelliteFallback, true) : satellite_tlogs; RoleFitness newSatelliteTLogFit(newSatelliteTLogs, ProcessClass::TLog); std::map,int32_t> satellite_priority; diff --git a/fdbserver/Status.actor.cpp b/fdbserver/Status.actor.cpp index d1cab6de22..4b6a9b302c 100644 --- a/fdbserver/Status.actor.cpp +++ b/fdbserver/Status.actor.cpp @@ -1573,7 +1573,7 @@ static int getExtraTLogEligibleZones(const vector& workers, const for(auto& region : configuration.regions) { int eligible = dcId_zone[region.dcId].size() - std::max(configuration.remoteTLogReplicationFactor, std::max(configuration.tLogReplicationFactor, configuration.storageTeamSize) ); //FIXME: does not take into account fallback satellite policies - if(region.satelliteTLogReplicationFactor > 0) { + if(region.satelliteTLogReplicationFactor > 0 && configuration.usableRegions > 1) { int totalSatelliteEligible = 0; for(auto& sat : region.satellites) { totalSatelliteEligible += dcId_zone[sat.dcId].size(); diff --git a/fdbserver/TagPartitionedLogSystem.actor.cpp b/fdbserver/TagPartitionedLogSystem.actor.cpp index fba01991e2..625784a22e 100644 --- a/fdbserver/TagPartitionedLogSystem.actor.cpp +++ b/fdbserver/TagPartitionedLogSystem.actor.cpp @@ -2019,7 +2019,7 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCountedtLogVersion < TLogVersion::V4; } - if(region.satelliteTLogReplicationFactor > 0) { + if(region.satelliteTLogReplicationFactor > 0 && configuration.usableRegions > 1) { logSystem->tLogs.emplace_back(new LogSet()); if(recr.satelliteFallback) { logSystem->tLogs[1]->tLogWriteAntiQuorum = region.satelliteTLogWriteAntiQuorumFallback; @@ -2167,7 +2167,7 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted> recoveryComplete; - if(region.satelliteTLogReplicationFactor > 0) { + if(region.satelliteTLogReplicationFactor > 0 && configuration.usableRegions > 1) { state vector> satelliteInitializationReplies; vector< InitializeTLogRequest > sreqs( recr.satelliteTLogs.size() ); std::vector satelliteTags; From a39effa57d63dc2a27989f2dadfbeafec3e8b320 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Fri, 13 Mar 2020 10:28:32 -0700 Subject: [PATCH 0888/1604] delay recoveries after 70 outstanding generations, and stop recoveries after 100 outstanding generations to prevent a death spiral from filling up the coordinated state --- fdbclient/Knobs.cpp | 4 ++++ fdbclient/Knobs.h | 4 ++++ fdbserver/masterserver.actor.cpp | 13 +++++++++++++ 3 files changed, 21 insertions(+) diff --git a/fdbclient/Knobs.cpp b/fdbclient/Knobs.cpp index 43e467e2b5..92595f2030 100644 --- a/fdbclient/Knobs.cpp +++ b/fdbclient/Knobs.cpp @@ -41,6 +41,10 @@ ClientKnobs::ClientKnobs(bool randomize) { init( CLIENT_FAILURE_TIMEOUT_DELAY, FAILURE_MIN_DELAY ); init( FAILURE_EMERGENCY_DELAY, 30.0 ); init( FAILURE_MAX_GENERATIONS, 10 ); + init( RECOVERY_DELAY_START_GENERATION, 70 ); + init( RECOVERY_DELAY_SECONDS_PER_GENERATION, 60.0 ); + init( MAX_GENERATIONS, 100 ); + init( MAX_GENERATIONS_OVERRIDE, 0 ); init( COORDINATOR_RECONNECTION_DELAY, 1.0 ); init( CLIENT_EXAMPLE_AMOUNT, 20 ); diff --git a/fdbclient/Knobs.h b/fdbclient/Knobs.h index ed6dfec15f..2be31e6d27 100644 --- a/fdbclient/Knobs.h +++ b/fdbclient/Knobs.h @@ -40,6 +40,10 @@ public: double CLIENT_FAILURE_TIMEOUT_DELAY; double FAILURE_EMERGENCY_DELAY; double FAILURE_MAX_GENERATIONS; + double RECOVERY_DELAY_START_GENERATION; + double RECOVERY_DELAY_SECONDS_PER_GENERATION; + double MAX_GENERATIONS; + double MAX_GENERATIONS_OVERRIDE; double COORDINATOR_RECONNECTION_DELAY; int CLIENT_EXAMPLE_AMOUNT; diff --git a/fdbserver/masterserver.actor.cpp b/fdbserver/masterserver.actor.cpp index 2f462e8cd9..f469d70b55 100644 --- a/fdbserver/masterserver.actor.cpp +++ b/fdbserver/masterserver.actor.cpp @@ -1245,10 +1245,23 @@ ACTOR Future masterCore( Reference self ) { .detail("StatusCode", RecoveryStatus::locking_coordinated_state) .detail("Status", RecoveryStatus::names[RecoveryStatus::locking_coordinated_state]) .detail("TLogs", self->cstate.prevDBState.tLogs.size()) + .detail("OldGenerations", self->cstate.myDBState.oldTLogData.size()) .detail("MyRecoveryCount", self->cstate.prevDBState.recoveryCount+2) .detail("ForceRecovery", self->forceRecovery) .trackLatest("MasterRecoveryState"); + if (self->cstate.myDBState.oldTLogData.size() > CLIENT_KNOBS->MAX_GENERATIONS_OVERRIDE) { + if (self->cstate.myDBState.oldTLogData.size() >= CLIENT_KNOBS->MAX_GENERATIONS) { + TraceEvent(SevError, "RecoveryStoppedTooManyOldGenerations").detail("OldGenerations", self->cstate.myDBState.oldTLogData.size()) + .detail("Reason", "Recovery stopped because too many recoveries have happened since the last time the cluster was fully_recovered. Set --knob_max_generations_override to a value larger than OldGenerations on your server processes to resume recovery once the underlying problem has been fixed."); + wait(Future(Never())); + } else if (self->cstate.myDBState.oldTLogData.size() > CLIENT_KNOBS->RECOVERY_DELAY_START_GENERATION) { + TraceEvent(SevError, "RecoveryDelayedTooManyOldGenerations").detail("OldGenerations", self->cstate.myDBState.oldTLogData.size()) + .detail("Reason", "Recovery is delayed because too many recoveries have happened since the last time the cluster was fully_recovered. Set --knob_max_generations_override to a value larger than OldGenerations on your server processes to resume recovery once the underlying problem has been fixed."); + wait(delay(CLIENT_KNOBS->RECOVERY_DELAY_SECONDS_PER_GENERATION*(self->cstate.myDBState.oldTLogData.size() - CLIENT_KNOBS->RECOVERY_DELAY_START_GENERATION))); + } + } + state Reference>> oldLogSystems( new AsyncVar> ); state Future recoverAndEndEpoch = ILogSystem::recoverAndEndEpoch(oldLogSystems, self->dbgid, self->cstate.prevDBState, self->myInterface.tlogRejoin.getFuture(), self->myInterface.locality, &self->forceRecovery); From d6d347f665cfa1b77bd376217a7d8c9a8ad6ba63 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Fri, 13 Mar 2020 10:31:59 -0700 Subject: [PATCH 0889/1604] treat a tlog which takes a long time to create its disk queue as failed --- fdbserver/Knobs.cpp | 1 + fdbserver/Knobs.h | 1 + fdbserver/OldTLogServer_6_0.actor.cpp | 12 +++++++++++- fdbserver/TLogServer.actor.cpp | 12 +++++++++++- 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index d3b229f689..fd1184b519 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -82,6 +82,7 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs, bool isSimula init( TLOG_DEGRADED_DURATION, 5.0 ); init( TLOG_IGNORE_POP_AUTO_ENABLE_DELAY, 300.0 ); init( TXS_POPPED_MAX_DELAY, 1.0 ); if ( randomize && BUGGIFY ) TXS_POPPED_MAX_DELAY = deterministicRandom()->random01(); + init( TLOG_MAX_CREATE_DURATION, 10.0 ); // disk snapshot max timeout, to be put in TLog, storage and coordinator nodes init( SNAP_CREATE_MAX_TIMEOUT, 300.0 ); diff --git a/fdbserver/Knobs.h b/fdbserver/Knobs.h index 2473f78e0f..73401f1ae4 100644 --- a/fdbserver/Knobs.h +++ b/fdbserver/Knobs.h @@ -84,6 +84,7 @@ public: int DISK_QUEUE_MAX_TRUNCATE_BYTES; // A truncate larger than this will cause the file to be replaced instead. double TLOG_DEGRADED_DURATION; double TXS_POPPED_MAX_DELAY; + double TLOG_MAX_CREATE_DURATION; // Data distribution queue double HEALTH_POLL_TIME; diff --git a/fdbserver/OldTLogServer_6_0.actor.cpp b/fdbserver/OldTLogServer_6_0.actor.cpp index 07a047d5a8..23f81745fa 100644 --- a/fdbserver/OldTLogServer_6_0.actor.cpp +++ b/fdbserver/OldTLogServer_6_0.actor.cpp @@ -2332,7 +2332,17 @@ ACTOR Future tLog( IKeyValueStore* persistentData, IDiskQueue* persistentQ if(restoreFromDisk) { wait( restorePersistentState( &self, locality, oldLog, recovered, tlogRequests ) ); } else { - wait( checkEmptyQueue(&self) && checkRecovered(&self) ); + choose { + when( wait( checkEmptyQueue(&self) && checkRecovered(&self) ) ) {} + when( wait( lowPriorityDelay(SERVER_KNOBS->TLOG_MAX_CREATE_DURATION) ) ) { + Error err = io_timeout(); + if(g_network->isSimulated()) { + err = err.asInjectedFault(); + } + TraceEvent(SevError, "TLogInitializeFilesTimeout", tlogId).error(err); + throw err; + } + } } //Disk errors need a chance to kill this actor. diff --git a/fdbserver/TLogServer.actor.cpp b/fdbserver/TLogServer.actor.cpp index 85eb10461d..6d474d6e5b 100644 --- a/fdbserver/TLogServer.actor.cpp +++ b/fdbserver/TLogServer.actor.cpp @@ -2766,7 +2766,17 @@ ACTOR Future tLog( IKeyValueStore* persistentData, IDiskQueue* persistentQ if(restoreFromDisk) { wait( restorePersistentState( &self, locality, oldLog, recovered, tlogRequests ) ); } else { - wait( checkEmptyQueue(&self) && checkRecovered(&self) ); + choose { + when( wait( checkEmptyQueue(&self) && checkRecovered(&self) ) ) {} + when( wait( lowPriorityDelay(SERVER_KNOBS->TLOG_MAX_CREATE_DURATION) ) ) { + Error err = io_timeout(); + if(g_network->isSimulated()) { + err = err.asInjectedFault(); + } + TraceEvent(SevError, "TLogInitializeFilesTimeout", tlogId).error(err); + throw err; + } + } } //Disk errors need a chance to kill this actor. From 2994d74f6a2f0c2f42da9291121f9278b154ba04 Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Fri, 13 Mar 2020 17:49:34 +0000 Subject: [PATCH 0890/1604] added script that will generated a dev-docker img --- build/gen_dev_docker.sh | 50 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100755 build/gen_dev_docker.sh diff --git a/build/gen_dev_docker.sh b/build/gen_dev_docker.sh new file mode 100755 index 0000000000..52593e8a8d --- /dev/null +++ b/build/gen_dev_docker.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash + +set -e + +DIR_UUID=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1) +user=$(id -un) +group=$(id -gn) +uid=$(id -u) +gid=$(id -g) +tmpdir="/tmp/fdb-docker-${DIR_UUID}" +image=fdb-dev + +pushd . +mkdir ${tmpdir} +cd ${tmpdir} + +cat <> Dockerfile +FROM foundationdb/foundationdb-build:latest +RUN groupadd -g ${gid} ${group} && useradd -u ${uid} -g ${gid} -m ${user} + +USER ${user} +CMD scl enable devtoolset-8 rh-python36 rh-ruby24 -- bash + +EOF + +echo "Created ${tmpdir}" +echo "Buidling Docker container ${image}" +sudo docker build -t ${image} . + +popd + +echo "Writing startup script" +mkdir -p $HOME/bin +cat < $HOME/bin/fdb-dev +#!/usr/bin/bash + +sudo docker run --rm `# delete (temporary) image after return` \\ + -it `# Run in interactive mode and simulate a TTY` \\ + --privileged=true `# Run in privileged mode ` \\ + --cap-add=SYS_PTRACE \\ + --security-opt seccomp=unconfined \\ + -v '/home:/home' `# Mount home directory` \\ + -e "CCACHE_DIR=$CCACHE_DIR" \\ + -e "CCACHE_UMASK=$CCACHE_UMASK" \\ + ${image} +EOF + +chmod +x $HOME/bin/fdb-dev +echo "To start the dev docker image run $HOME/bin/fdb-dev" +echo "You can edit this file but be aware that this script will overwrite your changes if you rerun it" From d9c21fb98ff46e2fd59527593148b155c52213e9 Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Fri, 13 Mar 2020 19:33:31 +0000 Subject: [PATCH 0891/1604] don't rely on `/home` being home... --- build/gen_dev_docker.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/gen_dev_docker.sh b/build/gen_dev_docker.sh index 52593e8a8d..2d2f25decf 100755 --- a/build/gen_dev_docker.sh +++ b/build/gen_dev_docker.sh @@ -39,7 +39,7 @@ sudo docker run --rm `# delete (temporary) image after return` \\ --privileged=true `# Run in privileged mode ` \\ --cap-add=SYS_PTRACE \\ --security-opt seccomp=unconfined \\ - -v '/home:/home' `# Mount home directory` \\ + -v '${HOME}:${HOME}' `# Mount home directory` \\ -e "CCACHE_DIR=$CCACHE_DIR" \\ -e "CCACHE_UMASK=$CCACHE_UMASK" \\ ${image} From 17c8b1f51367b9f7b2c5df05d135cdef4ea706f2 Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Fri, 13 Mar 2020 19:34:39 +0000 Subject: [PATCH 0892/1604] fixed string interpolation --- build/gen_dev_docker.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/gen_dev_docker.sh b/build/gen_dev_docker.sh index 2d2f25decf..4d5184af7d 100755 --- a/build/gen_dev_docker.sh +++ b/build/gen_dev_docker.sh @@ -39,7 +39,7 @@ sudo docker run --rm `# delete (temporary) image after return` \\ --privileged=true `# Run in privileged mode ` \\ --cap-add=SYS_PTRACE \\ --security-opt seccomp=unconfined \\ - -v '${HOME}:${HOME}' `# Mount home directory` \\ + -v "${HOME}:${HOME}" `# Mount home directory` \\ -e "CCACHE_DIR=$CCACHE_DIR" \\ -e "CCACHE_UMASK=$CCACHE_UMASK" \\ ${image} From 6e92716be749c572864828d666b972c00d99008b Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Fri, 13 Mar 2020 12:41:48 -0700 Subject: [PATCH 0893/1604] update comments --- fdbclient/ReadYourWrites.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbclient/ReadYourWrites.actor.cpp b/fdbclient/ReadYourWrites.actor.cpp index 769a70cc7b..df039e83b3 100644 --- a/fdbclient/ReadYourWrites.actor.cpp +++ b/fdbclient/ReadYourWrites.actor.cpp @@ -1284,7 +1284,7 @@ Future< Standalone > ReadYourWritesTransaction::getRange( // The returned key value pairs are interpretted as : // prefix/ : '1' - any keys equal or larger than this key are (probably) conflicting keys // prefix/ : '0' - any keys equal or larger than this key are (definitely) not conflicting keys - // Currently, the conflicting keyranges returned are original read_conflict_ranges. + // Currently, the conflicting keyranges returned are original read_conflict_ranges or union of them. // TODO : This interface needs to be integrated into the framework that handles special keys' calls in the future if (begin.getKey().startsWith(conflictingKeysAbsolutePrefix) && end.getKey().startsWith(conflictingKeysAbsolutePrefix)) { // Remove the special key prefix "\xff\xff" From aef9b515de44f1c0ad12f3d5a2426fe94ef2bd5c Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Fri, 13 Mar 2020 12:42:28 -0700 Subject: [PATCH 0894/1604] Change the workload to a more controlled test like ConflictRange test --- .../workloads/ReportConflictingKeys.actor.cpp | 90 +++++++++++++------ tests/fast/ReportConflictingKeys.txt | 8 +- 2 files changed, 69 insertions(+), 29 deletions(-) diff --git a/fdbserver/workloads/ReportConflictingKeys.actor.cpp b/fdbserver/workloads/ReportConflictingKeys.actor.cpp index 9afee15f54..9d38b0a16a 100644 --- a/fdbserver/workloads/ReportConflictingKeys.actor.cpp +++ b/fdbserver/workloads/ReportConflictingKeys.actor.cpp @@ -48,9 +48,10 @@ struct ReportConflictingKeysWorkload : TestWorkload { readConflictRangeCount = getOption(options, LiteralStringRef("readConflictRangeCountPerTx"), 1); writeConflictRangeCount = getOption(options, LiteralStringRef("writeConflictRangeCountPerTx"), 1); - // modeled by geometric distribution: (1 - prob) / prob = mean - addReadConflictRangeProb = readConflictRangeCount / (readConflictRangeCount + 1.0); - addWriteConflictRangeProb = writeConflictRangeCount / (writeConflictRangeCount + 1.0); + ASSERT(readConflictRangeCount >= 1 && writeConflictRangeCount >= 1); + // modeled by geometric distribution: (1 - prob) / prob = mean - 1, since we add at least one conflictRange to each tx + addReadConflictRangeProb = (readConflictRangeCount - 1.0) / readConflictRangeCount; + addWriteConflictRangeProb = (writeConflictRangeCount - 1.0) / writeConflictRangeCount; ASSERT(keyPrefix.size() + 16 <= keyBytes); // make sure the string format is valid nodeCount = getOption(options, LiteralStringRef("nodeCount"), 100); } @@ -93,29 +94,32 @@ struct ReportConflictingKeysWorkload : TestWorkload { .withPrefix(keyPrefix); } - void addRandomReadConflictRange(ReadYourWritesTransaction* tr, std::vector& readConflictRanges) { + void addRandomReadConflictRange(ReadYourWritesTransaction* tr, std::vector* readConflictRanges) { int startIdx, endIdx; Key startKey, endKey; - while (deterministicRandom()->random01() < addReadConflictRangeProb) { + do { // add at least one startIdx = deterministicRandom()->randomInt(0, nodeCount); - endIdx = deterministicRandom()->randomInt(startIdx, nodeCount); + endIdx = deterministicRandom()->randomInt(startIdx, nodeCount+1); startKey = keyForIndex(startIdx); endKey = keyForIndex(endIdx); tr->addReadConflictRange(KeyRangeRef(startKey, endKey)); - readConflictRanges.push_back(KeyRangeRef(startKey, endKey)); - } + if (readConflictRanges) + readConflictRanges->push_back(KeyRangeRef(startKey, endKey)); + } while (deterministicRandom()->random01() < addReadConflictRangeProb); } - void addRandomWriteConflictRange(ReadYourWritesTransaction* tr) { + void addRandomWriteConflictRange(ReadYourWritesTransaction* tr, std::vector* writeConflictRanges) { int startIdx, endIdx; Key startKey, endKey; - while (deterministicRandom()->random01() < addWriteConflictRangeProb) { + do { // add at least one startIdx = deterministicRandom()->randomInt(0, nodeCount); - endIdx = deterministicRandom()->randomInt(startIdx, nodeCount); + endIdx = deterministicRandom()->randomInt(startIdx, nodeCount+1); startKey = keyForIndex(startIdx); endKey = keyForIndex(endIdx); tr->addWriteConflictRange(KeyRangeRef(startKey, endKey)); - } + if (writeConflictRanges) + writeConflictRanges->push_back(KeyRangeRef(startKey, endKey)); + } while (deterministicRandom()->random01() < addWriteConflictRangeProb); } ACTOR Future conflictingClient(Database cx, ReportConflictingKeysWorkload* self) { @@ -123,38 +127,47 @@ struct ReportConflictingKeysWorkload : TestWorkload { state ReadYourWritesTransaction tr(cx); state ReadYourWritesTransaction tr2(cx); state std::vector readConflictRanges; + state std::vector writeConflictRanges; loop { try { - tr.setOption(FDBTransactionOptions::REPORT_CONFLICTING_KEYS); + tr2.setOption(FDBTransactionOptions::REPORT_CONFLICTING_KEYS); // If READ_YOUR_WRITES_DISABLE set, it behaves like native transaction object // where overlapped conflict ranges are not merged. if (deterministicRandom()->random01() < 0.5) tr.setOption(FDBTransactionOptions::READ_YOUR_WRITES_DISABLE); - self->addRandomReadConflictRange(&tr, readConflictRanges); - self->addRandomWriteConflictRange(&tr); + if (deterministicRandom()->random01() < 0.5) + tr2.setOption(FDBTransactionOptions::READ_YOUR_WRITES_DISABLE); + Version readVersion = wait( tr.getReadVersion() ); + tr2.setVersion(readVersion); + self->addRandomReadConflictRange(&tr, nullptr); + self->addRandomWriteConflictRange(&tr, &writeConflictRanges); ++self->commits; wait(tr.commit()); ++self->xacts; - } catch (Error& e) { - TraceEvent("FailedToCommitTx").error(e); - state bool isConflict = false; - if (e.code() == error_code_operation_cancelled) - throw; - else if (e.code() == error_code_not_committed) { + + state bool foundConflict = false; + try { + self->addRandomReadConflictRange(&tr2, &readConflictRanges); + self->addRandomWriteConflictRange(&tr2, nullptr); + ++self->commits; + wait(tr2.commit()); + ++self->xacts; + } catch (Error& e) { + if( e.code() != error_code_not_committed ) + throw e; + foundConflict = true; ++self->conflicts; - isConflict = true; } - wait(tr.onError(e)); // check API correctness - if (isConflict) { + if (foundConflict) { // \xff\xff/transaction/conflicting_keys is always false, we skip it here for simplicity state KeyRange ckr = KeyRangeRef(keyAfter(LiteralStringRef("").withPrefix(conflictingKeysAbsolutePrefix)), LiteralStringRef("\xff\xff").withPrefix(conflictingKeysAbsolutePrefix)); // The getRange here using the special key prefix "\xff\xff/transaction/conflicting_keys/" happens // locally Thus, the error handling is not needed here Future> conflictingKeyRangesFuture = - tr.getRange(ckr, readConflictRanges.size() * 2); + tr2.getRange(ckr, readConflictRanges.size() * 2); ASSERT(conflictingKeyRangesFuture.isReady()); const Standalone conflictingKeyRanges = conflictingKeyRangesFuture.get(); ASSERT(conflictingKeyRanges.size() && (conflictingKeyRanges.size() % 2 == 0)); @@ -177,12 +190,37 @@ struct ReportConflictingKeysWorkload : TestWorkload { ++self->invalidReports; TraceEvent(SevError, "TestFailure").detail("Reason", "InvalidKeyRangeReturned"); } + else if (!std::any_of(writeConflictRanges.begin(), writeConflictRanges.end(), [&kr](KeyRange wCR) { + // Returned key range should be conflicting with at least one writeConflictRange + return kr.intersects(wCR); + })) { + ++self->invalidReports; + TraceEvent(SevError, "TestFailure").detail("Reason", "Returned keyranges are not conflicting with any write ranges"); + } + } + } else { + // make sure no conflicts between readConflictRange and writeConflictRange + for (const KeyRange& rCR : readConflictRanges) { + if (std::any_of(writeConflictRanges.begin(), writeConflictRanges.end(), [&rCR](KeyRange wCR){ + bool result = wCR.intersects(rCR); + if (result) + TraceEvent(SevError, "TestFailure").detail("WriteRange", wCR.toString()).detail("ReadRange", rCR.toString()); + return result; + })) { + TraceEvent(SevError, "TestFailure").detail("Reason", "No conflicts but should be"); + break; + } } } - ++self->retries; + } catch (Error& e) { + state Error e2 = e; + wait(tr.onError(e2)); + wait(tr2.onError(e2)); } readConflictRanges.clear(); + writeConflictRanges.clear(); tr.reset(); + tr2.reset(); } } }; diff --git a/tests/fast/ReportConflictingKeys.txt b/tests/fast/ReportConflictingKeys.txt index 1010e2493a..36599059c7 100644 --- a/tests/fast/ReportConflictingKeys.txt +++ b/tests/fast/ReportConflictingKeys.txt @@ -1,8 +1,10 @@ testTitle=ReportConflictingKeysTest testName=ReportConflictingKeys - testDuration=10.0 + testDuration=20.0 nodeCount=10000 keyPrefix=RCK keyBytes=64 - readConflictRangeCountPerTx=1 - writeConflictRangeCountPerTx=1 \ No newline at end of file + readConflictRangeCountPerTx=10 + writeConflictRangeCountPerTx=10 + connectionFailuresDisableDuration=100000 + buggify=off \ No newline at end of file From 8ee4fea3d36eb2d8af8566577ea372975d16f793 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Fri, 13 Mar 2020 12:54:12 -0700 Subject: [PATCH 0895/1604] clang-format --- .../workloads/ReportConflictingKeys.actor.cpp | 67 ++++++++++--------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/fdbserver/workloads/ReportConflictingKeys.actor.cpp b/fdbserver/workloads/ReportConflictingKeys.actor.cpp index 9d38b0a16a..f2ee0aaf2b 100644 --- a/fdbserver/workloads/ReportConflictingKeys.actor.cpp +++ b/fdbserver/workloads/ReportConflictingKeys.actor.cpp @@ -49,7 +49,8 @@ struct ReportConflictingKeysWorkload : TestWorkload { readConflictRangeCount = getOption(options, LiteralStringRef("readConflictRangeCountPerTx"), 1); writeConflictRangeCount = getOption(options, LiteralStringRef("writeConflictRangeCountPerTx"), 1); ASSERT(readConflictRangeCount >= 1 && writeConflictRangeCount >= 1); - // modeled by geometric distribution: (1 - prob) / prob = mean - 1, since we add at least one conflictRange to each tx + // modeled by geometric distribution: (1 - prob) / prob = mean - 1, since we add at least one conflictRange to + // each tx addReadConflictRangeProb = (readConflictRangeCount - 1.0) / readConflictRangeCount; addWriteConflictRangeProb = (writeConflictRangeCount - 1.0) / writeConflictRangeCount; ASSERT(keyPrefix.size() + 16 <= keyBytes); // make sure the string format is valid @@ -63,8 +64,7 @@ struct ReportConflictingKeysWorkload : TestWorkload { Future start(const Database& cx) override { return _start(cx->clone(), this); } ACTOR Future _start(Database cx, ReportConflictingKeysWorkload* self) { - if (self->clientId == 0) - wait(timeout(self->conflictingClient(cx, self), self->testDuration, Void())); + if (self->clientId == 0) wait(timeout(self->conflictingClient(cx, self), self->testDuration, Void())); return Void(); } @@ -90,8 +90,7 @@ struct ReportConflictingKeysWorkload : TestWorkload { double p = (double)n / nodeCount; int paddingLen = keyBytes - 16 - keyPrefix.size(); // left padding by zero - return StringRef(format("%0*llx", paddingLen, *(uint64_t*)&p)) - .withPrefix(keyPrefix); + return StringRef(format("%0*llx", paddingLen, *(uint64_t*)&p)).withPrefix(keyPrefix); } void addRandomReadConflictRange(ReadYourWritesTransaction* tr, std::vector* readConflictRanges) { @@ -99,12 +98,11 @@ struct ReportConflictingKeysWorkload : TestWorkload { Key startKey, endKey; do { // add at least one startIdx = deterministicRandom()->randomInt(0, nodeCount); - endIdx = deterministicRandom()->randomInt(startIdx, nodeCount+1); + endIdx = deterministicRandom()->randomInt(startIdx, nodeCount + 1); startKey = keyForIndex(startIdx); endKey = keyForIndex(endIdx); tr->addReadConflictRange(KeyRangeRef(startKey, endKey)); - if (readConflictRanges) - readConflictRanges->push_back(KeyRangeRef(startKey, endKey)); + if (readConflictRanges) readConflictRanges->push_back(KeyRangeRef(startKey, endKey)); } while (deterministicRandom()->random01() < addReadConflictRangeProb); } @@ -113,12 +111,11 @@ struct ReportConflictingKeysWorkload : TestWorkload { Key startKey, endKey; do { // add at least one startIdx = deterministicRandom()->randomInt(0, nodeCount); - endIdx = deterministicRandom()->randomInt(startIdx, nodeCount+1); + endIdx = deterministicRandom()->randomInt(startIdx, nodeCount + 1); startKey = keyForIndex(startIdx); endKey = keyForIndex(endIdx); tr->addWriteConflictRange(KeyRangeRef(startKey, endKey)); - if (writeConflictRanges) - writeConflictRanges->push_back(KeyRangeRef(startKey, endKey)); + if (writeConflictRanges) writeConflictRanges->push_back(KeyRangeRef(startKey, endKey)); } while (deterministicRandom()->random01() < addWriteConflictRangeProb); } @@ -138,7 +135,7 @@ struct ReportConflictingKeysWorkload : TestWorkload { tr.setOption(FDBTransactionOptions::READ_YOUR_WRITES_DISABLE); if (deterministicRandom()->random01() < 0.5) tr2.setOption(FDBTransactionOptions::READ_YOUR_WRITES_DISABLE); - Version readVersion = wait( tr.getReadVersion() ); + Version readVersion = wait(tr.getReadVersion()); tr2.setVersion(readVersion); self->addRandomReadConflictRange(&tr, nullptr); self->addRandomWriteConflictRange(&tr, &writeConflictRanges); @@ -154,16 +151,16 @@ struct ReportConflictingKeysWorkload : TestWorkload { wait(tr2.commit()); ++self->xacts; } catch (Error& e) { - if( e.code() != error_code_not_committed ) - throw e; + if (e.code() != error_code_not_committed) throw e; foundConflict = true; ++self->conflicts; } // check API correctness if (foundConflict) { - // \xff\xff/transaction/conflicting_keys is always false, we skip it here for simplicity - state KeyRange ckr = KeyRangeRef(keyAfter(LiteralStringRef("").withPrefix(conflictingKeysAbsolutePrefix)), - LiteralStringRef("\xff\xff").withPrefix(conflictingKeysAbsolutePrefix)); + // \xff\xff/transaction/conflicting_keys is always initialized to false, skip it here + state KeyRange ckr = + KeyRangeRef(keyAfter(LiteralStringRef("").withPrefix(conflictingKeysAbsolutePrefix)), + LiteralStringRef("\xff\xff").withPrefix(conflictingKeysAbsolutePrefix)); // The getRange here using the special key prefix "\xff\xff/transaction/conflicting_keys/" happens // locally Thus, the error handling is not needed here Future> conflictingKeyRangesFuture = @@ -188,26 +185,34 @@ struct ReportConflictingKeysWorkload : TestWorkload { return kr.contains(rCR); })) { ++self->invalidReports; - TraceEvent(SevError, "TestFailure").detail("Reason", "InvalidKeyRangeReturned"); - } - else if (!std::any_of(writeConflictRanges.begin(), writeConflictRanges.end(), [&kr](KeyRange wCR) { - // Returned key range should be conflicting with at least one writeConflictRange - return kr.intersects(wCR); - })) { + TraceEvent(SevError, "TestFailure") + .detail( + "Reason", + "Returned conflicting keys are not original readConflictRanges or union of them"); + } else if (!std::any_of(writeConflictRanges.begin(), writeConflictRanges.end(), + [&kr](KeyRange wCR) { + // Returned key range should be conflicting with at least one + // writeConflictRange + return kr.intersects(wCR); + })) { ++self->invalidReports; - TraceEvent(SevError, "TestFailure").detail("Reason", "Returned keyranges are not conflicting with any write ranges"); + TraceEvent(SevError, "TestFailure") + .detail("Reason", "Returned keyranges are not conflicting with any write ranges"); } } } else { // make sure no conflicts between readConflictRange and writeConflictRange for (const KeyRange& rCR : readConflictRanges) { - if (std::any_of(writeConflictRanges.begin(), writeConflictRanges.end(), [&rCR](KeyRange wCR){ - bool result = wCR.intersects(rCR); - if (result) - TraceEvent(SevError, "TestFailure").detail("WriteRange", wCR.toString()).detail("ReadRange", rCR.toString()); - return result; - })) { - TraceEvent(SevError, "TestFailure").detail("Reason", "No conflicts but should be"); + if (std::any_of(writeConflictRanges.begin(), writeConflictRanges.end(), [&rCR](KeyRange wCR) { + bool result = wCR.intersects(rCR); + if (result) + TraceEvent(SevError, "TestFailure") + .detail("WriteConflictRange", wCR.toString()) + .detail("ReadConflictRange", rCR.toString()); + return result; + })) { + ++self->invalidReports; + TraceEvent(SevError, "TestFailure").detail("Reason", "No conflicts returned but it should"); break; } } From c4c38c5eca7a45ba27baf74fe2db11b658b30ce9 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Fri, 13 Mar 2020 12:58:12 -0700 Subject: [PATCH 0896/1604] Delete commented code --- fdbclient/NativeAPI.actor.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index 13c3825bef..2ac668ffcc 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -2746,7 +2746,8 @@ ACTOR static Future tryCommit( Database cx, Reference // clear the RYW transaction which contains previous conflicting keys tr->info.conflictingKeysRYW.reset(); if (ci.conflictingKRIndices.present()){ - // In general, if we want to use getRange to expose conflicting keys, we need to support all the parameters getRange provides. + // In general, if we want to use getRange to expose conflicting keys, + // we need to support all the parameters getRange provides. // It is difficult to take care of all corner cases of what getRange does. // Consequently, we use a hack way here to achieve it. // We create an empty RYWTransaction and write all conflicting key/values to it. @@ -2761,16 +2762,12 @@ ACTOR static Future tryCommit( Database cx, Reference // Clear the whole key space, thus, RYWTr knows to only read keys locally tr->info.conflictingKeysRYW->clear(normalKeys); // initialize value - // wait(krmSetRange(hackTr, conflictingKeysPrefix, normalKeys, conflictingKeysFalse)); tr->info.conflictingKeysRYW->set(conflictingKeysPrefix, conflictingKeysFalse); // drop duplicate indices and merge overlapped ranges // Note: addReadConflictRange in native transaction object does not merge overlapped ranges state std::set mergedIds(conflictingKRIndices.begin(), conflictingKRIndices.end()); for (auto const & rCRIndex : mergedIds) { const KeyRange kr = req.transaction.read_conflict_ranges[rCRIndex]; - // tr->info.conflictingKeysRYW->set(kr.begin, conflictingKeysTrue); - // tr->info.conflictingKeysRYW->set(kr.end, conflictingKeysFalse); - // wait(krmSetRange(hackTr, conflictingKeysPrefix, kr, conflictingKeysTrue)); wait(krmSetRangeCoalescing(hackTr, conflictingKeysPrefix, kr, allKeys, conflictingKeysTrue)); } hackTr.extractPtr(); // Avoid the Reference to destroy the RYW object From a3b0dce3cd577a1ed90bbc34986f131200df6fef Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Fri, 13 Mar 2020 13:12:22 -0700 Subject: [PATCH 0897/1604] Rename vars, update comments --- .../workloads/ReportConflictingKeys.actor.cpp | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/fdbserver/workloads/ReportConflictingKeys.actor.cpp b/fdbserver/workloads/ReportConflictingKeys.actor.cpp index f2ee0aaf2b..b7ed1db500 100644 --- a/fdbserver/workloads/ReportConflictingKeys.actor.cpp +++ b/fdbserver/workloads/ReportConflictingKeys.actor.cpp @@ -26,6 +26,7 @@ #include "fdbserver/workloads/BulkSetup.actor.h" #include "flow/actorcompiler.h" // This must be the last #include. +//For this test to report properly buggify must be disabled (flow.h) , and failConnection must be disabled in (sim2.actor.cpp) struct ReportConflictingKeysWorkload : TestWorkload { double testDuration, transactionsPerSecond, addReadConflictRangeProb, addWriteConflictRangeProb; @@ -49,7 +50,7 @@ struct ReportConflictingKeysWorkload : TestWorkload { readConflictRangeCount = getOption(options, LiteralStringRef("readConflictRangeCountPerTx"), 1); writeConflictRangeCount = getOption(options, LiteralStringRef("writeConflictRangeCountPerTx"), 1); ASSERT(readConflictRangeCount >= 1 && writeConflictRangeCount >= 1); - // modeled by geometric distribution: (1 - prob) / prob = mean - 1, since we add at least one conflictRange to + // 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; @@ -121,7 +122,7 @@ struct ReportConflictingKeysWorkload : TestWorkload { ACTOR Future conflictingClient(Database cx, ReportConflictingKeysWorkload* self) { - state ReadYourWritesTransaction tr(cx); + state ReadYourWritesTransaction tr1(cx); state ReadYourWritesTransaction tr2(cx); state std::vector readConflictRanges; state std::vector writeConflictRanges; @@ -132,15 +133,18 @@ struct ReportConflictingKeysWorkload : TestWorkload { // If READ_YOUR_WRITES_DISABLE set, it behaves like native transaction object // where overlapped conflict ranges are not merged. if (deterministicRandom()->random01() < 0.5) - tr.setOption(FDBTransactionOptions::READ_YOUR_WRITES_DISABLE); + tr1.setOption(FDBTransactionOptions::READ_YOUR_WRITES_DISABLE); if (deterministicRandom()->random01() < 0.5) tr2.setOption(FDBTransactionOptions::READ_YOUR_WRITES_DISABLE); - Version readVersion = wait(tr.getReadVersion()); + // We have the two tx with same grv, then commit the first + // If the second one is not able to commit due to conflicts, verify the returned conflicting keys + // Otherwise, there is no conflicts between tr1's writeConflictRange and tr2's readConflictRange + Version readVersion = wait(tr1.getReadVersion()); tr2.setVersion(readVersion); - self->addRandomReadConflictRange(&tr, nullptr); - self->addRandomWriteConflictRange(&tr, &writeConflictRanges); + self->addRandomReadConflictRange(&tr1, nullptr); + self->addRandomWriteConflictRange(&tr1, &writeConflictRanges); ++self->commits; - wait(tr.commit()); + wait(tr1.commit()); ++self->xacts; state bool foundConflict = false; @@ -201,7 +205,7 @@ struct ReportConflictingKeysWorkload : TestWorkload { } } } else { - // make sure no conflicts between readConflictRange and writeConflictRange + // make sure no conflicts between tr2's readConflictRange and tr1's writeConflictRange for (const KeyRange& rCR : readConflictRanges) { if (std::any_of(writeConflictRanges.begin(), writeConflictRanges.end(), [&rCR](KeyRange wCR) { bool result = wCR.intersects(rCR); @@ -219,12 +223,12 @@ struct ReportConflictingKeysWorkload : TestWorkload { } } catch (Error& e) { state Error e2 = e; - wait(tr.onError(e2)); + wait(tr1.onError(e2)); wait(tr2.onError(e2)); } readConflictRanges.clear(); writeConflictRanges.clear(); - tr.reset(); + tr1.reset(); tr2.reset(); } } From c246f79d726ebb720309a8bcee3c5400233ca2bd Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Fri, 13 Mar 2020 13:18:18 -0700 Subject: [PATCH 0898/1604] Update comments --- fdbserver/workloads/ReportConflictingKeys.actor.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fdbserver/workloads/ReportConflictingKeys.actor.cpp b/fdbserver/workloads/ReportConflictingKeys.actor.cpp index b7ed1db500..121b97c403 100644 --- a/fdbserver/workloads/ReportConflictingKeys.actor.cpp +++ b/fdbserver/workloads/ReportConflictingKeys.actor.cpp @@ -26,7 +26,8 @@ #include "fdbserver/workloads/BulkSetup.actor.h" #include "flow/actorcompiler.h" // This must be the last #include. -//For this test to report properly buggify must be disabled (flow.h) , and failConnection must be disabled in (sim2.actor.cpp) +// For this test to report properly buggify must be disabled (flow.h) , and failConnection must be disabled in +// (sim2.actor.cpp) struct ReportConflictingKeysWorkload : TestWorkload { double testDuration, transactionsPerSecond, addReadConflictRangeProb, addWriteConflictRangeProb; @@ -184,15 +185,14 @@ struct ReportConflictingKeysWorkload : TestWorkload { if (!std::any_of(readConflictRanges.begin(), readConflictRanges.end(), [&kr](KeyRange rCR) { // Read_conflict_range remains same in the resolver. // Thus, the returned keyrange is either the original read_conflict_range or merged - // by several overlapped ones In either case, it contains at least one original + // by several overlapped ones in either cases, it contains at least one original // read_conflict_range return kr.contains(rCR); })) { ++self->invalidReports; TraceEvent(SevError, "TestFailure") - .detail( - "Reason", - "Returned conflicting keys are not original readConflictRanges or union of them"); + .detail("Reason", + "Returned conflicting keys are not original or merged readConflictRanges"); } else if (!std::any_of(writeConflictRanges.begin(), writeConflictRanges.end(), [&kr](KeyRange wCR) { // Returned key range should be conflicting with at least one @@ -201,7 +201,7 @@ struct ReportConflictingKeysWorkload : TestWorkload { })) { ++self->invalidReports; TraceEvent(SevError, "TestFailure") - .detail("Reason", "Returned keyranges are not conflicting with any write ranges"); + .detail("Reason", "Returned keyrange is not conflicting with any writeConflictRange"); } } } else { From 9e99a00c8f28c9b87b0a82a6a8f7e697df9cecc7 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Fri, 13 Mar 2020 13:56:46 -0700 Subject: [PATCH 0899/1604] fix: do not use priority 0 left when calculating priorities for empty teams --- fdbserver/DataDistribution.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/DataDistribution.actor.cpp b/fdbserver/DataDistribution.actor.cpp index 035dfab078..d8b6841f86 100644 --- a/fdbserver/DataDistribution.actor.cpp +++ b/fdbserver/DataDistribution.actor.cpp @@ -2917,7 +2917,7 @@ ACTOR Future teamTracker(DDTeamCollection* self, Reference tea // t is the team in primary DC or the remote DC auto& t = j < teams.first.size() ? teams.first[j] : teams.second[j-teams.first.size()]; if( !t.servers.size() ) { - maxPriority = SERVER_KNOBS->PRIORITY_TEAM_0_LEFT; + maxPriority = std::max( maxPriority, SERVER_KNOBS->PRIORITY_POPULATE_REGION ); break; } From 5be7fa52bc1e3093f0df8f3256b2dc952ccc4738 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Fri, 13 Mar 2020 14:51:56 -0700 Subject: [PATCH 0900/1604] Remove comma, and add schema change to documentation --- documentation/sphinx/source/mr-status-json-schemas.rst.inc | 3 +++ fdbclient/Schemas.cpp | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/documentation/sphinx/source/mr-status-json-schemas.rst.inc b/documentation/sphinx/source/mr-status-json-schemas.rst.inc index c8d81f5c95..5aa943aad9 100644 --- a/documentation/sphinx/source/mr-status-json-schemas.rst.inc +++ b/documentation/sphinx/source/mr-status-json-schemas.rst.inc @@ -188,6 +188,9 @@ }, "megabits_received":{ "hz":0.0 + }, + "tls_policy_failures":{ + "hz":0.0 } }, "run_loop_busy":0.2 // fraction of time the run loop was busy diff --git a/fdbclient/Schemas.cpp b/fdbclient/Schemas.cpp index 44a2de1c2f..fa9d4f069f 100644 --- a/fdbclient/Schemas.cpp +++ b/fdbclient/Schemas.cpp @@ -209,9 +209,9 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema( "megabits_received":{ "hz":0.0 }, - "tls_policy_failures":{ - "hz":0.0 - }, + "tls_policy_failures":{ + "hz":0.0 + } }, "run_loop_busy":0.2 } From 12f2b327701521a456bd028c628c76673b5ae69c Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Fri, 13 Mar 2020 15:19:33 -0700 Subject: [PATCH 0901/1604] added additional logging in data distribution --- fdbserver/DataDistribution.actor.cpp | 8 ++++++-- fdbserver/DataDistributionQueue.actor.cpp | 2 +- fdbserver/DataDistributionTracker.actor.cpp | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/fdbserver/DataDistribution.actor.cpp b/fdbserver/DataDistribution.actor.cpp index d8b6841f86..9dfca6abe7 100644 --- a/fdbserver/DataDistribution.actor.cpp +++ b/fdbserver/DataDistribution.actor.cpp @@ -2746,7 +2746,7 @@ ACTOR Future serverTeamRemover(DDTeamCollection* self) { ACTOR Future teamTracker(DDTeamCollection* self, Reference team, bool badTeam, bool redundantTeam) { state int lastServersLeft = team->size(); state bool lastAnyUndesired = false; - state bool logTeamEvents = g_network->isSimulated() || !badTeam; + state bool logTeamEvents = g_network->isSimulated() || !badTeam || team->size() <= self->configuration.storageTeamSize; state bool lastReady = false; state bool lastHealthy; state bool lastOptimal; @@ -2789,6 +2789,10 @@ ACTOR Future teamTracker(DDTeamCollection* self, Reference tea } } + if(serversLeft == 0) { + logTeamEvents = true; + } + // Failed server should not trigger DD if SS failures are set to be ignored if (!badTeam && self->healthyZone.get().present() && (self->healthyZone.get().get() == ignoreSSFailuresZoneString)) { ASSERT_WE_THINK(serversLeft == self->configuration.storageTeamSize); @@ -3946,7 +3950,7 @@ ACTOR Future dataDistributionTeamCollection( .detail("StorageTeamSize", self->configuration.storageTeamSize) .detail("HighestPriority", highestPriority) .trackLatest(self->primary ? "TotalDataInFlight" : "TotalDataInFlightRemote"); - loggingTrigger = delay( SERVER_KNOBS->DATA_DISTRIBUTION_LOGGING_INTERVAL ); + loggingTrigger = delay( SERVER_KNOBS->DATA_DISTRIBUTION_LOGGING_INTERVAL, TaskPriority::FlushTrace ); } when( wait( self->serverTrackerErrorOut.getFuture() ) ) {} // Propagate errors from storageServerTracker when( wait( error ) ) {} diff --git a/fdbserver/DataDistributionQueue.actor.cpp b/fdbserver/DataDistributionQueue.actor.cpp index e3c09f2348..b167b1ddbb 100644 --- a/fdbserver/DataDistributionQueue.actor.cpp +++ b/fdbserver/DataDistributionQueue.actor.cpp @@ -1491,7 +1491,7 @@ ACTOR Future dataDistributionQueue( Promise req; getAverageShardBytes.send( req ); - recordMetrics = delay(SERVER_KNOBS->DD_QUEUE_LOGGING_INTERVAL); + recordMetrics = delay(SERVER_KNOBS->DD_QUEUE_LOGGING_INTERVAL, TaskPriority::FlushTrace); int highestPriorityRelocation = 0; for( auto it = self.priority_relocations.begin(); it != self.priority_relocations.end(); ++it ) { diff --git a/fdbserver/DataDistributionTracker.actor.cpp b/fdbserver/DataDistributionTracker.actor.cpp index 220c4c3a25..4e49c672ad 100644 --- a/fdbserver/DataDistributionTracker.actor.cpp +++ b/fdbserver/DataDistributionTracker.actor.cpp @@ -764,7 +764,7 @@ ACTOR Future dataDistributionTracker( .detail("SystemSizeBytes", self.systemSizeEstimate) .trackLatest( "DDTrackerStats" ); - loggingTrigger = delay(SERVER_KNOBS->DATA_DISTRIBUTION_LOGGING_INTERVAL); + loggingTrigger = delay(SERVER_KNOBS->DATA_DISTRIBUTION_LOGGING_INTERVAL, TaskPriority::FlushTrace); } when( GetMetricsRequest req = waitNext( getShardMetrics.getFuture() ) ) { self.sizeChanges.add( fetchShardMetrics( &self, req ) ); From 700b13e5f8f9334c3ed2351e1635f5779cb09ce1 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Fri, 13 Mar 2020 15:21:33 -0700 Subject: [PATCH 0902/1604] Remember the best team from team requests, which will likely be the best again and can save us some computation. --- fdbserver/DataDistribution.actor.cpp | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/fdbserver/DataDistribution.actor.cpp b/fdbserver/DataDistribution.actor.cpp index 76c9d62165..221fc6ba96 100644 --- a/fdbserver/DataDistribution.actor.cpp +++ b/fdbserver/DataDistribution.actor.cpp @@ -627,6 +627,9 @@ struct DDTeamCollection : ReferenceCounted { double medianAvailableSpace; double lastMedianAvailableSpaceUpdate; + int lowestUtilizationTeam; + int highestUtilizationTeam; + void resetLocalitySet() { storageServerSet = Reference(new LocalityMap()); LocalityMap* storageServerMap = (LocalityMap*) storageServerSet.getPtr(); @@ -671,7 +674,7 @@ struct DDTeamCollection : ReferenceCounted { optimalTeamCount(0), recruitingStream(0), restartRecruiting(SERVER_KNOBS->DEBOUNCE_RECRUITING_DELAY), unhealthyServers(0), includedDCs(includedDCs), otherTrackedDCs(otherTrackedDCs), zeroHealthyTeams(zeroHealthyTeams), zeroOptimalTeams(true), primary(primary), medianAvailableSpace(SERVER_KNOBS->MIN_AVAILABLE_SPACE_RATIO), - lastMedianAvailableSpaceUpdate(0), processingUnhealthy(processingUnhealthy) { + lastMedianAvailableSpaceUpdate(0), processingUnhealthy(processingUnhealthy), lowestUtilizationTeam(0), highestUtilizationTeam(0) { if(!primary || configuration.usableRegions == 1) { TraceEvent("DDTrackerStarting", distributorId) .detail( "State", "Inactive" ) @@ -809,19 +812,29 @@ struct DDTeamCollection : ReferenceCounted { if( req.wantsTrueBest ) { ASSERT( !bestOption.present() ); + auto &startIndex = req.preferLowerUtilization ? self->lowestUtilizationTeam : self->highestUtilizationTeam; + if(startIndex >= self->teams.size()) { + startIndex = 0; + } + + int bestIndex = startIndex; for( int i = 0; i < self->teams.size(); i++ ) { - if (self->teams[i]->isHealthy() && - (!req.preferLowerUtilization || self->teams[i]->hasHealthyAvailableSpace(self->medianAvailableSpace))) + int currentIndex = (startIndex + i) % self->teams.size(); + if (self->teams[currentIndex]->isHealthy() && + (!req.preferLowerUtilization || self->teams[currentIndex]->hasHealthyAvailableSpace(self->medianAvailableSpace))) { - int64_t loadBytes = self->teams[i]->getLoadBytes(true, req.inflightPenalty); + int64_t loadBytes = self->teams[currentIndex]->getLoadBytes(true, req.inflightPenalty); if((!bestOption.present() || (req.preferLowerUtilization && loadBytes < bestLoadBytes) || (!req.preferLowerUtilization && loadBytes > bestLoadBytes)) && - (!req.teamMustHaveShards || self->shardsAffectedByTeamFailure->hasShards(ShardsAffectedByTeamFailure::Team(self->teams[i]->getServerIDs(), self->primary)))) + (!req.teamMustHaveShards || self->shardsAffectedByTeamFailure->hasShards(ShardsAffectedByTeamFailure::Team(self->teams[currentIndex]->getServerIDs(), self->primary)))) { bestLoadBytes = loadBytes; - bestOption = self->teams[i]; + bestOption = self->teams[currentIndex]; + bestIndex = currentIndex; } } } + + startIndex = bestIndex; } else { int nTries = 0; From 39a37531db7bec7311d9ea181fbfe1ac2740464b Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Fri, 13 Mar 2020 15:42:15 -0700 Subject: [PATCH 0903/1604] Fix issues according to Andrew's comments --- fdbclient/KeyRangeMap.actor.cpp | 76 +++---------------- fdbclient/NativeAPI.actor.cpp | 34 +++++---- .../workloads/ReportConflictingKeys.actor.cpp | 12 +-- 3 files changed, 35 insertions(+), 87 deletions(-) diff --git a/fdbclient/KeyRangeMap.actor.cpp b/fdbclient/KeyRangeMap.actor.cpp index 5dc785b2cf..98ef568cd7 100644 --- a/fdbclient/KeyRangeMap.actor.cpp +++ b/fdbclient/KeyRangeMap.actor.cpp @@ -150,7 +150,8 @@ ACTOR Future krmSetRange( Reference tr, Key map //Sets a range of keys in a key range map, coalescing with adjacent regions if the values match //Ranges outside of maxRange will not be coalesced //CAUTION: use care when attempting to coalesce multiple ranges in the same prefix in a single transaction -ACTOR Future krmSetRangeCoalescing( Transaction *tr, Key mapPrefix, KeyRange range, KeyRange maxRange, Value value ) { +ACTOR template +static Future krmSetRangeCoalescing_( Transaction *tr, Key mapPrefix, KeyRange range, KeyRange maxRange, Value value ) { ASSERT(maxRange.contains(range)); state KeyRange withPrefix = KeyRangeRef( mapPrefix.toString() + range.begin.toString(), mapPrefix.toString() + range.end.toString() ); @@ -216,70 +217,11 @@ ACTOR Future krmSetRangeCoalescing( Transaction *tr, Key mapPrefix, KeyRan return Void(); } - -ACTOR Future krmSetRangeCoalescing( Reference tr, Key mapPrefix, KeyRange range, KeyRange maxRange, Value value ) { - ASSERT(maxRange.contains(range)); - - state KeyRange withPrefix = KeyRangeRef( mapPrefix.toString() + range.begin.toString(), mapPrefix.toString() + range.end.toString() ); - state KeyRange maxWithPrefix = KeyRangeRef( mapPrefix.toString() + maxRange.begin.toString(), mapPrefix.toString() + maxRange.end.toString() ); - - state vector>> keys; - keys.push_back(tr->getRange(lastLessThan(withPrefix.begin), firstGreaterOrEqual(withPrefix.begin), 1, true)); - keys.push_back(tr->getRange(lastLessOrEqual(withPrefix.end), firstGreaterThan(withPrefix.end) + 1, 2, true)); - wait(waitForAll(keys)); - - //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(""); - - state Key beginKey = withPrefix.begin; - if(beginValue == value) { - bool outsideRange = !hasBegin || beginRange[0].key < maxWithPrefix.begin; - beginKey = outsideRange ? maxWithPrefix.begin : beginRange[0].key; - } - - //Determine how far to extend this range at the end - auto endRange = keys[1].get(); - 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(""); - bool valueMatches = value == existingValue; - - KeyRange conflictRange = KeyRangeRef( hasBegin ? beginRange[0].key : mapPrefix, withPrefix.begin ); - if( !conflictRange.empty() ) - tr->addReadConflictRange( conflictRange ); - - conflictRange = KeyRangeRef( hasEnd ? endRange[0].key : mapPrefix, hasNext ? keyAfter(endRange.end()[-1].key) : strinc( mapPrefix ) ); - if( !conflictRange.empty() ) - tr->addReadConflictRange( conflictRange ); - - state Key endKey; - state Value endValue; - - //Case 1: Coalesce completely with the following range - if(hasNext && endRange.end()[-1].key <= maxWithPrefix.end && valueMatches) { - endKey = endRange.end()[-1].key; - endValue = endRange.end()[-1].value; - } - - //Case 2: Coalesce with the following range only up to the end of maxRange - else if(valueMatches) { - endKey = maxWithPrefix.end; - endValue = existingValue; - } - - //Case 3: Don't coalesce - else { - endKey = withPrefix.end; - endValue = existingValue; - } - - tr->clear(KeyRangeRef(beginKey, endKey)); - - ASSERT(value != endValue || endKey == maxWithPrefix.end); - tr->set(beginKey, value); - tr->set(endKey, endValue); - - return Void(); +Future krmSetRangeCoalescing(Transaction* const& tr, Key const& mapPrefix, KeyRange const& range, + KeyRange const& maxRange, Value const& value) { + return krmSetRangeCoalescing_(tr, mapPrefix, range, maxRange, value); +} +Future krmSetRangeCoalescing(Reference const& tr, Key const& mapPrefix, + KeyRange const& range, KeyRange const& maxRange, Value const& value) { + return holdWhile(tr, krmSetRangeCoalescing_(tr.getPtr(), mapPrefix, range, maxRange, value)); } diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index 2ac668ffcc..9a9026be8c 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -21,6 +21,7 @@ #include "fdbclient/NativeAPI.actor.h" #include +#include #include "fdbclient/Atomic.h" #include "fdbclient/ClusterInterface.h" @@ -2755,20 +2756,25 @@ ACTOR static Future tryCommit( Database cx, Reference tr->info.conflictingKeysRYW = std::make_shared(tr->getDatabase()); state Reference hackTr = Reference(tr->info.conflictingKeysRYW.get()); - state Standalone> conflictingKRIndices = ci.conflictingKRIndices.get(); - // To make the getRange call local, we need to explicitly set the read version here. - // This version number 100 set here does nothing but prevent getting read version from the proxy - tr->info.conflictingKeysRYW->setVersion(100); - // Clear the whole key space, thus, RYWTr knows to only read keys locally - tr->info.conflictingKeysRYW->clear(normalKeys); - // initialize value - tr->info.conflictingKeysRYW->set(conflictingKeysPrefix, conflictingKeysFalse); - // drop duplicate indices and merge overlapped ranges - // Note: addReadConflictRange in native transaction object does not merge overlapped ranges - state std::set mergedIds(conflictingKRIndices.begin(), conflictingKRIndices.end()); - for (auto const & rCRIndex : mergedIds) { - const KeyRange kr = req.transaction.read_conflict_ranges[rCRIndex]; - wait(krmSetRangeCoalescing(hackTr, conflictingKeysPrefix, kr, allKeys, conflictingKeysTrue)); + try { + state Standalone> conflictingKRIndices = ci.conflictingKRIndices.get(); + // To make the getRange call local, we need to explicitly set the read version here. + // This version number 100 set here does nothing but prevent getting read version from the proxy + tr->info.conflictingKeysRYW->setVersion(100); + // Clear the whole key space, thus, RYWTr knows to only read keys locally + tr->info.conflictingKeysRYW->clear(normalKeys); + // initialize value + tr->info.conflictingKeysRYW->set(conflictingKeysPrefix, conflictingKeysFalse); + // drop duplicate indices and merge overlapped ranges + // Note: addReadConflictRange in native transaction object does not merge overlapped ranges + state std::unordered_set mergedIds(conflictingKRIndices.begin(), conflictingKRIndices.end()); + for (auto const & rCRIndex : mergedIds) { + const KeyRange kr = req.transaction.read_conflict_ranges[rCRIndex]; + wait(krmSetRangeCoalescing(hackTr, conflictingKeysPrefix, kr, allKeys, conflictingKeysTrue)); + } + } catch (Error& e) { + hackTr.extractPtr(); // Make sure the RYW is not freed twice in case exception thrown + throw; } hackTr.extractPtr(); // Avoid the Reference to destroy the RYW object } diff --git a/fdbserver/workloads/ReportConflictingKeys.actor.cpp b/fdbserver/workloads/ReportConflictingKeys.actor.cpp index 121b97c403..4955017342 100644 --- a/fdbserver/workloads/ReportConflictingKeys.actor.cpp +++ b/fdbserver/workloads/ReportConflictingKeys.actor.cpp @@ -35,10 +35,10 @@ struct ReportConflictingKeysWorkload : TestWorkload { int nodeCount, actorCount, keyBytes, valueBytes, readConflictRangeCount, writeConflictRangeCount; - PerfIntCounter invalidReports, commits, conflicts, retries, xacts; + PerfIntCounter invalidReports, commits, conflicts, xacts; ReportConflictingKeysWorkload(WorkloadContext const& wcx) - : TestWorkload(wcx), invalidReports("InvalidReports"), conflicts("Conflicts"), retries("Retries"), + : TestWorkload(wcx), invalidReports("InvalidReports"), conflicts("Conflicts"), commits("Commits"), xacts("Transactions") { testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); // transactionsPerSecond = getOption(options, LiteralStringRef("transactionsPerSecond"), 5000.0) / clientCount; @@ -80,8 +80,6 @@ struct ReportConflictingKeysWorkload : TestWorkload { m.push_back(PerfMetric("Commits/sec", commits.getValue() / testDuration, true)); m.push_back(conflicts.getMetric()); m.push_back(PerfMetric("Conflicts/sec", conflicts.getValue() / testDuration, true)); - m.push_back(retries.getMetric()); - m.push_back(PerfMetric("Retries/sec", retries.getValue() / testDuration, true)); } // disable the default timeout setting @@ -169,10 +167,12 @@ struct ReportConflictingKeysWorkload : TestWorkload { // The getRange here using the special key prefix "\xff\xff/transaction/conflicting_keys/" happens // locally Thus, the error handling is not needed here Future> conflictingKeyRangesFuture = - tr2.getRange(ckr, readConflictRanges.size() * 2); + tr2.getRange(ckr, CLIENT_KNOBS->TOO_MANY); ASSERT(conflictingKeyRangesFuture.isReady()); const Standalone conflictingKeyRanges = conflictingKeyRangesFuture.get(); - ASSERT(conflictingKeyRanges.size() && (conflictingKeyRanges.size() % 2 == 0)); + ASSERT(conflictingKeyRanges.size() && (conflictingKeyRanges.size() <= readConflictRanges.size() * 2)); + ASSERT(conflictingKeyRanges.size() % 2 == 0); + ASSERT(!conflictingKeyRanges.more); for (int i = 0; i < conflictingKeyRanges.size(); i += 2) { KeyValueRef startKeyWithPrefix = conflictingKeyRanges[i]; ASSERT(startKeyWithPrefix.value == conflictingKeysTrue); From 9dc441c65ab0757b2a3dae41809b2efd9c9305f6 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Fri, 13 Mar 2020 15:43:01 -0700 Subject: [PATCH 0904/1604] clang-format --- fdbserver/workloads/ReportConflictingKeys.actor.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fdbserver/workloads/ReportConflictingKeys.actor.cpp b/fdbserver/workloads/ReportConflictingKeys.actor.cpp index 4955017342..a51a80edab 100644 --- a/fdbserver/workloads/ReportConflictingKeys.actor.cpp +++ b/fdbserver/workloads/ReportConflictingKeys.actor.cpp @@ -38,8 +38,8 @@ struct ReportConflictingKeysWorkload : TestWorkload { PerfIntCounter invalidReports, commits, conflicts, xacts; ReportConflictingKeysWorkload(WorkloadContext const& wcx) - : TestWorkload(wcx), invalidReports("InvalidReports"), conflicts("Conflicts"), - commits("Commits"), xacts("Transactions") { + : TestWorkload(wcx), invalidReports("InvalidReports"), conflicts("Conflicts"), commits("Commits"), + xacts("Transactions") { testDuration = getOption(options, LiteralStringRef("testDuration"), 10.0); // transactionsPerSecond = getOption(options, LiteralStringRef("transactionsPerSecond"), 5000.0) / clientCount; actorCount = getOption(options, LiteralStringRef("actorsPerClient"), 1); @@ -170,7 +170,8 @@ struct ReportConflictingKeysWorkload : TestWorkload { tr2.getRange(ckr, CLIENT_KNOBS->TOO_MANY); ASSERT(conflictingKeyRangesFuture.isReady()); const Standalone conflictingKeyRanges = conflictingKeyRangesFuture.get(); - ASSERT(conflictingKeyRanges.size() && (conflictingKeyRanges.size() <= readConflictRanges.size() * 2)); + ASSERT(conflictingKeyRanges.size() && + (conflictingKeyRanges.size() <= readConflictRanges.size() * 2)); ASSERT(conflictingKeyRanges.size() % 2 == 0); ASSERT(!conflictingKeyRanges.more); for (int i = 0; i < conflictingKeyRanges.size(); i += 2) { From a5568b2fc6d8c0d7493c816192a3f6c30b6eeaae Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Fri, 13 Mar 2020 15:46:03 -0700 Subject: [PATCH 0905/1604] Rewrite tlsinfo into --debug-tls, and print out configuration. --- fdbcli/fdbcli.actor.cpp | 140 ++++++++++++++++++++------------------- flow/TLSConfig.actor.cpp | 2 +- 2 files changed, 72 insertions(+), 70 deletions(-) diff --git a/fdbcli/fdbcli.actor.cpp b/fdbcli/fdbcli.actor.cpp index f4f7a094eb..adb76125b0 100644 --- a/fdbcli/fdbcli.actor.cpp +++ b/fdbcli/fdbcli.actor.cpp @@ -71,7 +71,8 @@ enum { OPT_STATUS_FROM_JSON, OPT_VERSION, OPT_TRACE_FORMAT, - OPT_KNOB + OPT_KNOB, + OPT_DEBUG_TLS }; CSimpleOpt::SOption g_rgOptions[] = { { OPT_CONNFILE, "-C", SO_REQ_SEP }, @@ -90,6 +91,7 @@ CSimpleOpt::SOption g_rgOptions[] = { { OPT_CONNFILE, "-C", SO_REQ_SEP }, { OPT_VERSION, "-v", SO_NONE }, { OPT_TRACE_FORMAT, "--trace_format", SO_REQ_SEP }, { OPT_KNOB, "--knob_", SO_REQ_SEP }, + { OPT_DEBUG_TLS, "--debug-tls", SO_NONE }, #ifndef TLS_DISABLED TLS_OPTION_FLAGS @@ -404,23 +406,6 @@ static std::vector> parseLine(std::string& line, bool& er return ret; } -// This function has to be outside of cli(), because the actor compiler doesn't -// understand preprocessor macros. -bool loadAndPrintTLSCertificates() { -#ifndef TLS_DISABLED - try { - LoadedTLSConfig loaded = g_network->getTLSConfig().loadSync(); - loaded.print(stdout); - } catch (Error& e) { - printf("Please use --log and check the log file for more details on the error."); - } - return false; -#else - printf("This fdbcli was built with TLS disabled.\n"); - return true; -#endif -} - static void printProgramUsage(const char* name) { printf("FoundationDB CLI " FDB_VT_PACKAGE_NAME " (v" FDB_VT_VERSION ")\n" "usage: %s [OPTIONS]\n" @@ -445,6 +430,8 @@ static void printProgramUsage(const char* name) { #endif " --knob_KNOBNAME KNOBVALUE\n" " Changes a knob option. KNOBNAME should be lowercase.\n" + " --debug-tls Prints the TLS configuration and certificate chain.\n" + " Useful in reporting and diagnosing TLS issues.\n" " -v, --version Print FoundationDB CLI version information and exit.\n" " -h, --help Display this help and exit.\n"); } @@ -574,10 +561,6 @@ void initHelp() { "consistencycheck [on|off]", "permits or prevents consistency checking", "Calling this command with `on' permits consistency check processes to run and `off' will halt their checking. Calling this command with no arguments will display if consistency checking is currently allowed.\n"); - helpMap["tlsinfo"] = CommandHelp( - "tlsinfo", - "prints a textual representation of the configured TLS Certificates", - "This prints the TLS certificate and the CA certificate, which is likely to be helpful in debugging verify_peers failures."); hiddenCommands.insert("expensive_data_check"); hiddenCommands.insert("datadistribution"); @@ -2453,17 +2436,18 @@ void LogCommand(std::string line, UID randomID, std::string errMsg) { struct CLIOptions { std::string program_name; - int exit_code; + int exit_code = -1; std::string commandLine; std::string clusterFile; - bool trace; + bool trace = false; std::string traceDir; std::string traceFormat; - int exit_timeout; + int exit_timeout = 0; Optional exec; - bool initialStatusCheck; + bool initialStatusCheck = true; + bool debugTLS = false; std::string tlsCertPath; std::string tlsKeyPath; std::string tlsVerifyPeers; @@ -2473,10 +2457,6 @@ struct CLIOptions { std::vector> knobs; CLIOptions( int argc, char* argv[] ) - : trace(false), - exit_timeout(0), - initialStatusCheck(true), - exit_code(-1) { program_name = argv[0]; for (int a = 0; a cli(CLIOptions opt, LineNoise* plinenoise) { continue; } - if (tokencmp(tokens[0], "tlsinfo")) { - is_error = loadAndPrintTLSCertificates(); - continue; - } - if (tokencmp(tokens[0], "profile")) { if (tokens.size() == 1) { printf("ERROR: Usage: profile \n"); @@ -3813,6 +3791,30 @@ int main(int argc, char **argv) { return 1; } + if (opt.debugTLS) { +#ifndef TLS_DISABLED + // Backdoor into NativeAPI's tlsConfig, which is where the above network option settings ended up. + extern TLSConfig tlsConfig; + printf("TLS Configuration:\n"); + printf("\tCertificate Path: %s\n", tlsConfig.getCertificatePathSync().c_str()); + printf("\tKey Path: %s\n", tlsConfig.getKeyPathSync().c_str()); + printf("\tCA Path: %s\n", tlsConfig.getCAPathSync().c_str()); + try { + LoadedTLSConfig loaded = tlsConfig.loadSync(); + printf("\tPassword: %s\n", loaded.getPassword().empty() ? "Not configured" : "Exists, but redacted"); + printf("\n"); + loaded.print(stdout); + } catch (Error& e) { + printf("ERROR: %s (%d)\n", e.what(), e.code()); + printf("Use --log and look at the trace logs for more detailed information on the failure.\n"); + return 1; + } +#else + printf("This fdbcli was built with TLS disabled.\n"); +#endif + return 0; + } + try { setupNetwork(); Future cliFuture = runCli(opt); diff --git a/flow/TLSConfig.actor.cpp b/flow/TLSConfig.actor.cpp index 90b75d8bc1..6706dae4ec 100644 --- a/flow/TLSConfig.actor.cpp +++ b/flow/TLSConfig.actor.cpp @@ -96,7 +96,7 @@ void LoadedTLSConfig::print(FILE* fp) { ConfigureSSLContext(*this, &context); } catch (Error& e) { fprintf(fp, "There was an error in loading the certificate chain.\n"); - return; + throw; } X509_STORE* store = SSL_CTX_get_cert_store(context.native_handle()); From 6ee992eb35e49d8689fb7c98b8b10880cb3c0e8a Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Fri, 13 Mar 2020 15:47:16 -0700 Subject: [PATCH 0906/1604] Add that debug tls exits after running. --- fdbcli/fdbcli.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbcli/fdbcli.actor.cpp b/fdbcli/fdbcli.actor.cpp index adb76125b0..fe47438d8b 100644 --- a/fdbcli/fdbcli.actor.cpp +++ b/fdbcli/fdbcli.actor.cpp @@ -430,7 +430,7 @@ static void printProgramUsage(const char* name) { #endif " --knob_KNOBNAME KNOBVALUE\n" " Changes a knob option. KNOBNAME should be lowercase.\n" - " --debug-tls Prints the TLS configuration and certificate chain.\n" + " --debug-tls Prints the TLS configuration and certificate chain, then exits.\n" " Useful in reporting and diagnosing TLS issues.\n" " -v, --version Print FoundationDB CLI version information and exit.\n" " -h, --help Display this help and exit.\n"); From 2f2f56020fa66b48f8a163a595f7558c30e70803 Mon Sep 17 00:00:00 2001 From: Evan Tschannen <36455792+etschannen@users.noreply.github.com> Date: Fri, 13 Mar 2020 15:54:13 -0700 Subject: [PATCH 0907/1604] Update fdbserver/masterserver.actor.cpp Co-Authored-By: A.J. Beamon --- fdbserver/masterserver.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/masterserver.actor.cpp b/fdbserver/masterserver.actor.cpp index f469d70b55..c4c9f66498 100644 --- a/fdbserver/masterserver.actor.cpp +++ b/fdbserver/masterserver.actor.cpp @@ -1253,7 +1253,7 @@ ACTOR Future masterCore( Reference self ) { if (self->cstate.myDBState.oldTLogData.size() > CLIENT_KNOBS->MAX_GENERATIONS_OVERRIDE) { if (self->cstate.myDBState.oldTLogData.size() >= CLIENT_KNOBS->MAX_GENERATIONS) { TraceEvent(SevError, "RecoveryStoppedTooManyOldGenerations").detail("OldGenerations", self->cstate.myDBState.oldTLogData.size()) - .detail("Reason", "Recovery stopped because too many recoveries have happened since the last time the cluster was fully_recovered. Set --knob_max_generations_override to a value larger than OldGenerations on your server processes to resume recovery once the underlying problem has been fixed."); + .detail("Reason", "Recovery stopped because too many recoveries have happened since the last time the cluster was fully_recovered. Set --knob_max_generations_override on your server processes to a value larger than OldGenerations to resume recovery once the underlying problem has been fixed."); wait(Future(Never())); } else if (self->cstate.myDBState.oldTLogData.size() > CLIENT_KNOBS->RECOVERY_DELAY_START_GENERATION) { TraceEvent(SevError, "RecoveryDelayedTooManyOldGenerations").detail("OldGenerations", self->cstate.myDBState.oldTLogData.size()) From 031b579ede478882f1e7ffd4e192d37b2df434a1 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Fri, 13 Mar 2020 16:20:23 -0700 Subject: [PATCH 0908/1604] Increase priority of the logging of various metrics trace events. --- fdbclient/NativeAPI.actor.cpp | 2 +- flow/Stats.actor.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index 7dbf08cdc7..171cf09a93 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -224,7 +224,7 @@ template <> void delref( DatabaseContext* ptr ) { ptr->delref(); } ACTOR Future databaseLogger( DatabaseContext *cx ) { state double lastLogged = 0; loop { - wait(delay(CLIENT_KNOBS->SYSTEM_MONITOR_INTERVAL, cx->taskID)); + wait(delay(CLIENT_KNOBS->SYSTEM_MONITOR_INTERVAL, TaskPriority::FlushTrace)); TraceEvent ev("TransactionMetrics", cx->dbId); ev.detail("Elapsed", (lastLogged == 0) ? 0 : now() - lastLogged) diff --git a/flow/Stats.actor.cpp b/flow/Stats.actor.cpp index d621188277..5f966cb47b 100644 --- a/flow/Stats.actor.cpp +++ b/flow/Stats.actor.cpp @@ -95,6 +95,6 @@ ACTOR Future traceCounters(std::string traceEventName, UID traceEventID, d } last_interval = now(); - wait(delay(interval)); + wait(delay(interval, TaskPriority::FlushTrace)); } } From 607f08127efdcce4ab0edab568f3dff61f9bea56 Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Fri, 13 Mar 2020 23:38:16 +0000 Subject: [PATCH 0909/1604] copy all user groups to docker, mount ccache dir this is necessary for configurations where ccache is shared across multiple users --- build/gen_dev_docker.sh | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/build/gen_dev_docker.sh b/build/gen_dev_docker.sh index 4d5184af7d..896164f812 100755 --- a/build/gen_dev_docker.sh +++ b/build/gen_dev_docker.sh @@ -7,6 +7,8 @@ user=$(id -un) group=$(id -gn) uid=$(id -u) gid=$(id -g) +gids=( $(id -G) ) +groups=( $(id -Gn) ) tmpdir="/tmp/fdb-docker-${DIR_UUID}" image=fdb-dev @@ -14,9 +16,30 @@ pushd . mkdir ${tmpdir} cd ${tmpdir} +echo + cat <> Dockerfile FROM foundationdb/foundationdb-build:latest -RUN groupadd -g ${gid} ${group} && useradd -u ${uid} -g ${gid} -m ${user} +EOF + +num_groups=${#gids[@]} +additional_groups="" +for ((i=0;i> Dockerfile + if [ ${gids[i]} -ne ${gid} ] + then + if [ -z ${additional_groups} ] + then + additional_groups="-G ${gids[$i]}" + else + additional_groups="${additional_groups},${gids[$i]}" + fi + fi +done + +cat <> Dockerfile +RUN useradd -u ${uid} -g ${gid} ${additional_groups} -m ${user} USER ${user} CMD scl enable devtoolset-8 rh-python36 rh-ruby24 -- bash @@ -34,14 +57,22 @@ mkdir -p $HOME/bin cat < $HOME/bin/fdb-dev #!/usr/bin/bash +if [ -d "\${CCACHE_DIR}" ] +then + args="-v \${CCACHE_DIR}:\${CCACHE_DIR}" + args="\${args} -e CCACHE_DIR=\${CCACHE_DIR}" + args="\${args} -e CCACHE_UMASK=\${CCACHE_UMASK}" + ccache_args=\$args +fi + + sudo docker run --rm `# delete (temporary) image after return` \\ -it `# Run in interactive mode and simulate a TTY` \\ --privileged=true `# Run in privileged mode ` \\ --cap-add=SYS_PTRACE \\ --security-opt seccomp=unconfined \\ -v "${HOME}:${HOME}" `# Mount home directory` \\ - -e "CCACHE_DIR=$CCACHE_DIR" \\ - -e "CCACHE_UMASK=$CCACHE_UMASK" \\ + \${ccache_args} \\ ${image} EOF From c085abf66f6a1ecc8ec4fc41df71ad104956b5d1 Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Sat, 14 Mar 2020 00:12:03 +0000 Subject: [PATCH 0910/1604] User docker group instead of sudo --- build/gen_dev_docker.sh | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/build/gen_dev_docker.sh b/build/gen_dev_docker.sh index 896164f812..47cab79866 100755 --- a/build/gen_dev_docker.sh +++ b/build/gen_dev_docker.sh @@ -2,8 +2,26 @@ set -e -DIR_UUID=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1) +# we first check whether the user is in the group docker user=$(id -un) +is_in_docker_group=0 +for group in $(id -Gn) +do + if [ $group = "docker" ] + then + is_in_docker_group=1 + fi +done + +if [ $is_in_docker_group -eq 0 ] +then + echo "Adding user to docker group" + sudo usermod -a -G docker ${user} + echo "Please log out and back in again to reload group list" + exit +fi + +DIR_UUID=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1) group=$(id -gn) uid=$(id -u) gid=$(id -g) @@ -48,7 +66,7 @@ EOF echo "Created ${tmpdir}" echo "Buidling Docker container ${image}" -sudo docker build -t ${image} . +docker build -t ${image} . popd @@ -66,7 +84,7 @@ then fi -sudo docker run --rm `# delete (temporary) image after return` \\ +docker run --rm `# delete (temporary) image after return` \\ -it `# Run in interactive mode and simulate a TTY` \\ --privileged=true `# Run in privileged mode ` \\ --cap-add=SYS_PTRACE \\ From f9d97ee1abece8e88a775eb91eb5e23c5a807004 Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Sat, 14 Mar 2020 00:14:15 +0000 Subject: [PATCH 0911/1604] Added more notes to script --- build/gen_dev_docker.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/build/gen_dev_docker.sh b/build/gen_dev_docker.sh index 47cab79866..8294df5260 100755 --- a/build/gen_dev_docker.sh +++ b/build/gen_dev_docker.sh @@ -18,6 +18,7 @@ then echo "Adding user to docker group" sudo usermod -a -G docker ${user} echo "Please log out and back in again to reload group list" + echo "Afterwards you need to call this script again" exit fi From 79d5511149f221b6baf54250dd89276fd8b0736b Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Fri, 13 Mar 2020 17:49:02 -0700 Subject: [PATCH 0912/1604] A "proxy" class process would not be preferred as the "first proxy" for restore and DR purposes --- fdbserver/ClusterController.actor.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fdbserver/ClusterController.actor.cpp b/fdbserver/ClusterController.actor.cpp index 5cbc6ebb8a..fe6ce12961 100644 --- a/fdbserver/ClusterController.actor.cpp +++ b/fdbserver/ClusterController.actor.cpp @@ -696,8 +696,8 @@ public: auto proxies = getWorkersForRoleInDatacenter( dcId, ProcessClass::Proxy, req.configuration.getDesiredProxies()-1, req.configuration, id_used, first_proxy ); auto resolvers = getWorkersForRoleInDatacenter( dcId, ProcessClass::Resolver, req.configuration.getDesiredResolvers()-1, req.configuration, id_used, first_resolver ); - proxies.push_back(first_proxy.worker); - resolvers.push_back(first_resolver.worker); + proxies.insert(proxies.begin(), first_proxy.worker); + resolvers.insert(resolvers.begin(), first_resolver.worker); for(int i = 0; i < resolvers.size(); i++) result.resolvers.push_back(resolvers[i].interf); @@ -829,8 +829,8 @@ public: auto proxies = getWorkersForRoleInDatacenter( dcId, ProcessClass::Proxy, req.configuration.getDesiredProxies()-1, req.configuration, used, first_proxy ); auto resolvers = getWorkersForRoleInDatacenter( dcId, ProcessClass::Resolver, req.configuration.getDesiredResolvers()-1, req.configuration, used, first_resolver ); - proxies.push_back(first_proxy.worker); - resolvers.push_back(first_resolver.worker); + proxies.insert(proxies.begin(), first_proxy.worker); + resolvers.insert(resolvers.begin(), first_resolver.worker); RoleFitnessPair fitness( RoleFitness(proxies, ProcessClass::Proxy), RoleFitness(resolvers, ProcessClass::Resolver) ); @@ -1137,8 +1137,8 @@ public: auto proxies = getWorkersForRoleInDatacenter( clusterControllerDcId, ProcessClass::Proxy, db.config.getDesiredProxies()-1, db.config, id_used, first_proxy, true ); auto resolvers = getWorkersForRoleInDatacenter( clusterControllerDcId, ProcessClass::Resolver, db.config.getDesiredResolvers()-1, db.config, id_used, first_resolver, true ); - proxies.push_back(first_proxy.worker); - resolvers.push_back(first_resolver.worker); + proxies.insert(proxies.begin(), first_proxy.worker); + resolvers.insert(resolvers.begin(), first_resolver.worker); RoleFitnessPair newInFit(RoleFitness(proxies, ProcessClass::Proxy), RoleFitness(resolvers, ProcessClass::Resolver)); if(oldInFit.proxy.betterFitness(newInFit.proxy) || oldInFit.resolver.betterFitness(newInFit.resolver)) { From ebbf4490b3acd94a8e6d57b8a3f78c1902a6d86f Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Fri, 13 Mar 2020 18:07:48 -0700 Subject: [PATCH 0913/1604] use a Deque for each priority instead of a priority queue to improve CPU with large numbers of outstanding requests --- fdbserver/MasterProxyServer.actor.cpp | 56 ++++++++++++++++++--------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/fdbserver/MasterProxyServer.actor.cpp b/fdbserver/MasterProxyServer.actor.cpp index a990d41fd6..5e6675b0bd 100644 --- a/fdbserver/MasterProxyServer.actor.cpp +++ b/fdbserver/MasterProxyServer.actor.cpp @@ -136,13 +136,14 @@ ACTOR Future getRate(UID myID, Reference> db, int64 } ACTOR Future queueTransactionStartRequests( - std::priority_queue< std::pair, std::vector< std::pair > > *transactionQueue, + Deque *systemQueue, + Deque *defaultQueue, + Deque *batchQueue, FutureStream readVersionRequests, PromiseStream GRVTimer, double *lastGRVTime, double *GRVBatchTime, FutureStream replyTimes, ProxyStats* stats) { - state int64_t counter = 0; loop choose{ when(GetReadVersionRequest req = waitNext(readVersionRequests)) { if( stats->txnRequestIn.getValue() - stats->txnRequestOut.getValue() > SERVER_KNOBS->START_TRANSACTION_MAX_QUEUE_SIZE ) { @@ -156,20 +157,22 @@ ACTOR Future queueTransactionStartRequests( if (req.debugID.present()) g_traceBatch.addEvent("TransactionDebug", req.debugID.get().first(), "MasterProxyServer.queueTransactionStartRequests.Before"); - ++stats->txnRequestIn; - stats->txnStartIn += req.transactionCount; - if (req.priority() >= GetReadVersionRequest::PRIORITY_SYSTEM_IMMEDIATE) - stats->txnSystemPriorityStartIn += req.transactionCount; - else if (req.priority() >= GetReadVersionRequest::PRIORITY_DEFAULT) - stats->txnDefaultPriorityStartIn += req.transactionCount; - else - stats->txnBatchPriorityStartIn += req.transactionCount; - - if (transactionQueue->empty()) { + if (systemQueue->empty() && defaultQueue->empty() && batchQueue->empty()) { forwardPromise(GRVTimer, delayJittered(std::max(0.0, *GRVBatchTime - (now() - *lastGRVTime)), TaskPriority::ProxyGRVTimer)); } - transactionQueue->push(std::make_pair(req, counter--)); + ++stats->txnRequestIn; + stats->txnStartIn += req.transactionCount; + if (req.priority() >= GetReadVersionRequest::PRIORITY_SYSTEM_IMMEDIATE) { + stats->txnSystemPriorityStartIn += req.transactionCount; + systemQueue->push_back(req); + } else if (req.priority() >= GetReadVersionRequest::PRIORITY_DEFAULT) { + stats->txnDefaultPriorityStartIn += req.transactionCount; + defaultQueue->push_back(req); + } else { + stats->txnBatchPriorityStartIn += req.transactionCount; + batchQueue->push_back(req); + } } } // dynamic batching monitors reply latencies @@ -1230,12 +1233,14 @@ ACTOR static Future transactionStarter( state TransactionRateInfo normalRateInfo(10); state TransactionRateInfo batchRateInfo(0); - state std::priority_queue, std::vector>> transactionQueue; + state Deque systemQueue, + state Deque defaultQueue, + state Deque batchQueue, state vector otherProxies; state PromiseStream replyTimes; addActor.send(getRate(proxy.id(), db, &transactionCount, &batchTransactionCount, &normalRateInfo.rate, &batchRateInfo.rate, healthMetricsReply, detailedHealthMetricsReply)); - addActor.send(queueTransactionStartRequests(&transactionQueue, proxy.getConsistentReadVersion.getFuture(), GRVTimer, &lastGRVTime, &GRVBatchTime, replyTimes.getFuture(), &commitData->stats)); + addActor.send(queueTransactionStartRequests(&systemQueue, &defaultQueue, &batchQueue, proxy.getConsistentReadVersion.getFuture(), GRVTimer, &lastGRVTime, &GRVBatchTime, replyTimes.getFuture(), &commitData->stats)); // Get a list of the other proxies that go together with us while (std::find(db->get().client.proxies.begin(), db->get().client.proxies.end(), proxy) == db->get().client.proxies.end()) @@ -1270,8 +1275,20 @@ ACTOR static Future transactionStarter( Optional debugID; int requestsToStart = 0; - while (!transactionQueue.empty() && requestsToStart < SERVER_KNOBS->START_TRANSACTION_MAX_REQUESTS_TO_START) { - auto& req = transactionQueue.top().first; + + while (requestsToStart < SERVER_KNOBS->START_TRANSACTION_MAX_REQUESTS_TO_START) { + Deque* transactionQueue; + if(!systemQueue.empty()) { + transactionQueue = &systemQueue; + } else if(!defaultQueue.empty()) { + transactionQueue = &defaultQueue; + } else if(!batchQueue.empty()) { + transactionQueue = &batchQueue; + } else { + break; + } + + auto& req = transactionQueue->front(); int tc = req.transactionCount; if(req.priority() < GetReadVersionRequest::PRIORITY_DEFAULT && !batchRateInfo.canStart(transactionsStarted[0] + transactionsStarted[1])) { @@ -1295,12 +1312,13 @@ ACTOR static Future transactionStarter( batchPriTransactionsStarted[req.flags & 1] += tc; start[req.flags & 1].push_back(std::move(req)); static_assert(GetReadVersionRequest::FLAG_CAUSAL_READ_RISKY == 1, "Implementation dependent on flag value"); - transactionQueue.pop(); + transactionQueue->pop_front(); requestsToStart++; } - if (!transactionQueue.empty()) + if (!systemQueue.empty() || !defaultQueue.empty() || !batchQueue.empty()) { forwardPromise(GRVTimer, delayJittered(SERVER_KNOBS->START_TRANSACTION_BATCH_QUEUE_CHECK_INTERVAL, TaskPriority::ProxyGRVTimer)); + } /*TraceEvent("GRVBatch", proxy.id()) .detail("Elapsed", elapsed) From a71e61f57be02e74dee21745b4cd8ed5d2e7f5d0 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Fri, 13 Mar 2020 18:22:38 -0700 Subject: [PATCH 0914/1604] fixed compiler issue --- fdbserver/MasterProxyServer.actor.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fdbserver/MasterProxyServer.actor.cpp b/fdbserver/MasterProxyServer.actor.cpp index 5e6675b0bd..a3a44b1eaf 100644 --- a/fdbserver/MasterProxyServer.actor.cpp +++ b/fdbserver/MasterProxyServer.actor.cpp @@ -1233,9 +1233,9 @@ ACTOR static Future transactionStarter( state TransactionRateInfo normalRateInfo(10); state TransactionRateInfo batchRateInfo(0); - state Deque systemQueue, - state Deque defaultQueue, - state Deque batchQueue, + state Deque systemQueue; + state Deque defaultQueue; + state Deque batchQueue; state vector otherProxies; state PromiseStream replyTimes; From 04b752b40a4378921c54ad27c8ecb099fe5060af Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Fri, 13 Mar 2020 18:31:22 -0700 Subject: [PATCH 0915/1604] Added additional logging related to memory errors (including in status) --- .../sphinx/source/mr-status-json-schemas.rst.inc | 10 ++++++++++ fdbclient/MasterProxyInterface.h | 1 - fdbclient/Schemas.cpp | 10 ++++++++++ fdbserver/MasterProxyServer.actor.cpp | 12 ++++++++++-- fdbserver/Status.actor.cpp | 8 ++++++++ 5 files changed, 38 insertions(+), 3 deletions(-) diff --git a/documentation/sphinx/source/mr-status-json-schemas.rst.inc b/documentation/sphinx/source/mr-status-json-schemas.rst.inc index c8d81f5c95..1ba005c5ba 100644 --- a/documentation/sphinx/source/mr-status-json-schemas.rst.inc +++ b/documentation/sphinx/source/mr-status-json-schemas.rst.inc @@ -417,6 +417,16 @@ "hz":0.0, "counter":0, "roughness":0.0 + }, + "location_requests":{ // measures number of incoming key server location requests + "hz":0.0, + "counter":0, + "roughness":0.0 + }, + "memory_errors":{ // measures number of proxy_memory_limit_exceeded errors + "hz":0.0, + "counter":0, + "roughness":0.0 } }, "bytes":{ // measures number of logical bytes read/written (ignoring replication factor and overhead on disk). Perfectly spaced operations will have a roughness of 1.0. Randomly spaced (Poisson-distributed) operations will have a roughness of 2.0, with increased bunching resulting in increased values. Higher roughness can result in increased latency due to increased queuing. diff --git a/fdbclient/MasterProxyInterface.h b/fdbclient/MasterProxyInterface.h index 7333f588ed..4547448d06 100644 --- a/fdbclient/MasterProxyInterface.h +++ b/fdbclient/MasterProxyInterface.h @@ -72,7 +72,6 @@ struct MasterProxyInterface { commit.getEndpoint(TaskPriority::ReadSocket); getStorageServerRejoinInfo.getEndpoint(TaskPriority::ProxyStorageRejoin); getKeyServersLocations.getEndpoint(TaskPriority::ReadSocket); //priority lowered to TaskPriority::DefaultEndpoint on the proxy - //getKeyServersLocations.getEndpoint(TaskProxyGetKeyServersLocations); //do not increase the priority of these requests, because clients cans bring down the cluster with too many of these messages. } }; diff --git a/fdbclient/Schemas.cpp b/fdbclient/Schemas.cpp index 51572cf015..63d1a1a6b5 100644 --- a/fdbclient/Schemas.cpp +++ b/fdbclient/Schemas.cpp @@ -443,6 +443,16 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema( "hz":0.0, "counter":0, "roughness":0.0 + }, + "location_requests":{ + "hz":0.0, + "counter":0, + "roughness":0.0 + }, + "memory_errors":{ + "hz":0.0, + "counter":0, + "roughness":0.0 } }, "bytes":{ diff --git a/fdbserver/MasterProxyServer.actor.cpp b/fdbserver/MasterProxyServer.actor.cpp index a3a44b1eaf..012b24b46d 100644 --- a/fdbserver/MasterProxyServer.actor.cpp +++ b/fdbserver/MasterProxyServer.actor.cpp @@ -55,7 +55,7 @@ struct ProxyStats { Counter txnSystemPriorityStartIn, txnSystemPriorityStartOut; Counter txnBatchPriorityStartIn, txnBatchPriorityStartOut; Counter txnDefaultPriorityStartIn, txnDefaultPriorityStartOut; - Counter txnCommitIn, txnCommitVersionAssigned, txnCommitResolving, txnCommitResolved, txnCommitOut, txnCommitOutSuccess; + Counter txnCommitIn, txnCommitVersionAssigned, txnCommitResolving, txnCommitResolved, txnCommitOut, txnCommitOutSuccess, txnCommitErrors; Counter txnConflicts; Counter commitBatchIn, commitBatchOut; Counter mutationBytes; @@ -73,7 +73,7 @@ struct ProxyStats { : cc("ProxyStats", id.toString()), txnRequestIn("TxnRequestIn", cc), txnRequestOut("TxnRequestOut", cc), txnRequestErrors("TxnRequestErrors", cc), txnStartIn("TxnStartIn", cc), txnStartOut("TxnStartOut", cc), txnStartBatch("TxnStartBatch", cc), txnSystemPriorityStartIn("TxnSystemPriorityStartIn", cc), txnSystemPriorityStartOut("TxnSystemPriorityStartOut", cc), txnBatchPriorityStartIn("TxnBatchPriorityStartIn", cc), txnBatchPriorityStartOut("TxnBatchPriorityStartOut", cc), txnDefaultPriorityStartIn("TxnDefaultPriorityStartIn", cc), txnDefaultPriorityStartOut("TxnDefaultPriorityStartOut", cc), txnCommitIn("TxnCommitIn", cc), txnCommitVersionAssigned("TxnCommitVersionAssigned", cc), txnCommitResolving("TxnCommitResolving", cc), txnCommitResolved("TxnCommitResolved", cc), txnCommitOut("TxnCommitOut", cc), - txnCommitOutSuccess("TxnCommitOutSuccess", cc), txnConflicts("TxnConflicts", cc), commitBatchIn("CommitBatchIn", cc), commitBatchOut("CommitBatchOut", cc), mutationBytes("MutationBytes", cc), mutations("Mutations", cc), conflictRanges("ConflictRanges", cc), keyServerLocationIn("KeyServerLocationIn", cc), keyServerLocationOut("KeyServerLocationOut", cc), keyServerLocationErrors("KeyServerLocationErrors", cc), + txnCommitOutSuccess("TxnCommitOutSuccess", cc), txnCommitErrors("TxnCommitErrors", cc), txnConflicts("TxnConflicts", cc), commitBatchIn("CommitBatchIn", cc), commitBatchOut("CommitBatchOut", cc), mutationBytes("MutationBytes", cc), mutations("Mutations", cc), conflictRanges("ConflictRanges", cc), keyServerLocationIn("KeyServerLocationIn", cc), keyServerLocationOut("KeyServerLocationOut", cc), keyServerLocationErrors("KeyServerLocationErrors", cc), lastCommitVersionAssigned(0), commitLatencyBands("CommitLatencyMetrics", id, SERVER_KNOBS->STORAGE_LOGGING_DELAY), grvLatencyBands("GRVLatencyMetrics", id, SERVER_KNOBS->STORAGE_LOGGING_DELAY) { specialCounter(cc, "LastAssignedCommitVersion", [this](){return this->lastCommitVersionAssigned;}); @@ -146,6 +146,7 @@ ACTOR Future queueTransactionStartRequests( { loop choose{ when(GetReadVersionRequest req = waitNext(readVersionRequests)) { + //WARNING: this code is run at a high priority, so it needs to do as little work as possible if( stats->txnRequestIn.getValue() - stats->txnRequestOut.getValue() > SERVER_KNOBS->START_TRANSACTION_MAX_QUEUE_SIZE ) { ++stats->txnRequestErrors; //FIXME: send an error instead of giving an unreadable version when the client can support the error: req.reply.sendError(proxy_memory_limit_exceeded()); @@ -153,6 +154,7 @@ ACTOR Future queueTransactionStartRequests( rep.version = 1; rep.locked = true; req.reply.send(rep); + TraceEvent(SevWarnAlways, "ProxyGRVThresholdExceeded").suppressFor(60); } else { if (req.debugID.present()) g_traceBatch.addEvent("TransactionDebug", req.debugID.get().first(), "MasterProxyServer.queueTransactionStartRequests.Before"); @@ -415,10 +417,12 @@ ACTOR Future commitBatcher(ProxyCommitData *commitData, PromiseStreamCOMMIT_TRANSACTION_BATCH_COUNT_MAX || batchBytes >= desiredBytes)) { choose{ when(CommitTransactionRequest req = waitNext(in)) { + //WARNING: this code is run at a high priority, so it needs to do as little work as possible int bytes = getBytes(req); // Drop requests if memory is under severe pressure if(commitData->commitBatchesMemBytesCount + bytes > memBytesLimit) { + ++commitData->stats.txnCommitErrors; req.reply.sendError(proxy_memory_limit_exceeded()); TraceEvent(SevWarnAlways, "ProxyCommitBatchMemoryThresholdExceeded").suppressFor(60).detail("MemBytesCount", commitData->commitBatchesMemBytesCount).detail("MemLimit", memBytesLimit); continue; @@ -505,6 +509,7 @@ ACTOR Future commitBatch( vector trs, int currentBatchMemBytesCount) { + //WARNING: this code is run at a high priority (until the first delay(0)), so it needs to do as little work as possible state int64_t localBatchNumber = ++self->localCommitBatchesStarted; state LogPushData toCommit(self->logSystem); state double t1 = now(); @@ -1405,10 +1410,12 @@ ACTOR static Future doKeyServerLocationRequest( GetKeyServerLocationsReque ACTOR static Future readRequestServer( MasterProxyInterface proxy, PromiseStream> addActor, ProxyCommitData* commitData ) { loop { GetKeyServerLocationsRequest req = waitNext(proxy.getKeyServersLocations.getFuture()); + //WARNING: this code is run at a high priority, so it needs to do as little work as possible if(req.limit != CLIENT_KNOBS->STORAGE_METRICS_SHARD_LIMIT && //Always do data distribution requests commitData->stats.keyServerLocationIn.getValue() - commitData->stats.keyServerLocationOut.getValue() > SERVER_KNOBS->KEY_LOCATION_MAX_QUEUE_SIZE) { ++commitData->stats.keyServerLocationErrors; req.reply.sendError(proxy_memory_limit_exceeded()); + TraceEvent(SevWarnAlways, "ProxyLocationRequestThresholdExceeded").suppressFor(60); } else { ++commitData->stats.keyServerLocationIn; addActor.send(doKeyServerLocationRequest(req, commitData)); @@ -1737,6 +1744,7 @@ ACTOR Future masterProxyServerCore( } when(wait(onError)) {} when(std::pair, int> batchedRequests = waitNext(batchedCommits.getFuture())) { + //WARNING: this code is run at a high priority, so it needs to do as little work as possible const vector &trs = batchedRequests.first; int batchBytes = batchedRequests.second; //TraceEvent("MasterProxyCTR", proxy.id()).detail("CommitTransactions", trs.size()).detail("TransactionRate", transactionRate).detail("TransactionQueue", transactionQueue.size()).detail("ReleasedTransactionCount", transactionCount); diff --git a/fdbserver/Status.actor.cpp b/fdbserver/Status.actor.cpp index d1cab6de22..67476b367c 100644 --- a/fdbserver/Status.actor.cpp +++ b/fdbserver/Status.actor.cpp @@ -1647,6 +1647,8 @@ ACTOR static Future workloadStatusFetcher(Reference workloadStatusFetcher(Reference Date: Sat, 14 Mar 2020 15:02:19 -0700 Subject: [PATCH 0916/1604] make sure the number of logRouterTags is larger than the number of satelliteTLogs to avoid having satellites with no data. --- fdbserver/TagPartitionedLogSystem.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/TagPartitionedLogSystem.actor.cpp b/fdbserver/TagPartitionedLogSystem.actor.cpp index 625784a22e..f3e084a542 100644 --- a/fdbserver/TagPartitionedLogSystem.actor.cpp +++ b/fdbserver/TagPartitionedLogSystem.actor.cpp @@ -1997,7 +1997,7 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCountedrecruitmentID = logSystem->recruitmentID; if(configuration.usableRegions > 1) { - logSystem->logRouterTags = recr.tLogs.size() * std::max(1, configuration.desiredLogRouterCount / std::max(1,recr.tLogs.size())); + logSystem->logRouterTags = std::max(recr.satelliteTLogs.size(), recr.tLogs.size()) * std::max(1, configuration.desiredLogRouterCount / std::max(1,std::max(recr.satelliteTLogs.size(), recr.tLogs.size()))); logSystem->expectedLogSets++; logSystem->addPseudoLocality(tagLocalityLogRouterMapped); } From 818537ed2d503afbda129b9aa4d1e087d417872a Mon Sep 17 00:00:00 2001 From: Evan Tschannen <36455792+etschannen@users.noreply.github.com> Date: Sat, 14 Mar 2020 15:04:46 -0700 Subject: [PATCH 0917/1604] Update fdbserver/masterserver.actor.cpp Co-Authored-By: A.J. Beamon --- fdbserver/masterserver.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/masterserver.actor.cpp b/fdbserver/masterserver.actor.cpp index c4c9f66498..2b9cbf15f2 100644 --- a/fdbserver/masterserver.actor.cpp +++ b/fdbserver/masterserver.actor.cpp @@ -1257,7 +1257,7 @@ ACTOR Future masterCore( Reference self ) { wait(Future(Never())); } else if (self->cstate.myDBState.oldTLogData.size() > CLIENT_KNOBS->RECOVERY_DELAY_START_GENERATION) { TraceEvent(SevError, "RecoveryDelayedTooManyOldGenerations").detail("OldGenerations", self->cstate.myDBState.oldTLogData.size()) - .detail("Reason", "Recovery is delayed because too many recoveries have happened since the last time the cluster was fully_recovered. Set --knob_max_generations_override to a value larger than OldGenerations on your server processes to resume recovery once the underlying problem has been fixed."); + .detail("Reason", "Recovery is delayed because too many recoveries have happened since the last time the cluster was fully_recovered. Set --knob_max_generations_override on your server processes to a value larger than OldGenerations to resume recovery once the underlying problem has been fixed."); wait(delay(CLIENT_KNOBS->RECOVERY_DELAY_SECONDS_PER_GENERATION*(self->cstate.myDBState.oldTLogData.size() - CLIENT_KNOBS->RECOVERY_DELAY_START_GENERATION))); } } From cda45481f697bb2410ee773b02a9de49b2ed3735 Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Sun, 15 Mar 2020 17:45:18 +0000 Subject: [PATCH 0918/1604] revert to run with sudo `docker` group is non-standard so sudo seems to be the most portable option` --- build/gen_dev_docker.sh | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/build/gen_dev_docker.sh b/build/gen_dev_docker.sh index 8294df5260..3779aa6eb2 100755 --- a/build/gen_dev_docker.sh +++ b/build/gen_dev_docker.sh @@ -4,24 +4,6 @@ set -e # we first check whether the user is in the group docker user=$(id -un) -is_in_docker_group=0 -for group in $(id -Gn) -do - if [ $group = "docker" ] - then - is_in_docker_group=1 - fi -done - -if [ $is_in_docker_group -eq 0 ] -then - echo "Adding user to docker group" - sudo usermod -a -G docker ${user} - echo "Please log out and back in again to reload group list" - echo "Afterwards you need to call this script again" - exit -fi - DIR_UUID=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1) group=$(id -gn) uid=$(id -u) @@ -48,7 +30,7 @@ do echo "RUN groupadd -g ${gids[$i]} ${groups[$i]}" >> Dockerfile if [ ${gids[i]} -ne ${gid} ] then - if [ -z ${additional_groups} ] + if [ -z "${additional_groups}" ] then additional_groups="-G ${gids[$i]}" else @@ -67,7 +49,7 @@ EOF echo "Created ${tmpdir}" echo "Buidling Docker container ${image}" -docker build -t ${image} . +sudo docker build -t ${image} . popd @@ -85,7 +67,7 @@ then fi -docker run --rm `# delete (temporary) image after return` \\ +sudo docker run --rm `# delete (temporary) image after return` \\ -it `# Run in interactive mode and simulate a TTY` \\ --privileged=true `# Run in privileged mode ` \\ --cap-add=SYS_PTRACE \\ From 75baa99925f7119bf0abff0e06bb54d2e5c15939 Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Sun, 15 Mar 2020 17:56:44 +0000 Subject: [PATCH 0919/1604] give user sudo access in docker container --- build/gen_dev_docker.sh | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/build/gen_dev_docker.sh b/build/gen_dev_docker.sh index 3779aa6eb2..7f23b66354 100755 --- a/build/gen_dev_docker.sh +++ b/build/gen_dev_docker.sh @@ -21,21 +21,19 @@ echo cat <> Dockerfile FROM foundationdb/foundationdb-build:latest +RUN yum install -y sudo +RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers +RUN groupadd -g 1100 sudo EOF num_groups=${#gids[@]} -additional_groups="" +additional_groups="-G sudo" for ((i=0;i> Dockerfile if [ ${gids[i]} -ne ${gid} ] then - if [ -z "${additional_groups}" ] - then - additional_groups="-G ${gids[$i]}" - else - additional_groups="${additional_groups},${gids[$i]}" - fi + additional_groups="${additional_groups},${gids[$i]}" fi done From 15c48b9e1907d331d71e5091d1a4efb4a2744bda Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Thu, 12 Mar 2020 16:21:37 -0700 Subject: [PATCH 0920/1604] Add event for getDesired coordinators --- fdbclient/ManagementAPI.actor.cpp | 20 +++++++++++++++++++ .../workloads/ConsistencyCheck.actor.cpp | 10 ++++++++-- .../workloads/RemoveServersSafely.actor.cpp | 4 ++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/fdbclient/ManagementAPI.actor.cpp b/fdbclient/ManagementAPI.actor.cpp index 3c5d858e7a..37c6bd27b2 100644 --- a/fdbclient/ManagementAPI.actor.cpp +++ b/fdbclient/ManagementAPI.actor.cpp @@ -1152,16 +1152,34 @@ struct AutoQuorumChange : IQuorumChange { } chosen.resize((chosen.size() - 1) | 1); } + // Sanity check if chosen coordinators will be injected fault + if (g_network->isSimulated()) { + for (auto& addr : chosen) { + ISimulator::ProcessInfo* p = g_simulator.getProcessByAddress(addr); + TraceEvent("MXGetDesiredCoordinator").detail("Address", addr.toString()).detail("Reliable", p->isReliable()).detail("Protected", g_simulator.protectedAddresses.count(addr)).detail("ReliableInfo", p->getReliableInfo()); + } + } return chosen; } + // Select a desired set of workers such that (1) the number of workers at each locality type (e.g., dcid) <= desiredCount; and + // (2) prefer workers at a locality where less workers has been chosen than other localities: evenly distribute workers. void addDesiredWorkers(vector& chosen, const vector& workers, int desiredCount, const std::set& excluded) { vector remainingWorkers(workers); deterministicRandom()->randomShuffle(remainingWorkers); std::partition(remainingWorkers.begin(), remainingWorkers.end(), [](const ProcessData& data) { return (data.processClass == ProcessClass::CoordinatorClass); }); + TraceEvent("AutoSelectCoordinators").detail("CandidateWorkers", remainingWorkers.size()); + for (auto worker = remainingWorkers.begin(); worker != remainingWorkers.end(); worker++) { + TraceEvent("SelectCoordinators").detail("Worker", worker->processClass.toString()).detail("Address", worker->address.toString()).detail("Locality", worker->locality.toString()); + } + TraceEvent("AutoSelectCoordinators").detail("ExcludedAddress", excluded.size()); + for(auto& excludedAddr : excluded) { + TraceEvent("AutoSelectCoordinators").detail("ExcludedAddress", excludedAddr.toString()); + } + std::map maxCounts; std::map> currentCounts; std::map hardLimits; @@ -1414,6 +1432,7 @@ ACTOR Future printHealthyZone( Database cx ) { ACTOR Future clearHealthyZone(Database cx, bool printWarning, bool clearSSFailureZoneString) { state Transaction tr(cx); + TraceEvent("ClearHealthyZone").detail("ClearSSFailureZoneString", clearSSFailureZoneString); loop { try { tr.setOption(FDBTransactionOptions::LOCK_AWARE); @@ -1439,6 +1458,7 @@ ACTOR Future clearHealthyZone(Database cx, bool printWarning, bool clearSS ACTOR Future setHealthyZone(Database cx, StringRef zoneId, double seconds, bool printWarning) { state Transaction tr(cx); + TraceEvent("SetHealthyZone").detail("Zone", zoneId).detail("DurationSeconds", seconds); loop { try { tr.setOption(FDBTransactionOptions::LOCK_AWARE); diff --git a/fdbserver/workloads/ConsistencyCheck.actor.cpp b/fdbserver/workloads/ConsistencyCheck.actor.cpp index 6c4a3f312b..eadbf66f71 100644 --- a/fdbserver/workloads/ConsistencyCheck.actor.cpp +++ b/fdbserver/workloads/ConsistencyCheck.actor.cpp @@ -295,7 +295,7 @@ struct ConsistencyCheckWorkload : TestWorkload } wait(::success(self->checkForStorage(cx, configuration, self))); - wait(::success(self->waitForUnreliableExtraStoreReboot(cx, self))); + // wait(::success(self->waitForUnreliableExtraStoreReboot(cx, self))); wait(::success(self->checkForExtraDataStores(cx, self))); //Check that each machine is operating as its desired class @@ -1344,7 +1344,11 @@ struct ConsistencyCheckWorkload : TestWorkload if(g_network->isSimulated()) { //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 - auto p = g_simulator.getProcessByAddress(itr->interf.address()); + ISimulator::ProcessInfo* p = g_simulator.getProcessByAddress(itr->interf.address()); + ISimulator::ProcessInfo* p2 = nullptr; + if (itr->interf.secondaryAddress().present()) { + p2 = g_simulator.getProcessByAddress(itr->interf.secondaryAddress().get()); + } // 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") @@ -1352,6 +1356,8 @@ struct ConsistencyCheckWorkload : TestWorkload itr->interf.address()) // worker's primary address (i.e., the first address) .detail("ProcessPrimaryAddress", p->address) .detail("ProcessAddresses", p->addresses.toString()) + .detail("ProcessAtPrimaryAddressIsReliable", p->isReliable()) + .detail("ProcessAtSecondaryAddressIsReliable", p2 != nullptr ? (p2->isReliable() ? "True" : "False") : "unset") .detail("DataStoreID", id) .detail("Protected", g_simulator.protectedAddresses.count(itr->interf.address())) .detail("Reliable", p->isReliable()) diff --git a/fdbserver/workloads/RemoveServersSafely.actor.cpp b/fdbserver/workloads/RemoveServersSafely.actor.cpp index bda028d61b..3f2e2842ff 100644 --- a/fdbserver/workloads/RemoveServersSafely.actor.cpp +++ b/fdbserver/workloads/RemoveServersSafely.actor.cpp @@ -495,9 +495,13 @@ struct RemoveServersSafelyWorkload : TestWorkload { // Wait for removal to be safe TraceEvent("RemoveAndKill", functionId).detail("Step", "Wait For Server Exclusion").detail("Addresses", describe(toKill)).detail("ClusterAvailable", g_simulator.isAvailable()); wait(success(checkForExcludingServers(cx, toKillArray, true /* wait for exclusion */))); + // TODO: We have to wait for faulty machine to die or reborn; otherwise, + // machine that is in failing due to injected fault may be chosen as coordinator. TraceEvent("RemoveAndKill", functionId).detail("Step", "coordinators auto").detail("DesiredCoordinators", g_simulator.desiredCoordinators).detail("ClusterAvailable", g_simulator.isAvailable()); + // Ensure coordinators do not use faulty node + // Setup the coordinators BEFORE the exclusion // Otherwise, we may end up with NotEnoughMachinesForCoordinators state int cycle=0; From e5d53c863be8a4ed358e436dc733680f0087ab25 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Mon, 16 Mar 2020 10:29:17 -0700 Subject: [PATCH 0921/1604] report in status the number of active generations --- fdbserver/Status.actor.cpp | 9 ++++++++- fdbserver/masterserver.actor.cpp | 10 +++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/fdbserver/Status.actor.cpp b/fdbserver/Status.actor.cpp index d1cab6de22..614ff349db 100644 --- a/fdbserver/Status.actor.cpp +++ b/fdbserver/Status.actor.cpp @@ -961,8 +961,9 @@ ACTOR static Future recoveryStateStatusFetcher(WorkerDetails state JsonBuilderObject message; try { + state Future activeGens = timeoutError(mWorker.interf.eventLogRequest.getReply( EventLogRequest( LiteralStringRef("MasterRecoveryGenerations") ) ), 1.0); TraceEventFields md = wait( timeoutError(mWorker.interf.eventLogRequest.getReply( EventLogRequest( LiteralStringRef("MasterRecoveryState") ) ), 1.0) ); - state int mStatusCode = md.getInt("StatusCode"); + int mStatusCode = md.getInt("StatusCode"); if (mStatusCode < 0 || mStatusCode >= RecoveryStatus::END) throw attribute_not_found(); @@ -986,6 +987,12 @@ ACTOR static Future recoveryStateStatusFetcher(WorkerDetails // TODO: time_in_recovery: 0.5 // time_in_state: 0.1 + TraceEventFields md = wait(activeGens); + if(md.size()) { + int activeGenerations = md.getInt("ActiveGenerations"); + message["active_generations"] = activeGenerations; + } + } catch (Error &e){ if (e.code() == error_code_actor_cancelled) throw; diff --git a/fdbserver/masterserver.actor.cpp b/fdbserver/masterserver.actor.cpp index 2b9cbf15f2..2a77359edc 100644 --- a/fdbserver/masterserver.actor.cpp +++ b/fdbserver/masterserver.actor.cpp @@ -1161,6 +1161,10 @@ ACTOR Future trackTlogRecovery( Reference self, Referencedbgid) + .detail("ActiveGenerations", 0) + .trackLatest("MasterRecoveryGenerations"); } else if( !newState.oldTLogData.size() && self->recoveryState < RecoveryState::STORAGE_RECOVERED ) { self->recoveryState = RecoveryState::STORAGE_RECOVERED; TraceEvent("MasterRecoveryState", self->dbgid) @@ -1245,11 +1249,15 @@ ACTOR Future masterCore( Reference self ) { .detail("StatusCode", RecoveryStatus::locking_coordinated_state) .detail("Status", RecoveryStatus::names[RecoveryStatus::locking_coordinated_state]) .detail("TLogs", self->cstate.prevDBState.tLogs.size()) - .detail("OldGenerations", self->cstate.myDBState.oldTLogData.size()) + .detail("ActiveGenerations", self->cstate.myDBState.oldTLogData.size()) .detail("MyRecoveryCount", self->cstate.prevDBState.recoveryCount+2) .detail("ForceRecovery", self->forceRecovery) .trackLatest("MasterRecoveryState"); + TraceEvent("MasterRecoveryGenerations", self->dbgid) + .detail("ActiveGenerations", self->cstate.myDBState.oldTLogData.size()) + .trackLatest("MasterRecoveryGenerations"); + if (self->cstate.myDBState.oldTLogData.size() > CLIENT_KNOBS->MAX_GENERATIONS_OVERRIDE) { if (self->cstate.myDBState.oldTLogData.size() >= CLIENT_KNOBS->MAX_GENERATIONS) { TraceEvent(SevError, "RecoveryStoppedTooManyOldGenerations").detail("OldGenerations", self->cstate.myDBState.oldTLogData.size()) From 76db8343c03e3fd8510acd1e1a7a7934a639b9b4 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Mon, 16 Mar 2020 11:00:51 -0700 Subject: [PATCH 0922/1604] update status schema --- documentation/sphinx/source/mr-status-json-schemas.rst.inc | 1 + fdbclient/Schemas.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/documentation/sphinx/source/mr-status-json-schemas.rst.inc b/documentation/sphinx/source/mr-status-json-schemas.rst.inc index c8d81f5c95..a18e06fb4a 100644 --- a/documentation/sphinx/source/mr-status-json-schemas.rst.inc +++ b/documentation/sphinx/source/mr-status-json-schemas.rst.inc @@ -399,6 +399,7 @@ }, "required_logs":3, "missing_logs":"7f8d623d0cb9966e", + "active_generations":1, "description":"Recovery complete." }, "workload":{ diff --git a/fdbclient/Schemas.cpp b/fdbclient/Schemas.cpp index 51572cf015..cd0328d485 100644 --- a/fdbclient/Schemas.cpp +++ b/fdbclient/Schemas.cpp @@ -425,6 +425,7 @@ const KeyRef JSONSchemas::statusSchema = LiteralStringRef(R"statusSchema( }, "required_logs":3, "missing_logs":"7f8d623d0cb9966e", + "active_generations":1, "description":"Recovery complete." }, "workload":{ From 56dee89e6ef8d47d0ceb3bcc454825173a67b7a8 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Mon, 16 Mar 2020 11:09:42 -0700 Subject: [PATCH 0923/1604] active generations should include the current one --- fdbserver/masterserver.actor.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fdbserver/masterserver.actor.cpp b/fdbserver/masterserver.actor.cpp index 2a77359edc..a99e0b970d 100644 --- a/fdbserver/masterserver.actor.cpp +++ b/fdbserver/masterserver.actor.cpp @@ -1163,7 +1163,7 @@ ACTOR Future trackTlogRecovery( Reference self, Referencedbgid) - .detail("ActiveGenerations", 0) + .detail("ActiveGenerations", 1) .trackLatest("MasterRecoveryGenerations"); } else if( !newState.oldTLogData.size() && self->recoveryState < RecoveryState::STORAGE_RECOVERED ) { self->recoveryState = RecoveryState::STORAGE_RECOVERED; @@ -1249,13 +1249,13 @@ ACTOR Future masterCore( Reference self ) { .detail("StatusCode", RecoveryStatus::locking_coordinated_state) .detail("Status", RecoveryStatus::names[RecoveryStatus::locking_coordinated_state]) .detail("TLogs", self->cstate.prevDBState.tLogs.size()) - .detail("ActiveGenerations", self->cstate.myDBState.oldTLogData.size()) + .detail("ActiveGenerations", self->cstate.myDBState.oldTLogData.size() + 1) .detail("MyRecoveryCount", self->cstate.prevDBState.recoveryCount+2) .detail("ForceRecovery", self->forceRecovery) .trackLatest("MasterRecoveryState"); TraceEvent("MasterRecoveryGenerations", self->dbgid) - .detail("ActiveGenerations", self->cstate.myDBState.oldTLogData.size()) + .detail("ActiveGenerations", self->cstate.myDBState.oldTLogData.size() + 1) .trackLatest("MasterRecoveryGenerations"); if (self->cstate.myDBState.oldTLogData.size() > CLIENT_KNOBS->MAX_GENERATIONS_OVERRIDE) { From ea98c7a40a9d6c82f09174f34d8a4ebaa7deb9cb Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Mon, 16 Mar 2020 11:38:14 -0700 Subject: [PATCH 0924/1604] added additional timeout on initPersistentState --- fdbserver/OldTLogServer_6_0.actor.cpp | 16 +++------------- fdbserver/TLogServer.actor.cpp | 16 +++------------- flow/genericactors.actor.h | 17 ++++++++++++++++- 3 files changed, 22 insertions(+), 27 deletions(-) diff --git a/fdbserver/OldTLogServer_6_0.actor.cpp b/fdbserver/OldTLogServer_6_0.actor.cpp index 23f81745fa..564bb96370 100644 --- a/fdbserver/OldTLogServer_6_0.actor.cpp +++ b/fdbserver/OldTLogServer_6_0.actor.cpp @@ -1416,7 +1416,7 @@ ACTOR Future initPersistentState( TLogData* self, Reference logDa // PERSIST: Initial setup of persistentData for a brand new tLog for a new database state IKeyValueStore *storage = self->persistentData; - wait(storage->init()); + wait( ioTimeoutError( storage->init(), SERVER_KNOBS->TLOG_MAX_CREATE_DURATION ) ); storage->set( persistFormat ); storage->set( KeyValueRef( BinaryWriter::toValue(logData->logId,Unversioned()).withPrefix(persistCurrentVersionKeys.begin), BinaryWriter::toValue(logData->version.get(), Unversioned()) ) ); storage->set( KeyValueRef( BinaryWriter::toValue(logData->logId,Unversioned()).withPrefix(persistKnownCommittedVersionKeys.begin), BinaryWriter::toValue(logData->knownCommittedVersion, Unversioned()) ) ); @@ -1432,7 +1432,7 @@ ACTOR Future initPersistentState( TLogData* self, Reference logDa } TraceEvent("TLogInitCommit", logData->logId); - wait( self->persistentData->commit() ); + wait( ioTimeoutError( self->persistentData->commit(), SERVER_KNOBS->TLOG_MAX_CREATE_DURATION ) ); return Void(); } @@ -2332,17 +2332,7 @@ ACTOR Future tLog( IKeyValueStore* persistentData, IDiskQueue* persistentQ if(restoreFromDisk) { wait( restorePersistentState( &self, locality, oldLog, recovered, tlogRequests ) ); } else { - choose { - when( wait( checkEmptyQueue(&self) && checkRecovered(&self) ) ) {} - when( wait( lowPriorityDelay(SERVER_KNOBS->TLOG_MAX_CREATE_DURATION) ) ) { - Error err = io_timeout(); - if(g_network->isSimulated()) { - err = err.asInjectedFault(); - } - TraceEvent(SevError, "TLogInitializeFilesTimeout", tlogId).error(err); - throw err; - } - } + wait( ioTimeoutError( checkEmptyQueue(&self) && checkRecovered(&self), SERVER_KNOBS->TLOG_MAX_CREATE_DURATION ) ); } //Disk errors need a chance to kill this actor. diff --git a/fdbserver/TLogServer.actor.cpp b/fdbserver/TLogServer.actor.cpp index 6d474d6e5b..c090a9dcae 100644 --- a/fdbserver/TLogServer.actor.cpp +++ b/fdbserver/TLogServer.actor.cpp @@ -1807,7 +1807,7 @@ ACTOR Future initPersistentState( TLogData* self, Reference logDa // PERSIST: Initial setup of persistentData for a brand new tLog for a new database state IKeyValueStore *storage = self->persistentData; - wait(storage->init()); + wait( ioTimeoutError( storage->init(), SERVER_KNOBS->TLOG_MAX_CREATE_DURATION ) ); storage->set( persistFormat ); storage->set( KeyValueRef( BinaryWriter::toValue(logData->logId,Unversioned()).withPrefix(persistCurrentVersionKeys.begin), BinaryWriter::toValue(logData->version.get(), Unversioned()) ) ); storage->set( KeyValueRef( BinaryWriter::toValue(logData->logId,Unversioned()).withPrefix(persistKnownCommittedVersionKeys.begin), BinaryWriter::toValue(logData->knownCommittedVersion, Unversioned()) ) ); @@ -1824,7 +1824,7 @@ ACTOR Future initPersistentState( TLogData* self, Reference logDa } TraceEvent("TLogInitCommit", logData->logId); - wait( self->persistentData->commit() ); + wait( ioTimeoutError( self->persistentData->commit(), SERVER_KNOBS->TLOG_MAX_CREATE_DURATION ) ); return Void(); } @@ -2766,17 +2766,7 @@ ACTOR Future tLog( IKeyValueStore* persistentData, IDiskQueue* persistentQ if(restoreFromDisk) { wait( restorePersistentState( &self, locality, oldLog, recovered, tlogRequests ) ); } else { - choose { - when( wait( checkEmptyQueue(&self) && checkRecovered(&self) ) ) {} - when( wait( lowPriorityDelay(SERVER_KNOBS->TLOG_MAX_CREATE_DURATION) ) ) { - Error err = io_timeout(); - if(g_network->isSimulated()) { - err = err.asInjectedFault(); - } - TraceEvent(SevError, "TLogInitializeFilesTimeout", tlogId).error(err); - throw err; - } - } + wait( ioTimeoutError( checkEmptyQueue(&self) && checkRecovered(&self), SERVER_KNOBS->TLOG_MAX_CREATE_DURATION ) ); } //Disk errors need a chance to kill this actor. diff --git a/flow/genericactors.actor.h b/flow/genericactors.actor.h index 14db6428ab..b1978bbbc4 100644 --- a/flow/genericactors.actor.h +++ b/flow/genericactors.actor.h @@ -199,7 +199,6 @@ Future timeoutError( Future what, double time, TaskPriority taskID = TaskP } } - ACTOR template Future delayed( Future what, double time = 0.0, TaskPriority taskID = TaskPriority::DefaultDelay ) { try { @@ -866,6 +865,22 @@ Future timeoutWarningCollector( FutureStream const& input, double co Future quorumEqualsTrue( std::vector> const& futures, int const& required ); Future lowPriorityDelay( double const& waitTime ); +ACTOR template +Future ioTimeoutError( Future what, double time ) { + Future end = lowPriorityDelay( time ); + choose { + when( T t = wait( what ) ) { return t; } + when( wait( end ) ) { + Error err = io_timeout(); + if(g_network->isSimulated()) { + err = err.asInjectedFault(); + } + TraceEvent(SevError, "IoTimeoutError").error(err); + throw err; + } + } +} + ACTOR template Future streamHelper( PromiseStream output, PromiseStream errors, Future input ) { try { From bbb07e860b8aca2205cda3fd09b2695ecfdd72e5 Mon Sep 17 00:00:00 2001 From: Evan Tschannen <36455792+etschannen@users.noreply.github.com> Date: Mon, 16 Mar 2020 11:42:27 -0700 Subject: [PATCH 0925/1604] Update documentation/sphinx/source/mr-status-json-schemas.rst.inc --- documentation/sphinx/source/mr-status-json-schemas.rst.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/sphinx/source/mr-status-json-schemas.rst.inc b/documentation/sphinx/source/mr-status-json-schemas.rst.inc index 1ba005c5ba..6eee48a7a8 100644 --- a/documentation/sphinx/source/mr-status-json-schemas.rst.inc +++ b/documentation/sphinx/source/mr-status-json-schemas.rst.inc @@ -418,7 +418,7 @@ "counter":0, "roughness":0.0 }, - "location_requests":{ // measures number of incoming key server location requests + "location_requests":{ // measures number of outgoing key server location requests "hz":0.0, "counter":0, "roughness":0.0 From 012344e2979cd681986f2f2927392b3f23a1090b Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Mon, 16 Mar 2020 11:50:17 -0700 Subject: [PATCH 0926/1604] refactor getWorkersForRoleInDatacenter --- fdbserver/ClusterController.actor.cpp | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/fdbserver/ClusterController.actor.cpp b/fdbserver/ClusterController.actor.cpp index fe6ce12961..72a9681c9f 100644 --- a/fdbserver/ClusterController.actor.cpp +++ b/fdbserver/ClusterController.actor.cpp @@ -484,8 +484,12 @@ public: vector getWorkersForRoleInDatacenter(Optional> const& dcId, ProcessClass::ClusterRole role, int amount, DatabaseConfiguration const& conf, std::map< Optional>, int>& id_used, Optional minWorker = Optional(), bool checkStable = false ) { std::map, std::pair,vector>> fitness_workers; vector results; - if (amount <= 0) + if(minWorker.present()) { + results.push_back(minWorker.get().worker); + } + if (amount <= results.size()) { return results; + } for( auto& it : id_worker ) { auto fitness = it.second.details.processClass.machineClassFitness( role ); @@ -693,11 +697,8 @@ public: auto first_resolver = getWorkerForRoleInDatacenter( dcId, ProcessClass::Resolver, ProcessClass::ExcludeFit, req.configuration, id_used ); auto first_proxy = getWorkerForRoleInDatacenter( dcId, ProcessClass::Proxy, ProcessClass::ExcludeFit, req.configuration, id_used ); - auto proxies = getWorkersForRoleInDatacenter( dcId, ProcessClass::Proxy, req.configuration.getDesiredProxies()-1, req.configuration, id_used, first_proxy ); - auto resolvers = getWorkersForRoleInDatacenter( dcId, ProcessClass::Resolver, req.configuration.getDesiredResolvers()-1, req.configuration, id_used, first_resolver ); - - proxies.insert(proxies.begin(), first_proxy.worker); - resolvers.insert(resolvers.begin(), first_resolver.worker); + auto proxies = getWorkersForRoleInDatacenter( dcId, ProcessClass::Proxy, req.configuration.getDesiredProxies(), req.configuration, id_used, first_proxy ); + auto resolvers = getWorkersForRoleInDatacenter( dcId, ProcessClass::Resolver, req.configuration.getDesiredResolvers(), req.configuration, id_used, first_resolver ); for(int i = 0; i < resolvers.size(); i++) result.resolvers.push_back(resolvers[i].interf); @@ -826,11 +827,8 @@ public: auto first_resolver = getWorkerForRoleInDatacenter( dcId, ProcessClass::Resolver, ProcessClass::ExcludeFit, req.configuration, used ); auto first_proxy = getWorkerForRoleInDatacenter( dcId, ProcessClass::Proxy, ProcessClass::ExcludeFit, req.configuration, used ); - auto proxies = getWorkersForRoleInDatacenter( dcId, ProcessClass::Proxy, req.configuration.getDesiredProxies()-1, req.configuration, used, first_proxy ); - auto resolvers = getWorkersForRoleInDatacenter( dcId, ProcessClass::Resolver, req.configuration.getDesiredResolvers()-1, req.configuration, used, first_resolver ); - - proxies.insert(proxies.begin(), first_proxy.worker); - resolvers.insert(resolvers.begin(), first_resolver.worker); + auto proxies = getWorkersForRoleInDatacenter( dcId, ProcessClass::Proxy, req.configuration.getDesiredProxies(), req.configuration, used, first_proxy ); + auto resolvers = getWorkersForRoleInDatacenter( dcId, ProcessClass::Resolver, req.configuration.getDesiredResolvers(), req.configuration, used, first_resolver ); RoleFitnessPair fitness( RoleFitness(proxies, ProcessClass::Proxy), RoleFitness(resolvers, ProcessClass::Resolver) ); @@ -1135,10 +1133,8 @@ public: auto first_resolver = getWorkerForRoleInDatacenter( clusterControllerDcId, ProcessClass::Resolver, ProcessClass::ExcludeFit, db.config, id_used, true ); auto first_proxy = getWorkerForRoleInDatacenter( clusterControllerDcId, ProcessClass::Proxy, ProcessClass::ExcludeFit, db.config, id_used, true ); - auto proxies = getWorkersForRoleInDatacenter( clusterControllerDcId, ProcessClass::Proxy, db.config.getDesiredProxies()-1, db.config, id_used, first_proxy, true ); - auto resolvers = getWorkersForRoleInDatacenter( clusterControllerDcId, ProcessClass::Resolver, db.config.getDesiredResolvers()-1, db.config, id_used, first_resolver, true ); - proxies.insert(proxies.begin(), first_proxy.worker); - resolvers.insert(resolvers.begin(), first_resolver.worker); + auto proxies = getWorkersForRoleInDatacenter( clusterControllerDcId, ProcessClass::Proxy, db.config.getDesiredProxies(), db.config, id_used, first_proxy, true ); + auto resolvers = getWorkersForRoleInDatacenter( clusterControllerDcId, ProcessClass::Resolver, db.config.getDesiredResolvers(), db.config, id_used, first_resolver, true ); RoleFitnessPair newInFit(RoleFitness(proxies, ProcessClass::Proxy), RoleFitness(resolvers, ProcessClass::Resolver)); if(oldInFit.proxy.betterFitness(newInFit.proxy) || oldInFit.resolver.betterFitness(newInFit.resolver)) { From a068d4063f0976a1588d433c200110c5e15c346d Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Mon, 16 Mar 2020 12:11:32 -0700 Subject: [PATCH 0927/1604] renamed ProxyGetConsistentReadVersion --- fdbclient/NativeAPI.actor.cpp | 2 +- fdbserver/MasterInterface.h | 2 +- flow/network.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index 2d8a616ab2..1796790ee2 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -3106,7 +3106,7 @@ ACTOR Future readVersionBatcher( DatabaseContext *cx, FutureStream< std::p if (requests.size() == CLIENT_KNOBS->MAX_BATCH_SIZE) send_batch = true; else if (!timeout.isValid()) - timeout = delay(batchTime, TaskPriority::ProxyGetConsistentReadVersion); + timeout = delay(batchTime, TaskPriority::GetConsistentReadVersion); } when(wait(timeout.isValid() ? timeout : Never())) { send_batch = true; diff --git a/fdbserver/MasterInterface.h b/fdbserver/MasterInterface.h index 534ce01610..54ea383ede 100644 --- a/fdbserver/MasterInterface.h +++ b/fdbserver/MasterInterface.h @@ -50,7 +50,7 @@ struct MasterInterface { } void initEndpoints() { - getCommitVersion.getEndpoint( TaskPriority::ProxyGetConsistentReadVersion ); + getCommitVersion.getEndpoint( TaskPriority::GetConsistentReadVersion ); tlogRejoin.getEndpoint( TaskPriority::MasterTLogRejoin ); } }; diff --git a/flow/network.h b/flow/network.h index 485b9acefd..edfe031337 100644 --- a/flow/network.h +++ b/flow/network.h @@ -73,7 +73,7 @@ enum class TaskPriority { TLogConfirmRunningReply = 8530, TLogConfirmRunning = 8520, ProxyGRVTimer = 8510, - ProxyGetConsistentReadVersion = 8500, + GetConsistentReadVersion = 8500, DefaultPromiseEndpoint = 8000, DefaultOnMainThread = 7500, DefaultDelay = 7010, From ec27125102d16b4802ba3c6554e9436bc62ff698 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Mon, 16 Mar 2020 12:15:40 -0700 Subject: [PATCH 0928/1604] Update documentation/sphinx/source/mr-status-json-schemas.rst.inc --- documentation/sphinx/source/mr-status-json-schemas.rst.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/sphinx/source/mr-status-json-schemas.rst.inc b/documentation/sphinx/source/mr-status-json-schemas.rst.inc index 6eee48a7a8..0e2e6a9088 100644 --- a/documentation/sphinx/source/mr-status-json-schemas.rst.inc +++ b/documentation/sphinx/source/mr-status-json-schemas.rst.inc @@ -418,7 +418,7 @@ "counter":0, "roughness":0.0 }, - "location_requests":{ // measures number of outgoing key server location requests + "location_requests":{ // measures number of outgoing key server location responses "hz":0.0, "counter":0, "roughness":0.0 From 72326fe8af269ab409fd191ccd2db71dfded3c47 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 16 Mar 2020 12:46:13 -0700 Subject: [PATCH 0929/1604] Fix the build. --- flow/TLSConfig.actor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/TLSConfig.actor.h b/flow/TLSConfig.actor.h index 0257aab65a..820c90d5c9 100644 --- a/flow/TLSConfig.actor.h +++ b/flow/TLSConfig.actor.h @@ -222,7 +222,7 @@ PRIVATE_EXCEPT_FOR_TLSCONFIG_CPP: #ifndef TLS_DISABLED namespace boost { namespace asio { namespace ssl { struct context; }}} -void ConfigureSSLContext(const LoadedTLSConfig& loaded, boost::asio::ssl::context* context, std::function onPolicyFailure); +void ConfigureSSLContext(const LoadedTLSConfig& loaded, boost::asio::ssl::context* context, std::function onPolicyFailure = [](){}); #endif class TLSPolicy : ReferenceCounted { From 89861c661e8a637369eb078680aa6ec628b68d60 Mon Sep 17 00:00:00 2001 From: Xin Dong Date: Mon, 16 Mar 2020 13:36:55 -0700 Subject: [PATCH 0930/1604] Fix the random crash. Use a thread safe 'ThreadReturnPromise' instead of the ThreadFuture. --- fdbserver/worker.actor.cpp | 11 +++-------- flow/Trace.cpp | 23 +++++++++++++---------- flow/Trace.h | 5 +++-- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/fdbserver/worker.actor.cpp b/fdbserver/worker.actor.cpp index face2a17fd..5420a89907 100644 --- a/fdbserver/worker.actor.cpp +++ b/fdbserver/worker.actor.cpp @@ -749,17 +749,12 @@ ACTOR Future workerSnapCreate(WorkerSnapRequest snapReq, StringRef snapFol ACTOR Future monitorTraceLogIssues(Optional>>> issues) { state bool pingTimeout = false; - state ThreadFuture f; - state Reference> callback; loop { wait(delay(SERVER_KNOBS->TRACE_LOG_FLUSH_FAILURE_CHECK_INTERVAL_SECONDS)); - f = ThreadFuture(new ThreadSingleAssignmentVar); - callback = Reference>(new CompletionCallback(f)); - callback->self = callback; - f.callOrSetAsCallback(callback.getPtr(), callback->userParam, 0); - pingTraceLogWriterThread(f); + TraceEvent("CrashDebugPingActionSetupInWorker"); + Future pingAck = pingTraceLogWriterThread(); try { - wait(timeoutError(callback->promise.getFuture(), SERVER_KNOBS->TRACE_LOG_PING_TIMEOUT_SECONDS)); + wait(timeoutError(pingAck, SERVER_KNOBS->TRACE_LOG_PING_TIMEOUT_SECONDS)); } catch (Error& e) { if (e.code() == error_code_timed_out) { pingTimeout = true; diff --git a/flow/Trace.cpp b/flow/Trace.cpp index 4cf1cedb37..c769baca4d 100644 --- a/flow/Trace.cpp +++ b/flow/Trace.cpp @@ -312,16 +312,17 @@ public: } struct Ping : TypedAction { - ThreadFuture p; + ThreadReturnPromise ack; - explicit Ping(ThreadFuture p) : p(p){}; + explicit Ping(){}; virtual double getTimeEstimate() { return 0; } }; - void action(Ping& a) { + void action(Ping& ping) { try { - ((ThreadSingleAssignmentVar*)a.p.getPtr())->send(Void()); + ping.ack.send(Void()); } catch (Error& e) { - TraceEvent(SevError, "PingActionFailed").error(e); + TraceEvent(SevError, "CrashDebugPingActionFailed").error(e); + throw; } } }; @@ -541,9 +542,11 @@ public: } } - void pingWriterThread(ThreadFuture& p) { - auto a = new WriterThread::Ping(p); - writer->post(a); + Future pingWriterThread() { + auto ping = new WriterThread::Ping; + writer->post(ping); + auto f = ping->ack.getFuture(); + return f; } void retriveTraceLogIssues(std::set& out) { return issues->retrieveIssues(out); } @@ -787,8 +790,8 @@ void retriveTraceLogIssues(std::set& out) { return g_traceLog.retriveTraceLogIssues(out); } -void pingTraceLogWriterThread(ThreadFuture& p) { - return g_traceLog.pingWriterThread(p); +Future pingTraceLogWriterThread() { + return g_traceLog.pingWriterThread(); } TraceEvent::TraceEvent( const char* type, UID id ) : id(id), type(type), severity(SevInfo), initialized(false), enabled(true), logged(false) { diff --git a/flow/Trace.h b/flow/Trace.h index c4c26b90a8..bb3077c0a7 100644 --- a/flow/Trace.h +++ b/flow/Trace.h @@ -609,8 +609,9 @@ void addTraceRole(std::string role); void removeTraceRole(std::string role); void retriveTraceLogIssues(std::set& out); template -struct ThreadFuture; -void pingTraceLogWriterThread(ThreadFuture& p); +struct Future; +struct Void; +Future pingTraceLogWriterThread(); enum trace_clock_t { TRACE_CLOCK_NOW, TRACE_CLOCK_REALTIME }; extern std::atomic g_trace_clock; From d8cfabe73b7e159fefd3918b8db5590621c9f792 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Mon, 16 Mar 2020 13:59:31 -0700 Subject: [PATCH 0931/1604] Extend the allocation tracing disabling flag to cover more parts of trace logging as a precaution. Make it possible to disable via knob. --- flow/Arena.cpp | 6 +++--- flow/FastAlloc.cpp | 6 +++--- flow/Knobs.cpp | 1 + flow/Knobs.h | 1 + flow/Trace.cpp | 28 +++++++++++++++++++++++++++- flow/Trace.h | 2 +- 6 files changed, 36 insertions(+), 8 deletions(-) diff --git a/flow/Arena.cpp b/flow/Arena.cpp index 88837e102b..e362b72164 100644 --- a/flow/Arena.cpp +++ b/flow/Arena.cpp @@ -184,11 +184,11 @@ ArenaBlock* ArenaBlock::create(int dataSize, Reference& next) { b->bigSize = reqSize; b->bigUsed = sizeof(ArenaBlock); - if (FLOW_KNOBS && !g_tracing_allocation && + if (FLOW_KNOBS && g_allocation_tracing_disabled > 0 && nondeterministicRandom()->random01() < (reqSize / FLOW_KNOBS->HUGE_ARENA_LOGGING_BYTES)) { - g_tracing_allocation = true; + ++g_allocation_tracing_disabled; hugeArenaSample(reqSize); - g_tracing_allocation = false; + --g_allocation_tracing_disabled; } g_hugeArenaMemory.fetch_add(reqSize); diff --git a/flow/FastAlloc.cpp b/flow/FastAlloc.cpp index 8cac3948b4..06adaf383d 100644 --- a/flow/FastAlloc.cpp +++ b/flow/FastAlloc.cpp @@ -445,10 +445,10 @@ void FastAllocator::getMagazine() { // FIXME: We should be able to allocate larger magazine sizes here if we // detect that the underlying system supports hugepages. Using hugepages // with smaller-than-2MiB magazine sizes strands memory. See issue #909. - if(FLOW_KNOBS && !g_tracing_allocation && nondeterministicRandom()->random01() < (magazine_size * Size)/FLOW_KNOBS->FAST_ALLOC_LOGGING_BYTES) { - g_tracing_allocation = true; + if(FLOW_KNOBS && g_allocation_tracing_disabled > 0 && nondeterministicRandom()->random01() < (magazine_size * Size)/FLOW_KNOBS->FAST_ALLOC_LOGGING_BYTES) { + ++g_allocation_tracing_disabled; TraceEvent("GetMagazineSample").detail("Size", Size).backtrace(); - g_tracing_allocation = false; + --g_allocation_tracing_disabled; } block = (void **)::allocate(magazine_size * Size, false); #endif diff --git a/flow/Knobs.cpp b/flow/Knobs.cpp index 276857a966..4057489c52 100644 --- a/flow/Knobs.cpp +++ b/flow/Knobs.cpp @@ -153,6 +153,7 @@ FlowKnobs::FlowKnobs(bool randomize, bool isSimulated) { init( TRACE_EVENT_THROTTLER_MSG_LIMIT, 20000 ); init( MAX_TRACE_FIELD_LENGTH, 495 ); // If the value of this is changed, the corresponding default in Trace.cpp should be changed as well init( MAX_TRACE_EVENT_LENGTH, 4000 ); // If the value of this is changed, the corresponding default in Trace.cpp should be changed as well + init( ALLOCATION_TRACING_ENABLED, true ); //TDMetrics init( MAX_METRICS, 600 ); diff --git a/flow/Knobs.h b/flow/Knobs.h index a3bdd1572f..ae8d58ef6a 100644 --- a/flow/Knobs.h +++ b/flow/Knobs.h @@ -175,6 +175,7 @@ public: int TRACE_EVENT_THROTTLER_MSG_LIMIT; int MAX_TRACE_FIELD_LENGTH; int MAX_TRACE_EVENT_LENGTH; + bool ALLOCATION_TRACING_ENABLED; //TDMetrics int64_t MAX_METRIC_SIZE; diff --git a/flow/Trace.cpp b/flow/Trace.cpp index 9f6a77bc1f..cdab1315ae 100644 --- a/flow/Trace.cpp +++ b/flow/Trace.cpp @@ -43,7 +43,15 @@ #undef min #endif -thread_local bool g_tracing_allocation = false; +// Allocations can only be logged when this value is 0. +// Anybody that needs to disable tracing should increment this by 1 for the duration +// that they need the disabling to be in effect. +// +// This is done for multiple reasons: +// 1. To avoid recursion in the allocation tracing when each trace event does an allocation +// 2. To avoid a historically documented but unknown crash that occurs when logging allocations +// during an open trace event +thread_local int g_allocation_tracing_disabled = 1; class DummyThreadPool : public IThreadPool, ReferenceCounted { public: @@ -745,6 +753,9 @@ bool TraceEvent::init() { if(initialized) { return enabled; } + + ++g_allocation_tracing_disabled; + initialized = true; ASSERT(*type != '\0'); @@ -790,6 +801,7 @@ bool TraceEvent::init() { tmpEventMetric = nullptr; } + --g_allocation_tracing_disabled; return enabled; } @@ -819,6 +831,7 @@ TraceEvent& TraceEvent::errorImpl(class Error const& error, bool includeCancelle TraceEvent& TraceEvent::detailImpl( std::string&& key, std::string&& value, bool writeEventMetricField) { init(); if (enabled) { + ++g_allocation_tracing_disabled; if( maxFieldLength >= 0 && value.size() > maxFieldLength ) { value = value.substr(0, maxFieldLength) + "..."; } @@ -833,20 +846,27 @@ TraceEvent& TraceEvent::detailImpl( std::string&& key, std::string&& value, bool TraceEvent(g_network && g_network->isSimulated() ? SevError : SevWarnAlways, "TraceEventOverflow").setMaxEventLength(1000).detail("TraceFirstBytes", fields.toString().substr(300)); enabled = false; } + --g_allocation_tracing_disabled; } return *this; } void TraceEvent::setField(const char* key, int64_t value) { + ++g_allocation_tracing_disabled; tmpEventMetric->setField(key, value); + --g_allocation_tracing_disabled; } void TraceEvent::setField(const char* key, double value) { + ++g_allocation_tracing_disabled; tmpEventMetric->setField(key, value); + --g_allocation_tracing_disabled; } void TraceEvent::setField(const char* key, const std::string& value) { + ++g_allocation_tracing_disabled; tmpEventMetric->setField(key, Standalone(value)); + --g_allocation_tracing_disabled; } TraceEvent& TraceEvent::detailf( std::string key, const char* valueFormat, ... ) { @@ -977,6 +997,7 @@ TraceEvent& TraceEvent::backtrace(const std::string& prefix) { void TraceEvent::log() { if(!logged) { init(); + ++g_allocation_tracing_disabled; try { if (enabled) { fields.mutate(timeIndex).second = format("%.6f", TraceEvent::getCurrentTime()); @@ -1011,6 +1032,7 @@ void TraceEvent::log() { } delete tmpEventMetric; logged = true; + --g_allocation_tracing_disabled; } } @@ -1021,6 +1043,10 @@ TraceEvent::~TraceEvent() { thread_local bool TraceEvent::networkThread = false; void TraceEvent::setNetworkThread() { + if(FLOW_KNOBS->ALLOCATION_TRACING_ENABLED) { + --g_allocation_tracing_disabled; + } + traceEventThrottlerCache = new TransientThresholdMetricSample>(FLOW_KNOBS->TRACE_EVENT_METRIC_UNITS_PER_SAMPLE, FLOW_KNOBS->TRACE_EVENT_THROTTLER_MSG_LIMIT); networkThread = true; } diff --git a/flow/Trace.h b/flow/Trace.h index 5d7bb242d1..baabcceded 100644 --- a/flow/Trace.h +++ b/flow/Trace.h @@ -43,7 +43,7 @@ inline int fastrand() { //inline static bool TRACE_SAMPLE() { return fastrand()<16; } inline static bool TRACE_SAMPLE() { return false; } -extern thread_local bool g_tracing_allocation; +extern thread_local int g_allocation_tracing_disabled; enum Severity { SevSample=1, From 7769218303395524b71d15c922af284ddb856af6 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Mon, 16 Mar 2020 14:11:07 -0700 Subject: [PATCH 0932/1604] Move an increment after an ASSERT. --- flow/Trace.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flow/Trace.cpp b/flow/Trace.cpp index cdab1315ae..9b5974af3c 100644 --- a/flow/Trace.cpp +++ b/flow/Trace.cpp @@ -754,11 +754,11 @@ bool TraceEvent::init() { return enabled; } + initialized = true; + ASSERT(*type != '\0'); + ++g_allocation_tracing_disabled; - initialized = true; - - ASSERT(*type != '\0'); enabled = enabled && ( !g_network || severity >= FLOW_KNOBS->MIN_TRACE_SEVERITY ); // Backstop to throttle very spammy trace events From 1513df22f3e873d5ae383cd7fae12e87b95181ea Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Mon, 16 Mar 2020 10:20:49 -0700 Subject: [PATCH 0933/1604] AutoQuorumChange:Exclude unreliable node from coordinator in simulation --- fdbclient/ManagementAPI.actor.cpp | 5 +++++ fdbserver/workloads/RemoveServersSafely.actor.cpp | 2 -- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/fdbclient/ManagementAPI.actor.cpp b/fdbclient/ManagementAPI.actor.cpp index 37c6bd27b2..6e6fcbf18d 100644 --- a/fdbclient/ManagementAPI.actor.cpp +++ b/fdbclient/ManagementAPI.actor.cpp @@ -1206,6 +1206,11 @@ struct AutoQuorumChange : IQuorumChange { if(addressExcluded(excluded, worker->address)) { continue; } + // Exclude faulty node due to machine assassination + if (g_network->isSimulated() && g_simulator.protectedAddresses.count(worker->address) && !g_simulator.getProcessByAddress(worker->address)->isReliable()) { + TraceEvent("AutoSelectCoordinators").detail("SkipUnreliableWorker", worker->address.toString()); + continue; + } bool valid = true; for(auto field = fields.begin(); field != fields.end(); field++) { if(maxCounts[*field] == 0) { diff --git a/fdbserver/workloads/RemoveServersSafely.actor.cpp b/fdbserver/workloads/RemoveServersSafely.actor.cpp index 3f2e2842ff..4359b3e97f 100644 --- a/fdbserver/workloads/RemoveServersSafely.actor.cpp +++ b/fdbserver/workloads/RemoveServersSafely.actor.cpp @@ -500,8 +500,6 @@ struct RemoveServersSafelyWorkload : TestWorkload { TraceEvent("RemoveAndKill", functionId).detail("Step", "coordinators auto").detail("DesiredCoordinators", g_simulator.desiredCoordinators).detail("ClusterAvailable", g_simulator.isAvailable()); - // Ensure coordinators do not use faulty node - // Setup the coordinators BEFORE the exclusion // Otherwise, we may end up with NotEnoughMachinesForCoordinators state int cycle=0; From 7f559bc7121af2c15ada5fdc3098ee60184be011 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Mon, 16 Mar 2020 15:07:20 -0700 Subject: [PATCH 0934/1604] Cleanup code and apply clang-format Self code review --- fdbclient/ManagementAPI.actor.cpp | 26 ++- fdbrpc/FlowTransport.h | 4 - fdbrpc/sim2.actor.cpp | 1 - .../workloads/ConsistencyCheck.actor.cpp | 176 ++++-------------- .../workloads/RemoveServersSafely.actor.cpp | 6 - 5 files changed, 44 insertions(+), 169 deletions(-) diff --git a/fdbclient/ManagementAPI.actor.cpp b/fdbclient/ManagementAPI.actor.cpp index 6e6fcbf18d..77f2cc7a86 100644 --- a/fdbclient/ManagementAPI.actor.cpp +++ b/fdbclient/ManagementAPI.actor.cpp @@ -1152,18 +1152,12 @@ struct AutoQuorumChange : IQuorumChange { } chosen.resize((chosen.size() - 1) | 1); } - // Sanity check if chosen coordinators will be injected fault - if (g_network->isSimulated()) { - for (auto& addr : chosen) { - ISimulator::ProcessInfo* p = g_simulator.getProcessByAddress(addr); - TraceEvent("MXGetDesiredCoordinator").detail("Address", addr.toString()).detail("Reliable", p->isReliable()).detail("Protected", g_simulator.protectedAddresses.count(addr)).detail("ReliableInfo", p->getReliableInfo()); - } - } return chosen; } - // Select a desired set of workers such that (1) the number of workers at each locality type (e.g., dcid) <= desiredCount; and + // Select a desired set of workers such that + // (1) the number of workers at each locality type (e.g., dcid) <= desiredCount; and // (2) prefer workers at a locality where less workers has been chosen than other localities: evenly distribute workers. void addDesiredWorkers(vector& chosen, const vector& workers, int desiredCount, const std::set& excluded) { vector remainingWorkers(workers); @@ -1171,13 +1165,16 @@ struct AutoQuorumChange : IQuorumChange { std::partition(remainingWorkers.begin(), remainingWorkers.end(), [](const ProcessData& data) { return (data.processClass == ProcessClass::CoordinatorClass); }); - TraceEvent("AutoSelectCoordinators").detail("CandidateWorkers", remainingWorkers.size()); + TraceEvent(SevDebug, "AutoSelectCoordinators").detail("CandidateWorkers", remainingWorkers.size()); for (auto worker = remainingWorkers.begin(); worker != remainingWorkers.end(); worker++) { - TraceEvent("SelectCoordinators").detail("Worker", worker->processClass.toString()).detail("Address", worker->address.toString()).detail("Locality", worker->locality.toString()); + TraceEvent(SevDebug, "AutoSelectCoordinators") + .detail("Worker", worker->processClass.toString()) + .detail("Address", worker->address.toString()) + .detail("Locality", worker->locality.toString()); } - TraceEvent("AutoSelectCoordinators").detail("ExcludedAddress", excluded.size()); - for(auto& excludedAddr : excluded) { - TraceEvent("AutoSelectCoordinators").detail("ExcludedAddress", excludedAddr.toString()); + TraceEvent(SevDebug, "AutoSelectCoordinators").detail("ExcludedAddress", excluded.size()); + for (auto& excludedAddr : excluded) { + TraceEvent(SevDebug, "AutoSelectCoordinators").detail("ExcludedAddress", excludedAddr.toString()); } std::map maxCounts; @@ -1207,7 +1204,8 @@ struct AutoQuorumChange : IQuorumChange { continue; } // Exclude faulty node due to machine assassination - if (g_network->isSimulated() && g_simulator.protectedAddresses.count(worker->address) && !g_simulator.getProcessByAddress(worker->address)->isReliable()) { + if (g_network->isSimulated() && g_simulator.protectedAddresses.count(worker->address) && + !g_simulator.getProcessByAddress(worker->address)->isReliable()) { TraceEvent("AutoSelectCoordinators").detail("SkipUnreliableWorker", worker->address.toString()); continue; } diff --git a/fdbrpc/FlowTransport.h b/fdbrpc/FlowTransport.h index 1f76cf7337..f99554fdc2 100644 --- a/fdbrpc/FlowTransport.h +++ b/fdbrpc/FlowTransport.h @@ -45,10 +45,6 @@ public: void choosePrimaryAddress() { if(addresses.secondaryAddress.present() && !g_network->getLocalAddresses().secondaryAddress.present() && (addresses.address.isTLS() != g_network->getLocalAddresses().address.isTLS())) { - // if (addresses.address.isTLS()) { - // TraceEvent(SevWarn, "MXDEBUGChoosePrimaryAddressSwap").detail("PrimaryAddressWillBeTLS", - // addresses.secondaryAddress.get().isTLS()).backtrace(); - // } std::swap(addresses.address, addresses.secondaryAddress.get()); } } diff --git a/fdbrpc/sim2.actor.cpp b/fdbrpc/sim2.actor.cpp index ab1eeec11a..111fe1290e 100644 --- a/fdbrpc/sim2.actor.cpp +++ b/fdbrpc/sim2.actor.cpp @@ -1585,7 +1585,6 @@ public: NetworkAddress normalizedAddress(address.ip, address.port, true, address.isTLS()); ASSERT( addressMap.count( normalizedAddress ) ); // NOTE: addressMap[normalizedAddress]->address may not equal to normalizedAddress - // ASSERT_WE_THINK( addressMap[normalizedAddress]->address == normalizedAddress ); return addressMap[normalizedAddress]; } diff --git a/fdbserver/workloads/ConsistencyCheck.actor.cpp b/fdbserver/workloads/ConsistencyCheck.actor.cpp index eadbf66f71..e767b2bbb7 100644 --- a/fdbserver/workloads/ConsistencyCheck.actor.cpp +++ b/fdbserver/workloads/ConsistencyCheck.actor.cpp @@ -295,7 +295,6 @@ struct ConsistencyCheckWorkload : TestWorkload } wait(::success(self->checkForStorage(cx, configuration, self))); - // wait(::success(self->waitForUnreliableExtraStoreReboot(cx, self))); wait(::success(self->checkForExtraDataStores(cx, self))); //Check that each machine is operating as its desired class @@ -1171,108 +1170,6 @@ struct ConsistencyCheckWorkload : TestWorkload return true; } - ACTOR Future waitForUnreliableExtraStoreReboot(Database cx, ConsistencyCheckWorkload* self) { - state int waitCount = 0; - loop { - state std::vector workers = wait(getWorkers(self->dbInfo)); - state std::vector storageServers = wait(getStorageServers(cx)); - state std::vector coordWorkers = wait(getCoordWorkers(cx, self->dbInfo)); - auto& db = self->dbInfo->get(); - state std::vector logs = db.logSystemConfig.allPresentLogs(); - - state std::vector::iterator itr; - state bool foundExtraDataStore = false; - state std::vector protectedProcessesToKill; - - state std::map> statefulProcesses; - for (const auto& ss : storageServers) { - statefulProcesses[ss.address()].insert(ss.id()); - // Add both addresses so that we will not mistakenly trigger ConsistencyCheck_ExtraDataStore - if (ss.secondaryAddress().present()) { - statefulProcesses[ss.secondaryAddress().get()].insert(ss.id()); - } - TraceEvent(SevCCheckInfo, "StatefulProcess") - .detail("StorageServer", ss.id()) - .detail("PrimaryAddress", ss.address().toString()) - .detail("SecondaryAddress", - ss.secondaryAddress().present() ? ss.secondaryAddress().get().toString() : "Unset"); - } - for (const auto& log : logs) { - statefulProcesses[log.address()].insert(log.id()); - if (log.secondaryAddress().present()) { - statefulProcesses[log.secondaryAddress().get()].insert(log.id()); - } - TraceEvent(SevCCheckInfo, "StatefulProcess") - .detail("Log", log.id()) - .detail("PrimaryAddress", log.address().toString()) - .detail("SecondaryAddress", - log.secondaryAddress().present() ? log.secondaryAddress().get().toString() : "Unset"); - } - // Coordinators are also stateful processes - for (const auto& cWorker : coordWorkers) { - statefulProcesses[cWorker.address()].insert(cWorker.id()); - if (cWorker.secondaryAddress().present()) { - statefulProcesses[cWorker.secondaryAddress().get()].insert(cWorker.id()); - } - TraceEvent(SevCCheckInfo, "StatefulProcess") - .detail("Coordinator", cWorker.id()) - .detail("PrimaryAddress", cWorker.address().toString()) - .detail("SecondaryAddress", cWorker.secondaryAddress().present() - ? cWorker.secondaryAddress().get().toString() - : "Unset"); - } - - // Wait for extra store process that is unreliable (i.e., in the process of rebooting) to finish; Otherwise, - // the test will try to kill the extra store process which may be protected. This causes failure. - state bool protectedExtraStoreUnreliable = false; - - for (itr = workers.begin(); itr != workers.end(); ++itr) { - ErrorOr>> stores = - wait(itr->interf.diskStoreRequest.getReplyUnlessFailedFor(DiskStoreRequest(false), 2, 0)); - if (stores.isError()) { - TraceEvent("ConsistencyCheck_GetDataStoreFailure") - .error(stores.getError()) - .detail("Address", itr->interf.address()); - self->testFailure("Failed to get data stores"); - return false; - } - - TraceEvent(SevCCheckInfo, "CheckProtectedExtraStoreRebootProgress") - .detail("Worker", itr->interf.id().toString()) - .detail("PrimaryAddress", itr->interf.address().toString()) - .detail("SecondaryAddress", itr->interf.secondaryAddress().present() - ? itr->interf.secondaryAddress().get().toString() - : "Unset"); - for (const auto& id : stores.get()) { - if (statefulProcesses[itr->interf.address()].count(id)) { - continue; - } else { - if (g_network->isSimulated()) { - auto p = g_simulator.getProcessByAddress(itr->interf.address()); - if (g_simulator.protectedAddresses.count(p->address) && !p->isReliable()) { - protectedExtraStoreUnreliable = true; - break; - } - } - } - } - if (protectedExtraStoreUnreliable) { - break; - } - } - if (protectedExtraStoreUnreliable) { - wait(delay(10.0)); - waitCount++; - } - if (waitCount > 20) { - TraceEvent(SevError, "ProtectedExtraStoreUnreliableStuck") - .detail("ExpectedBehavior", "Extra store should be cleaned up after process reboot"); - break; - } - } - return waitCount <= 20; - } - ACTOR Future checkForExtraDataStores(Database cx, ConsistencyCheckWorkload *self) { state std::vector workers = wait(getWorkers(self->dbInfo)); state std::vector storageServers = wait(getStorageServers(cx)); @@ -1287,7 +1184,7 @@ struct ConsistencyCheckWorkload : TestWorkload state std::map> statefulProcesses; for (const auto& ss : storageServers) { statefulProcesses[ss.address()].insert(ss.id()); - // Add both addresses so that we will not mistakenly trigger ConsistencyCheck_ExtraDataStore + // A process may have two addresses (same ip, different ports) if (ss.secondaryAddress().present()) { statefulProcesses[ss.secondaryAddress().get()].insert(ss.id()); } @@ -1336,50 +1233,41 @@ struct ConsistencyCheckWorkload : TestWorkload ? itr->interf.secondaryAddress().get().toString() : "Unset"); for (const auto& id : stores.get()) { - // if (statefulProcesses[itr->interf.address()].count(id)) { - // continue; - // } - if(!statefulProcesses[itr->interf.address()].count(id)) { - TraceEvent("ConsistencyCheck_ExtraDataStore").detail("Address", itr->interf.address()).detail("DataStoreID", id); - if(g_network->isSimulated()) { - //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* p2 = nullptr; - if (itr->interf.secondaryAddress().present()) { - p2 = g_simulator.getProcessByAddress(itr->interf.secondaryAddress().get()); - } - // 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") - .detail("Address", - itr->interf.address()) // worker's primary address (i.e., the first address) - .detail("ProcessPrimaryAddress", p->address) - .detail("ProcessAddresses", p->addresses.toString()) - .detail("ProcessAtPrimaryAddressIsReliable", p->isReliable()) - .detail("ProcessAtSecondaryAddressIsReliable", p2 != nullptr ? (p2->isReliable() ? "True" : "False") : "unset") - .detail("DataStoreID", id) - .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); - } else { - g_simulator.killProcess(p, ISimulator::KillInstantly); - } - } - - foundExtraDataStore = true; + if (statefulProcesses[itr->interf.address()].count(id)) { + continue; } + // For extra data store + TraceEvent("ConsistencyCheck_ExtraDataStore") + .detail("Address", itr->interf.address()) + .detail("DataStoreID", id); + if (g_network->isSimulated()) { + // 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()); + // 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") + .detail("Address", + itr->interf.address()) // worker's primary address (i.e., the first address) + .detail("ProcessPrimaryAddress", p->address) + .detail("ProcessAddresses", p->addresses.toString()) + .detail("DataStoreID", id) + .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); + } else { + g_simulator.killProcess(p, ISimulator::KillInstantly); + } + } + + foundExtraDataStore = true; } } - // kill or reboot protected process - // for () { - - // } - if(foundExtraDataStore) { self->testFailure("Extra data stores present on workers"); return false; diff --git a/fdbserver/workloads/RemoveServersSafely.actor.cpp b/fdbserver/workloads/RemoveServersSafely.actor.cpp index 4359b3e97f..945c9ff676 100644 --- a/fdbserver/workloads/RemoveServersSafely.actor.cpp +++ b/fdbserver/workloads/RemoveServersSafely.actor.cpp @@ -113,14 +113,12 @@ struct RemoveServersSafelyWorkload : TestWorkload { toKill2.insert(processSet.begin(), processSet.end()); } - // std::vector disableAddrs1; for( AddressExclusion ex : toKill1 ) { AddressExclusion machineIp(ex.ip); ASSERT(machine_ids.count(machineIp)); g_simulator.disableSwapToMachine(machine_ids[machineIp]); } - // std::vector disableAddrs2; for( AddressExclusion ex : toKill2 ) { AddressExclusion machineIp(ex.ip); ASSERT(machine_ids.count(machineIp)); @@ -311,7 +309,6 @@ struct RemoveServersSafelyWorkload : TestWorkload { TraceEvent("RemoveAndKill").detail("Step", "include all first").detail("KillTotal", toKill1.size()).detail("ToKill", describe(toKill1)).detail("ClusterAvailable", g_simulator.isAvailable()); wait( includeServers( cx, vector(1) ) ); self->includeAddresses(toKill1); - //TraceEvent("RemoveAndKill").detail("Step", "included all first").detail("KillTotal", toKill1.size()).detail("ToKill", describe(toKill1)).detail("ClusterAvailable", g_simulator.isAvailable()); } // Get the list of protected servers @@ -337,7 +334,6 @@ struct RemoveServersSafelyWorkload : TestWorkload { TraceEvent("RemoveAndKill").detail("Step", "include all second").detail("KillTotal", toKill2.size()).detail("ToKill", describe(toKill2)).detail("ClusterAvailable", g_simulator.isAvailable()); wait( includeServers( cx, vector(1) ) ); self->includeAddresses(toKill2); - //TraceEvent("RemoveAndKill").detail("Step", "included all second").detail("KillTotal", toKill2.size()).detail("ToKill", describe(toKill2)).detail("ClusterAvailable", g_simulator.isAvailable()); } return Void(); @@ -495,8 +491,6 @@ struct RemoveServersSafelyWorkload : TestWorkload { // Wait for removal to be safe TraceEvent("RemoveAndKill", functionId).detail("Step", "Wait For Server Exclusion").detail("Addresses", describe(toKill)).detail("ClusterAvailable", g_simulator.isAvailable()); wait(success(checkForExcludingServers(cx, toKillArray, true /* wait for exclusion */))); - // TODO: We have to wait for faulty machine to die or reborn; otherwise, - // machine that is in failing due to injected fault may be chosen as coordinator. TraceEvent("RemoveAndKill", functionId).detail("Step", "coordinators auto").detail("DesiredCoordinators", g_simulator.desiredCoordinators).detail("ClusterAvailable", g_simulator.isAvailable()); From 96187618a05c72f222de5d6ce9cd749e2cf92bce Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Mon, 16 Mar 2020 15:12:50 -0700 Subject: [PATCH 0935/1604] Fix condition to check whether allocation tracing is enabled --- flow/Arena.cpp | 2 +- flow/FastAlloc.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/flow/Arena.cpp b/flow/Arena.cpp index e362b72164..5e635640df 100644 --- a/flow/Arena.cpp +++ b/flow/Arena.cpp @@ -184,7 +184,7 @@ ArenaBlock* ArenaBlock::create(int dataSize, Reference& next) { b->bigSize = reqSize; b->bigUsed = sizeof(ArenaBlock); - if (FLOW_KNOBS && g_allocation_tracing_disabled > 0 && + if (FLOW_KNOBS && g_allocation_tracing_disabled == 0 && nondeterministicRandom()->random01() < (reqSize / FLOW_KNOBS->HUGE_ARENA_LOGGING_BYTES)) { ++g_allocation_tracing_disabled; hugeArenaSample(reqSize); diff --git a/flow/FastAlloc.cpp b/flow/FastAlloc.cpp index 06adaf383d..a05c91cc79 100644 --- a/flow/FastAlloc.cpp +++ b/flow/FastAlloc.cpp @@ -445,7 +445,7 @@ void FastAllocator::getMagazine() { // FIXME: We should be able to allocate larger magazine sizes here if we // detect that the underlying system supports hugepages. Using hugepages // with smaller-than-2MiB magazine sizes strands memory. See issue #909. - if(FLOW_KNOBS && g_allocation_tracing_disabled > 0 && nondeterministicRandom()->random01() < (magazine_size * Size)/FLOW_KNOBS->FAST_ALLOC_LOGGING_BYTES) { + if(FLOW_KNOBS && g_allocation_tracing_disabled == 0 && nondeterministicRandom()->random01() < (magazine_size * Size)/FLOW_KNOBS->FAST_ALLOC_LOGGING_BYTES) { ++g_allocation_tracing_disabled; TraceEvent("GetMagazineSample").detail("Size", Size).backtrace(); --g_allocation_tracing_disabled; From f1523bd47229f2361ea5bdc94755c7c5dbd33293 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Mon, 16 Mar 2020 15:37:06 -0700 Subject: [PATCH 0936/1604] Setting the network thread more than once is a no-op --- flow/Trace.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/flow/Trace.cpp b/flow/Trace.cpp index 9b5974af3c..de93602da8 100644 --- a/flow/Trace.cpp +++ b/flow/Trace.cpp @@ -1043,12 +1043,14 @@ TraceEvent::~TraceEvent() { thread_local bool TraceEvent::networkThread = false; void TraceEvent::setNetworkThread() { - if(FLOW_KNOBS->ALLOCATION_TRACING_ENABLED) { - --g_allocation_tracing_disabled; - } + if(!networkThread) { + if(FLOW_KNOBS->ALLOCATION_TRACING_ENABLED) { + --g_allocation_tracing_disabled; + } - traceEventThrottlerCache = new TransientThresholdMetricSample>(FLOW_KNOBS->TRACE_EVENT_METRIC_UNITS_PER_SAMPLE, FLOW_KNOBS->TRACE_EVENT_THROTTLER_MSG_LIMIT); - networkThread = true; + traceEventThrottlerCache = new TransientThresholdMetricSample>(FLOW_KNOBS->TRACE_EVENT_METRIC_UNITS_PER_SAMPLE, FLOW_KNOBS->TRACE_EVENT_THROTTLER_MSG_LIMIT); + networkThread = true; + } } bool TraceEvent::isNetworkThread() { From 7edeaa3da528be266913b33f2ddea25572b4945f Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Mon, 16 Mar 2020 17:22:06 -0700 Subject: [PATCH 0937/1604] updated release notes for 6.2.19 --- documentation/sphinx/source/downloads.rst | 24 +++++++++---------- documentation/sphinx/source/release-notes.rst | 24 ++++++++++++++++++- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/documentation/sphinx/source/downloads.rst b/documentation/sphinx/source/downloads.rst index c9084089ab..9802afd4e5 100644 --- a/documentation/sphinx/source/downloads.rst +++ b/documentation/sphinx/source/downloads.rst @@ -10,38 +10,38 @@ macOS The macOS installation package is supported on macOS 10.7+. It includes the client and (optionally) the server. -* `FoundationDB-6.2.18.pkg `_ +* `FoundationDB-6.2.19.pkg `_ Ubuntu ------ The Ubuntu packages are supported on 64-bit Ubuntu 12.04+, but beware of the Linux kernel bug in Ubuntu 12.x. -* `foundationdb-clients-6.2.18-1_amd64.deb `_ -* `foundationdb-server-6.2.18-1_amd64.deb `_ (depends on the clients package) +* `foundationdb-clients-6.2.19-1_amd64.deb `_ +* `foundationdb-server-6.2.19-1_amd64.deb `_ (depends on the clients package) RHEL/CentOS EL6 --------------- The RHEL/CentOS EL6 packages are supported on 64-bit RHEL/CentOS 6.x. -* `foundationdb-clients-6.2.18-1.el6.x86_64.rpm `_ -* `foundationdb-server-6.2.18-1.el6.x86_64.rpm `_ (depends on the clients package) +* `foundationdb-clients-6.2.19-1.el6.x86_64.rpm `_ +* `foundationdb-server-6.2.19-1.el6.x86_64.rpm `_ (depends on the clients package) RHEL/CentOS EL7 --------------- The RHEL/CentOS EL7 packages are supported on 64-bit RHEL/CentOS 7.x. -* `foundationdb-clients-6.2.18-1.el7.x86_64.rpm `_ -* `foundationdb-server-6.2.18-1.el7.x86_64.rpm `_ (depends on the clients package) +* `foundationdb-clients-6.2.19-1.el7.x86_64.rpm `_ +* `foundationdb-server-6.2.19-1.el7.x86_64.rpm `_ (depends on the clients package) Windows ------- The Windows installer is supported on 64-bit Windows XP and later. It includes the client and (optionally) the server. -* `foundationdb-6.2.18-x64.msi `_ +* `foundationdb-6.2.19-x64.msi `_ API Language Bindings ===================== @@ -58,18 +58,18 @@ On macOS and Windows, the FoundationDB Python API bindings are installed as part If you need to use the FoundationDB Python API from other Python installations or paths, download the Python package: -* `foundationdb-6.2.18.tar.gz `_ +* `foundationdb-6.2.19.tar.gz `_ Ruby 1.9.3/2.0.0+ ----------------- -* `fdb-6.2.18.gem `_ +* `fdb-6.2.19.gem `_ Java 8+ ------- -* `fdb-java-6.2.18.jar `_ -* `fdb-java-6.2.18-javadoc.jar `_ +* `fdb-java-6.2.19.jar `_ +* `fdb-java-6.2.19-javadoc.jar `_ Go 1.11+ -------- diff --git a/documentation/sphinx/source/release-notes.rst b/documentation/sphinx/source/release-notes.rst index 3a479bbe76..ec1724507a 100644 --- a/documentation/sphinx/source/release-notes.rst +++ b/documentation/sphinx/source/release-notes.rst @@ -2,6 +2,28 @@ Release Notes ############# +6.2.19 +====== + +Fixes +----- + +* Protect the proxies from running out of memory when bombarded with requests from clients. `(PR #2812) `_. +* One process with a ``proxy`` class would not become the first proxy when put with other ``stateless`` class processes. `(PR #2819) `_. +* If a transaction log stalled on a disk operation during recruitment the cluster would become unavailable until the process died. `(PR #2815) `_. +* Avoid recruiting satellite transaction logs when usable_regions=1. `(PR #2813) `_. +* Prevent the cluster from having too many active generations as a safety measure against repeated failures. `(PR #2814) `_. +* ``fdbcli`` status JSON could become truncated because of unprintable characters. `(PR #2807) `_. +* The data distributor used too much CPU in large clusters (broken in 6.2.16). `(PR #2806) `_. + +Status +------ + +* Added ``cluster.workload.operations.memory_errors`` to measure the number of requests rejected by the proxies because the memory limit has been exceeded. `(PR #2812) `_. +* Added ``cluster.workload.operations.location_requests`` to measure the number of outgoing key server location responses from the proxies. `(PR #2812) `_. +* Added ``cluster.recovery_state.active_generations`` to track the number of generations for which the cluster still requires transaction logs. `(PR #2814) `_. +* Added ``network.tls_policy_failures`` to the ``processes`` section to record the number of TLS policy failures each process has observed. `(PR #2811) `_. + 6.2.18 ====== @@ -22,7 +44,7 @@ Performance Features -------- -* Add support for setting knobs to modify the behavior of fdbcli. `(PR #2773) `_. +* Add support for setting knobs to modify the behavior of ``fdbcli``. `(PR #2773) `_. Other Changes ------------- From 8e96e5a525d012fe0ddd4f9bfd84a830022be9c1 Mon Sep 17 00:00:00 2001 From: Alex Miller <35046903+alexmiller-apple@users.noreply.github.com> Date: Mon, 16 Mar 2020 18:25:56 -0700 Subject: [PATCH 0938/1604] Expand comment about ignoring malformed escape sequences --- fdbcli/fdbcli.actor.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fdbcli/fdbcli.actor.cpp b/fdbcli/fdbcli.actor.cpp index df4aebb46f..24f58dad44 100644 --- a/fdbcli/fdbcli.actor.cpp +++ b/fdbcli/fdbcli.actor.cpp @@ -3670,7 +3670,10 @@ ACTOR Future runCli(CLIOptions opt) { StringRef command = parsed.back().front(); int finishedParameters = parsed.back().size() + error; - // We don't want the hint to flip to parse error and back, e.g. while \" is being typed. + // As a user is typing an escaped character, e.g. \", after the \ and before the " is typed + // the string will be a parse error. Ignore this parse error to avoid flipping the hint to + // {malformed escape sequence} and back to the original hint for the span of one character + // being entered. if (error && line.back() != '\\') return LineNoise::Hint(std::string(" {malformed escape sequence}"), 90, false); auto iter = helpMap.find(command.toString()); From 04052226df072ee5114785015e08a319c5d2c8f0 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Tue, 17 Mar 2020 09:41:44 -0700 Subject: [PATCH 0939/1604] reverting a change which causes data inconsistency between the primary and secondary --- fdbserver/TagPartitionedLogSystem.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/TagPartitionedLogSystem.actor.cpp b/fdbserver/TagPartitionedLogSystem.actor.cpp index f3e084a542..625784a22e 100644 --- a/fdbserver/TagPartitionedLogSystem.actor.cpp +++ b/fdbserver/TagPartitionedLogSystem.actor.cpp @@ -1997,7 +1997,7 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCountedrecruitmentID = logSystem->recruitmentID; if(configuration.usableRegions > 1) { - logSystem->logRouterTags = std::max(recr.satelliteTLogs.size(), recr.tLogs.size()) * std::max(1, configuration.desiredLogRouterCount / std::max(1,std::max(recr.satelliteTLogs.size(), recr.tLogs.size()))); + logSystem->logRouterTags = recr.tLogs.size() * std::max(1, configuration.desiredLogRouterCount / std::max(1,recr.tLogs.size())); logSystem->expectedLogSets++; logSystem->addPseudoLocality(tagLocalityLogRouterMapped); } From 9dd7df564eb200de3b59540176b2b1051bf21e8c Mon Sep 17 00:00:00 2001 From: Evan Tschannen <36455792+etschannen@users.noreply.github.com> Date: Tue, 17 Mar 2020 09:42:15 -0700 Subject: [PATCH 0940/1604] Update documentation/sphinx/source/release-notes.rst Co-Authored-By: A.J. Beamon --- documentation/sphinx/source/release-notes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/sphinx/source/release-notes.rst b/documentation/sphinx/source/release-notes.rst index ec1724507a..1ac02528a2 100644 --- a/documentation/sphinx/source/release-notes.rst +++ b/documentation/sphinx/source/release-notes.rst @@ -11,7 +11,7 @@ Fixes * Protect the proxies from running out of memory when bombarded with requests from clients. `(PR #2812) `_. * One process with a ``proxy`` class would not become the first proxy when put with other ``stateless`` class processes. `(PR #2819) `_. * If a transaction log stalled on a disk operation during recruitment the cluster would become unavailable until the process died. `(PR #2815) `_. -* Avoid recruiting satellite transaction logs when usable_regions=1. `(PR #2813) `_. +* Avoid recruiting satellite transaction logs when ``usable_regions=1``. `(PR #2813) `_. * Prevent the cluster from having too many active generations as a safety measure against repeated failures. `(PR #2814) `_. * ``fdbcli`` status JSON could become truncated because of unprintable characters. `(PR #2807) `_. * The data distributor used too much CPU in large clusters (broken in 6.2.16). `(PR #2806) `_. From f92c7dbd6483f3faeb20662cc1754b023ce1ef31 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Tue, 17 Mar 2020 09:46:39 -0700 Subject: [PATCH 0941/1604] added another release note --- documentation/sphinx/source/release-notes.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/documentation/sphinx/source/release-notes.rst b/documentation/sphinx/source/release-notes.rst index 1ac02528a2..a958bc91d7 100644 --- a/documentation/sphinx/source/release-notes.rst +++ b/documentation/sphinx/source/release-notes.rst @@ -24,6 +24,11 @@ Status * Added ``cluster.recovery_state.active_generations`` to track the number of generations for which the cluster still requires transaction logs. `(PR #2814) `_. * Added ``network.tls_policy_failures`` to the ``processes`` section to record the number of TLS policy failures each process has observed. `(PR #2811) `_. +Features +-------- + +* Added ``--debug-tls`` as a command line argument to ``fdbcli`` to help diagnose TLS issues. `(PR #2810) `_. + 6.2.18 ====== From 31a9f0a26c0d1420bfd873ade053e3721a3c793d Mon Sep 17 00:00:00 2001 From: Xin Dong Date: Tue, 17 Mar 2020 11:03:46 -0700 Subject: [PATCH 0942/1604] Fix the segfault --- flow/Trace.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/Trace.cpp b/flow/Trace.cpp index c769baca4d..dcd4a8e107 100644 --- a/flow/Trace.cpp +++ b/flow/Trace.cpp @@ -544,8 +544,8 @@ public: Future pingWriterThread() { auto ping = new WriterThread::Ping; - writer->post(ping); auto f = ping->ack.getFuture(); + writer->post(ping); return f; } From 330e78b06af2a3b0c6718abc5f7a9f4e31055865 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Tue, 17 Mar 2020 11:47:51 -0700 Subject: [PATCH 0943/1604] update version to 6.2.20 --- CMakeLists.txt | 2 +- versions.target | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 622ebb02d5..fe9c560dfb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ # limitations under the License. cmake_minimum_required(VERSION 3.12) project(foundationdb - VERSION 6.2.19 + VERSION 6.2.20 DESCRIPTION "FoundationDB is a scalable, fault-tolerant, ordered key-value store with full ACID transactions." HOMEPAGE_URL "http://www.foundationdb.org/" LANGUAGES C CXX ASM) diff --git a/versions.target b/versions.target index 495ee9d5cb..43ddf5c1f7 100644 --- a/versions.target +++ b/versions.target @@ -1,7 +1,7 @@ - 6.2.19 + 6.2.20 6.2 From 29f16630f5f4c5a6fb2d22eb93aa64050811a834 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Tue, 17 Mar 2020 11:47:51 -0700 Subject: [PATCH 0944/1604] update installer WIX GUID following release --- packaging/msi/FDBInstaller.wxs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/msi/FDBInstaller.wxs b/packaging/msi/FDBInstaller.wxs index 990cb2896b..a9bfc62418 100644 --- a/packaging/msi/FDBInstaller.wxs +++ b/packaging/msi/FDBInstaller.wxs @@ -32,7 +32,7 @@ Date: Tue, 17 Mar 2020 13:41:14 -0700 Subject: [PATCH 0945/1604] Delete unnecessary parameters --- fdbserver/workloads/Mako.actor.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/fdbserver/workloads/Mako.actor.cpp b/fdbserver/workloads/Mako.actor.cpp index b85769b2e8..60ab7eaec2 100644 --- a/fdbserver/workloads/Mako.actor.cpp +++ b/fdbserver/workloads/Mako.actor.cpp @@ -11,8 +11,6 @@ enum {OP_GETREADVERSION, OP_GET, OP_GETRANGE, OP_SGET, OP_SGETRANGE, OP_UPDATE, OP_INSERT, OP_INSERTRANGE, OP_CLEAR, OP_SETCLEAR, OP_CLEARRANGE, OP_SETCLEARRANGE, OP_COMMIT, MAX_OP}; enum {OP_COUNT, OP_RANGE}; -constexpr int MAXKEYVALUESIZE = 1000; -constexpr int RANGELIMIT = 10000; struct MakoWorkload : TestWorkload { uint64_t rowCount, seqNumLen, sampleSize, actorCountPerClient, keyBytes, maxValueBytes, minValueBytes, csSize, csCount, csPartitionSize, csStepSizeInPartition; double testDuration, loadTime, warmingDelay, maxInsertRate, transactionsPerSecond, allowedLatency, periodicLoggingInterval, zipfConstant; @@ -79,8 +77,6 @@ struct MakoWorkload : TestWorkload { seqNumLen = digits(rowCount); // check keyBytes, maxValueBytes is valid ASSERT(seqNumLen + KEYPREFIXLEN <= keyBytes); - ASSERT(keyBytes <= MAXKEYVALUESIZE); - ASSERT(maxValueBytes <= MAXKEYVALUESIZE); // user input: a sequence of operations to be executed; e.g. "g10i5" means to do GET 10 times and Insert 5 times // One operation type is defined as "" or ":". // When Count is omitted, it's equivalent to setting it to 1. (e.g. "g" is equivalent to "g1") @@ -382,7 +378,7 @@ struct MakoWorkload : TestWorkload { if (i == OP_COMMIT) continue; for (count = 0; count < self->operations[i][0]; ++count) { - range = std::min(RANGELIMIT, self->operations[i][1]); + range = self->operations[i][1]; rangeLen = digits(range); // generate random key-val pair for operation indBegin = self->getRandomKeyIndex(self->rowCount); @@ -409,13 +405,13 @@ struct MakoWorkload : TestWorkload { else if (i == OP_GET){ wait(logLatency(tr.get(rkey, false), &self->opLatencies[i])); } else if (i == OP_GETRANGE){ - wait(logLatency(tr.getRange(rkeyRangeRef, RANGELIMIT, false), &self->opLatencies[i])); + wait(logLatency(tr.getRange(rkeyRangeRef, CLIENT_KNOBS->TOO_MANY, false), &self->opLatencies[i])); } else if (i == OP_SGET){ wait(logLatency(tr.get(rkey, true), &self->opLatencies[i])); } else if (i == OP_SGETRANGE){ //do snapshot get range here - wait(logLatency(tr.getRange(rkeyRangeRef, RANGELIMIT, true), &self->opLatencies[i])); + wait(logLatency(tr.getRange(rkeyRangeRef, CLIENT_KNOBS->TOO_MANY, true), &self->opLatencies[i])); } else if (i == OP_UPDATE){ wait(logLatency(tr.get(rkey, false), &self->opLatencies[OP_GET])); tr.set(rkey, rval); @@ -632,8 +628,6 @@ struct MakoWorkload : TestWorkload { ptr++; } /* set range */ - if (num > RANGELIMIT) - TraceEvent(SevError, "TestFailure").detail("Reason", "RangeExceedLimit").detail("RangeLimit", RANGELIMIT).detail("Range", num); operations[op][OP_RANGE] = num; } } From 5ad9807c2190a78f755547326d0e958f139e8ca4 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Tue, 17 Mar 2020 17:21:52 -0400 Subject: [PATCH 0946/1604] Small fixes --- contrib/commit_debug.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/contrib/commit_debug.py b/contrib/commit_debug.py index db45e220d5..7f6de3ff91 100755 --- a/contrib/commit_debug.py +++ b/contrib/commit_debug.py @@ -54,16 +54,15 @@ class CommitDebugHandler(xml.sax.ContentHandler, object): self._f.write(json.dumps(d) + ', ') def startElement(self, name, attrs): - if self._starttime is None: - self._starttime = float(attrs['Time']) - # I've flipped from using Async spans to Duration spans, because # I kept on running into issues with trace viewer believeing there # is no start or end of an emitted span even when there actually is. if name == "Event" and attrs.get('Type') == "CommitDebug": + if self._starttime is None: + self._starttime = float(attrs['Time']) + attr_id = attrs['ID'] - trace_id = self._idmap.setdefault(attr_id, attr_id) # Trace viewer doesn't seem to care about types, so use host as pid and port as tid (pid, tid) = attrs['Machine'].split(':') traces = locationToPhase[attrs["Location"]] From 747434a13de743c922ec18ad4b46955edcb2bcf7 Mon Sep 17 00:00:00 2001 From: Balachandar Namasivayam Date: Tue, 17 Mar 2020 14:36:07 -0700 Subject: [PATCH 0947/1604] Increate QuietDatabase time to 90 seconds for real world cases. --- fdbserver/QuietDatabase.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/QuietDatabase.actor.cpp b/fdbserver/QuietDatabase.actor.cpp index 737bd25131..fa8d9d8fa5 100644 --- a/fdbserver/QuietDatabase.actor.cpp +++ b/fdbserver/QuietDatabase.actor.cpp @@ -578,7 +578,7 @@ ACTOR Future waitForQuietDatabase( Database cx, ReferenceisSimulated() ? 2.0 : 30.0)); } } } catch (Error& e) { From 572f08e5fc9386d141d78e20ef59ccf069bc2c57 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Tue, 17 Mar 2020 22:21:31 +0000 Subject: [PATCH 0948/1604] Add option to set transaction as debug --- fdbclient/NativeAPI.actor.cpp | 5 +++++ fdbclient/vexillographer/fdb.options | 2 ++ 2 files changed, 7 insertions(+) diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index 273eb0d993..08df523eaf 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -3005,6 +3005,11 @@ void Transaction::setOption( FDBTransactionOptions::Option option, OptionalrandomUniqueID()); + break; + case FDBTransactionOptions::MAX_RETRY_DELAY: validateOptionValue(value, true); options.maxBackoff = extractIntOption(value, 0, std::numeric_limits::max()) / 1000.0; diff --git a/fdbclient/vexillographer/fdb.options b/fdbclient/vexillographer/fdb.options index 5be79357e8..545f11e9d1 100644 --- a/fdbclient/vexillographer/fdb.options +++ b/fdbclient/vexillographer/fdb.options @@ -223,6 +223,8 @@ description is not currently required but encouraged. description="Enables tracing for this transaction and logs results to the client trace logs. The DEBUG_TRANSACTION_IDENTIFIER option must be set before using this option, and client trace logging must be enabled to get log output." />
View on GitHub

" - -.PHONY: default help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext buildsphinx publish uptodate - -default: html - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " livehtml to launch a local webserver that auto-updates as changes are made" - @echo " publish to build the html and push it to GitHub pages" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " xml to make Docutils-native XML files" - @echo " pseudoxml to make pseudoxml-XML files for display purposes" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - @echo " buildsphinx to install sphinx binary in virtualenv" - -buildsphinx: - if [ ! -e $(SPHINXBUILD) ]; then \ - mkdir $(BUILDDIR); \ - cd $(BUILDDIR); \ - python3 -m venv venv; \ - fi - . $(VENVDIR)/bin/activate && \ - cp .pip.conf $(VENVDIR)/pip.conf && \ - pip install --upgrade pip && \ - pip install --upgrade -r $(ROOTDIR)/requirements.txt; - -clean: - rm -rf $(BUILDDIR) - -cleanhtml: - rm -rf $(BUILDDIR)/html - -cleanvirtualenv: - rm -rf $(VENVDIR) - -html: buildsphinx cleanhtml - $(SPHINXBUILD) -W -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -check: checkwarnings linkcheck - -checkwarnings: buildsphinx - $(SPHINXBUILD) -n -W -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo "Check finished." - -livehtml: html - $(SPHINXAUTOBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - -# removed html prerequisite because it is previously explictly invoked -package: - mkdir -p $(DISTDIR) - rm -f $(DISTDIR)/$(PROJECT_NAME)-$(VERSION).tar.gz - cd $(BUILDDIR)/html && tar czf $(DISTDIR)/$(PROJECT_NAME)-$(VERSION).tar.gz . diff --git a/fdbbackup/fdbbackup.vcxproj b/fdbbackup/fdbbackup.vcxproj deleted file mode 100644 index d701282076..0000000000 --- a/fdbbackup/fdbbackup.vcxproj +++ /dev/null @@ -1,137 +0,0 @@ - - - - - -PRERELEASE - - - - - FDB_CLEAN_BUILD;%(PreprocessorDefinitions) - - - - Debug - X64 - - - Release - X64 - - - - - - - {8E959DA5-5925-45CE-BFC4-C84EB632A29B} - v4.5 - Win32Proj - flow - - - $(SolutionDir)bin\$(Configuration)\ - $(SystemDrive)\temp\msvcfdb\$(Platform)$(Configuration)\$(MSBuildProjectName)\ - $(IntDir)\$(MSBuildProjectName).log - - - - Application - MultiByte - v141 - - - Application - MultiByte - v141 - - - - - - - - - - true - $(IncludePath);../;C:\Program Files\boost_1_72_0 - - - false - $(IncludePath);../;C:\Program Files\boost_1_72_0 - PreBuildEvent - - - - $(TargetDir)fdbclient.lib - - - FDB_VT_VERSION="$(Version)$(PreReleaseDecoration)";FDB_VT_PACKAGE_NAME="$(PackageName)";%(PreprocessorDefinitions) - stdcpp17 - - - - - - - Level3 - false - ProgramDatabase - Disabled - EnableFastChecks - MultiThreadedDebug - TLS_DISABLED;WIN32;_WIN32_WINNT=0x0502;WINVER=0x0502;BOOST_ALL_NO_LIB;NTDDI_VERSION=0x05020000;_DEBUG;_HAS_ITERATOR_DEBUGGING=0;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - true - /bigobj @../flow/no_intellisense.opt %(AdditionalOptions) - stdcpp17 - - - Console - true - $(SolutionDir)bin\$(Configuration)\fdbclient.lib;Advapi32.lib - - - - - Level3 - - - ProgramDatabase - Full - MultiThreaded - true - TLS_DISABLED;WIN32;_WIN32_WINNT=0x0502;WINVER=0x0502;BOOST_ALL_NO_LIB;NTDDI_VERSION=0x05020000;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - NotSet - false - /bigobj @../flow/no_intellisense.opt %(AdditionalOptions) - true - Speed - false - false - stdcpp17 - - - Console - true - false - false - $(SolutionDir)bin\$(Configuration)\fdbclient.lib;Advapi32.lib - /LTCG %(AdditionalOptions) - - - - - - - - - - - - - - - - - diff --git a/fdbbackup/local.mk b/fdbbackup/local.mk deleted file mode 100644 index 1c717db8c0..0000000000 --- a/fdbbackup/local.mk +++ /dev/null @@ -1,58 +0,0 @@ -# -# local.mk -# -# This source file is part of the FoundationDB open source project -# -# Copyright 2013-2018 Apple Inc. and the FoundationDB project authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# -*- mode: makefile; -*- - -fdbbackup_CFLAGS := $(fdbclient_CFLAGS) -fdbbackup_LDFLAGS := $(fdbrpc_LDFLAGS) -fdbbackup_LIBS := lib/libfdbclient.a lib/libfdbrpc.a lib/libflow.a $(FDB_TLS_LIB) -fdbbackup_STATIC_LIBS := $(TLS_LIBS) - -ifeq ($(PLATFORM),linux) - fdbbackup_LDFLAGS += -static-libstdc++ -static-libgcc -ldl -lpthread -lrt - - # GPerfTools profiler (uncomment to use) - # fdbbackup_CFLAGS += -I/opt/gperftools/include -DUSE_GPERFTOOLS=1 -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-free - # fdbbackup_LDFLAGS += -L/opt/gperftools/lib - # fdbbackup_STATIC_LIBS += -ltcmalloc -lunwind -lprofiler -else ifeq ($(PLATFORM),osx) - fdbbackup_LDFLAGS += -lc++ -endif - -fdbbackup_GENERATED_SOURCES += versions.h - -#ifeq ($(WORKLOADS),false) -# fdbbackup_ALL_SOURCES := $(filter-out fdbbackup/workloads/%,$(fdbbackup_ALL_SOURCES)) -# fdbbackup_BUILD_SOURCES := $(filter-out fdbbackup/workloads/%,$(fdbbackup_BUILD_SOURCES)) -#endif - -bin/fdbbackup: bin/coverage.fdbbackup.xml - -bin/fdbbackup.debug: bin/fdbbackup - -BACKUP_ALIASES = fdbrestore fdbdr dr_agent backup_agent - -$(addprefix bin/, $(BACKUP_ALIASES)): bin/fdbbackup - @[ -f $@ ] || (echo "SymLinking $@" && ln -s fdbbackup $@) - -$(addprefix bin/, $(addsuffix .debug, $(BACKUP_ALIASES))): bin/fdbbackup.debug - @[ -f $@ ] || (echo "SymLinking $@" && ln -s fdbbackup.debug $@) - -FORCE: diff --git a/fdbcli/fdbcli.vcxproj b/fdbcli/fdbcli.vcxproj deleted file mode 100644 index 7299dfcdec..0000000000 --- a/fdbcli/fdbcli.vcxproj +++ /dev/null @@ -1,137 +0,0 @@ - - - - - -PRERELEASE - - - - - - - - Debug - x64 - - - Release - x64 - - - - - - - - - - - - - - - {4631CC93-52A3-4537-9BE9-6B237A3AC6B2} - Win32Proj - fdbcli - - - - Application - true - MultiByte - v141 - - - Application - false - false - MultiByte - v141 - - - - - - - - - - - - - true - $(SolutionDir)bin\$(Configuration)\ - $(SystemDrive)\temp\msvcfdb\$(Platform)$(Configuration)\$(MSBuildProjectName)\ - $(IncludePath);../;C:\Program Files\boost_1_72_0 - - - false - $(SolutionDir)bin\$(Configuration)\ - $(SystemDrive)\temp\msvcfdb\$(Platform)$(Configuration)\$(MSBuildProjectName)\ - $(IncludePath);../;C:\Program Files\boost_1_72_0 - - - - FDB_VT_VERSION="$(Version)$(PreReleaseDecoration)";FDB_VT_PACKAGE_NAME="$(PackageName)";%(PreprocessorDefinitions) - stdcpp17 - - - - - - - Level3 - Disabled - TLS_DISABLED;WIN32;_WIN32_WINNT=0x0502;WINVER=0x0502;BOOST_ALL_NO_LIB;NTDDI_VERSION=0x05020000;_DEBUG;_HAS_ITERATOR_DEBUGGING=0;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - ..\zookeeper\win32;..\zookeeper\generated;..\zookeeper\include;%(AdditionalIncludeDirectories) - true - /bigobj @../flow/no_intellisense.opt %(AdditionalOptions) - false - MultiThreadedDebug - stdcpp17 - - - Console - true - $(SolutionDir)bin\$(Configuration)\fdbclient.lib;Advapi32.lib - - - - - - - - - Level3 - - - Full - true - TLS_DISABLED;WIN32;_WIN32_WINNT=0x0502;WINVER=0x0502;BOOST_ALL_NO_LIB;NTDDI_VERSION=0x05020000;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - ..\zookeeper\win32;..\zookeeper\generated;..\zookeeper\include;%(AdditionalIncludeDirectories) - true - /bigobj @../flow/no_intellisense.opt %(AdditionalOptions) - Speed - MultiThreaded - false - StreamingSIMDExtensions2 - stdcpp17 - - - Console - true - false - false - $(SolutionDir)bin\$(Configuration)\fdbclient.lib;Advapi32.lib - Default - - - - - - - - - - - diff --git a/fdbcli/fdbcli.vcxproj.filters b/fdbcli/fdbcli.vcxproj.filters deleted file mode 100644 index e4363c462f..0000000000 --- a/fdbcli/fdbcli.vcxproj.filters +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/fdbcli/local.mk b/fdbcli/local.mk deleted file mode 100644 index 3af026b911..0000000000 --- a/fdbcli/local.mk +++ /dev/null @@ -1,39 +0,0 @@ -# -# local.mk -# -# This source file is part of the FoundationDB open source project -# -# Copyright 2013-2018 Apple Inc. and the FoundationDB project authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# -*- mode: makefile; -*- - -fdbcli_CFLAGS := $(fdbclient_CFLAGS) -fdbcli_LDFLAGS := $(fdbrpc_LDFLAGS) -fdbcli_LIBS := lib/libfdbclient.a lib/libfdbrpc.a lib/libflow.a $(FDB_TLS_LIB) -fdbcli_STATIC_LIBS := $(TLS_LIBS) - -fdbcli_GENERATED_SOURCES += versions.h - -ifeq ($(PLATFORM),linux) - fdbcli_LDFLAGS += -static-libstdc++ -static-libgcc -lpthread -lrt -ldl -else ifeq ($(PLATFORM),osx) - fdbcli_LDFLAGS += -lc++ -endif - -test_fdbcli_status: fdbcli - python scripts/test_status.py - -bin/fdbcli.debug: bin/fdbcli diff --git a/fdbclient/fdbclient.vcxproj b/fdbclient/fdbclient.vcxproj deleted file mode 100644 index a312c158c7..0000000000 --- a/fdbclient/fdbclient.vcxproj +++ /dev/null @@ -1,246 +0,0 @@ - - - - - -PRERELEASE - - - - - FDB_CLEAN_BUILD;%(PreprocessorDefinitions) - - - - Debug - X64 - - - Release - X64 - - - - - false - false - - - - - false - false - - - - - - - - - - - false - false - - - - - - - - - - - - - - - - - - - - - - - false - false - - - - - - - - - false - false - - - - - - - - - - - - - - - false - - - - - - false - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {E2939DAA-238E-4970-96C4-4C57980F93BD} - v4.5.2 - Win32Proj - flow - - - $(SolutionDir)bin\$(Configuration)\ - $(SystemDrive)\temp\msvcfdb\$(Platform)$(Configuration)\$(MSBuildProjectName)\ - $(IntDir)\$(MSBuildProjectName).log - - - - StaticLibrary - MultiByte - v141 - - - StaticLibrary - MultiByte - v141 - - - - - - - - - - true - $(IncludePath);../;C:\Program Files\boost_1_72_0 - - - false - $(IncludePath);../;C:\Program Files\boost_1_72_0 - - - - FDB_VT_VERSION="$(Version)$(PreReleaseDecoration)";FDB_VT_PACKAGE_NAME="$(PackageName)";%(PreprocessorDefinitions) - stdcpp17 - - - - - - - Level3 - false - ProgramDatabase - Disabled - EnableFastChecks - MultiThreadedDebug - TLS_DISABLED;WIN32;_WIN32_WINNT=0x0502;WINVER=0x0502;BOOST_ALL_NO_LIB;NTDDI_VERSION=0x05020000;_DEBUG;_HAS_ITERATOR_DEBUGGING=0;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - true - /bigobj @../flow/no_intellisense.opt %(AdditionalOptions) - stdcpp17 - - - Console - true - Advapi32.lib - - - $(TargetDir)flow.lib;$(TargetDir)fdbrpc.lib;winmm.lib - - - - - Level3 - - - ProgramDatabase - Full - MultiThreaded - true - TLS_DISABLED;WIN32;_WIN32_WINNT=0x0502;WINVER=0x0502;BOOST_ALL_NO_LIB;NTDDI_VERSION=0x05020000;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - NotSet - false - /bigobj @../flow/no_intellisense.opt %(AdditionalOptions) - true - Speed - false - stdcpp17 - - - Console - true - false - false - Default - Advapi32.lib - /LTCG %(AdditionalOptions) - - - $(TargetDir)flow.lib;$(TargetDir)fdbrpc.lib;winmm.lib - - - - - - - - - - diff --git a/fdbclient/local.mk b/fdbclient/local.mk deleted file mode 100644 index f3631dbee9..0000000000 --- a/fdbclient/local.mk +++ /dev/null @@ -1,32 +0,0 @@ -# -# local.mk -# -# This source file is part of the FoundationDB open source project -# -# Copyright 2013-2018 Apple Inc. and the FoundationDB project authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# -*- mode: makefile; -*- - -fdbclient_CFLAGS := $(fdbrpc_CFLAGS) - -fdbclient_GENERATED_SOURCES += fdbclient/FDBOptions.g.h - -fdbclient/FDBOptions.g.cpp: fdbclient/FDBOptions.g.h -fdbclient/FDBOptions.g.h: bin/vexillographer.exe fdbclient/vexillographer/fdb.options fdbclient/FDBOptions.h - @echo "Building $@" - @$(MONO) bin/vexillographer.exe fdbclient/vexillographer/fdb.options cpp fdbclient/FDBOptions.g - -lib/libfdbclient.a: bin/coverage.fdbclient.xml diff --git a/fdbclient/vexillographer/local.mk b/fdbclient/vexillographer/local.mk deleted file mode 100644 index cfa6372725..0000000000 --- a/fdbclient/vexillographer/local.mk +++ /dev/null @@ -1,20 +0,0 @@ -# -# local.mk -# -# This source file is part of the FoundationDB open source project -# -# Copyright 2013-2018 Apple Inc. and the FoundationDB project authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - diff --git a/fdbmonitor/fdbmonitor.vcxproj b/fdbmonitor/fdbmonitor.vcxproj deleted file mode 100644 index 41097a6d73..0000000000 --- a/fdbmonitor/fdbmonitor.vcxproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - x64 - - - Release - x64 - - - - {9A1D17A1-1B56-44D8-90C8-56F1726C1C4C} - fdbmonitor - - - - Application - true - MultiByte - v141 - - - Application - false - true - MultiByte - v141 - - - - - - - - - - - - - $(SystemDrive)\temp\msvcfdb\$(Platform)$(Configuration)\$(MSBuildProjectName)\ - - - $(SystemDrive)\temp\msvcfdb\$(Platform)$(Configuration)\$(MSBuildProjectName)\ - - - - Level3 - Disabled - stdcpp17 - - - true - - - - - Level3 - MaxSpeed - true - true - stdcpp17 - - - true - true - true - - - - - - - - - - - - - diff --git a/fdbmonitor/local.mk b/fdbmonitor/local.mk deleted file mode 100644 index 255a15c3db..0000000000 --- a/fdbmonitor/local.mk +++ /dev/null @@ -1,32 +0,0 @@ -# -# local.mk -# -# This source file is part of the FoundationDB open source project -# -# Copyright 2013-2018 Apple Inc. and the FoundationDB project authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# -*- mode: makefile; -*- - -# SimpleOpt.h lives here :-/ -fdbmonitor_CFLAGS := -I. - -ifeq ($(PLATFORM),linux) - fdbmonitor_LDFLAGS := -static-libstdc++ -static-libgcc -pthread -lrt -else ifeq ($(PLATFORM),osx) - fdbmonitor_LDFLAGS := -lc++ -endif - -bin/fdbmonitor.debug: bin/fdbmonitor diff --git a/fdbrpc/fdbrpc.vcxproj b/fdbrpc/fdbrpc.vcxproj deleted file mode 100644 index a4622b04f3..0000000000 --- a/fdbrpc/fdbrpc.vcxproj +++ /dev/null @@ -1,229 +0,0 @@ - - - - - Debug - X64 - - - Release - X64 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - false - - - false - - - false - - - false - - - false - - - false - - - - - - false - - - false - - - - - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {00AC9086-0377-4871-9991-DF267CF12ACA} - v4.5.2 - Win32Proj - fdbrpc - - - $(SolutionDir)bin\$(Configuration)\ - $(SystemDrive)\temp\msvcfdb\$(Platform)$(Configuration)\$(MSBuildProjectName)\ - $(IntDir)\$(MSBuildProjectName).log - - - - StaticLibrary - MultiByte - v141 - - - StaticLibrary - MultiByte - v141 - - - - - - - - - - true - $(IncludePath);../;C:\Program Files\boost_1_72_0 - - - false - $(IncludePath);../;C:\Program Files\boost_1_72_0 - - - - echo const char *sourceVersion = "Current version id not currently supported within Windows."; > SourceVersion.temp.h && fc /b SourceVersion.temp.h SourceVersion.h > nul || copy SourceVersion.temp.h SourceVersion.h > nul - Checking source version - fake.out - - - - - - - Level3 - false - ProgramDatabase - Disabled - EnableFastChecks - MultiThreadedDebug - TLS_DISABLED;WIN32;_WIN32_WINNT=0x0502;WINVER=0x0502;BOOST_ALL_NO_LIB;NTDDI_VERSION=0x05020000;_DEBUG;_HAS_ITERATOR_DEBUGGING=0;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - true - /bigobj @../flow/no_intellisense.opt %(AdditionalOptions) - stdcpp17 - - - Console - true - Advapi32.lib - - - - - - - - - Level3 - - - ProgramDatabase - Full - MultiThreaded - true - TLS_DISABLED;WIN32;_WIN32_WINNT=0x0502;WINVER=0x0502;BOOST_ALL_NO_LIB;NTDDI_VERSION=0x05020000;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;FDB_CLEAN_BUILD;%(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - NotSet - false - /bigobj @../flow/no_intellisense.opt %(AdditionalOptions) - true - Speed - false - stdcpp17 - - - Console - true - false - false - Default - Advapi32.lib - /LTCG %(AdditionalOptions) - - - - - - - - - - - - - - diff --git a/fdbrpc/fdbrpc.vcxproj.filters b/fdbrpc/fdbrpc.vcxproj.filters deleted file mode 100644 index 0c84599d09..0000000000 --- a/fdbrpc/fdbrpc.vcxproj.filters +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - libcoroutine - - - libcoroutine - - - - zlib - - - zlib - - - zlib - - - zlib - - - zlib - - - zlib - - - zlib - - - zlib - - - zlib - - - zlib - - - zlib - - - zlib - - - zlib - - - - - - - - - - - - - - - - - libcoroutine - - - libcoroutine - - - libcoroutine - - - - zlib - - - zlib - - - zlib - - - zlib - - - zlib - - - zlib - - - zlib - - - zlib - - - zlib - - - zlib - - - zlib - - - - - - - - - - - - - - - - - - - - - - - - - - - {c6db8910-449e-4436-8a6c-9e76b3e0ca1d} - - - {b79fbb2a-5d80-4135-b363-f6de83e62e73} - - - diff --git a/fdbrpc/local.mk b/fdbrpc/local.mk deleted file mode 100644 index fd3636aedd..0000000000 --- a/fdbrpc/local.mk +++ /dev/null @@ -1,34 +0,0 @@ -# -# local.mk -# -# This source file is part of the FoundationDB open source project -# -# Copyright 2013-2018 Apple Inc. and the FoundationDB project authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# -*- mode: makefile; -*- - -fdbrpc_BUILD_SOURCES += fdbrpc/libeio/eio.c - -fdbrpc_CFLAGS := -isystem$(BOOSTDIR) -I. -Ifdbrpc/libeio -DUSE_UCONTEXT -fdbrpc_LDFLAGS := - -ifeq ($(PLATFORM),osx) - fdbrpc_CFLAGS += -fasynchronous-unwind-tables -fno-omit-frame-pointer - fdbrpc_BUILD_SOURCES += fdbrpc/libcoroutine/asm.S fdbrpc/libcoroutine/context.c - fdbrpc_LDFLAGS += -framework CoreFoundation -framework IOKit -endif - -lib/libfdbrpc.a: bin/coverage.fdbrpc.xml diff --git a/fdbserver/fdbserver.vcxproj b/fdbserver/fdbserver.vcxproj deleted file mode 100644 index 3b6d686170..0000000000 --- a/fdbserver/fdbserver.vcxproj +++ /dev/null @@ -1,379 +0,0 @@ - - - - - -PRERELEASE - - - - - FDB_CLEAN_BUILD;%(PreprocessorDefinitions) - - - - Debug - X64 - - - Release - X64 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - false - - - - - - - - false - - - - - false - - - - - - - - - - - - - - false - false - - - - false - - - - - false - - - - - - - - false - - - false - - - false - - - false - - - false - - - false - - - - - - - - - - - - - - - - false - false - - - - false - - - - - - false - false - - - - {8E959DA5-5925-45CE-BFC4-C84EB632A29A} - v4.5.2 - Win32Proj - flow - - - $(SolutionDir)bin\$(Configuration)\ - $(SystemDrive)\temp\msvcfdb\$(Platform)$(Configuration)\$(MSBuildProjectName)\ - $(IntDir)\$(MSBuildProjectName).log - - - - Application - MultiByte - v141 - - - Application - MultiByte - v141 - - - - - - - - - - true - $(IncludePath);../;C:\Program Files\boost_1_72_0 - - - false - $(IncludePath);../;C:\Program Files\boost_1_72_0 - PreBuildEvent - - - - $(TargetDir)fdbclient.lib - - - FDB_VT_VERSION="$(Version)$(PreReleaseDecoration)";FDB_VT_PACKAGE_NAME="$(PackageName)";%(PreprocessorDefinitions) - stdcpp17 - - - - - - - Level3 - false - ProgramDatabase - Disabled - EnableFastChecks - MultiThreadedDebug - TLS_DISABLED;WIN32;_WIN32_WINNT=0x0502;WINVER=0x0502;BOOST_ALL_NO_LIB;NTDDI_VERSION=0x05020000;_DEBUG;_HAS_ITERATOR_DEBUGGING=0;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - true - /bigobj @../flow/no_intellisense.opt %(AdditionalOptions) - false - stdcpp17 - - - Console - true - $(SolutionDir)bin\$(Configuration)\fdbclient.lib;Advapi32.lib - - - - - Level3 - - - ProgramDatabase - Full - MultiThreaded - true - TLS_DISABLED;WIN32;_WIN32_WINNT=0x0502;WINVER=0x0502;BOOST_ALL_NO_LIB;NTDDI_VERSION=0x05020000;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - NotSet - false - /bigobj @../flow/no_intellisense.opt %(AdditionalOptions) - true - Speed - false - false - stdcpp17 - - - Console - true - false - false - $(SolutionDir)bin\$(Configuration)\fdbclient.lib;Advapi32.lib - /LTCG %(AdditionalOptions) - - - - - - - - - - - - diff --git a/fdbserver/fdbserver.vcxproj.filters b/fdbserver/fdbserver.vcxproj.filters deleted file mode 100644 index e6cd19be27..0000000000 --- a/fdbserver/fdbserver.vcxproj.filters +++ /dev/null @@ -1,406 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - - workloads - - - workloads - - - workloads - - - workloads - - - - - workloads - - - workloads - - - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - - workloads - - - workloads - - - workloads - - - - - - - - workloads - - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - - - - - workloads - - - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - workloads - - - - - - - workloads - - - sqlite - - - sqlite - - - - workloads - - - - workloads - - - - - - - - - - - - - - - - - - workloads - - - - - - - sqlite - - - sqlite - - - sqlite - - - sqlite - - - sqlite - - - sqlite - - - - - - - - - - - - - - - workloads - - - workloads - - - - - - - - - - - - - - - - - {6a79fc02-2f89-451d-9dd5-999d753b3159} - - - {de5e282f-8d97-4054-b795-0a75b772326f} - - - diff --git a/fdbserver/local.mk b/fdbserver/local.mk deleted file mode 100644 index 1e794d2fac..0000000000 --- a/fdbserver/local.mk +++ /dev/null @@ -1,52 +0,0 @@ -# -# local.mk -# -# This source file is part of the FoundationDB open source project -# -# Copyright 2013-2018 Apple Inc. and the FoundationDB project authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# -*- mode: makefile; -*- - -fdbserver_CFLAGS := $(fdbclient_CFLAGS) -fdbserver_LDFLAGS := $(fdbrpc_LDFLAGS) -fdbserver_LIBS := lib/libfdbclient.a lib/libfdbrpc.a lib/libflow.a $(FDB_TLS_LIB) -fdbserver_STATIC_LIBS := $(TLS_LIBS) - -ifeq ($(PLATFORM),linux) - fdbserver_LDFLAGS += -ldl -lpthread -lrt -static-libstdc++ -static-libgcc - - # GPerfTools profiler (uncomment to use) - # fdbserver_CFLAGS += -I/opt/gperftools/include -DUSE_GPERFTOOLS=1 -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-free - # fdbserver_LDFLAGS += -L/opt/gperftools/lib - # fdbserver_STATIC_LIBS += -ltcmalloc -lunwind -lprofiler -else ifeq ($(PLATFORM),osx) - fdbserver_LDFLAGS += -lc++ -endif - -ifeq ($(WORKLOADS),false) - fdbserver_ALL_SOURCES := $(filter-out fdbserver/workloads/%,$(fdbserver_ALL_SOURCES)) - fdbserver_BUILD_SOURCES := $(filter-out fdbserver/workloads/%,$(fdbserver_BUILD_SOURCES)) -endif - -bin/fdbserver: bin/coverage.fdbserver.xml - -bin/fdbserver.debug: bin/fdbserver - -FORCE: - -createtemplatedb: bin/fdbserver - bin/fdbserver -r createtemplatedb - python -c 'import textwrap; s=open("template.fdb", "rb").read().encode("hex").upper(); t="".join(["\\x"+x+y for (x,y) in zip(s[0::2], s[1::2])]) ; open("fdbserver/template_fdb.h","wb").write("static const char template_fdb[] = \\\n\t\"%s\";"%"\" \\\n\t\"".join(textwrap.wrap(t,80)))' diff --git a/fdbservice/fdbservice.vcxproj b/fdbservice/fdbservice.vcxproj deleted file mode 100644 index 18dc8084e8..0000000000 --- a/fdbservice/fdbservice.vcxproj +++ /dev/null @@ -1,99 +0,0 @@ - - - - - -PRERELEASE - - - - - FDB_CLEAN_BUILD;%(PreprocessorDefinitions) - - - - Debug - x64 - - - Release - x64 - - - - {0C938D09-39F6-48C0-9C6A-45D3ADBDCFB7} - fdbservice - - - - Application - true - MultiByte - v141 - - - Application - false - true - MultiByte - v141 - - - - - - - - - - $(SolutionDir)bin\$(Configuration)\ - $(SystemDrive)\temp\msvcfdb\$(Platform)$(Configuration)\$(MSBuildProjectName)\ - fdbmonitor - - - - FDB_VT_VERSION="$(Version)$(PreReleaseDecoration)";%(PreprocessorDefinitions) - stdcpp17 - - - - - Level3 - Disabled - MultiThreaded - _WIN32_WINNT=0x0502;WINVER=0x0502;BOOST_ALL_NO_LIB;NTDDI_VERSION=0x05020000;_MBCS;%(PreprocessorDefinitions) - stdcpp17 - - - true - Console - - - - - Level3 - MaxSpeed - true - true - MultiThreaded - _WIN32_WINNT=0x0502;WINVER=0x0502;BOOST_ALL_NO_LIB;NTDDI_VERSION=0x05020000;_MBCS;%(PreprocessorDefinitions) - stdcpp17 - - - true - true - true - Console - - - - - - - - - - - - - - diff --git a/fdbservice/fdbservice.vcxproj.filters b/fdbservice/fdbservice.vcxproj.filters deleted file mode 100644 index 9ed9b8c46d..0000000000 --- a/fdbservice/fdbservice.vcxproj.filters +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/flow/actorcompiler/local.mk b/flow/actorcompiler/local.mk deleted file mode 100644 index cfa6372725..0000000000 --- a/flow/actorcompiler/local.mk +++ /dev/null @@ -1,20 +0,0 @@ -# -# local.mk -# -# This source file is part of the FoundationDB open source project -# -# Copyright 2013-2018 Apple Inc. and the FoundationDB project authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - diff --git a/flow/coveragetool/local.mk b/flow/coveragetool/local.mk deleted file mode 100644 index cfa6372725..0000000000 --- a/flow/coveragetool/local.mk +++ /dev/null @@ -1,20 +0,0 @@ -# -# local.mk -# -# This source file is part of the FoundationDB open source project -# -# Copyright 2013-2018 Apple Inc. and the FoundationDB project authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - diff --git a/flow/flow.vcxproj b/flow/flow.vcxproj deleted file mode 100644 index 4afeb4076c..0000000000 --- a/flow/flow.vcxproj +++ /dev/null @@ -1,222 +0,0 @@ - - - - - Debug - X64 - - - Release - X64 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - false - false - - - false - - - - false - - - - - - - - - - - - - - - - - - false - - - - - false - - - false - - - - - - - {00AC9087-0378-4872-9992-DF267CF12ACB} - v4.5.2 - Win32Proj - flow - - - $(SolutionDir)bin\$(Configuration)\ - $(SystemDrive)\temp\msvcfdb\$(Platform)$(Configuration)\$(MSBuildProjectName)\ - $(IntDir)\$(MSBuildProjectName).log - - - - StaticLibrary - MultiByte - v141 - - - StaticLibrary - MultiByte - v141 - - - - - - - - - - true - $(IncludePath);../;C:\Program Files\boost_1_72_0 - - - false - $(IncludePath);../;C:\Program Files\boost_1_72_0 - - - - echo const char *sourceVersion = "Current version id not currently supported within Windows."; > SourceVersion.temp.h && fc /b SourceVersion.temp.h SourceVersion.h > nul || copy SourceVersion.temp.h SourceVersion.h > nul - Checking source version - - - - - - - Level3 - false - ProgramDatabase - Disabled - EnableFastChecks - MultiThreadedDebug - TLS_DISABLED;WIN32;_WIN32_WINNT=0x0502;WINVER=0x0502;BOOST_ALL_NO_LIB;BOOST_ERROR_CODE_HEADER_ONLY;BOOST_SYSTEM_NO_DEPRECATED;NTDDI_VERSION=0x05020000;_DEBUG;_HAS_ITERATOR_DEBUGGING=0;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - true - /bigobj @../flow/no_intellisense.opt %(AdditionalOptions) - stdcpp17 - - - Console - true - Advapi32.lib - - - psapi.lib - - - - - Level3 - - - ProgramDatabase - Full - MultiThreaded - true - TLS_DISABLED;WIN32;_WIN32_WINNT=0x0502;WINVER=0x0502;BOOST_ALL_NO_LIB;BOOST_ERROR_CODE_HEADER_ONLY;BOOST_SYSTEM_NO_DEPRECATED;NTDDI_VERSION=0x05020000;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;FDB_CLEAN_BUILD;%(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - NotSet - false - /bigobj @../flow/no_intellisense.opt %(AdditionalOptions) - true - Speed - false - stdcpp17 - - - Console - true - false - false - Default - Advapi32.lib - /LTCG %(AdditionalOptions) - - - psapi.lib - - - - - - - - - - diff --git a/flow/flow.vcxproj.filters b/flow/flow.vcxproj.filters deleted file mode 100644 index 53a3f2947b..0000000000 --- a/flow/flow.vcxproj.filters +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/flow/local.mk b/flow/local.mk deleted file mode 100644 index 6c6d0d69bb..0000000000 --- a/flow/local.mk +++ /dev/null @@ -1,63 +0,0 @@ -# -# local.mk -# -# This source file is part of the FoundationDB open source project -# -# Copyright 2013-2018 Apple Inc. and the FoundationDB project authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# -*- mode: makefile; -*- - -flow_CFLAGS := -isystem$(BOOSTDIR) -I. -DUSE_UCONTEXT -flow_LDFLAGS := - -ifeq ($(PLATFORM),osx) - flow_CFLAGS += -fasynchronous-unwind-tables -fno-omit-frame-pointer - flow_LDFLAGS += -framework CoreFoundation -framework IOKit -endif - -flow_GENERATED_SOURCES += flow/SourceVersion.h versions.h - -flow/SourceVersion.h: FORCE - @echo "Checking SourceVersion.h" - @echo "const char *sourceVersion = \"$(VERSION_ID)\";" > flow/SourceVersion.h.new - @([ -e flow/SourceVersion.h ] && diff -q flow/SourceVersion.h flow/SourceVersion.h.new >/dev/null && rm flow/SourceVersion.h.new) || mv flow/SourceVersion.h.new flow/SourceVersion.h - -lib/libflow.a: bin/coverage.flow.xml - -ifeq ($(RELEASE),true) - FLOWVER = $(VERSION) -else - FLOWVER = $(VERSION)-PRERELEASE -endif - -packages/flow-$(FLOWVER)-$(PLATFORM)-$(ARCH).tar.gz: flow - @echo "Packaging flow" - @rm -rf packages/flow-$(FLOWVER)-$(PLATFORM)-$(ARCH) - @mkdir -p packages/flow-$(FLOWVER)-$(PLATFORM)-$(ARCH)/bin packages/flow-$(FLOWVER)-$(PLATFORM)-$(ARCH)/lib packages/flow-$(FLOWVER)-$(PLATFORM)-$(ARCH)/include/flow - @cp lib/libflow.a packages/flow-$(FLOWVER)-$(PLATFORM)-$(ARCH)/lib - @cp bin/actorcompiler.exe packages/flow-$(FLOWVER)-$(PLATFORM)-$(ARCH)/bin - @find flow -name '*.h' -exec cp {} packages/flow-$(FLOWVER)-$(PLATFORM)-$(ARCH)/include/flow \; - @tar czf packages/flow-$(FLOWVER)-$(PLATFORM)-$(ARCH).tar.gz -C packages flow-$(FLOWVER)-$(PLATFORM)-$(ARCH) - @rm -rf packages/flow-$(FLOWVER)-$(PLATFORM)-$(ARCH) - -FLOW: packages/flow-$(FLOWVER)-$(PLATFORM)-$(ARCH).tar.gz - -FLOW_clean: - @echo "Cleaning flow" - @rm -rf packages/flow-*.tar.gz - -packages: FLOW -packages_clean: FLOW_clean diff --git a/foundationdb.sln b/foundationdb.sln deleted file mode 100644 index 35a08f9918..0000000000 --- a/foundationdb.sln +++ /dev/null @@ -1,160 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "actorcompiler", "flow\actorcompiler\actorcompiler.csproj", "{0ECC1314-3FC2-458D-8E41-B50B4EA24E51}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "flow", "flow\flow.vcxproj", "{00AC9087-0378-4872-9992-DF267CF12ACB}" - ProjectSection(ProjectDependencies) = postProject - {0ECC1314-3FC2-458D-8E41-B50B4EA24E51} = {0ECC1314-3FC2-458D-8E41-B50B4EA24E51} - {664A9ABB-3ED2-4088-8C95-FF6B5414F0AD} = {664A9ABB-3ED2-4088-8C95-FF6B5414F0AD} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fdbrpc", "fdbrpc\fdbrpc.vcxproj", "{00AC9086-0377-4871-9991-DF267CF12ACA}" - ProjectSection(ProjectDependencies) = postProject - {00AC9087-0378-4872-9992-DF267CF12ACB} = {00AC9087-0378-4872-9992-DF267CF12ACB} - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coveragetool", "flow\coveragetool\coveragetool.csproj", "{664A9ABB-3ED2-4088-8C95-FF6B5414F0AD}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fdb_java", "bindings\java\fdb_java.vcxproj", "{9617584C-22E8-4272-934F-733F378BF6AE}" - ProjectSection(ProjectDependencies) = postProject - {CACB2C8E-3E55-4309-A411-2A9C56C6C1CB} = {CACB2C8E-3E55-4309-A411-2A9C56C6C1CB} - {E0780196-FFC8-49BA-9451-44EAD3E3CB10} = {E0780196-FFC8-49BA-9451-44EAD3E3CB10} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fdbclient", "fdbclient\fdbclient.vcxproj", "{E2939DAA-238E-4970-96C4-4C57980F93BD}" - ProjectSection(ProjectDependencies) = postProject - {00AC9086-0377-4871-9991-DF267CF12ACA} = {00AC9086-0377-4871-9991-DF267CF12ACA} - {E0780196-FFC8-49BA-9451-44EAD3E3CB10} = {E0780196-FFC8-49BA-9451-44EAD3E3CB10} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fdbserver", "fdbserver\fdbserver.vcxproj", "{8E959DA5-5925-45CE-BFC4-C84EB632A29A}" - ProjectSection(ProjectDependencies) = postProject - {E2939DAA-238E-4970-96C4-4C57980F93BD} = {E2939DAA-238E-4970-96C4-4C57980F93BD} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fdbcli", "fdbcli\fdbcli.vcxproj", "{4631CC93-52A3-4537-9BE9-6B237A3AC6B2}" - ProjectSection(ProjectDependencies) = postProject - {E2939DAA-238E-4970-96C4-4C57980F93BD} = {E2939DAA-238E-4970-96C4-4C57980F93BD} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fdb_c", "bindings\c\fdb_c.vcxproj", "{CACB2C8E-3E55-4309-A411-2A9C56C6C1CB}" - ProjectSection(ProjectDependencies) = postProject - {E0780196-FFC8-49BA-9451-44EAD3E3CB10} = {E0780196-FFC8-49BA-9451-44EAD3E3CB10} - {E2939DAA-238E-4970-96C4-4C57980F93BD} = {E2939DAA-238E-4970-96C4-4C57980F93BD} - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "vexillographer", "fdbclient\vexillographer\vexillographer.csproj", "{E0780196-FFC8-49BA-9451-44EAD3E3CB10}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fdbmonitor", "fdbmonitor\fdbmonitor.vcxproj", "{9A1D17A1-1B56-44D8-90C8-56F1726C1C4C}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fdbservice", "fdbservice\fdbservice.vcxproj", "{0C938D09-39F6-48C0-9C6A-45D3ADBDCFB7}" -EndProject -Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "MSIInstaller", "packaging\msi\MSIInstaller.wixproj", "{C797E922-C07D-489F-B3CF-5AC35B709F4C}" - ProjectSection(ProjectDependencies) = postProject - {0C938D09-39F6-48C0-9C6A-45D3ADBDCFB7} = {0C938D09-39F6-48C0-9C6A-45D3ADBDCFB7} - {9617584C-22E8-4272-934F-733F378BF6AE} = {9617584C-22E8-4272-934F-733F378BF6AE} - {CACB2C8E-3E55-4309-A411-2A9C56C6C1CB} = {CACB2C8E-3E55-4309-A411-2A9C56C6C1CB} - {4631CC93-52A3-4537-9BE9-6B237A3AC6B2} = {4631CC93-52A3-4537-9BE9-6B237A3AC6B2} - {E0780196-FFC8-49BA-9451-44EAD3E3CB10} = {E0780196-FFC8-49BA-9451-44EAD3E3CB10} - {8E959DA5-5925-45CE-BFC4-C84EB632A29A} = {8E959DA5-5925-45CE-BFC4-C84EB632A29A} - {8E959DA5-5925-45CE-BFC4-C84EB632A29B} = {8E959DA5-5925-45CE-BFC4-C84EB632A29B} - {E2939DAA-238E-4970-96C4-4C57980F93BD} = {E2939DAA-238E-4970-96C4-4C57980F93BD} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fdb_flow", "bindings\flow\fdb_flow.vcxproj", "{2BA0A5E2-EB4C-4A32-948C-CBAABD77AF87}" - ProjectSection(ProjectDependencies) = postProject - {00AC9087-0378-4872-9992-DF267CF12ACB} = {00AC9087-0378-4872-9992-DF267CF12ACB} - {CACB2C8E-3E55-4309-A411-2A9C56C6C1CB} = {CACB2C8E-3E55-4309-A411-2A9C56C6C1CB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fdbbackup", "fdbbackup\fdbbackup.vcxproj", "{8E959DA5-5925-45CE-BFC4-C84EB632A29B}" - ProjectSection(ProjectDependencies) = postProject - {E2939DAA-238E-4970-96C4-4C57980F93BD} = {E2939DAA-238E-4970-96C4-4C57980F93BD} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fdb_flow_tester", "bindings\flow\tester\fdb_flow_tester.vcxproj", "{086EB89C-CDBD-4ABE-8296-5CA224244C80}" - ProjectSection(ProjectDependencies) = postProject - {2BA0A5E2-EB4C-4A32-948C-CBAABD77AF87} = {2BA0A5E2-EB4C-4A32-948C-CBAABD77AF87} - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|X64 = Debug|X64 - Release|X64 = Release|X64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0ECC1314-3FC2-458D-8E41-B50B4EA24E51}.Debug|X64.ActiveCfg = Debug|Any CPU - {0ECC1314-3FC2-458D-8E41-B50B4EA24E51}.Debug|X64.Build.0 = Debug|Any CPU - {0ECC1314-3FC2-458D-8E41-B50B4EA24E51}.Release|X64.ActiveCfg = Release|Any CPU - {0ECC1314-3FC2-458D-8E41-B50B4EA24E51}.Release|X64.Build.0 = Release|Any CPU - {00AC9087-0378-4872-9992-DF267CF12ACB}.Debug|X64.ActiveCfg = Debug|X64 - {00AC9087-0378-4872-9992-DF267CF12ACB}.Debug|X64.Build.0 = Debug|X64 - {00AC9087-0378-4872-9992-DF267CF12ACB}.Release|X64.ActiveCfg = Release|X64 - {00AC9087-0378-4872-9992-DF267CF12ACB}.Release|X64.Build.0 = Release|X64 - {00AC9086-0377-4871-9991-DF267CF12ACA}.Debug|X64.ActiveCfg = Debug|X64 - {00AC9086-0377-4871-9991-DF267CF12ACA}.Debug|X64.Build.0 = Debug|X64 - {00AC9086-0377-4871-9991-DF267CF12ACA}.Release|X64.ActiveCfg = Release|X64 - {00AC9086-0377-4871-9991-DF267CF12ACA}.Release|X64.Build.0 = Release|X64 - {664A9ABB-3ED2-4088-8C95-FF6B5414F0AD}.Debug|X64.ActiveCfg = Debug|Any CPU - {664A9ABB-3ED2-4088-8C95-FF6B5414F0AD}.Debug|X64.Build.0 = Debug|Any CPU - {664A9ABB-3ED2-4088-8C95-FF6B5414F0AD}.Release|X64.ActiveCfg = Release|Any CPU - {664A9ABB-3ED2-4088-8C95-FF6B5414F0AD}.Release|X64.Build.0 = Release|Any CPU - {9617584C-22E8-4272-934F-733F378BF6AE}.Debug|X64.ActiveCfg = Debug|x64 - {9617584C-22E8-4272-934F-733F378BF6AE}.Debug|X64.Build.0 = Debug|x64 - {9617584C-22E8-4272-934F-733F378BF6AE}.Release|X64.ActiveCfg = Release|x64 - {9617584C-22E8-4272-934F-733F378BF6AE}.Release|X64.Build.0 = Release|x64 - {E2939DAA-238E-4970-96C4-4C57980F93BD}.Debug|X64.ActiveCfg = Debug|X64 - {E2939DAA-238E-4970-96C4-4C57980F93BD}.Debug|X64.Build.0 = Debug|X64 - {E2939DAA-238E-4970-96C4-4C57980F93BD}.Release|X64.ActiveCfg = Release|X64 - {E2939DAA-238E-4970-96C4-4C57980F93BD}.Release|X64.Build.0 = Release|X64 - {8E959DA5-5925-45CE-BFC4-C84EB632A29A}.Debug|X64.ActiveCfg = Debug|X64 - {8E959DA5-5925-45CE-BFC4-C84EB632A29A}.Debug|X64.Build.0 = Debug|X64 - {8E959DA5-5925-45CE-BFC4-C84EB632A29A}.Release|X64.ActiveCfg = Release|X64 - {8E959DA5-5925-45CE-BFC4-C84EB632A29A}.Release|X64.Build.0 = Release|X64 - {4631CC93-52A3-4537-9BE9-6B237A3AC6B2}.Debug|X64.ActiveCfg = Debug|x64 - {4631CC93-52A3-4537-9BE9-6B237A3AC6B2}.Debug|X64.Build.0 = Debug|x64 - {4631CC93-52A3-4537-9BE9-6B237A3AC6B2}.Release|X64.ActiveCfg = Release|x64 - {4631CC93-52A3-4537-9BE9-6B237A3AC6B2}.Release|X64.Build.0 = Release|x64 - {CACB2C8E-3E55-4309-A411-2A9C56C6C1CB}.Debug|X64.ActiveCfg = Debug|x64 - {CACB2C8E-3E55-4309-A411-2A9C56C6C1CB}.Debug|X64.Build.0 = Debug|x64 - {CACB2C8E-3E55-4309-A411-2A9C56C6C1CB}.Release|X64.ActiveCfg = Release|x64 - {CACB2C8E-3E55-4309-A411-2A9C56C6C1CB}.Release|X64.Build.0 = Release|x64 - {E0780196-FFC8-49BA-9451-44EAD3E3CB10}.Debug|X64.ActiveCfg = Debug|Any CPU - {E0780196-FFC8-49BA-9451-44EAD3E3CB10}.Debug|X64.Build.0 = Debug|Any CPU - {E0780196-FFC8-49BA-9451-44EAD3E3CB10}.Release|X64.ActiveCfg = Release|Any CPU - {E0780196-FFC8-49BA-9451-44EAD3E3CB10}.Release|X64.Build.0 = Release|Any CPU - {9A1D17A1-1B56-44D8-90C8-56F1726C1C4C}.Debug|X64.ActiveCfg = Debug|x64 - {9A1D17A1-1B56-44D8-90C8-56F1726C1C4C}.Release|X64.ActiveCfg = Release|x64 - {0C938D09-39F6-48C0-9C6A-45D3ADBDCFB7}.Debug|X64.ActiveCfg = Debug|x64 - {0C938D09-39F6-48C0-9C6A-45D3ADBDCFB7}.Debug|X64.Build.0 = Debug|x64 - {0C938D09-39F6-48C0-9C6A-45D3ADBDCFB7}.Release|X64.ActiveCfg = Release|x64 - {0C938D09-39F6-48C0-9C6A-45D3ADBDCFB7}.Release|X64.Build.0 = Release|x64 - {C797E922-C07D-489F-B3CF-5AC35B709F4C}.Debug|X64.ActiveCfg = Debug|x64 - {C797E922-C07D-489F-B3CF-5AC35B709F4C}.Debug|X64.Build.0 = Debug|x64 - {C797E922-C07D-489F-B3CF-5AC35B709F4C}.Release|X64.ActiveCfg = Release|x64 - {C797E922-C07D-489F-B3CF-5AC35B709F4C}.Release|X64.Build.0 = Release|x64 - {E936E200-689E-49FD-8463-32FE763F1860}.Debug|X64.ActiveCfg = Debug|x64 - {E936E200-689E-49FD-8463-32FE763F1860}.Debug|X64.Build.0 = Debug|x64 - {E936E200-689E-49FD-8463-32FE763F1860}.Release|X64.ActiveCfg = Release|x64 - {E936E200-689E-49FD-8463-32FE763F1860}.Release|X64.Build.0 = Release|x64 - {E22D4EF8-E75D-4281-93F9-A9F73936DE54}.Debug|X64.ActiveCfg = Debug|x64 - {E22D4EF8-E75D-4281-93F9-A9F73936DE54}.Debug|X64.Build.0 = Debug|x64 - {E22D4EF8-E75D-4281-93F9-A9F73936DE54}.Release|X64.ActiveCfg = Release|x64 - {E22D4EF8-E75D-4281-93F9-A9F73936DE54}.Release|X64.Build.0 = Release|x64 - {2BA0A5E2-EB4C-4A32-948C-CBAABD77AF87}.Debug|X64.ActiveCfg = Debug|X64 - {2BA0A5E2-EB4C-4A32-948C-CBAABD77AF87}.Debug|X64.Build.0 = Debug|X64 - {2BA0A5E2-EB4C-4A32-948C-CBAABD77AF87}.Release|X64.ActiveCfg = Release|X64 - {2BA0A5E2-EB4C-4A32-948C-CBAABD77AF87}.Release|X64.Build.0 = Release|X64 - {8E959DA5-5925-45CE-BFC4-C84EB632A29B}.Debug|X64.ActiveCfg = Debug|X64 - {8E959DA5-5925-45CE-BFC4-C84EB632A29B}.Debug|X64.Build.0 = Debug|X64 - {8E959DA5-5925-45CE-BFC4-C84EB632A29B}.Release|X64.ActiveCfg = Release|X64 - {8E959DA5-5925-45CE-BFC4-C84EB632A29B}.Release|X64.Build.0 = Release|X64 - {086EB89C-CDBD-4ABE-8296-5CA224244C80}.Debug|X64.ActiveCfg = Debug|x64 - {086EB89C-CDBD-4ABE-8296-5CA224244C80}.Release|X64.ActiveCfg = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/versions.target b/versions.target deleted file mode 100644 index f49888f2ce..0000000000 --- a/versions.target +++ /dev/null @@ -1,7 +0,0 @@ - - - - 6.3.0 - 6.3 - - From 6ab4a571233ac68bc063eae3b0bc3b965fb512fa Mon Sep 17 00:00:00 2001 From: tclinken Date: Tue, 7 Apr 2020 11:06:55 -0700 Subject: [PATCH 1355/1604] Allow RequestStream::send to use move semantics --- fdbrpc/fdbrpc.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fdbrpc/fdbrpc.h b/fdbrpc/fdbrpc.h index 76fb156e13..b80b88f4a3 100644 --- a/fdbrpc/fdbrpc.h +++ b/fdbrpc/fdbrpc.h @@ -247,6 +247,15 @@ public: else queue->send(value); } + + void send(T&& value) const { + if (queue->isRemoteEndpoint()) { + FlowTransport::transport().sendUnreliable(SerializeSource(std::move(value)), getEndpoint(), true); + } + else + queue->send(std::move(value)); + } + /*void sendError(const Error& error) const { ASSERT( !queue->isRemoteEndpoint() ); queue->sendError(error); From b93d8e8b21423030908df3f9aa90d39ea20327a7 Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Tue, 7 Apr 2020 11:09:11 -0700 Subject: [PATCH 1356/1604] fix cmake dependency to version.target --- CMakeLists.txt | 39 +++------------------------------------ 1 file changed, 3 insertions(+), 36 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f651b76800..5f8d4417be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,42 +80,9 @@ message(STATUS "Current git version ${CURRENT_GIT_VERSION}") # Version information ################################################################################ -if(NOT WIN32) - add_custom_target(version_file ALL DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/versions.target) - execute_process( - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build/get_version.sh ${CMAKE_CURRENT_SOURCE_DIR}/versions.target - OUTPUT_VARIABLE FDB_VERSION_WNL) - execute_process( - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build/get_package_name.sh ${CMAKE_CURRENT_SOURCE_DIR}/versions.target - OUTPUT_VARIABLE FDB_PACKAGE_NAME_WNL) - string(STRIP "${FDB_VERSION_WNL}" FDB_VERSION_TARGET_FILE) - string(STRIP "${FDB_PACKAGE_NAME_WNL}" FDB_PACKAGE_NAME_TARGET_FILE) -endif() - -set(USE_VERSIONS_TARGET OFF CACHE BOOL "Use the deprecated versions.target file") -if(USE_VERSIONS_TARGET) - if (WIN32) - message(FATAL_ERROR "USE_VERSION_TARGET us not supported on Windows") - endif() - set(FDB_VERSION ${FDB_VERION_TARGET_FILE}) - set(FDB_PACKAGE_NAME ${FDB_PACKAGE_NAME_TARGET_FILE}) - set(FDB_VERSION_PLAIN ${FDB_VERSION}) -else() - set(FDB_PACKAGE_NAME "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") - set(FDB_VERSION ${PROJECT_VERSION}) - set(FDB_VERSION_PLAIN ${FDB_VERSION}) - if(NOT WIN32) - # we need to assert that the cmake version is in sync with the target version - if(NOT (FDB_VERSION STREQUAL FDB_VERSION_TARGET_FILE)) - message(SEND_ERROR "The project version in cmake is set to ${FDB_VERSION},\ - but versions.target has it at ${FDB_VERSION_TARGET_FILE}") - endif() - if(NOT (FDB_PACKAGE_NAME STREQUAL FDB_PACKAGE_NAME_TARGET_FILE)) - message(SEND_ERROR "The package name in cmake is set to ${FDB_PACKAGE_NAME},\ - but versions.target has it set to ${FDB_PACKAGE_NAME_TARGET_FILE}") - endif() - endif() -endif() +set(FDB_PACKAGE_NAME "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") +set(FDB_VERSION ${PROJECT_VERSION}) +set(FDB_VERSION_PLAIN ${FDB_VERSION}) message(STATUS "FDB version is ${FDB_VERSION}") message(STATUS "FDB package name is ${FDB_PACKAGE_NAME}") From b616a6b3b602b7fdfefd7b42ebab78b69a94ef8a Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Tue, 7 Apr 2020 11:33:47 -0700 Subject: [PATCH 1357/1604] generate versions.target with cmake --- .gitignore | 1 + CMakeLists.txt | 1 + versions.target.cmake | 8 ++++++++ 3 files changed, 10 insertions(+) create mode 100644 versions.target.cmake diff --git a/.gitignore b/.gitignore index 8902f61d74..5fc9981a4f 100644 --- a/.gitignore +++ b/.gitignore @@ -94,3 +94,4 @@ flow/coveragetool/obj .envrc .DS_Store temp/ +/versions.target diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f8d4417be..9f013d57e6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,6 +83,7 @@ message(STATUS "Current git version ${CURRENT_GIT_VERSION}") set(FDB_PACKAGE_NAME "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") set(FDB_VERSION ${PROJECT_VERSION}) set(FDB_VERSION_PLAIN ${FDB_VERSION}) +configure_file(${CMAKE_SOURCE_DIR}/versions.target.cmake ${CMAKE_SOURCE_DIR}/versions.target) message(STATUS "FDB version is ${FDB_VERSION}") message(STATUS "FDB package name is ${FDB_PACKAGE_NAME}") diff --git a/versions.target.cmake b/versions.target.cmake new file mode 100644 index 0000000000..ad9e4b3fc8 --- /dev/null +++ b/versions.target.cmake @@ -0,0 +1,8 @@ + + + + + ${CMAKE_PROJECT_VERSION} + ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR} + + From 7a4817b8bb68bf93dadaefb947c036fe8fda09eb Mon Sep 17 00:00:00 2001 From: Steve Atherton Date: Tue, 7 Apr 2020 11:47:19 -0700 Subject: [PATCH 1358/1604] Re-enable ART mutation buffer in Redwood. --- fdbserver/VersionedBTree.actor.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index 651c248771..bee56642d9 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -3290,7 +3290,7 @@ private: public: -//#include "ArtMutationBuffer.h" +#include "ArtMutationBuffer.h" struct MutationBufferStdMap { MutationBufferStdMap() { // Create range representing the entire keyspace. This reduces edge cases to applying mutations @@ -3388,7 +3388,7 @@ public: } }; -//#define USE_ART_MUTATION_BUFFER 1 +#define USE_ART_MUTATION_BUFFER 1 #ifdef USE_ART_MUTATION_BUFFER typedef struct MutationBufferART MutationBuffer; @@ -5006,7 +5006,7 @@ private: }; -//#include "art_impl.h" +#include "art_impl.h" RedwoodRecordRef VersionedBTree::dbBegin(StringRef(), 0); RedwoodRecordRef VersionedBTree::dbEnd(LiteralStringRef("\xff\xff\xff\xff\xff")); From 99295b81e1aae5b447a8f80970fa33d049b44149 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Tue, 7 Apr 2020 11:48:10 -0700 Subject: [PATCH 1359/1604] Make the README give instructions to use ninja to build. Make builds one project to completion, and then builds the next project. Ninja can build multiple projects in parallel, and thus is strictly faster. --- README.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 510943a6cc..a3964f63a8 100755 --- a/README.md +++ b/README.md @@ -33,8 +33,8 @@ CMake-based build system. Both of them should currently work for most users, and CMake should be the preferred choice as it will eventually become the only build system available. -If compiling for local development, please set -DUSE_WERROR=ON in -cmake. Our CI compiles with -Werror on, so this way you'll find out about +If compiling for local development, please set `-DUSE_WERROR=ON` in +cmake. Our CI compiles with `-Werror` on, so this way you'll find out about compiler warnings that break the build earlier. ## CMake @@ -51,8 +51,8 @@ Mac OS - for Windows see below): 1. Create a build directory (you can have the build directory anywhere you like): `mkdir build` 1. `cd build` -1. `cmake -DBOOST_ROOT= ` -1. `make` +1. `cmake -GNinja -DBOOST_ROOT= ` +1. `ninja` CMake will try to find its dependencies. However, for LibreSSL this can be often problematic (especially if OpenSSL is installed as well). For that we recommend @@ -61,7 +61,7 @@ LibreSSL is installed under `/usr/local/libressl-2.8.3`, you should call cmake l this: ``` -cmake -DLibreSSL_ROOT=/usr/local/libressl-2.8.3/ ../foundationdb +cmake -GNinja -DLibreSSL_ROOT=/usr/local/libressl-2.8.3/ ../foundationdb ``` FoundationDB will build just fine without LibreSSL, however, the resulting @@ -133,8 +133,8 @@ If you want to create a package you have to tell cmake what platform it is for. And then you can build by simply calling `cpack`. So for debian, call: ``` -cmake -make +cmake -GNinja +ninja cpack -G DEB ``` @@ -142,21 +142,21 @@ For RPM simply replace `DEB` with `RPM`. ### MacOS -The build under MacOS will work the same way as on Linux. To get LibreSSL and boost you -can use [Homebrew](https://brew.sh/). LibreSSL will not be installed in -`/usr/local` instead it will stay in `/usr/local/Cellar`. So the cmake command -will look something like this: +The build under MacOS will work the same way as on Linux. To get LibreSSL, +boost, and ninja you can use [Homebrew](https://brew.sh/). LibreSSL will not be +installed in `/usr/local` instead it will stay in `/usr/local/Cellar`. So the +cmake command will look something like this: ```sh -cmake -DLibreSSL_ROOT=/usr/local/Cellar/libressl/2.8.3 +cmake -GNinja -DLibreSSL_ROOT=/usr/local/Cellar/libressl/2.8.3 ``` To generate a installable package, you have to call CMake with the corresponding arguments and then use cpack to generate the package: ```sh -cmake -make +cmake -GNinja +ninja cpack -G productbuild ``` From efca39a09a984c30b754dde4574fa8c9fd39a54b Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Tue, 7 Apr 2020 12:49:46 -0700 Subject: [PATCH 1360/1604] make traces SevDebug to avoid too verbose --- fdbclient/SpecialKeySpace.actor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fdbclient/SpecialKeySpace.actor.cpp b/fdbclient/SpecialKeySpace.actor.cpp index a1732a77ba..7bcdfa6707 100644 --- a/fdbclient/SpecialKeySpace.actor.cpp +++ b/fdbclient/SpecialKeySpace.actor.cpp @@ -25,7 +25,7 @@ ACTOR Future SpecialKeyRangeBaseImpl::normalizeKeySelectorActor(const Spec if (pkrImpl->range.contains(ks->getKey())) startKey = ks->getKey(); } - TraceEvent("NormalizeKeySelector") + TraceEvent(SevDebug, "NormalizeKeySelector") .detail("OriginalKey", ks->getKey()) .detail("OriginalOffset", ks->offset) .detail("SpecialKeyRangeStart", pkrImpl->range.begin) @@ -54,7 +54,7 @@ ACTOR Future SpecialKeyRangeBaseImpl::normalizeKeySelectorActor(const Spec ks->offset -= result.size(); } } - TraceEvent("NormalizeKeySelector") + TraceEvent(SevDebug, "NormalizeKeySelector") .detail("NormalizedKey", ks->getKey()) .detail("NormalizedOffset", ks->offset) .detail("SpecialKeyRangeStart", pkrImpl->range.begin) From 0034d6fc8532878ac044b9a0a1a1b462ffd4cdde Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Tue, 7 Apr 2020 13:28:11 -0700 Subject: [PATCH 1361/1604] FastRestore:Master:Fix:Hnadling the last log file --- fdbserver/RestoreMaster.actor.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/fdbserver/RestoreMaster.actor.h b/fdbserver/RestoreMaster.actor.h index b6d5dcb1de..9488f647a0 100644 --- a/fdbserver/RestoreMaster.actor.h +++ b/fdbserver/RestoreMaster.actor.h @@ -282,8 +282,11 @@ struct RestoreMasterData : RestoreRoleData, public ReferenceCountedemplace(vb.beginVersion, vb); } // Invariant: The last vb endverion should be no smaller than targetVersion - ASSERT(maxVBVersion >= targetVersion); + if(maxVBVersion < targetVersion) { + TraceEvent(SevError, "FastRestoreBuildVersionBatch") + .detail("TargetVersion", targetVersion) + .detail("MaxVersionBatchVersion", maxVBVersion); + } } void initBackupContainer(Key url) { From a38c1f3799797ba166222a45c8b99dccb2a87723 Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Tue, 7 Apr 2020 14:26:44 -0700 Subject: [PATCH 1362/1604] fixed versions.target file --- versions.target.cmake | 1 - 1 file changed, 1 deletion(-) diff --git a/versions.target.cmake b/versions.target.cmake index ad9e4b3fc8..5ea66adafd 100644 --- a/versions.target.cmake +++ b/versions.target.cmake @@ -1,4 +1,3 @@ - From e5b2cd81d5bf751faf40a4cd97dc71e31eddbf03 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Tue, 7 Apr 2020 15:56:44 -0700 Subject: [PATCH 1363/1604] FastRestore:Cleanup debug code --- fdbclient/RestoreWorkerInterface.actor.h | 3 ++- fdbserver/RestoreApplier.actor.cpp | 14 ++++---------- fdbserver/RestoreMaster.actor.cpp | 1 + fdbserver/RestoreMaster.actor.h | 5 ++++- fdbserver/RestoreUtil.actor.cpp | 9 ++++++++- fdbserver/fdbserver.actor.cpp | 24 +++++++----------------- 6 files changed, 26 insertions(+), 30 deletions(-) diff --git a/fdbclient/RestoreWorkerInterface.actor.h b/fdbclient/RestoreWorkerInterface.actor.h index 719a49fa45..8bbf4d10a4 100644 --- a/fdbclient/RestoreWorkerInterface.actor.h +++ b/fdbclient/RestoreWorkerInterface.actor.h @@ -253,7 +253,8 @@ struct RestoreAsset { // Is mutation's begin and end keys are in RestoreAsset's range bool isInKeyRange(MutationRef mutation) const { - if (mutation.type == MutationRef::ClearRange) { + if (isRangeMutation(mutation)) { + // Range mutation's right side is exclusive return mutation.param1 >= range.begin && mutation.param2 <= range.end; } else { return mutation.param1 >= range.begin && mutation.param1 < range.end; diff --git a/fdbserver/RestoreApplier.actor.cpp b/fdbserver/RestoreApplier.actor.cpp index a3be7e6f5c..f04db6885d 100644 --- a/fdbserver/RestoreApplier.actor.cpp +++ b/fdbserver/RestoreApplier.actor.cpp @@ -150,15 +150,8 @@ ACTOR static Future handleSendMutationVectorRequest(RestoreSendVersionedMu batchData->counters.receivedMutations += 1; batchData->counters.receivedAtomicOps += isAtomicOp((MutationRef::Type)mutation.type) ? 1 : 0; // Sanity check - if (g_network->isSimulated()) { - // TODO: Use asset.isInKeyRange(); - if (isRangeMutation(mutation)) { - ASSERT(mutation.param1 >= req.asset.range.begin && - mutation.param2 <= req.asset.range.end); // Range mutation's right side is exclusive - } else { - ASSERT(mutation.param1 >= req.asset.range.begin && mutation.param1 < req.asset.range.end); - } - } + ASSERT_WE_THINK(req.asset.isInKeyRange(mutation)); + // Note: Log and range mutations may be delivered out of order. Can we handle it? if (mutation.type == MutationRef::SetVersionstampedKey || mutation.type == MutationRef::SetVersionstampedValue) { @@ -309,7 +302,8 @@ ACTOR static Future precomputeMutationsResult(Reference .detail("ClearRangeUpperBound", rangeMutation.mutation.param2) .detail("UsedUpperBound", ub->first); } - // Q: Can beginKey = endKey and clear beginKey? + // We make the beginKey = endKey for the ClearRange on purpose so that + // we can sanity check ClearRange mutation when we apply it to DB. MutationRef clearKey(MutationRef::ClearRange, lb->first, lb->first); lb->second.add(clearKey, rangeMutation.version); lb++; diff --git a/fdbserver/RestoreMaster.actor.cpp b/fdbserver/RestoreMaster.actor.cpp index fafc85d030..69accf9041 100644 --- a/fdbserver/RestoreMaster.actor.cpp +++ b/fdbserver/RestoreMaster.actor.cpp @@ -322,6 +322,7 @@ ACTOR static Future loadFilesOnLoaders(Reference batchDat int paramIdx = 0; for (auto& file : *files) { + // TODO: Allow empty files in version batch; Filter out them here. if (loader == loadersInterf.end()) { loader = loadersInterf.begin(); } diff --git a/fdbserver/RestoreMaster.actor.h b/fdbserver/RestoreMaster.actor.h index 9488f647a0..d06c396983 100644 --- a/fdbserver/RestoreMaster.actor.h +++ b/fdbserver/RestoreMaster.actor.h @@ -283,6 +283,7 @@ struct RestoreMasterData : RestoreRoleData, public ReferenceCounted RestoreRoleStr = { "Invalid", "Master", "Loader", "Applier" }; int numRoles = RestoreRoleStr.size(); -StringRef debugFRKey = LiteralStringRef("0000000000arl"); +// 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"); +// Track any mutation in fast restore that has overlap with debugFRKey bool debugFRMutation( const char* context, Version version, MutationRef const& mutation ) { if (mutation.type != mutation.ClearRange && mutation.param1 == debugFRKey) { // Single key mutation TraceEvent("FastRestoreMutationTracking").detail("At", context).detail("Version", version).detail("MutationType", getTypeString((MutationRef::Type)mutation.type)).detail("Key", mutation.param1).detail("Value", mutation.param2); @@ -39,6 +42,10 @@ bool debugFRMutation( const char* context, Version version, MutationRef const& m return true; } +#else +// Default implementation. +bool debugFRMutation( const char* context, Version version, MutationRef const& mutation ) { return false; } +#endif std::string getRoleStr(RestoreRole role) { if ((int)role >= numRoles || (int)role < 0) { diff --git a/fdbserver/fdbserver.actor.cpp b/fdbserver/fdbserver.actor.cpp index 63db210d66..45ee0e92a8 100644 --- a/fdbserver/fdbserver.actor.cpp +++ b/fdbserver/fdbserver.actor.cpp @@ -201,23 +201,15 @@ bool enableFailures = true; vector< Standalone> > debugEntries; int64_t totalDebugEntriesSize = 0; -#if CENABLED(1, NOT_IN_CLEAN) -StringRef debugKey2 = LiteralStringRef("0000000000ar"); -StringRef debugKey = LiteralStringRef("\xff\xff\xff\xff"); -StringRef debugKeyBegin = LiteralStringRef("0000000000a"); -StringRef debugKeyEnd = LiteralStringRef("z000000000z"); +#if CENABLED(0, NOT_IN_CLEAN) +StringRef debugKey = LiteralStringRef(""); +StringRef debugKey2 = LiteralStringRef("\xff\xff\xff\xff"); bool debugMutation( const char* context, Version version, MutationRef const& mutation ) { if ((mutation.type == mutation.SetValue || mutation.type == mutation.AddValue || mutation.type==mutation.DebugKey) && (mutation.param1 == debugKey || mutation.param1 == debugKey2)) - TraceEvent("MutationTracking").detail("At", context).detail("Version", version).detail("MutationType", "SetValue").detail("Key", mutation.param1).detail("Value", mutation.param2); - //else if ((mutation.type == mutation.ClearRange || mutation.type == mutation.DebugKeyRange) && ((mutation.param1<=debugKey && mutation.param2>debugKey) || (mutation.param1<=debugKey2 && mutation.param2>debugKey2))) - else if ((mutation.type == mutation.ClearRange || mutation.type == mutation.DebugKeyRange) && (mutation.param1>=debugKeyBegin && mutation.param2<=debugKeyEnd)) - TraceEvent("MutationTracking") - .detail("At", context) - .detail("Version", version) - .detail("MutationType", "ClearRange") - .detail("KeyBegin", mutation.param1) - .detail("KeyEnd", mutation.param2); + ;//TraceEvent("MutationTracking").detail("At", context).detail("Version", version).detail("MutationType", "SetValue").detail("Key", mutation.param1).detail("Value", mutation.param2); + else if ((mutation.type == mutation.ClearRange || mutation.type == mutation.DebugKeyRange) && ((mutation.param1<=debugKey && mutation.param2>debugKey) || (mutation.param1<=debugKey2 && mutation.param2>debugKey2))) + ;//TraceEvent("MutationTracking").detail("At", context).detail("Version", version).detail("MutationType", "ClearRange").detail("KeyBegin", mutation.param1).detail("KeyEnd", mutation.param2); else return false; const char* type = @@ -227,9 +219,7 @@ bool debugMutation( const char* context, Version version, MutationRef const& mut mutation.type == MutationRef::DebugKeyRange ? "DebugKeyRange" : mutation.type == MutationRef::DebugKey ? "DebugKey" : "UnknownMutation"; - // printf("DEBUGMUTATION:\t%.6f\t%s\t%s\t%lld\t%s\t%s\t%s\n", now(), - // g_network->getLocalAddress().toString().c_str(), context, version, type, printable(mutation.param1).c_str(), - // printable(mutation.param2).c_str()); + printf("DEBUGMUTATION:\t%.6f\t%s\t%s\t%lld\t%s\t%s\t%s\n", now(), g_network->getLocalAddress().toString().c_str(), context, version, type, printable(mutation.param1).c_str(), printable(mutation.param2).c_str()); return true; } From 5ebafdb94c78a6b504b003db47468f00de70b496 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Tue, 7 Apr 2020 15:57:03 -0700 Subject: [PATCH 1364/1604] FastRestore:Apply clang-format to changes --- fdbserver/RestoreApplier.actor.cpp | 14 +++++++---- fdbserver/RestoreMaster.actor.cpp | 8 +++--- fdbserver/RestoreMaster.actor.h | 39 ++++++++++++++++-------------- fdbserver/RestoreUtil.actor.cpp | 23 ++++++++++++++---- fdbserver/RestoreUtil.h | 2 +- 5 files changed, 53 insertions(+), 33 deletions(-) diff --git a/fdbserver/RestoreApplier.actor.cpp b/fdbserver/RestoreApplier.actor.cpp index f04db6885d..6df711acd6 100644 --- a/fdbserver/RestoreApplier.actor.cpp +++ b/fdbserver/RestoreApplier.actor.cpp @@ -183,7 +183,8 @@ ACTOR static Future applyClearRangeMutations(StandalonesetOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); tr->setOption(FDBTransactionOptions::LOCK_AWARE); for (auto& range : ranges) { - debugFRMutation("FastRestoreApplierApplyClearRangeMutation", 0, MutationRef(MutationRef::ClearRange, range.begin, range.end)); + debugFRMutation("FastRestoreApplierApplyClearRangeMutation", 0, + MutationRef(MutationRef::ClearRange, range.begin, range.end)); tr->clear(range); } wait(tr->commit()); @@ -273,7 +274,8 @@ ACTOR static Future precomputeMutationsResult(Reference double curTxnSize = 0; for (auto& rangeMutation : batchData->stagingKeyRanges) { KeyRangeRef range(rangeMutation.mutation.param1, rangeMutation.mutation.param2); - debugFRMutation("FastRestoreApplierPrecomputeMutationsResultClearRange", rangeMutation.version.version, MutationRef(MutationRef::ClearRange, range.begin, range.end)); + debugFRMutation("FastRestoreApplierPrecomputeMutationsResultClearRange", rangeMutation.version.version, + MutationRef(MutationRef::ClearRange, range.begin, range.end)); clearRanges.push_back(clearRanges.arena(), range); curTxnSize += range.expectedSize(); if (curTxnSize >= SERVER_KNOBS->FASTRESTORE_TXN_BATCH_MAX_BYTES) { @@ -382,7 +384,8 @@ ACTOR static Future applyStagingKeysBatch(std::map::itera while (iter != end) { if (iter->second.type == MutationRef::SetValue) { tr->set(iter->second.key, iter->second.val); - TraceEvent(SevFRMutationInfo, "FastRestoreApplierPhaseApplyStagingKeysBatch", applierID).detail("SetKey", iter->second.key); + TraceEvent(SevFRMutationInfo, "FastRestoreApplierPhaseApplyStagingKeysBatch", applierID) + .detail("SetKey", iter->second.key); sets++; } else if (iter->second.type == MutationRef::ClearRange) { if (iter->second.key != iter->second.val) { @@ -393,7 +396,8 @@ ACTOR static Future applyStagingKeysBatch(std::map::itera .detail("SubVersion", iter->second.version.sub); } tr->clear(singleKeyRange(iter->second.key)); - TraceEvent(SevFRMutationInfo, "FastRestoreApplierPhaseApplyStagingKeysBatch", applierID).detail("ClearKey", iter->second.key); + TraceEvent(SevFRMutationInfo, "FastRestoreApplierPhaseApplyStagingKeysBatch", applierID) + .detail("ClearKey", iter->second.key); clears++; } else { ASSERT(false); @@ -409,7 +413,7 @@ ACTOR static Future applyStagingKeysBatch(std::map::itera } TraceEvent("FastRestoreApplierPhaseApplyStagingKeysBatchPrecommit", applierID) .detail("Begin", begin->first) - .detail("End", endKey) + .detail("End", endKey) .detail("Sets", sets) .detail("Clears", clears); wait(tr->commit()); diff --git a/fdbserver/RestoreMaster.actor.cpp b/fdbserver/RestoreMaster.actor.cpp index 69accf9041..26f9f69718 100644 --- a/fdbserver/RestoreMaster.actor.cpp +++ b/fdbserver/RestoreMaster.actor.cpp @@ -525,10 +525,10 @@ void splitKeyRangeForAppliers(Reference batchData, std::set keyrangeSplitter; // unique key to split key range for appliers keyrangeSplitter.insert(normalKeys.begin); // First slot TraceEvent("FastRestoreMasterPhaseCalculateApplierKeyRanges") - .detail("BatchIndex", batchIndex) - .detail("CumulativeSize", cumulativeSize) - .detail("Slot", 0) - .detail("LowerBoundKey", normalKeys.begin); + .detail("BatchIndex", batchIndex) + .detail("CumulativeSize", cumulativeSize) + .detail("Slot", 0) + .detail("LowerBoundKey", normalKeys.begin); int slotIdx = 1; while (cumulativeSize < batchData->samplesSize) { IndexedSet::iterator lowerBound = batchData->samples.index(cumulativeSize); diff --git a/fdbserver/RestoreMaster.actor.h b/fdbserver/RestoreMaster.actor.h index d06c396983..04d47f32b1 100644 --- a/fdbserver/RestoreMaster.actor.h +++ b/fdbserver/RestoreMaster.actor.h @@ -281,7 +281,10 @@ struct RestoreMasterData : RestoreRoleData, public ReferenceCountedFASTRESTORE_VERSIONBATCH_MAX_BYTES || @@ -382,16 +385,16 @@ struct RestoreMasterData : RestoreRoleData, public ReferenceCountedemplace(vb.beginVersion, vb); // copy vb to versionBatch TraceEvent("FastRestoreBuildVersionBatch") - .detail("FinishBatchIndex", vb.batchIndex) - .detail("VersionBatchBeginVersion", vb.beginVersion) - .detail("VersionBatchEndVersion", vb.endVersion) - .detail("VersionBatchLogFiles", vb.logFiles.size()) - .detail("VersionBatchRangeFiles", vb.rangeFiles.size()) - .detail("VersionBatchSize", vb.size) - .detail("RangeIndex", rangeIdx) - .detail("LogIndex", logIdx) - .detail("NewVersionBatchBeginVersion", prevEndVersion) - .detail("RewriteNextVersion", rewriteNextVersion); + .detail("FinishBatchIndex", vb.batchIndex) + .detail("VersionBatchBeginVersion", vb.beginVersion) + .detail("VersionBatchEndVersion", vb.endVersion) + .detail("VersionBatchLogFiles", vb.logFiles.size()) + .detail("VersionBatchRangeFiles", vb.rangeFiles.size()) + .detail("VersionBatchSize", vb.size) + .detail("RangeIndex", rangeIdx) + .detail("LogIndex", logIdx) + .detail("NewVersionBatchBeginVersion", prevEndVersion) + .detail("RewriteNextVersion", rewriteNextVersion); // start finding the next version batch vb.reset(); @@ -407,12 +410,12 @@ struct RestoreMasterData : RestoreRoleData, public ReferenceCountedemplace(vb.beginVersion, vb); } // Invariant: The last vb endverion should be no smaller than targetVersion - if(maxVBVersion < targetVersion) { + if (maxVBVersion < targetVersion) { // Q: Is the restorable version always less than the maximum version from all backup filenames? // A: This is true for the raw backup files returned by backup container before we remove the empty files. TraceEvent(SevWarnAlways, "FastRestoreBuildVersionBatch") - .detail("TargetVersion", targetVersion) - .detail("MaxVersionBatchVersion", maxVBVersion); + .detail("TargetVersion", targetVersion) + .detail("MaxVersionBatchVersion", maxVBVersion); } } diff --git a/fdbserver/RestoreUtil.actor.cpp b/fdbserver/RestoreUtil.actor.cpp index bfcc4e2ddd..7965ab60e4 100644 --- a/fdbserver/RestoreUtil.actor.cpp +++ b/fdbserver/RestoreUtil.actor.cpp @@ -32,11 +32,22 @@ int numRoles = RestoreRoleStr.size(); StringRef debugFRKey = LiteralStringRef("\xff\xff\xff\xff"); // Track any mutation in fast restore that has overlap with debugFRKey -bool debugFRMutation( const char* context, Version version, MutationRef const& mutation ) { +bool debugFRMutation(const char* context, Version version, MutationRef const& mutation) { if (mutation.type != mutation.ClearRange && mutation.param1 == debugFRKey) { // Single key mutation - TraceEvent("FastRestoreMutationTracking").detail("At", context).detail("Version", version).detail("MutationType", getTypeString((MutationRef::Type)mutation.type)).detail("Key", mutation.param1).detail("Value", mutation.param2); - } else if (mutation.type == mutation.ClearRange && debugFRKey >= mutation.param1 && debugFRKey < mutation.param2) { // debugFRKey is in the range mutation - TraceEvent("FastRestoreMutationTracking").detail("At", context).detail("Version", version).detail("MutationType", getTypeString((MutationRef::Type)mutation.type)).detail("Begin", mutation.param1).detail("End", mutation.param2); + TraceEvent("FastRestoreMutationTracking") + .detail("At", context) + .detail("Version", version) + .detail("MutationType", getTypeString((MutationRef::Type)mutation.type)) + .detail("Key", mutation.param1) + .detail("Value", mutation.param2); + } else if (mutation.type == mutation.ClearRange && debugFRKey >= mutation.param1 && + debugFRKey < mutation.param2) { // debugFRKey is in the range mutation + TraceEvent("FastRestoreMutationTracking") + .detail("At", context) + .detail("Version", version) + .detail("MutationType", getTypeString((MutationRef::Type)mutation.type)) + .detail("Begin", mutation.param1) + .detail("End", mutation.param2); } else return false; @@ -44,7 +55,9 @@ bool debugFRMutation( const char* context, Version version, MutationRef const& m } #else // Default implementation. -bool debugFRMutation( const char* context, Version version, MutationRef const& mutation ) { return false; } +bool debugFRMutation(const char* context, Version version, MutationRef const& mutation) { + return false; +} #endif std::string getRoleStr(RestoreRole role) { diff --git a/fdbserver/RestoreUtil.h b/fdbserver/RestoreUtil.h index 9f3552074d..1018f787ad 100644 --- a/fdbserver/RestoreUtil.h +++ b/fdbserver/RestoreUtil.h @@ -49,7 +49,7 @@ extern int numRoles; std::string getHexString(StringRef input); -bool debugFRMutation( const char* context, Version version, MutationRef const& mutation ); +bool debugFRMutation(const char* context, Version version, MutationRef const& mutation); struct RestoreCommonReply { constexpr static FileIdentifier file_identifier = 56140435; From 60407bdee345349def524b50c12f6996dac79212 Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Tue, 7 Apr 2020 15:46:34 -0700 Subject: [PATCH 1365/1604] Use LiteralStringRef for backup paused key --- fdbclient/FileBackupAgent.actor.cpp | 4 ++-- fdbclient/SystemData.cpp | 13 ------------- fdbclient/SystemData.h | 2 -- fdbserver/BackupWorker.actor.cpp | 2 +- 4 files changed, 3 insertions(+), 18 deletions(-) diff --git a/fdbclient/FileBackupAgent.actor.cpp b/fdbclient/FileBackupAgent.actor.cpp index d06581b83d..e16863791f 100644 --- a/fdbclient/FileBackupAgent.actor.cpp +++ b/fdbclient/FileBackupAgent.actor.cpp @@ -4059,7 +4059,7 @@ public: tr->setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); try { - tr->set(backupPausedKey, encodeBackupPausedValue(pause)); + tr->set(backupPausedKey, pause ? LiteralStringRef("1") : LiteralStringRef("0")); wait(tr->commit()); break; } catch (Error& e) { @@ -4067,7 +4067,7 @@ public: } } wait(change); - TraceEvent("FBA_ChangePaused").detail("Action", pause ? "Paused" : "Resumed"); + TraceEvent("FileBackupAgentChangePaused").detail("Action", pause ? "Paused" : "Resumed"); return Void(); } diff --git a/fdbclient/SystemData.cpp b/fdbclient/SystemData.cpp index de3bcf8287..f393dd83ca 100644 --- a/fdbclient/SystemData.cpp +++ b/fdbclient/SystemData.cpp @@ -523,19 +523,6 @@ UID decodeBackupProgressKey(const KeyRef& key) { return serverID; } -Value encodeBackupPausedValue(bool pause) { - BinaryWriter wr(Unversioned()); - wr << pause; - return wr.toValue(); -} - -bool decodeBackupPausedValue(const ValueRef& value) { - bool pause; - BinaryReader rd(value, Unversioned()); - rd >> pause; - return pause; -} - WorkerBackupStatus decodeBackupProgressValue(const ValueRef& value) { WorkerBackupStatus status; BinaryReader reader(value, IncludeVersion()); diff --git a/fdbclient/SystemData.h b/fdbclient/SystemData.h index 67b8ba5ffc..3b55731ac0 100644 --- a/fdbclient/SystemData.h +++ b/fdbclient/SystemData.h @@ -196,8 +196,6 @@ std::vector> decodeBackupStartedValue(const ValueRef& va // The key to signal backup workers that they should pause or resume. // "\xff\x02/backupPaused" := "[[0|1]]" extern const KeyRef backupPausedKey; -Value encodeBackupPausedValue(bool pause); -bool decodeBackupPausedValue(const ValueRef& value); extern const KeyRef coordinatorsKey; extern const KeyRef logsKey; diff --git a/fdbserver/BackupWorker.actor.cpp b/fdbserver/BackupWorker.actor.cpp index 5689b4bf76..20f6e363f3 100644 --- a/fdbserver/BackupWorker.actor.cpp +++ b/fdbserver/BackupWorker.actor.cpp @@ -915,7 +915,7 @@ ACTOR static Future monitorWorkerPause(BackupData* self) { tr->setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); Optional value = wait(tr->get(backupPausedKey)); - bool paused = value.present() && decodeBackupPausedValue(value.get()); + bool paused = value.present() && value.get() == LiteralStringRef("1"); if (self->paused.get() != paused) { TraceEvent(paused ? "BackupWorkerPaused" : "BackupWorkerResumed", self->myId); self->paused.set(paused); From ff3e3fcc13823dc01fbb766ae9f0a62b47b8a18c Mon Sep 17 00:00:00 2001 From: tclinken Date: Wed, 8 Apr 2020 00:45:56 -0700 Subject: [PATCH 1366/1604] Added /flow/PromiseStream/move unit test --- fdbrpc/FlowTests.actor.cpp | 54 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/fdbrpc/FlowTests.actor.cpp b/fdbrpc/FlowTests.actor.cpp index f53c0083aa..f378d9a794 100644 --- a/fdbrpc/FlowTests.actor.cpp +++ b/fdbrpc/FlowTests.actor.cpp @@ -1360,3 +1360,57 @@ TEST_CASE("/flow/DeterministicRandom/SignedOverflow") { std::numeric_limits::max() - 1); return Void(); } + +struct Tracker { + int copied; + bool moved; + Tracker(int copied = 0) : moved(false), copied(copied) {} + Tracker(Tracker&& other) : Tracker(other.copied) { + ASSERT(!other.moved); + other.moved = true; + } + Tracker(const Tracker& other) : Tracker(other.copied + 1) { ASSERT(!other.moved); } + Tracker& operator=(const Tracker& other) { + ASSERT(!other.moved); + this->moved = false; + this->copied = other.copied + 1; + return *this; + } + + ACTOR static Future listen(FutureStream stream) { + Tracker t = waitNext(stream); + ASSERT(!t.moved); + ASSERT(t.copied == 0); + return Void(); + } +}; + +TEST_CASE("/flow/PromiseStream/move") { + state PromiseStream stream; + { + // This tests the case when a callback is added before + // a value is sent + Future listener = Tracker::listen(stream.getFuture()); + stream.send(Tracker{}); + wait(listener); + } + { + // This tests the case when no callback is added until + // after a value is sent + stream.send(Tracker{}); + stream.send(Tracker{}); + { + Tracker t = waitNext(stream.getFuture()); + ASSERT(!t.moved); + ASSERT(t.copied == 0); + } + choose { + when(Tracker t = waitNext(stream.getFuture())) { + ASSERT(!t.moved); + ASSERT(t.copied == 0); + } + } + } + + return Void(); +} From 872877b221deb128d764ed96be4cbd27584440fa Mon Sep 17 00:00:00 2001 From: Steve Atherton Date: Wed, 8 Apr 2020 03:23:46 -0700 Subject: [PATCH 1367/1604] Added StringRef::copyTo(), a prettier way to memcpy a StringRef somewhere and with a more useful return value. --- flow/Arena.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/flow/Arena.h b/flow/Arena.h index 74a29c8b82..cfc756506d 100644 --- a/flow/Arena.h +++ b/flow/Arena.h @@ -576,6 +576,12 @@ public: return eatAny(StringRef((const uint8_t *)sep, strlen(sep)), foundSeparator); } + // Copies string contents to dst and returns a pointer to the next byte after + uint8_t * copyTo(uint8_t *dst) const { + memcpy(dst, data, length); + return dst + length; + } + private: // Unimplemented; blocks conversion through std::string StringRef( char* ); From 31fd4cd35a9dc983dc2db8c2fbda4927b910d0a4 Mon Sep 17 00:00:00 2001 From: Steve Atherton Date: Wed, 8 Apr 2020 03:25:04 -0700 Subject: [PATCH 1368/1604] Improved RedwoodRecord encoding/decoding tests, which found bugs in the version field scheme. Simplified it to select between different fixed size native integer formats. --- fdbserver/VersionedBTree.actor.cpp | 293 ++++++++++++++++------------- 1 file changed, 160 insertions(+), 133 deletions(-) diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index 2768871612..41de1d7329 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -2011,77 +2011,17 @@ struct RedwoodRecordRef { return key.expectedSize() + value.expectedSize(); } - class Writer { - public: - Writer(byte *ptr) : wptr(ptr) {} - - byte *wptr; - - void writeString(StringRef s) { - memcpy(wptr, s.begin(), s.size()); - wptr += s.size(); - } - - void writeFixedBigEndian(int64_t x, int len) { - if(len != 0) { - x = bigEndian64(x); - memcpy(wptr, ((byte *)&x) + sizeof(int64_t) - len, len); - wptr += len; - } - } - - // Find the number of bytes of precision to store for a version delta - // Possible return values are: 0, 2-8 - static int versionDeltaLen(Version x) { - if(x == 0) { - return 0; - } - - if(x < 0) { - x = ~x + 1; - } - x >>= 8; - - return 1 + sizeof(Version) - (clzll(x) >> 3); - } - }; - class Reader { public: Reader(const void *ptr) : rptr((const byte *)ptr) {} const byte *rptr; - int64_t readFixedBigEndian(int len) { - if(len == 0) { - return 0; - } - - // Start with all 0's or all 1's depending on sign - bool negative = int8_t(*rptr) < 0; - int64_t x = negative ? -1 : 0; - - // Copy len low bytes (in big endian form) into place - memcpy((uint8_t *)&x + sizeof(int64_t) - len, rptr, len); - rptr += len; - - // Convert to host byte order - x = bigEndian64(x); - - return x; - } - StringRef readString(int len) { StringRef s(rptr, len); rptr += len; return s; } - - const byte * readBytes(int len) { - const byte *b = rptr; - rptr += len; - return b; - } }; #pragma pack(push,1) @@ -2123,8 +2063,9 @@ struct RedwoodRecordRef { // Flags - 1 byte // 1 bit - borrow source is prev ancestor (otherwise next ancestor) // 1 bit - item is deleted - // 1 bit - has value (this is different from having a zero-length value) - // 3 bits - version delta length + // 1 bit - has value (different from zero-length value, if 0 value len will be 0) + // 1 bits - has nonzero version + // 2 bits - version delta integer size code, maps to 0, 2, 4, 8 // 2 bits - length fields format // // Length fields using 3 to 7 bytes total depending on length fields format @@ -2139,8 +2080,9 @@ struct RedwoodRecordRef { PREFIX_SOURCE_PREV = 0x80, IS_DELETED = 0x40, HAS_VALUE = 0x20, - VERSION_DELTA_LEN = 0x1C, - FORMAT = 0x03 + HAS_VERSION = 0x10, + VERSION_DELTA_SIZE = 0xC, + LENGTHS_FORMAT = 0x03 }; static inline int determineLengthFormat(int prefixLength, int suffixLength, int valueLength) { @@ -2159,8 +2101,9 @@ struct RedwoodRecordRef { } } + // Large prefix or suffix length, which should be rare, is format 3 byte * data() const { - switch(flags & FORMAT) { + switch(flags & LENGTHS_FORMAT) { case 0: return (byte *)(&LengthFormat0 + 1); case 1: return (byte *)(&LengthFormat1 + 1); case 2: return (byte *)(&LengthFormat2 + 1); @@ -2170,7 +2113,7 @@ struct RedwoodRecordRef { } int getKeyPrefixLength() const { - switch(flags & FORMAT) { + switch(flags & LENGTHS_FORMAT) { case 0: return LengthFormat0.prefixLength; case 1: return LengthFormat1.prefixLength; case 2: return LengthFormat2.prefixLength; @@ -2180,7 +2123,7 @@ struct RedwoodRecordRef { } int getKeySuffixLength() const { - switch(flags & FORMAT) { + switch(flags & LENGTHS_FORMAT) { case 0: return LengthFormat0.suffixLength; case 1: return LengthFormat1.suffixLength; case 2: return LengthFormat2.suffixLength; @@ -2190,7 +2133,7 @@ struct RedwoodRecordRef { } int getValueLength() const { - switch(flags & FORMAT) { + switch(flags & LENGTHS_FORMAT) { case 0: return LengthFormat0.valueLength; case 1: return LengthFormat1.valueLength; case 2: return LengthFormat2.valueLength; @@ -2207,21 +2150,63 @@ struct RedwoodRecordRef { return StringRef(data() + getKeySuffixLength(), getValueLength()); } - // version delta length is only 3 bits, so 0-7 represents length 0 and 2-8 - // Length of 1 is fairly useless anyway as version changes very rapidly. - void setVersionDeltaLen(int len) { - if(len > 0) { - --len; - } - flags |= (uint8_t)(len << 2); + bool hasVersion() const { + return flags & HAS_VERSION; } - int getVersionDeltaLength() const { - int len = (flags & VERSION_DELTA_LEN) >> 2; - if(len != 0) { - ++len; + int getVersionDeltaSizeBytes() const { + int code = (flags & VERSION_DELTA_SIZE) >> 2; + if(code != 0) { + return 1 << code; + } + return 0; + } + + static int getVersionDeltaSizeBytes(Version d) { + if(d == 0) { + return 0; + } + else if(d == (int16_t)d) { + return sizeof(uint16_t); + } + else if(d == (int32_t)d) { + return sizeof(int32_t); + } + return sizeof(int64_t); + } + + int getVersionDelta(const uint8_t *r) const { + int code = (flags & VERSION_DELTA_SIZE) >> 2; + switch(code) { + case 0: return 0; + case 1: return *(int16_t *)r; + case 2: return *(int32_t *)r; + case 3: + default: return *(int64_t *)r; + } + } + + // Version delta size should be 0 before calling + int setVersionDelta(Version d, uint8_t *w) { + flags |= HAS_VERSION; + if(d == 0) { + return 0; + } + else if(d == (int16_t)d) { + flags |= 1 << 2; + *(uint16_t *)w = d; + return sizeof(uint16_t); + } + else if(d == (int32_t)d) { + flags |= 2 << 2; + *(int32_t *)w = d; + return sizeof(int32_t); + } + else { + flags |= 3 << 2; + *(int64_t *)w = d; + return sizeof(int64_t); } - return len; } bool hasValue() const { @@ -2279,15 +2264,17 @@ struct RedwoodRecordRef { value = r.readString(valueLen); } - Version versionDelta = r.readFixedBigEndian(getVersionDeltaLength()); - Version v = base.version + versionDelta; + Version v = 0; + if(hasVersion()) { + v = base.version + getVersionDelta(r.rptr); + } return RedwoodRecordRef(k, v, value); } int size() const { - int size = 1 + getVersionDeltaLength(); - switch(flags & FORMAT) { + int size = 1 + getVersionDeltaSizeBytes(); + switch(flags & LENGTHS_FORMAT) { case 0: return size + sizeof(LengthFormat0) + LengthFormat0.suffixLength + LengthFormat0.valueLength; case 1: return size + sizeof(LengthFormat1) + LengthFormat1.suffixLength + LengthFormat1.valueLength; case 2: return size + sizeof(LengthFormat2) + LengthFormat2.suffixLength + LengthFormat2.valueLength; @@ -2299,23 +2286,26 @@ struct RedwoodRecordRef { std::string toString() const { std::string flagString = " "; if(flags & PREFIX_SOURCE_PREV) { - flagString += "PrefixSource "; + flagString += "PrefixSource|"; } if(flags & IS_DELETED) { - flagString += "IsDeleted"; + flagString += "IsDeleted|"; } if(hasValue()) { - flagString += "HasValue"; + flagString += "HasValue|"; } - int lengthFormat = flags & FORMAT; + if(hasVersion()) { + flagString += "HasVersion|"; + } + int lengthFormat = flags & LENGTHS_FORMAT; Reader r(data()); int prefixLen = getKeyPrefixLength(); int keySuffixLen = getKeySuffixLength(); int valueLen = getValueLength(); - return format("lengthFormat: %d totalDeltaSize: %d flags: %s prefixLen: %d keySuffixLen: %d versionDeltaLen: %d valueLen %d raw: %s", - lengthFormat, size(), flagString.c_str(), prefixLen, keySuffixLen, getVersionDeltaLength(), valueLen, StringRef((const uint8_t *)this, size()).toHexString().c_str()); + return format("lengthFormat: %d totalDeltaSize: %d flags: %s prefixLen: %d keySuffixLen: %d versionDeltaSizeBytes: %d valueLen %d raw: %s", + lengthFormat, size(), flagString.c_str(), prefixLen, keySuffixLen, getVersionDeltaSizeBytes(), valueLen, StringRef((const uint8_t *)this, size()).toHexString().c_str()); } }; @@ -2364,7 +2354,8 @@ struct RedwoodRecordRef { int valueLen = value.present() ? value.get().size() : 0; int formatType = Delta::determineLengthFormat(prefixLen, keySuffixLen, valueLen); - return 1 + Delta::LengthFormatSizes[formatType] + keySuffixLen + valueLen + Writer::versionDeltaLen(version - base.version); + int versionBytes = version == 0 ? 0 : Delta::getVersionDeltaSizeBytes(version - base.version); + return 1 + Delta::LengthFormatSizes[formatType] + keySuffixLen + valueLen + versionBytes; } // commonPrefix between *this and base can be passed if known @@ -2388,24 +2379,21 @@ struct RedwoodRecordRef { case 3: default: d.LengthFormat3.prefixLength = keyPrefixLen; d.LengthFormat3.suffixLength = keySuffix.size(); d.LengthFormat3.valueLength = valueLen; break; } - - d.flags |= Delta::determineLengthFormat(keyPrefixLen, keySuffix.size(), valueLen); - Writer w(d.data()); - // key suffix bytes - w.writeString(keySuffix); - // value bytes + uint8_t *wptr = d.data(); + // Write key suffix string + wptr = keySuffix.copyTo(wptr); + + // Write value bytes if(value.present()) { - w.writeString(value.get()); + wptr = value.get().copyTo(wptr); } - // version delta bytes, and set version delta len flags - Version versionDelta = version - base.version; - int versionDeltaLen = Writer::versionDeltaLen(versionDelta); - d.setVersionDeltaLen(versionDeltaLen); - w.writeFixedBigEndian(versionDelta, versionDeltaLen); + if(version != 0) { + wptr += d.setVersionDelta(version - base.version, wptr); + } - return w.wptr - (uint8_t *)&d; + return wptr - (uint8_t *)&d; } static std::string kvformat(StringRef s, int hexLimit = -1) { @@ -5454,43 +5442,53 @@ struct IntIntPair { } }; -void deltaTest(RedwoodRecordRef rec, RedwoodRecordRef base) { - char buf[1000]; - RedwoodRecordRef::Delta &d = *(RedwoodRecordRef::Delta *)buf; +int deltaTest(RedwoodRecordRef rec, RedwoodRecordRef base) { + std::vector buf(rec.key.size() + rec.value.orDefault(StringRef()).size() + 20); + RedwoodRecordRef::Delta &d = *(RedwoodRecordRef::Delta *)&buf.front(); Arena mem; int expectedSize = rec.deltaSize(base, false); int deltaSize = rec.writeDelta(d, base); RedwoodRecordRef decoded = d.apply(base, mem); - if(decoded != rec || expectedSize != deltaSize) { + if(decoded != rec || expectedSize != deltaSize || d.size() != deltaSize) { printf("\n"); printf("Base: %s\n", base.toString().c_str()); - printf("ExpectedSize: %d\n", expectedSize); - printf("DeltaSizeWritten: %d\n", deltaSize); - printf("DeltaToString: %s\n", d.toString().c_str()); printf("Record: %s\n", rec.toString().c_str()); printf("Decoded: %s\n", decoded.toString().c_str()); + printf("deltaSize(): %d\n", expectedSize); + printf("writeDelta(): %d\n", deltaSize); + printf("d.size(): %d\n", d.size()); + printf("DeltaToString: %s\n", d.toString().c_str()); printf("RedwoodRecordRef::Delta test failure!\n"); ASSERT(false); } + + return deltaSize; } -Standalone randomRedwoodRecordRef(int maxKeySize = 3, int maxValueSize = 500) { +RedwoodRecordRef randomRedwoodRecordRef(const std::string &keyBuffer, const std::string &valueBuffer) { RedwoodRecordRef rec; - KeyValue kv = randomKV(maxKeySize, maxValueSize); - rec.key = kv.key; - - if(deterministicRandom()->random01() < .9) { - rec.value = kv.value; + rec.key = StringRef((uint8_t *)keyBuffer.data(), deterministicRandom()->randomInt(0, keyBuffer.size())); + if(deterministicRandom()->coinflip()) { + rec.value = StringRef((uint8_t *)valueBuffer.data(), deterministicRandom()->randomInt(0, valueBuffer.size())); } - rec.version = deterministicRandom()->coinflip() ? 0 : deterministicRandom()->randomInt64(0, std::numeric_limits::max()); + int versionIntSize = deterministicRandom()->randomInt(0, 8) * 8; + if(versionIntSize > 0) { + --versionIntSize; + int64_t max = ((int64_t)1 << versionIntSize) - 1; + rec.version = deterministicRandom()->randomInt64(0, max); + } - return Standalone(rec, kv.arena()); + return rec; } TEST_CASE("!/redwood/correctness/unit/RedwoodRecordRef") { + ASSERT(RedwoodRecordRef::Delta::LengthFormatSizes[0] == 3); + ASSERT(RedwoodRecordRef::Delta::LengthFormatSizes[1] == 4); + ASSERT(RedwoodRecordRef::Delta::LengthFormatSizes[2] == 6); + ASSERT(RedwoodRecordRef::Delta::LengthFormatSizes[3] == 8); // Test pageID stuff. { @@ -5506,9 +5504,6 @@ TEST_CASE("!/redwood/correctness/unit/RedwoodRecordRef") { ASSERT(r2.getChildPage().begin() != id.begin()); } - // Testing common prefix calculation for integer fields using the member function that calculates this directly - // and by serializing the integer fields to arrays and finding the common prefix length of the two arrays - deltaTest(RedwoodRecordRef(LiteralStringRef(""), 0, LiteralStringRef("")), RedwoodRecordRef(LiteralStringRef(""), 0, LiteralStringRef("")) ); @@ -5521,16 +5516,32 @@ TEST_CASE("!/redwood/correctness/unit/RedwoodRecordRef") { RedwoodRecordRef(LiteralStringRef("abcd"), 0, LiteralStringRef("")) ); - deltaTest(RedwoodRecordRef(LiteralStringRef("abc"), 2, LiteralStringRef("")), + deltaTest(RedwoodRecordRef(LiteralStringRef("abcd"), 2, LiteralStringRef("")), RedwoodRecordRef(LiteralStringRef("abc"), 2, LiteralStringRef("")) ); - deltaTest(RedwoodRecordRef(LiteralStringRef("abc"), 2, LiteralStringRef("")), - RedwoodRecordRef(LiteralStringRef("ab"), 2, LiteralStringRef("")) + deltaTest(RedwoodRecordRef(std::string(300, 'k'), 2, std::string(1e6, 'v')), + RedwoodRecordRef(std::string(300, 'k'), 2, LiteralStringRef("")) ); - deltaTest(RedwoodRecordRef(LiteralStringRef("abc"), 2, LiteralStringRef("")), - RedwoodRecordRef(LiteralStringRef("abc"), 2, LiteralStringRef("")) + deltaTest(RedwoodRecordRef(LiteralStringRef(""), 2, LiteralStringRef("")), + RedwoodRecordRef(LiteralStringRef(""), 1, LiteralStringRef("")) + ); + + deltaTest(RedwoodRecordRef(LiteralStringRef(""), 0xffff, LiteralStringRef("")), + RedwoodRecordRef(LiteralStringRef(""), 1, LiteralStringRef("")) + ); + + deltaTest(RedwoodRecordRef(LiteralStringRef(""), 1, LiteralStringRef("")), + RedwoodRecordRef(LiteralStringRef(""), 0xffff, LiteralStringRef("")) + ); + + deltaTest(RedwoodRecordRef(LiteralStringRef(""), 0xffffff, LiteralStringRef("")), + RedwoodRecordRef(LiteralStringRef(""), 1, LiteralStringRef("")) + ); + + deltaTest(RedwoodRecordRef(LiteralStringRef(""), 1, LiteralStringRef("")), + RedwoodRecordRef(LiteralStringRef(""), 0xffffff, LiteralStringRef("")) ); Arena mem; @@ -5538,16 +5549,32 @@ TEST_CASE("!/redwood/correctness/unit/RedwoodRecordRef") { uint64_t total; uint64_t count; uint64_t i; + int64_t bytes; + std::string keyBuffer(30000, 'k'); + std::string valueBuffer(70000, 'v'); start = timer(); - total = 0; - count = 1e6; + count = 1000; + bytes = 0; for(i = 0; i < count; ++i) { - Standalone a = randomRedwoodRecordRef(); - Standalone b = randomRedwoodRecordRef(); - deltaTest(a, b); + RedwoodRecordRef a = randomRedwoodRecordRef(keyBuffer, valueBuffer); + RedwoodRecordRef b = randomRedwoodRecordRef(keyBuffer, valueBuffer); + bytes += deltaTest(a, b); } - printf("Random deltaTest() %g M/s\n", count / (timer() - start) / 1e6); + double elapsed = timer() - start; + printf("DeltaTest() on random large records %g M/s %g MB/s\n", count / elapsed / 1e6, bytes / elapsed / 1e6); + + keyBuffer.resize(30); + valueBuffer.resize(100); + start = timer(); + count = 1e6; + bytes = 0; + for(i = 0; i < count; ++i) { + RedwoodRecordRef a = randomRedwoodRecordRef(keyBuffer, valueBuffer); + RedwoodRecordRef b = randomRedwoodRecordRef(keyBuffer, valueBuffer); + bytes += deltaTest(a, b); + } + printf("DeltaTest() on random small records %g M/s %g MB/s\n", count / elapsed / 1e6, bytes / elapsed / 1e6); RedwoodRecordRef rec1; RedwoodRecordRef rec2; From d9792007998b2e0cbb1f41ecd543779dc4991530 Mon Sep 17 00:00:00 2001 From: Steve Atherton Date: Wed, 8 Apr 2020 03:29:12 -0700 Subject: [PATCH 1369/1604] Bump format version. --- fdbserver/VersionedBTree.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index 41de1d7329..4b66d176cd 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -2608,7 +2608,7 @@ public: #pragma pack(push, 1) struct MetaKey { - static constexpr int FORMAT_VERSION = 6; + static constexpr int FORMAT_VERSION = 7; // This serves as the format version for the entire tree, individual pages will not be versioned uint16_t formatVersion; uint8_t height; From 9e82788c285b53ee9cd2ad61de9e0896589d7283 Mon Sep 17 00:00:00 2001 From: Steve Atherton Date: Wed, 8 Apr 2020 03:38:37 -0700 Subject: [PATCH 1370/1604] Bug fix, BTree header wasn't large enough to hold the largest possible root page references. --- fdbserver/VersionedBTree.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index 4b66d176cd..b064a0cb78 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -3362,7 +3362,7 @@ private: // MetaKey changes size so allocate space for it to expand into union { - uint8_t headerSpace[sizeof(MetaKey) + sizeof(LogicalPageID) * 20]; + uint8_t headerSpace[sizeof(MetaKey) + sizeof(LogicalPageID) * 30]; MetaKey m_header; }; From 6916434f7d0717fc76b2ad1476f9af7145274004 Mon Sep 17 00:00:00 2001 From: Balachandar Namasivayam Date: Wed, 8 Apr 2020 10:48:32 -0700 Subject: [PATCH 1371/1604] Addressed review comments --- fdbserver/DataDistribution.actor.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/fdbserver/DataDistribution.actor.cpp b/fdbserver/DataDistribution.actor.cpp index 182e42cea9..371cd69985 100644 --- a/fdbserver/DataDistribution.actor.cpp +++ b/fdbserver/DataDistribution.actor.cpp @@ -689,8 +689,8 @@ struct DDTeamCollection : ReferenceCounted { } void removeLaggingStorageServer(Key zoneId) { - ASSERT(lagging_zones.find(zoneId) != lagging_zones.end()) auto iter = lagging_zones.find(zoneId); + ASSERT(iter != lagging_zones.end()); iter->second--; ASSERT(iter->second >= 0); if (iter->second == 0) @@ -2613,18 +2613,24 @@ ACTOR Future updateServerMetrics( TCServerInfo *server ) { } } - if ( server->serverMetrics.get().lastUpdate < now() - SERVER_KNOBS->DD_SS_STUCK_TIME_LIMIT && server->ssVersionTooFarBehind.get() == false ) { - TraceEvent("StorageServerStuck", server->collection->distributorId).detail("ServerId", server->id.toString()).detail("LastUpdate", server->serverMetrics.get().lastUpdate); - server->ssVersionTooFarBehind.set(true); - server->collection->addLaggingStorageServer(server->lastKnownInterface.locality.zoneId().get()); - } else if ( server->serverMetrics.get().versionLag > SERVER_KNOBS->DD_SS_FAILURE_VERSIONLAG && server->ssVersionTooFarBehind.get() == false ) { + if ( server->serverMetrics.get().lastUpdate < now() - SERVER_KNOBS->DD_SS_STUCK_TIME_LIMIT ) { + if (server->ssVersionTooFarBehind.get() == false) { + TraceEvent("StorageServerStuck", server->collection->distributorId).detail("ServerId", server->id.toString()).detail("LastUpdate", server->serverMetrics.get().lastUpdate); + server->ssVersionTooFarBehind.set(true); + server->collection->addLaggingStorageServer(server->lastKnownInterface.locality.zoneId().get()); + } + } else if ( server->serverMetrics.get().versionLag > SERVER_KNOBS->DD_SS_FAILURE_VERSIONLAG ) { + if (server->ssVersionTooFarBehind.get() == false) { TraceEvent("SSVersionDiffLarge", server->collection->distributorId).detail("ServerId", server->id.toString()).detail("VersionLag", server->serverMetrics.get().versionLag); server->ssVersionTooFarBehind.set(true); server->collection->addLaggingStorageServer(server->lastKnownInterface.locality.zoneId().get()); - } else if ( server->serverMetrics.get().versionLag < SERVER_KNOBS->DD_SS_ALLOWED_VERSIONLAG && server->ssVersionTooFarBehind.get() == true ) { + } + } else if ( server->serverMetrics.get().versionLag < SERVER_KNOBS->DD_SS_ALLOWED_VERSIONLAG ) { + if (server->ssVersionTooFarBehind.get() == true) { TraceEvent("SSVersionDiffNormal", server->collection->distributorId).detail("ServerId", server->id.toString()).detail("VersionLag", server->serverMetrics.get().versionLag); server->ssVersionTooFarBehind.set(false); server->collection->removeLaggingStorageServer(server->lastKnownInterface.locality.zoneId().get()); + } } return Void(); } @@ -3803,12 +3809,8 @@ ACTOR Future storageServerTracker( server->wakeUpTracker = Promise(); } when(wait(storeTypeTracker)) {} - when(wait(server->ssVersionTooFarBehind.onChange())) { - TraceEvent("SSVersionTooFarBehindGotUpdate", self->distributorId).detail("ServerID", server->id).detail("SSVersionTooFarBehind", server->ssVersionTooFarBehind.get()); - } - when(wait(self->disableFailingLaggingServers.onChange())) { - TraceEvent("DisableFailingLaggingServersGotUpdate", self->distributorId).detail("ServerID", server->id).detail("DisableFailingLaggingServers", self->disableFailingLaggingServers.get()); - } + when(wait(server->ssVersionTooFarBehind.onChange())) { } + when(wait(self->disableFailingLaggingServers.onChange())) { } } if (recordTeamCollectionInfo) { From 488c20e58e829bec133446516cece7024a549178 Mon Sep 17 00:00:00 2001 From: tclinken Date: Wed, 8 Apr 2020 11:24:56 -0700 Subject: [PATCH 1372/1604] Fixed failing "/flow/flow/promisestream callbacks" unit test --- fdbrpc/FlowTests.actor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/fdbrpc/FlowTests.actor.cpp b/fdbrpc/FlowTests.actor.cpp index f378d9a794..4fdb7aa343 100644 --- a/fdbrpc/FlowTests.actor.cpp +++ b/fdbrpc/FlowTests.actor.cpp @@ -80,6 +80,7 @@ class LambdaCallback : public CallbackType, public FastAllocated Date: Wed, 8 Apr 2020 11:33:07 -0700 Subject: [PATCH 1373/1604] fix issues according to andrew's comments --- fdbclient/NativeAPI.actor.cpp | 7 ++++--- fdbclient/ReadYourWrites.actor.cpp | 16 ++++------------ 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index d65f09b125..ffa5cbe110 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -520,7 +520,8 @@ DatabaseContext::DatabaseContext( transactionKeyServerLocationRequests("KeyServerLocationRequests", cc), transactionKeyServerLocationRequestsCompleted("KeyServerLocationRequestsCompleted", cc), transactionsTooOld("TooOld", cc), transactionsFutureVersions("FutureVersions", cc), transactionsNotCommitted("NotCommitted", cc), transactionsMaybeCommitted("MaybeCommitted", cc), transactionsResourceConstrained("ResourceConstrained", cc), transactionsThrottled("Throttled", cc), transactionsProcessBehind("ProcessBehind", cc), outstandingWatches(0), latencies(1000), readLatencies(1000), commitLatencies(1000), - GRVLatencies(1000), mutationsPerCommit(1000), bytesPerCommit(1000), mvCacheInsertLocation(0), healthMetricsLastUpdated(0), detailedHealthMetricsLastUpdated(0), internal(internal) + GRVLatencies(1000), mutationsPerCommit(1000), bytesPerCommit(1000), mvCacheInsertLocation(0), healthMetricsLastUpdated(0), detailedHealthMetricsLastUpdated(0), internal(internal), + specialKeySpace(std::make_shared(normalKeys.begin, specialKeys.end)), cKImpl(std::make_shared(conflictingKeys.begin, conflictingKeys.end)) { dbId = deterministicRandom()->randomUniqueID(); connected = clientInfo->get().proxies.size() ? Void() : clientInfo->onChange(); @@ -540,8 +541,8 @@ DatabaseContext::DatabaseContext( monitorMasterProxiesInfoChange = monitorMasterProxiesChange(clientInfo, &masterProxiesChangeTrigger); clientStatusUpdater.actor = clientStatusUpdateActor(this); - specialKeySpace = std::make_shared(normalKeys.begin, normalKeys.end); - cKImpl = std::make_shared(conflictingKeys.begin, conflictingKeys.end); + // specialKeySpace = std::make_shared(normalKeys.begin, normalKeys.end); + // cKImpl = std::make_shared(conflictingKeys.begin, conflictingKeys.end); specialKeySpace->registerKeyRange(conflictingKeys, cKImpl.get()); } diff --git a/fdbclient/ReadYourWrites.actor.cpp b/fdbclient/ReadYourWrites.actor.cpp index e1842b21a6..073785dc6d 100644 --- a/fdbclient/ReadYourWrites.actor.cpp +++ b/fdbclient/ReadYourWrites.actor.cpp @@ -1230,12 +1230,8 @@ Future< Optional > ReadYourWritesTransaction::get( const Key& key, bool s } // special key space are only allowed to query if both begin and end start with \xff\xff - if (key.startsWith(specialKeys.begin)) { - Reference self = Reference(this); - auto result = getDatabase()->specialKeySpace->get(self, key); - self.extractPtr(); // avoid to destory the transaction object itself - return result; - } + if (key.startsWith(specialKeys.begin)) + return getDatabase()->specialKeySpace->get(Reference::addRef(this), key); if(checkUsedDuringCommit()) { return used_during_commit(); @@ -1289,12 +1285,8 @@ Future< Standalone > ReadYourWritesTransaction::getRange( } // special key space are only allowed to query if both begin and end start with \xff\xff - if (begin.getKey().startsWith(specialKeys.begin) && end.getKey().startsWith(specialKeys.begin)) { - Reference self = Reference(this); - auto result = getDatabase()->specialKeySpace->getRange(self, begin, end, limits, reverse); - self.extractPtr(); // avoid to destory the transaction object itself - return result; - } + if (begin.getKey().startsWith(specialKeys.begin) && end.getKey().startsWith(specialKeys.begin)) + return getDatabase()->specialKeySpace->getRange(Reference::addRef(this), begin, end, limits, reverse); if(checkUsedDuringCommit()) { return used_during_commit(); From 2325ab209f1bba575bee0c240b90f62da5b40a44 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Wed, 8 Apr 2020 12:21:53 -0700 Subject: [PATCH 1374/1604] FastRestore:Applier:Avoid extra copy in getAndComputeStagingKeys --- fdbserver/RestoreApplier.actor.cpp | 6 ++---- fdbserver/RestoreCommon.actor.h | 8 ++++++++ fdbserver/RestoreLoader.actor.cpp | 5 +++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/fdbserver/RestoreApplier.actor.cpp b/fdbserver/RestoreApplier.actor.cpp index 6df711acd6..44bc4ec1b3 100644 --- a/fdbserver/RestoreApplier.actor.cpp +++ b/fdbserver/RestoreApplier.actor.cpp @@ -320,8 +320,7 @@ ACTOR static Future precomputeMutationsResult(Reference // Get keys in stagingKeys which does not have a baseline key by reading database cx, and precompute the key's value std::vector> fGetAndComputeKeys; - std::vector::iterator>> incompleteStagingKeysBuf(1); - std::map::iterator>& incompleteStagingKeys = incompleteStagingKeysBuf.back(); + std::map::iterator> incompleteStagingKeys; std::map::iterator stagingKeyIter = batchData->stagingKeys.begin(); int numKeysInBatch = 0; for (; stagingKeyIter != batchData->stagingKeys.end(); stagingKeyIter++) { @@ -333,8 +332,7 @@ ACTOR static Future precomputeMutationsResult(Reference if (numKeysInBatch == SERVER_KNOBS->FASTRESTORE_APPLIER_FETCH_KEYS_SIZE) { fGetAndComputeKeys.push_back(getAndComputeStagingKeys(incompleteStagingKeys, cx, applierID)); numKeysInBatch = 0; - incompleteStagingKeysBuf.push_back(std::map::iterator>()); - incompleteStagingKeys = incompleteStagingKeysBuf.back(); + incompleteStagingKeys.clear(); } } if (numKeysInBatch > 0) { diff --git a/fdbserver/RestoreCommon.actor.h b/fdbserver/RestoreCommon.actor.h index ea0e54837d..44a2c8c8b7 100644 --- a/fdbserver/RestoreCommon.actor.h +++ b/fdbserver/RestoreCommon.actor.h @@ -279,10 +279,18 @@ Future getBatchReplies(RequestStream Interface::*channel, std::ma state std::vector> ongoingReplies; state std::vector ongoingRepliesIndex; + state int loopCount = 0; loop { ongoingReplies.clear(); ongoingRepliesIndex.clear(); for (int i = 0; i < cmdReplies.size(); ++i) { + TraceEvent(SevInfo, "FastRestoreGetBatchReplies") + .detail("Requests", requests.size()) + .detail("OutstandingReplies", oustandingReplies) + .detail("ReplyIndex", i) + .detail("ReplyReady", cmdReplies[i].isReady()) + .detail("RequestNode", requests[i].first) + .detail("Request", requests[i].second.toString()); if (!cmdReplies[i].isReady()) { // still wait for reply ongoingReplies.push_back(cmdReplies[i]); ongoingRepliesIndex.push_back(i); diff --git a/fdbserver/RestoreLoader.actor.cpp b/fdbserver/RestoreLoader.actor.cpp index 90be2dbd43..664391ae42 100644 --- a/fdbserver/RestoreLoader.actor.cpp +++ b/fdbserver/RestoreLoader.actor.cpp @@ -518,10 +518,11 @@ ACTOR Future sendMutationsToApplier(VersionedMutationsMap* pkvOps, int bat batchIndex, asset, prevVersion, commitVersion.version, isRangeFile, applierMutationsBuffer[applierID], applierSubsBuffer[applierID])); } - TraceEvent(SevDebug, "FastRestore_SendMutationToApplier") + TraceEvent(SevDebug, "FastRestoreLoaderSendMutationToApplier") .detail("PrevVersion", prevVersion) .detail("CommitVersion", commitVersion.toString()) - .detail("RestoreAsset", asset.toString()); + .detail("RestoreAsset", asset.toString()) + .detail("Requests", requests.size()); ASSERT(prevVersion < commitVersion.version); prevVersion = commitVersion.version; wait(sendBatchRequests(&RestoreApplierInterface::sendMutationVector, *pApplierInterfaces, requests, From 20b298484167ec75634b2b4b3a712a23c7e07c76 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Wed, 8 Apr 2020 12:43:25 -0700 Subject: [PATCH 1375/1604] fix issues according to andrew's comments --- fdbclient/NativeAPI.actor.cpp | 12 ++++---- fdbclient/SpecialKeySpace.actor.cpp | 28 +++++++++---------- fdbclient/SystemData.cpp | 5 ++-- fdbclient/SystemData.h | 3 +- .../workloads/ReportConflictingKeys.actor.cpp | 12 ++++---- 5 files changed, 28 insertions(+), 32 deletions(-) diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index ffa5cbe110..8cb1153665 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -521,7 +521,7 @@ DatabaseContext::DatabaseContext( transactionsFutureVersions("FutureVersions", cc), transactionsNotCommitted("NotCommitted", cc), transactionsMaybeCommitted("MaybeCommitted", cc), transactionsResourceConstrained("ResourceConstrained", cc), transactionsThrottled("Throttled", cc), transactionsProcessBehind("ProcessBehind", cc), outstandingWatches(0), latencies(1000), readLatencies(1000), commitLatencies(1000), GRVLatencies(1000), mutationsPerCommit(1000), bytesPerCommit(1000), mvCacheInsertLocation(0), healthMetricsLastUpdated(0), detailedHealthMetricsLastUpdated(0), internal(internal), - specialKeySpace(std::make_shared(normalKeys.begin, specialKeys.end)), cKImpl(std::make_shared(conflictingKeys.begin, conflictingKeys.end)) + specialKeySpace(std::make_shared(normalKeys.begin, specialKeys.end)), cKImpl(std::make_shared(conflictingKeysRange.begin, conflictingKeysRange.end)) { dbId = deterministicRandom()->randomUniqueID(); connected = clientInfo->get().proxies.size() ? Void() : clientInfo->onChange(); @@ -541,9 +541,7 @@ DatabaseContext::DatabaseContext( monitorMasterProxiesInfoChange = monitorMasterProxiesChange(clientInfo, &masterProxiesChangeTrigger); clientStatusUpdater.actor = clientStatusUpdateActor(this); - // specialKeySpace = std::make_shared(normalKeys.begin, normalKeys.end); - // cKImpl = std::make_shared(conflictingKeys.begin, conflictingKeys.end); - specialKeySpace->registerKeyRange(conflictingKeys, cKImpl.get()); + specialKeySpace->registerKeyRange(conflictingKeysRange, cKImpl.get()); } DatabaseContext::DatabaseContext( const Error &err ) : deferredError(err), cc("TransactionMetrics"), transactionReadVersions("ReadVersions", cc), @@ -2758,7 +2756,7 @@ ACTOR static Future tryCommit( Database cx, Reference // clear the RYW transaction which contains previous conflicting keys tr->info.conflictingKeys.reset(); if (ci.conflictingKRIndices.present()) { - tr->info.conflictingKeys = std::make_shared>(conflictingKeysFalse); + tr->info.conflictingKeys = std::make_shared>(conflictingKeysFalse, specialKeys.end); state Standalone> conflictingKRIndices = ci.conflictingKRIndices.get(); // drop duplicate indices and merge overlapped ranges // Note: addReadConflictRange in native transaction object does not merge overlapped ranges @@ -2766,8 +2764,8 @@ ACTOR static Future tryCommit( Database cx, Reference conflictingKRIndices.end()); for (auto const& rCRIndex : mergedIds) { const KeyRangeRef kr = req.transaction.read_conflict_ranges[rCRIndex]; - const KeyRange krWithPrefix = KeyRangeRef(kr.begin.withPrefix(conflictingKeysPrefix), - kr.end.withPrefix(conflictingKeysPrefix)); + const KeyRange krWithPrefix = KeyRangeRef(kr.begin.withPrefix(conflictingKeysRange.begin), + kr.end.withPrefix(conflictingKeysRange.begin)); tr->info.conflictingKeys->insert(krWithPrefix, conflictingKeysTrue); } } diff --git a/fdbclient/SpecialKeySpace.actor.cpp b/fdbclient/SpecialKeySpace.actor.cpp index 7bcdfa6707..8637be29a2 100644 --- a/fdbclient/SpecialKeySpace.actor.cpp +++ b/fdbclient/SpecialKeySpace.actor.cpp @@ -73,10 +73,10 @@ ACTOR Future> SpecialKeySpace::getRangeAggregationAct state int actualEndOffset; // remove specialKeys prefix - if (withPrefix) { - begin.setKey(begin.getKey().removePrefix(specialKeys.begin)); - end.setKey(end.getKey().removePrefix(specialKeys.begin)); - } + // if (withPrefix) { + // begin.setKey(begin.getKey().removePrefix(specialKeys.begin)); + // end.setKey(end.getKey().removePrefix(specialKeys.begin)); + // } // make sure offset == 1 state RangeMap::Iterator beginIter = @@ -158,14 +158,14 @@ ACTOR Future> SpecialKeySpace::getRangeAggregationAct // limits handler for (int i = pairs.size() - 1; i >= 0; --i) { // TODO : use depends on with push_back - KeyValueRef element = - withPrefix ? KeyValueRef(pairs[i].key.withPrefix(specialKeys.begin, result.arena()), pairs[i].value) - : pairs[i]; - result.push_back(result.arena(), element); + // KeyValueRef element = + // withPrefix ? KeyValueRef(pairs[i].key.withPrefix(specialKeys.begin, result.arena()), pairs[i].value) + // : pairs[i]; + result.push_back(result.arena(), pairs[i]); // Note : behavior here is even the last k-v pair makes total bytes larger than specified, it is still // returned In other words, the total size of the returned value (less the last entry) will be less than // byteLimit - limits.decrement(element); + limits.decrement(pairs[i]); if (limits.isReached()) { result.more = true; result.readToBegin = false; @@ -184,14 +184,14 @@ ACTOR Future> SpecialKeySpace::getRangeAggregationAct // limits handler for (int i = 0; i < pairs.size(); ++i) { // TODO : use depends on with push_back - KeyValueRef element = - withPrefix ? KeyValueRef(pairs[i].key.withPrefix(specialKeys.begin, result.arena()), pairs[i].value) - : pairs[i]; - result.push_back(result.arena(), element); + // KeyValueRef element = + // withPrefix ? KeyValueRef(pairs[i].key.withPrefix(specialKeys.begin, result.arena()), pairs[i].value) + // : pairs[i]; + result.push_back(result.arena(), pairs[i]); // Note : behavior here is even the last k-v pair makes total bytes larger than specified, it is still // returned In other words, the total size of the returned value (less the last entry) will be less than // byteLimit - limits.decrement(element); + limits.decrement(pairs[i]); if (limits.isReached()) { result.more = true; result.readThroughEnd = false; diff --git a/fdbclient/SystemData.cpp b/fdbclient/SystemData.cpp index 7fe14ce422..af66917836 100644 --- a/fdbclient/SystemData.cpp +++ b/fdbclient/SystemData.cpp @@ -59,9 +59,8 @@ void decodeKeyServersValue( const ValueRef& value, vector& src, vector } } -const KeyRangeRef conflictingKeys = KeyRangeRef(LiteralStringRef("/transaction/conflicting_keys/"), LiteralStringRef("/transaction/conflicting_keys/\xff")); -const KeyRef conflictingKeysPrefix = conflictingKeys.begin; -const Key conflictingKeysAbsolutePrefix = conflictingKeysPrefix.withPrefix(specialKeys.begin); +const KeyRangeRef conflictingKeysRange = KeyRangeRef(LiteralStringRef("\xff\xff/transaction/conflicting_keys/"), LiteralStringRef("\xff\xff/transaction/conflicting_keys/\xff")); +const KeyRef conflictingKeysPrefix = LiteralStringRef("/transaction/conflicting_keys/"); const ValueRef conflictingKeysTrue = LiteralStringRef("1"); const ValueRef conflictingKeysFalse = LiteralStringRef("0"); diff --git a/fdbclient/SystemData.h b/fdbclient/SystemData.h index 21ae9b5e20..688493cc00 100644 --- a/fdbclient/SystemData.h +++ b/fdbclient/SystemData.h @@ -66,8 +66,7 @@ UID serverKeysDecodeServer( const KeyRef& key ); bool serverHasKey( ValueRef storedValue ); extern const KeyRef conflictingKeysPrefix; -extern const KeyRangeRef conflictingKeys; -extern const Key conflictingKeysAbsolutePrefix; +extern const KeyRangeRef conflictingKeysRange; extern const ValueRef conflictingKeysTrue, conflictingKeysFalse; extern const KeyRef cacheKeysPrefix; diff --git a/fdbserver/workloads/ReportConflictingKeys.actor.cpp b/fdbserver/workloads/ReportConflictingKeys.actor.cpp index 1ec33a67b8..c01bf288de 100644 --- a/fdbserver/workloads/ReportConflictingKeys.actor.cpp +++ b/fdbserver/workloads/ReportConflictingKeys.actor.cpp @@ -162,8 +162,8 @@ struct ReportConflictingKeysWorkload : TestWorkload { if (foundConflict) { // \xff\xff/transaction/conflicting_keys is always initialized to false, skip it here state KeyRange ckr = - KeyRangeRef(keyAfter(LiteralStringRef("").withPrefix(conflictingKeysAbsolutePrefix)), - LiteralStringRef("\xff\xff").withPrefix(conflictingKeysAbsolutePrefix)); + KeyRangeRef(keyAfter(LiteralStringRef("").withPrefix(conflictingKeysRange.begin)), + LiteralStringRef("\xff\xff").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 Future> conflictingKeyRangesFuture = @@ -176,14 +176,14 @@ struct ReportConflictingKeysWorkload : TestWorkload { ASSERT(!conflictingKeyRanges.more); for (int i = 0; i < conflictingKeyRanges.size(); i += 2) { KeyValueRef startKeyWithPrefix = conflictingKeyRanges[i]; - ASSERT(startKeyWithPrefix.key.startsWith(conflictingKeysAbsolutePrefix)); + ASSERT(startKeyWithPrefix.key.startsWith(conflictingKeysRange.begin)); ASSERT(startKeyWithPrefix.value == conflictingKeysTrue); KeyValueRef endKeyWithPrefix = conflictingKeyRanges[i + 1]; - ASSERT(endKeyWithPrefix.key.startsWith(conflictingKeysAbsolutePrefix)); + ASSERT(endKeyWithPrefix.key.startsWith(conflictingKeysRange.begin)); ASSERT(endKeyWithPrefix.value == conflictingKeysFalse); // Remove the prefix of returning keys - Key startKey = startKeyWithPrefix.key.removePrefix(conflictingKeysAbsolutePrefix); - Key endKey = endKeyWithPrefix.key.removePrefix(conflictingKeysAbsolutePrefix); + Key startKey = startKeyWithPrefix.key.removePrefix(conflictingKeysRange.begin); + Key endKey = endKeyWithPrefix.key.removePrefix(conflictingKeysRange.begin); KeyRangeRef kr = KeyRangeRef(startKey, endKey); if (!std::any_of(readConflictRanges.begin(), readConflictRanges.end(), [&kr](KeyRange rCR) { // Read_conflict_range remains same in the resolver. From 4a9658d6b8aae6566fadfe27cac49aa5a25baef5 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Wed, 8 Apr 2020 13:38:12 -0700 Subject: [PATCH 1376/1604] fix issues according to andrew's comments --- fdbclient/SpecialKeySpace.actor.cpp | 58 +++++++------------ fdbclient/SpecialKeySpace.actor.h | 17 +++--- fdbclient/SystemData.cpp | 1 - fdbclient/SystemData.h | 3 +- .../SpecialKeySpaceCorrectness.actor.cpp | 2 +- 5 files changed, 30 insertions(+), 51 deletions(-) diff --git a/fdbclient/SpecialKeySpace.actor.cpp b/fdbclient/SpecialKeySpace.actor.cpp index 8637be29a2..03f6c8971f 100644 --- a/fdbclient/SpecialKeySpace.actor.cpp +++ b/fdbclient/SpecialKeySpace.actor.cpp @@ -64,7 +64,7 @@ ACTOR Future SpecialKeyRangeBaseImpl::normalizeKeySelectorActor(const Spec ACTOR Future> SpecialKeySpace::getRangeAggregationActor( SpecialKeySpace* pks, Reference ryw, KeySelector begin, KeySelector end, - GetRangeLimits limits, bool reverse, bool withPrefix) { + GetRangeLimits limits, bool reverse) { // This function handles ranges which cover more than one keyrange and aggregates all results // KeySelector, GetRangeLimits and reverse are all handled here state Standalone result; @@ -72,12 +72,6 @@ ACTOR Future> SpecialKeySpace::getRangeAggregationAct state int actualBeginOffset; state int actualEndOffset; - // remove specialKeys prefix - // if (withPrefix) { - // begin.setKey(begin.getKey().removePrefix(specialKeys.begin)); - // end.setKey(end.getKey().removePrefix(specialKeys.begin)); - // } - // make sure offset == 1 state RangeMap::Iterator beginIter = pks->impls.rangeContaining(begin.getKey()); @@ -157,14 +151,10 @@ ACTOR Future> SpecialKeySpace::getRangeAggregationAct result.arena().dependsOn(pairs.arena()); // limits handler for (int i = pairs.size() - 1; i >= 0; --i) { - // TODO : use depends on with push_back - // KeyValueRef element = - // withPrefix ? KeyValueRef(pairs[i].key.withPrefix(specialKeys.begin, result.arena()), pairs[i].value) - // : pairs[i]; result.push_back(result.arena(), pairs[i]); - // Note : behavior here is even the last k-v pair makes total bytes larger than specified, it is still - // returned In other words, the total size of the returned value (less the last entry) will be less than - // byteLimit + // Note : behavior here is even the last k-v pair makes total bytes larger than specified, it's still + // returned. In other words, the total size of the returned value (less the last entry) will be less + // than byteLimit limits.decrement(pairs[i]); if (limits.isReached()) { result.more = true; @@ -183,14 +173,10 @@ ACTOR Future> SpecialKeySpace::getRangeAggregationAct result.arena().dependsOn(pairs.arena()); // limits handler for (int i = 0; i < pairs.size(); ++i) { - // TODO : use depends on with push_back - // KeyValueRef element = - // withPrefix ? KeyValueRef(pairs[i].key.withPrefix(specialKeys.begin, result.arena()), pairs[i].value) - // : pairs[i]; result.push_back(result.arena(), pairs[i]); - // Note : behavior here is even the last k-v pair makes total bytes larger than specified, it is still - // returned In other words, the total size of the returned value (less the last entry) will be less than - // byteLimit + // Note : behavior here is even the last k-v pair makes total bytes larger than specified, it's still + // returned. In other words, the total size of the returned value (less the last entry) will be less + // than byteLimit limits.decrement(pairs[i]); if (limits.isReached()) { result.more = true; @@ -205,27 +191,26 @@ ACTOR Future> SpecialKeySpace::getRangeAggregationAct Future> SpecialKeySpace::getRange(Reference ryw, KeySelector begin, KeySelector end, GetRangeLimits limits, - bool reverse, bool withPrefix) { + bool reverse) { // validate limits here if (!limits.isValid()) return range_limits_invalid(); if (limits.isReached()) { TEST(true); // read limit 0 return Standalone(); } - if (withPrefix) ASSERT(begin.getKey().startsWith(specialKeys.begin) && end.getKey().startsWith(specialKeys.begin)); // make sure orEqual == false begin.removeOrEqual(begin.arena()); end.removeOrEqual(end.arena()); - return getRangeAggregationActor(this, ryw, begin, end, limits, reverse, withPrefix); + return getRangeAggregationActor(this, ryw, begin, end, limits, reverse); } ACTOR Future> SpecialKeySpace::getActor(SpecialKeySpace* pks, Reference ryw, - KeyRef key, bool withPrefix) { + KeyRef key) { // use getRange to workaround this Standalone result = wait(pks->getRange(ryw, KeySelector(firstGreaterOrEqual(key)), KeySelector(firstGreaterOrEqual(keyAfter(key))), - GetRangeLimits(CLIENT_KNOBS->TOO_MANY), false, withPrefix)); + GetRangeLimits(CLIENT_KNOBS->TOO_MANY), false)); ASSERT(result.size() <= 1); if (result.size()) { return Optional(result[0].value); @@ -234,9 +219,8 @@ ACTOR Future> SpecialKeySpace::getActor(SpecialKeySpace* pks, Re } } -Future> SpecialKeySpace::get(Reference ryw, const Key& key, - bool withPrefix) { - return getActor(this, ryw, key, withPrefix); +Future> SpecialKeySpace::get(Reference ryw, const Key& key) { + return getActor(this, ryw, key); } ConflictingKeysImpl::ConflictingKeysImpl(KeyRef start, KeyRef end) : SpecialKeyRangeBaseImpl(start, end) {} @@ -277,7 +261,7 @@ public: Key getKeyForIndex(int idx) { return Key(prefix + format("%010d", idx)).withPrefix(range.begin); } int getSize() { return size; } Future> getRange(Reference ryw, - KeyRangeRef kr) const override { + KeyRangeRef kr) const override { int startIndex = 0, endIndex = size; while (startIndex < size && kvs[startIndex].key < kr.begin) ++startIndex; while (endIndex > startIndex && kvs[endIndex - 1].key >= kr.end) --endIndex; @@ -304,11 +288,11 @@ TEST_CASE("/fdbclient/SpecialKeySpace/Unittest") { auto nullRef = Reference(); // get { - auto resultFuture = pks.get(nullRef, LiteralStringRef("/cat/small0000000009"), false); + auto resultFuture = pks.get(nullRef, LiteralStringRef("/cat/small0000000009")); ASSERT(resultFuture.isReady()); auto result = resultFuture.getValue().get(); ASSERT(result == pkr1.getKeyValueForIndex(9).value); - auto emptyFuture = pks.get(nullRef, LiteralStringRef("/cat/small0000000010"), false); + auto emptyFuture = pks.get(nullRef, LiteralStringRef("/cat/small0000000010")); ASSERT(emptyFuture.isReady()); auto emptyResult = emptyFuture.getValue(); ASSERT(!emptyResult.present()); @@ -317,7 +301,7 @@ TEST_CASE("/fdbclient/SpecialKeySpace/Unittest") { { KeySelector start = KeySelectorRef(LiteralStringRef("/elepant"), false, -9); KeySelector end = KeySelectorRef(LiteralStringRef("/frog"), false, +11); - auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits(), false, false); + auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits()); ASSERT(resultFuture.isReady()); auto result = resultFuture.getValue(); ASSERT(result.size() == 20); @@ -328,7 +312,7 @@ TEST_CASE("/fdbclient/SpecialKeySpace/Unittest") { { KeySelector start = KeySelectorRef(pkr3.getKeyForIndex(999), true, -1110); KeySelector end = KeySelectorRef(pkr1.getKeyForIndex(0), false, +1112); - auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits(), false, false); + auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits()); ASSERT(resultFuture.isReady()); auto result = resultFuture.getValue(); ASSERT(result.size() == 1110); @@ -339,7 +323,7 @@ TEST_CASE("/fdbclient/SpecialKeySpace/Unittest") { { KeySelector start = KeySelectorRef(pkr2.getKeyForIndex(0), true, 0); KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(0), false, 0); - auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits(2), false, false); + auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits(2)); ASSERT(resultFuture.isReady()); auto result = resultFuture.getValue(); ASSERT(result.size() == 2); @@ -350,7 +334,7 @@ TEST_CASE("/fdbclient/SpecialKeySpace/Unittest") { { KeySelector start = KeySelectorRef(pkr2.getKeyForIndex(0), true, 0); KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(0), false, 0); - auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits(10, 100), false, false); + auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits(10, 100)); ASSERT(resultFuture.isReady()); auto result = resultFuture.getValue(); int bytes = 0; @@ -362,7 +346,7 @@ TEST_CASE("/fdbclient/SpecialKeySpace/Unittest") { { KeySelector start = KeySelectorRef(pkr2.getKeyForIndex(0), true, 0); KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(999), true, +1); - auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits(1100), true, false); + auto resultFuture = pks.getRange(nullRef, start, end, GetRangeLimits(1100), true); ASSERT(resultFuture.isReady()); auto result = resultFuture.getValue(); for (int i = 0; i < pkr3.getSize(); ++i) ASSERT(result[i] == pkr3.getKeyValueForIndex(pkr3.getSize() - 1 - i)); diff --git a/fdbclient/SpecialKeySpace.actor.h b/fdbclient/SpecialKeySpace.actor.h index dd0f751b1a..dee1a7ad91 100644 --- a/fdbclient/SpecialKeySpace.actor.h +++ b/fdbclient/SpecialKeySpace.actor.h @@ -32,13 +32,10 @@ protected: class SpecialKeySpace { public: - // withPrefix is true if the passing keys are prefixed with \xff\xff (cases from RYW), - // otherwise, false(cases from tests) - Future> get(Reference ryw, const Key& key, bool withPrefix = true); + Future> get(Reference ryw, const Key& key); Future> getRange(Reference ryw, KeySelector begin, - KeySelector end, GetRangeLimits limits, bool reverse = false, - bool withPrefix = true); + KeySelector end, GetRangeLimits limits, bool reverse = false); SpecialKeySpace(KeyRef spaceStartKey = Key(), KeyRef spaceEndKey = normalKeys.end) { // Default value is nullptr, begin of KeyRangeMap is Key() @@ -47,19 +44,18 @@ public: } void registerKeyRange(const KeyRangeRef& kr, SpecialKeyRangeBaseImpl* impl) { // range check + // TODO: add range check not to be replaced by overlapped ones ASSERT(kr.begin >= range.begin && kr.end <= range.end); impls.insert(kr, impl); } private: - ACTOR Future> getActor(SpecialKeySpace* pks, Reference ryw, KeyRef key, - bool withPrefix); + ACTOR Future> getActor(SpecialKeySpace* pks, Reference ryw, KeyRef key); ACTOR Future> getRangeAggregationActor(SpecialKeySpace* pks, Reference ryw, KeySelector begin, KeySelector end, - GetRangeLimits limits, bool reverse, - bool withPrefix); + GetRangeLimits limits, bool reverse); KeyRangeMap impls; KeyRange range; @@ -74,7 +70,8 @@ private: class ConflictingKeysImpl : public SpecialKeyRangeBaseImpl { public: explicit ConflictingKeysImpl(KeyRef start, KeyRef end); - Future> getRange(Reference ryw, KeyRangeRef kr) const override; + Future> getRange(Reference ryw, + KeyRangeRef kr) const override; }; #include "flow/unactorcompiler.h" diff --git a/fdbclient/SystemData.cpp b/fdbclient/SystemData.cpp index af66917836..3ed38bd0cd 100644 --- a/fdbclient/SystemData.cpp +++ b/fdbclient/SystemData.cpp @@ -60,7 +60,6 @@ void decodeKeyServersValue( const ValueRef& value, vector& src, vector } const KeyRangeRef conflictingKeysRange = KeyRangeRef(LiteralStringRef("\xff\xff/transaction/conflicting_keys/"), LiteralStringRef("\xff\xff/transaction/conflicting_keys/\xff")); -const KeyRef conflictingKeysPrefix = LiteralStringRef("/transaction/conflicting_keys/"); const ValueRef conflictingKeysTrue = LiteralStringRef("1"); const ValueRef conflictingKeysFalse = LiteralStringRef("0"); diff --git a/fdbclient/SystemData.h b/fdbclient/SystemData.h index 688493cc00..7cc6071c8c 100644 --- a/fdbclient/SystemData.h +++ b/fdbclient/SystemData.h @@ -36,7 +36,7 @@ extern const KeyRangeRef normalKeys; // '' to systemKeys.begin extern const KeyRangeRef systemKeys; // [FF] to [FF][FF] extern const KeyRangeRef nonMetadataSystemKeys; // [FF][00] to [FF][01] extern const KeyRangeRef allKeys; // '' to systemKeys.end -extern const KeyRangeRef specialKeys; // [FF][FF] to [FF][FF][FF] +extern const KeyRangeRef specialKeys; // [FF][FF] to [FF][FF][FF], some client functions are exposed through FDB calls using these special keys, see pr#2662 extern const KeyRef afterAllKeys; // "\xff/keyServers/[[begin]]" := "[[vector, vector]]" @@ -65,7 +65,6 @@ const Key serverKeysPrefixFor( UID serverID ); UID serverKeysDecodeServer( const KeyRef& key ); bool serverHasKey( ValueRef storedValue ); -extern const KeyRef conflictingKeysPrefix; extern const KeyRangeRef conflictingKeysRange; extern const ValueRef conflictingKeysTrue, conflictingKeysFalse; diff --git a/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp b/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp index f8dd8f1737..904e19620b 100644 --- a/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp +++ b/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp @@ -90,7 +90,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { auto correctResultFuture = self->ryw->getRange(begin, end, limit, false, reverse); ASSERT(correctResultFuture.isReady()); auto correctResult = correctResultFuture.getValue(); - auto testResultFuture = cx->specialKeySpace->getRange(self->ryw, begin, end, limit, reverse, false); + auto testResultFuture = cx->specialKeySpace->getRange(self->ryw, begin, end, limit, reverse); ASSERT(testResultFuture.isReady()); auto testResult = testResultFuture.getValue(); From 535efa0c4c69140d16009458a61d14b3a4aa07ed Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Wed, 8 Apr 2020 14:27:05 -0700 Subject: [PATCH 1377/1604] Add assertion to make sure new registered range is not overlapping with existing ones --- fdbclient/SpecialKeySpace.actor.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fdbclient/SpecialKeySpace.actor.h b/fdbclient/SpecialKeySpace.actor.h index dee1a7ad91..0f2e339aff 100644 --- a/fdbclient/SpecialKeySpace.actor.h +++ b/fdbclient/SpecialKeySpace.actor.h @@ -46,6 +46,9 @@ public: // range check // TODO: add range check not to be replaced by overlapped ones ASSERT(kr.begin >= range.begin && kr.end <= range.end); + // make sure the registered range is not overlapping with existing ones + // Note: kr.end should not be the same as another range's begin, although it should work even they are the same + ASSERT(impls.rangeContaining(kr.begin) == impls.rangeContaining(kr.end) && impls[kr.begin] == nullptr); impls.insert(kr, impl); } From 34cddd675c4a990d9241fdce09392a07c336b503 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Wed, 8 Apr 2020 14:33:41 -0700 Subject: [PATCH 1378/1604] clang-format --- fdbclient/NativeAPI.actor.cpp | 61 +++++++++++++++++++----------- fdbclient/ReadYourWrites.actor.cpp | 3 +- fdbclient/SystemData.cpp | 3 +- fdbclient/SystemData.h | 3 +- 4 files changed, 45 insertions(+), 25 deletions(-) diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index 8cb1153665..7ee962417c 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -503,26 +503,42 @@ ACTOR static Future getHealthMetricsActor(DatabaseContext *cx, bo Future DatabaseContext::getHealthMetrics(bool detailed = false) { return getHealthMetricsActor(this, detailed); } -DatabaseContext::DatabaseContext( - Reference>> connectionFile, Reference> clientInfo, Future clientInfoMonitor, - TaskPriority taskID, LocalityData const& clientLocality, bool enableLocalityLoadBalance, bool lockAware, bool internal, int apiVersion, bool switchable ) - : connectionFile(connectionFile),clientInfo(clientInfo), clientInfoMonitor(clientInfoMonitor), taskID(taskID), clientLocality(clientLocality), enableLocalityLoadBalance(enableLocalityLoadBalance), - lockAware(lockAware), apiVersion(apiVersion), switchable(switchable), provisional(false), cc("TransactionMetrics"), transactionReadVersions("ReadVersions", cc), - transactionReadVersionsCompleted("ReadVersionsCompleted", cc), transactionReadVersionBatches("ReadVersionBatches", cc), transactionBatchReadVersions("BatchPriorityReadVersions", cc), - transactionDefaultReadVersions("DefaultPriorityReadVersions", cc), transactionImmediateReadVersions("ImmediatePriorityReadVersions", cc), - transactionBatchReadVersionsCompleted("BatchPriorityReadVersionsCompleted", cc), transactionDefaultReadVersionsCompleted("DefaultPriorityReadVersionsCompleted", cc), - transactionImmediateReadVersionsCompleted("ImmediatePriorityReadVersionsCompleted", cc), transactionLogicalReads("LogicalUncachedReads", cc), transactionPhysicalReads("PhysicalReadRequests", cc), - transactionPhysicalReadsCompleted("PhysicalReadRequestsCompleted", cc), transactionGetKeyRequests("GetKeyRequests", cc), transactionGetValueRequests("GetValueRequests", cc), - transactionGetRangeRequests("GetRangeRequests", cc), transactionWatchRequests("WatchRequests", cc), transactionGetAddressesForKeyRequests("GetAddressesForKeyRequests", cc), - transactionBytesRead("BytesRead", cc), transactionKeysRead("KeysRead", cc), transactionMetadataVersionReads("MetadataVersionReads", cc), transactionCommittedMutations("CommittedMutations", cc), - transactionCommittedMutationBytes("CommittedMutationBytes", cc), transactionSetMutations("SetMutations", cc), transactionClearMutations("ClearMutations", cc), - transactionAtomicMutations("AtomicMutations", cc), transactionsCommitStarted("CommitStarted", cc), transactionsCommitCompleted("CommitCompleted", cc), - transactionKeyServerLocationRequests("KeyServerLocationRequests", cc), transactionKeyServerLocationRequestsCompleted("KeyServerLocationRequestsCompleted", cc), transactionsTooOld("TooOld", cc), - transactionsFutureVersions("FutureVersions", cc), transactionsNotCommitted("NotCommitted", cc), transactionsMaybeCommitted("MaybeCommitted", cc), - transactionsResourceConstrained("ResourceConstrained", cc), transactionsThrottled("Throttled", cc), transactionsProcessBehind("ProcessBehind", cc), outstandingWatches(0), latencies(1000), readLatencies(1000), commitLatencies(1000), - GRVLatencies(1000), mutationsPerCommit(1000), bytesPerCommit(1000), mvCacheInsertLocation(0), healthMetricsLastUpdated(0), detailedHealthMetricsLastUpdated(0), internal(internal), - specialKeySpace(std::make_shared(normalKeys.begin, specialKeys.end)), cKImpl(std::make_shared(conflictingKeysRange.begin, conflictingKeysRange.end)) -{ +DatabaseContext::DatabaseContext(Reference>> connectionFile, + Reference> clientInfo, Future clientInfoMonitor, + TaskPriority taskID, LocalityData const& clientLocality, + bool enableLocalityLoadBalance, bool lockAware, bool internal, int apiVersion, + bool switchable) + : connectionFile(connectionFile), clientInfo(clientInfo), clientInfoMonitor(clientInfoMonitor), taskID(taskID), + clientLocality(clientLocality), enableLocalityLoadBalance(enableLocalityLoadBalance), lockAware(lockAware), + apiVersion(apiVersion), switchable(switchable), provisional(false), cc("TransactionMetrics"), + transactionReadVersions("ReadVersions", cc), transactionReadVersionsCompleted("ReadVersionsCompleted", cc), + transactionReadVersionBatches("ReadVersionBatches", cc), + transactionBatchReadVersions("BatchPriorityReadVersions", cc), + transactionDefaultReadVersions("DefaultPriorityReadVersions", cc), + transactionImmediateReadVersions("ImmediatePriorityReadVersions", cc), + transactionBatchReadVersionsCompleted("BatchPriorityReadVersionsCompleted", cc), + transactionDefaultReadVersionsCompleted("DefaultPriorityReadVersionsCompleted", cc), + transactionImmediateReadVersionsCompleted("ImmediatePriorityReadVersionsCompleted", cc), + transactionLogicalReads("LogicalUncachedReads", cc), transactionPhysicalReads("PhysicalReadRequests", cc), + transactionPhysicalReadsCompleted("PhysicalReadRequestsCompleted", cc), + transactionGetKeyRequests("GetKeyRequests", cc), transactionGetValueRequests("GetValueRequests", cc), + transactionGetRangeRequests("GetRangeRequests", cc), transactionWatchRequests("WatchRequests", cc), + transactionGetAddressesForKeyRequests("GetAddressesForKeyRequests", cc), transactionBytesRead("BytesRead", cc), + transactionKeysRead("KeysRead", cc), transactionMetadataVersionReads("MetadataVersionReads", cc), + transactionCommittedMutations("CommittedMutations", cc), + transactionCommittedMutationBytes("CommittedMutationBytes", cc), transactionSetMutations("SetMutations", cc), + transactionClearMutations("ClearMutations", cc), transactionAtomicMutations("AtomicMutations", cc), + transactionsCommitStarted("CommitStarted", cc), transactionsCommitCompleted("CommitCompleted", cc), + transactionKeyServerLocationRequests("KeyServerLocationRequests", cc), + transactionKeyServerLocationRequestsCompleted("KeyServerLocationRequestsCompleted", cc), + transactionsTooOld("TooOld", cc), transactionsFutureVersions("FutureVersions", cc), + transactionsNotCommitted("NotCommitted", cc), transactionsMaybeCommitted("MaybeCommitted", cc), + transactionsResourceConstrained("ResourceConstrained", cc), transactionsThrottled("Throttled", cc), + transactionsProcessBehind("ProcessBehind", cc), outstandingWatches(0), latencies(1000), readLatencies(1000), + commitLatencies(1000), GRVLatencies(1000), mutationsPerCommit(1000), bytesPerCommit(1000), mvCacheInsertLocation(0), + healthMetricsLastUpdated(0), detailedHealthMetricsLastUpdated(0), internal(internal), + specialKeySpace(std::make_shared(normalKeys.begin, specialKeys.end)), + cKImpl(std::make_shared(conflictingKeysRange.begin, conflictingKeysRange.end)) { dbId = deterministicRandom()->randomUniqueID(); connected = clientInfo->get().proxies.size() ? Void() : clientInfo->onChange(); @@ -2756,7 +2772,8 @@ ACTOR static Future tryCommit( Database cx, Reference // clear the RYW transaction which contains previous conflicting keys tr->info.conflictingKeys.reset(); if (ci.conflictingKRIndices.present()) { - tr->info.conflictingKeys = std::make_shared>(conflictingKeysFalse, specialKeys.end); + tr->info.conflictingKeys = + std::make_shared>(conflictingKeysFalse, specialKeys.end); state Standalone> conflictingKRIndices = ci.conflictingKRIndices.get(); // drop duplicate indices and merge overlapped ranges // Note: addReadConflictRange in native transaction object does not merge overlapped ranges @@ -2765,7 +2782,7 @@ ACTOR static Future tryCommit( Database cx, Reference for (auto const& rCRIndex : mergedIds) { const KeyRangeRef kr = req.transaction.read_conflict_ranges[rCRIndex]; const KeyRange krWithPrefix = KeyRangeRef(kr.begin.withPrefix(conflictingKeysRange.begin), - kr.end.withPrefix(conflictingKeysRange.begin)); + kr.end.withPrefix(conflictingKeysRange.begin)); tr->info.conflictingKeys->insert(krWithPrefix, conflictingKeysTrue); } } diff --git a/fdbclient/ReadYourWrites.actor.cpp b/fdbclient/ReadYourWrites.actor.cpp index 073785dc6d..f47a01f54a 100644 --- a/fdbclient/ReadYourWrites.actor.cpp +++ b/fdbclient/ReadYourWrites.actor.cpp @@ -1286,7 +1286,8 @@ Future< Standalone > ReadYourWritesTransaction::getRange( // special key space are only allowed to query if both begin and end start with \xff\xff if (begin.getKey().startsWith(specialKeys.begin) && end.getKey().startsWith(specialKeys.begin)) - return getDatabase()->specialKeySpace->getRange(Reference::addRef(this), begin, end, limits, reverse); + return getDatabase()->specialKeySpace->getRange(Reference::addRef(this), begin, end, + limits, reverse); if(checkUsedDuringCommit()) { return used_during_commit(); diff --git a/fdbclient/SystemData.cpp b/fdbclient/SystemData.cpp index 3ed38bd0cd..2fa2650f54 100644 --- a/fdbclient/SystemData.cpp +++ b/fdbclient/SystemData.cpp @@ -59,7 +59,8 @@ void decodeKeyServersValue( const ValueRef& value, vector& src, vector } } -const KeyRangeRef conflictingKeysRange = KeyRangeRef(LiteralStringRef("\xff\xff/transaction/conflicting_keys/"), LiteralStringRef("\xff\xff/transaction/conflicting_keys/\xff")); +const KeyRangeRef conflictingKeysRange = KeyRangeRef(LiteralStringRef("\xff\xff/transaction/conflicting_keys/"), + LiteralStringRef("\xff\xff/transaction/conflicting_keys/\xff")); const ValueRef conflictingKeysTrue = LiteralStringRef("1"); const ValueRef conflictingKeysFalse = LiteralStringRef("0"); diff --git a/fdbclient/SystemData.h b/fdbclient/SystemData.h index 7cc6071c8c..445eed0124 100644 --- a/fdbclient/SystemData.h +++ b/fdbclient/SystemData.h @@ -36,7 +36,8 @@ extern const KeyRangeRef normalKeys; // '' to systemKeys.begin extern const KeyRangeRef systemKeys; // [FF] to [FF][FF] extern const KeyRangeRef nonMetadataSystemKeys; // [FF][00] to [FF][01] extern const KeyRangeRef allKeys; // '' to systemKeys.end -extern const KeyRangeRef specialKeys; // [FF][FF] to [FF][FF][FF], some client functions are exposed through FDB calls using these special keys, see pr#2662 +extern const KeyRangeRef specialKeys; // [FF][FF] to [FF][FF][FF], some client functions are exposed through FDB calls + // using these special keys, see pr#2662 extern const KeyRef afterAllKeys; // "\xff/keyServers/[[begin]]" := "[[vector, vector]]" From 3a01d249707c2906c4b0de9a7d7bee7474b65087 Mon Sep 17 00:00:00 2001 From: tclinken Date: Wed, 8 Apr 2020 14:50:41 -0700 Subject: [PATCH 1379/1604] Pass const ref to a_callback_fire --- fdbrpc/FlowTests.actor.cpp | 46 +++++++++++++++++++++++++---- flow/actorcompiler/ActorCompiler.cs | 2 +- flow/flow.h | 4 --- 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/fdbrpc/FlowTests.actor.cpp b/fdbrpc/FlowTests.actor.cpp index 4fdb7aa343..ade8946c35 100644 --- a/fdbrpc/FlowTests.actor.cpp +++ b/fdbrpc/FlowTests.actor.cpp @@ -80,7 +80,6 @@ class LambdaCallback : public CallbackType, public FastAllocatedmoved = false; + this->copied = other.copied; + return *this; + } Tracker(const Tracker& other) : Tracker(other.copied + 1) { ASSERT(!other.moved); } Tracker& operator=(const Tracker& other) { ASSERT(!other.moved); @@ -1386,18 +1392,27 @@ struct Tracker { } }; -TEST_CASE("/flow/PromiseStream/move") { +TEST_CASE("/flow/flow/PromiseStream/move") { state PromiseStream stream; { // This tests the case when a callback is added before - // a value is sent - Future listener = Tracker::listen(stream.getFuture()); + // a movable value is sent + state Future listener = Tracker::listen(stream.getFuture()); stream.send(Tracker{}); wait(listener); } + + { + // This tests the case when a callback is added before + // a unmovable value is sent + listener = Tracker::listen(stream.getFuture()); + Tracker namedTracker; + stream.send(namedTracker); + wait(listener); + } { // This tests the case when no callback is added until - // after a value is sent + // after a movable value is sent stream.send(Tracker{}); stream.send(Tracker{}); { @@ -1412,6 +1427,27 @@ TEST_CASE("/flow/PromiseStream/move") { } } } + { + // This tests the case when no callback is added until + // after an unmovable value is sent + Tracker namedTracker1; + Tracker namedTracker2; + stream.send(namedTracker1); + stream.send(namedTracker2); + { + Tracker t = waitNext(stream.getFuture()); + ASSERT(!t.moved); + // must copy onto queue + ASSERT(t.copied == 1); + } + choose { + when(Tracker t = waitNext(stream.getFuture())) { + ASSERT(!t.moved); + // must copy onto queue + ASSERT(t.copied == 1); + } + } + } return Void(); } diff --git a/flow/actorcompiler/ActorCompiler.cs b/flow/actorcompiler/ActorCompiler.cs index eab91e56a7..c95c9bf7f2 100644 --- a/flow/actorcompiler/ActorCompiler.cs +++ b/flow/actorcompiler/ActorCompiler.cs @@ -813,7 +813,7 @@ namespace actorcompiler returnType = "void", formalParameters = new string[] { ch.CallbackTypeInStateClass + "*", - ch.Stmt.wait.result.type + " value" + ch.Stmt.wait.result.type + " const& value" }, endIsUnreachable = true }; diff --git a/flow/flow.h b/flow/flow.h index 9ee95e4961..4d55f49b8b 100644 --- a/flow/flow.h +++ b/flow/flow.h @@ -391,7 +391,6 @@ struct SingleCallback { SingleCallback *next; virtual void fire(T const&) {} - virtual void fire(T &&) {} virtual void error(Error) {} virtual void unwait() {} @@ -1013,9 +1012,6 @@ struct ActorSingleCallback : SingleCallback { virtual void fire(ValueType const& value) { static_cast(this)->a_callback_fire(this, value); } - virtual void fire(ValueType &&value) { - static_cast(this)->a_callback_fire(this, std::move(value)); - } virtual void error(Error e) { static_cast(this)->a_callback_error(this, e); } From 2a6de93b92834304fed0999fdac18150d905f7a9 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Wed, 8 Apr 2020 14:50:55 -0700 Subject: [PATCH 1380/1604] add copyright information --- fdbclient/SpecialKeySpace.actor.cpp | 20 +++++++++++++++++++ fdbclient/SpecialKeySpace.actor.h | 20 +++++++++++++++++++ .../workloads/ReportConflictingKeys.actor.cpp | 2 +- .../SpecialKeySpaceCorrectness.actor.cpp | 20 +++++++++++++++++++ 4 files changed, 61 insertions(+), 1 deletion(-) diff --git a/fdbclient/SpecialKeySpace.actor.cpp b/fdbclient/SpecialKeySpace.actor.cpp index 03f6c8971f..9c5e4227c2 100644 --- a/fdbclient/SpecialKeySpace.actor.cpp +++ b/fdbclient/SpecialKeySpace.actor.cpp @@ -1,3 +1,23 @@ +/* + * SpecialKeySpace.actor.cpp + * + * This source file is part of the FoundationDB open source project + * + * Copyright 2013-2020 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/SpecialKeySpace.actor.h" #include "flow/UnitTest.h" #include "flow/actorcompiler.h" // This must be the last #include. diff --git a/fdbclient/SpecialKeySpace.actor.h b/fdbclient/SpecialKeySpace.actor.h index 0f2e339aff..e2cc0a417b 100644 --- a/fdbclient/SpecialKeySpace.actor.h +++ b/fdbclient/SpecialKeySpace.actor.h @@ -1,3 +1,23 @@ +/* + * SpecialKeySpace.actor.h + * + * This source file is part of the FoundationDB open source project + * + * Copyright 2013-2020 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. + */ + #pragma once #if defined(NO_INTELLISENSE) && !defined(FDBCLIENT_SPECIALKEYSPACE_ACTOR_G_H) diff --git a/fdbserver/workloads/ReportConflictingKeys.actor.cpp b/fdbserver/workloads/ReportConflictingKeys.actor.cpp index c01bf288de..aab69087de 100644 --- a/fdbserver/workloads/ReportConflictingKeys.actor.cpp +++ b/fdbserver/workloads/ReportConflictingKeys.actor.cpp @@ -3,7 +3,7 @@ * * This source file is part of the FoundationDB open source project * - * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors + * Copyright 2013-2020 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. diff --git a/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp b/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp index 904e19620b..5535b985ed 100644 --- a/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp +++ b/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp @@ -1,3 +1,23 @@ +/* + * SpecialKeySpaceCorrectness.actor.cpp + * + * This source file is part of the FoundationDB open source project + * + * Copyright 2013-2020 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/NativeAPI.actor.h" #include "fdbclient/ReadYourWrites.h" #include "fdbclient/SpecialKeySpace.actor.h" From a0c32f7a679a6d698a498accb0c8608b0e8a21a8 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Wed, 8 Apr 2020 15:37:08 -0700 Subject: [PATCH 1381/1604] FastRestore:getBatchReplies:Comment out trace for performance --- fdbserver/RestoreCommon.actor.h | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/fdbserver/RestoreCommon.actor.h b/fdbserver/RestoreCommon.actor.h index 44a2c8c8b7..268fbf26d2 100644 --- a/fdbserver/RestoreCommon.actor.h +++ b/fdbserver/RestoreCommon.actor.h @@ -279,18 +279,17 @@ Future getBatchReplies(RequestStream Interface::*channel, std::ma state std::vector> ongoingReplies; state std::vector ongoingRepliesIndex; - state int loopCount = 0; loop { ongoingReplies.clear(); ongoingRepliesIndex.clear(); for (int i = 0; i < cmdReplies.size(); ++i) { - TraceEvent(SevInfo, "FastRestoreGetBatchReplies") - .detail("Requests", requests.size()) - .detail("OutstandingReplies", oustandingReplies) - .detail("ReplyIndex", i) - .detail("ReplyReady", cmdReplies[i].isReady()) - .detail("RequestNode", requests[i].first) - .detail("Request", requests[i].second.toString()); + // TraceEvent(SevDebug, "FastRestoreGetBatchReplies") + // .detail("Requests", requests.size()) + // .detail("OutstandingReplies", oustandingReplies) + // .detail("ReplyIndex", i) + // .detail("ReplyReady", cmdReplies[i].isReady()) + // .detail("RequestNode", requests[i].first) + // .detail("Request", requests[i].second.toString()); if (!cmdReplies[i].isReady()) { // still wait for reply ongoingReplies.push_back(cmdReplies[i]); ongoingRepliesIndex.push_back(i); From 0cf60133571e2995534cecaf543e6b9965e1be57 Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Wed, 8 Apr 2020 15:50:21 -0700 Subject: [PATCH 1382/1604] Refactor to remove describePartitionedBackup() The backup container can figure out if partitioned logs are used by looking at mutation logs, thus consolidating the API to a single describeBackup() as before. --- fdbclient/BackupContainer.actor.cpp | 36 +++++++++++-------- fdbclient/BackupContainer.h | 4 +-- fdbserver/RestoreMaster.actor.cpp | 7 ++-- ...kupAndParallelRestoreCorrectness.actor.cpp | 9 +++-- 4 files changed, 29 insertions(+), 27 deletions(-) diff --git a/fdbclient/BackupContainer.actor.cpp b/fdbclient/BackupContainer.actor.cpp index 2fe1bf28e0..ae77cd6b5c 100644 --- a/fdbclient/BackupContainer.actor.cpp +++ b/fdbclient/BackupContainer.actor.cpp @@ -243,10 +243,10 @@ std::string BackupDescription::toJSON() const { * /plogs/...log,startVersion,endVersion,UID,tagID-of-N,blocksize * /logs/.../log,startVersion,endVersion,UID,blockSize * where ... is a multi level path which sorts lexically into version order and results in approximately 1 - * unique folder per day containing about 5,000 files. Logs after 7.0 are stored in "plogs" - * directory and are partitioned according to tagIDs (0, 1, 2, ...) and the total number - * partitions is N. Logs before 7.0 are - * stored in "logs" directory and are not partitioned. + * unique folder per day containing about 5,000 files. Logs after FDB 6.3 are stored in "plogs" + * directory and are partitioned according to tagIDs (0, 1, 2, ...) and the total number partitions is N. + * Old backup logs FDB 6.2 and earlier are stored in "logs" directory and are not partitioned. + * After FDB 6.3, users can choose to use the new partitioned logs or old logs. * * * BACKWARD COMPATIBILITY @@ -704,7 +704,8 @@ public: } } - ACTOR static Future describeBackup_impl(Reference bc, bool deepScan, Version logStartVersionOverride, bool partitioned) { + ACTOR static Future describeBackup_impl(Reference bc, bool deepScan, + Version logStartVersionOverride) { state BackupDescription desc; desc.url = bc->getURL(); @@ -722,8 +723,7 @@ public: // from which to resolve the relative version. // This could be handled more efficiently without recursion but it's tricky, this will do for now. if(logStartVersionOverride != invalidVersion && logStartVersionOverride < 0) { - BackupDescription tmp = wait(partitioned ? bc->describePartitionedBackup(false, invalidVersion) - : bc->describeBackup(false, invalidVersion)); + BackupDescription tmp = wait(bc->describeBackup(false, invalidVersion)); logStartVersionOverride = resolveRelativeVersion(tmp.maxLogEnd, logStartVersionOverride, "LogStartVersionOverride", invalid_option_value()); } @@ -811,9 +811,18 @@ public: } state std::vector logs; - wait(store(logs, bc->listLogFiles(scanBegin, scanEnd, partitioned)) && + state std::vector plogs; + wait(store(logs, bc->listLogFiles(scanBegin, scanEnd, false)) && + store(plogs, bc->listLogFiles(scanBegin, scanEnd, true)) && store(desc.snapshots, bc->listKeyspaceSnapshots())); + if (plogs.size() > 0) { + desc.partitioned = true; + logs.swap(plogs); + } else { + desc.partitioned = false; + } + // List logs in version order so log continuity can be analyzed std::sort(logs.begin(), logs.end()); @@ -823,7 +832,7 @@ public: // If we didn't get log versions above then seed them using the first log file if (!desc.contiguousLogEnd.present()) { desc.minLogBegin = logs.begin()->beginVersion; - if (partitioned) { + if (desc.partitioned) { // Cannot use the first file's end version, which may not be contiguous // for other partitions. Set to its beginVersion to be safe. desc.contiguousLogEnd = logs.begin()->beginVersion; @@ -832,7 +841,7 @@ public: } } - if (partitioned) { + if (desc.partitioned) { updatePartitionedLogsContinuousEnd(&desc, logs, scanBegin, scanEnd); } else { Version& end = desc.contiguousLogEnd.get(); @@ -906,11 +915,8 @@ public: // Uses the virtual methods to describe the backup contents Future describeBackup(bool deepScan, Version logStartVersionOverride) final { - return describeBackup_impl(Reference::addRef(this), deepScan, logStartVersionOverride, false); - } - - Future describePartitionedBackup(bool deepScan, Version logStartVersionOverride) final { - return describeBackup_impl(Reference::addRef(this), deepScan, logStartVersionOverride, true); + return describeBackup_impl(Reference::addRef(this), deepScan, + logStartVersionOverride); } ACTOR static Future expireData_impl(Reference bc, Version expireEndVersion, bool force, ExpireProgress *progress, Version restorableBeginVersion) { diff --git a/fdbclient/BackupContainer.h b/fdbclient/BackupContainer.h index d134d53887..91d6122942 100644 --- a/fdbclient/BackupContainer.h +++ b/fdbclient/BackupContainer.h @@ -178,6 +178,7 @@ struct BackupDescription { // The minimum version which this backup can be used to restore to Optional minRestorableVersion; std::string extendedDetail; // Freeform container-specific info. + bool partitioned; // If this backup contains partitioned mutation logs. // Resolves the versions above to timestamps using a given database's TimeKeeper data. // toString will use this information if present. @@ -260,9 +261,6 @@ public: // be after deleting all data prior to logStartVersionOverride. virtual Future describeBackup(bool deepScan = false, Version logStartVersionOverride = invalidVersion) = 0; - // The same as above, except using partitioned mutation logs. - virtual Future describePartitionedBackup(bool deepScan = false, Version logStartVersionOverride = invalidVersion) = 0; - virtual Future dumpFileList(Version begin = 0, Version end = std::numeric_limits::max()) = 0; // If there are partitioned log files, then returns true; otherwise, returns false. diff --git a/fdbserver/RestoreMaster.actor.cpp b/fdbserver/RestoreMaster.actor.cpp index 26f9f69718..92b189cb10 100644 --- a/fdbserver/RestoreMaster.actor.cpp +++ b/fdbserver/RestoreMaster.actor.cpp @@ -617,8 +617,7 @@ ACTOR static Future>> collectRestoreRequest ACTOR static Future collectBackupFiles(Reference bc, std::vector* rangeFiles, std::vector* logFiles, Database cx, RestoreRequest request) { - state bool partitioned = wait(bc->isPartitionedBackup()); - state BackupDescription desc = wait(partitioned ? bc->describePartitionedBackup() : bc->describeBackup()); + state BackupDescription desc = wait(bc->describeBackup()); // Convert version to real time for operators to read the BackupDescription desc. wait(desc.resolveVersionTimes(cx)); @@ -634,8 +633,8 @@ ACTOR static Future collectBackupFiles(Reference bc, std::cout << "Restore to version: " << request.targetVersion << "\nBackupDesc: \n" << desc.toString() << "\n\n"; } - Optional restorable = wait(partitioned ? bc->getPartitionedRestoreSet(request.targetVersion) - : bc->getRestoreSet(request.targetVersion)); + Optional restorable = wait(desc.partitioned ? bc->getPartitionedRestoreSet(request.targetVersion) + : bc->getRestoreSet(request.targetVersion)); if (!restorable.present()) { TraceEvent(SevWarn, "FastRestoreMasterPhaseCollectBackupFiles").detail("NotRestorable", request.targetVersion); diff --git a/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp b/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp index 51f1b0ea3e..3f00b5f781 100644 --- a/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp +++ b/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp @@ -213,13 +213,12 @@ struct BackupAndParallelRestoreCorrectnessWorkload : TestWorkload { state bool restorable = false; if (lastBackupContainer) { - state Future fdesc = self->usePartitionedLogs - ? lastBackupContainer->describePartitionedBackup() - : lastBackupContainer->describeBackup(); + state Future fdesc = lastBackupContainer->describeBackup(); wait(ready(fdesc)); if(!fdesc.isError()) { state BackupDescription desc = fdesc.get(); + ASSERT(self->usePartitionedLogs == desc.partitioned); wait(desc.resolveVersionTimes(cx)); printf("BackupDescription:\n%s\n", desc.toString().c_str()); restorable = desc.maxRestorableVersion.present(); @@ -436,8 +435,8 @@ struct BackupAndParallelRestoreCorrectnessWorkload : TestWorkload { .detail("BackupTag", printable(self->backupTag)); auto container = IBackupContainer::openContainer(lastBackupContainer->getURL()); - BackupDescription desc = wait(self->usePartitionedLogs ? container->describePartitionedBackup() - : container->describeBackup()); + BackupDescription desc = wait(container->describeBackup()); + ASSERT(self->usePartitionedLogs == desc.partitioned); TraceEvent("BAFRW_Restore", randomID) .detail("LastBackupContainer", lastBackupContainer->getURL()) From fd9caa88a085626a9cf7c7c6a7a4a4e0aaae0b00 Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Wed, 8 Apr 2020 16:09:18 -0700 Subject: [PATCH 1383/1604] Remove isPartitionedBackup() This is no longer needed, since describeBackup() figures this out. --- fdbclient/BackupContainer.actor.cpp | 12 ------------ fdbclient/BackupContainer.h | 3 --- 2 files changed, 15 deletions(-) diff --git a/fdbclient/BackupContainer.actor.cpp b/fdbclient/BackupContainer.actor.cpp index ae77cd6b5c..8b405c8679 100644 --- a/fdbclient/BackupContainer.actor.cpp +++ b/fdbclient/BackupContainer.actor.cpp @@ -657,18 +657,6 @@ public: return dumpFileList_impl(Reference::addRef(this), begin, end); } - ACTOR static Future isPartitionedBackup_impl(Reference bc) { - BackupFileList list = wait(bc->dumpFileList(0, std::numeric_limits::max())); - for (const auto& file : list.logs) { - if (file.isPartitionedLog()) return true; - } - return false; - } - - Future isPartitionedBackup() final { - return isPartitionedBackup_impl(Reference::addRef(this)); - } - static Version resolveRelativeVersion(Optional max, Version v, const char *name, Error e) { if(v == invalidVersion) { TraceEvent(SevError, "BackupExpireInvalidVersion").detail(name, v); diff --git a/fdbclient/BackupContainer.h b/fdbclient/BackupContainer.h index 91d6122942..6caeaaaa64 100644 --- a/fdbclient/BackupContainer.h +++ b/fdbclient/BackupContainer.h @@ -263,9 +263,6 @@ public: virtual Future dumpFileList(Version begin = 0, Version end = std::numeric_limits::max()) = 0; - // If there are partitioned log files, then returns true; otherwise, returns false. - virtual Future isPartitionedBackup() = 0; - // Get exactly the files necessary to restore to targetVersion. Returns non-present if // restore to given version is not possible. virtual Future> getRestoreSet(Version targetVersion) = 0; From b44105b54c72c8481b82e589aeea79a5aa1df10d Mon Sep 17 00:00:00 2001 From: tclinken Date: Wed, 8 Apr 2020 16:38:30 -0700 Subject: [PATCH 1384/1604] Print explanation when fdbcli unlock fails --- fdbcli/fdbcli.actor.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/fdbcli/fdbcli.actor.cpp b/fdbcli/fdbcli.actor.cpp index 2895cf8be6..e9bafa2a17 100644 --- a/fdbcli/fdbcli.actor.cpp +++ b/fdbcli/fdbcli.actor.cpp @@ -3010,8 +3010,16 @@ ACTOR Future cli(CLIOptions opt, LineNoise* plinenoise) { warn = checkStatus(timeWarning(5.0, "\nWARNING: Long delay (Ctrl-C to interrupt)\n"), db); if (input.present() && input.get() == passPhrase) { UID unlockUID = UID::fromString(tokens[1].toString()); - wait(makeInterruptable(unlockDatabase(db, unlockUID))); - printf("Database unlocked.\n"); + try { + wait(makeInterruptable(unlockDatabase(db, unlockUID))); + printf("Database unlocked.\n"); + } catch (Error& e) { + if (e.code() == error_code_database_locked) { + printf( + "Unable to unlock database. Make sure to unlock with the correct lock UID.\n"); + } + throw e; + } } else { printf("ERROR: Incorrect passphrase entered.\n"); is_error = true; From 13447f439f3a684a1d5d5deac8c0329452c7df49 Mon Sep 17 00:00:00 2001 From: Vishesh Yadav Date: Wed, 8 Apr 2020 19:34:40 -0700 Subject: [PATCH 1385/1604] fdbrpc: Add a constant to onFailedFor() Since, we mark an address as failed when connection is failed, this patch adds a contant to compensate the time needed to reconnect and make sure endpoint is actually down. This contant is equal to FAILURE_MIN_DELAY which was used by centralized FailureMonitoringClient earlier removed. --- fdbrpc/FailureMonitor.actor.cpp | 4 ++++ flow/Knobs.cpp | 1 + flow/Knobs.h | 1 + 3 files changed, 6 insertions(+) diff --git a/fdbrpc/FailureMonitor.actor.cpp b/fdbrpc/FailureMonitor.actor.cpp index b1fe85c83a..7d985fe854 100644 --- a/fdbrpc/FailureMonitor.actor.cpp +++ b/fdbrpc/FailureMonitor.actor.cpp @@ -32,6 +32,10 @@ ACTOR Future waitForStateEqual(IFailureMonitor* monitor, Endpoint endpoint ACTOR Future waitForContinuousFailure(IFailureMonitor* monitor, Endpoint endpoint, double sustainedFailureDuration, double slope) { state double startT = now(); + + // Since, FailureMonitoring is now localized we should add some slack for `connectionKeeper` + // to try reconnecting. + sustainedFailureDuration += FLOW_KNOBS->FAILURE_DETECTION_DELAY; loop { wait(monitor->onFailed(endpoint)); if (monitor->permanentlyFailed(endpoint)) return Void(); diff --git a/flow/Knobs.cpp b/flow/Knobs.cpp index 79718ec32d..7a057f1f50 100644 --- a/flow/Knobs.cpp +++ b/flow/Knobs.cpp @@ -203,6 +203,7 @@ void FlowKnobs::initialize(bool randomize, bool isSimulated) { init( LOAD_BALANCE_PENALTY_IS_BAD, true ); // Health Monitor + init( FAILURE_DETECTION_DELAY, 4.0 ); if( randomize && BUGGIFY ) FAILURE_DETECTION_DELAY = 1.0; init( HEALTH_MONITOR_MARK_FAILED_UNSTABLE_CONNECTIONS, true ); init( HEALTH_MONITOR_CLIENT_REQUEST_INTERVAL_SECS, 30 ); init( HEALTH_MONITOR_CONNECTION_MAX_CLOSED, 5 ); diff --git a/flow/Knobs.h b/flow/Knobs.h index 980611464c..063faeb6e5 100644 --- a/flow/Knobs.h +++ b/flow/Knobs.h @@ -222,6 +222,7 @@ public: bool LOAD_BALANCE_PENALTY_IS_BAD; // Health Monitor + int FAILURE_DETECTION_DELAY; bool HEALTH_MONITOR_MARK_FAILED_UNSTABLE_CONNECTIONS; int HEALTH_MONITOR_CLIENT_REQUEST_INTERVAL_SECS; int HEALTH_MONITOR_CONNECTION_MAX_CLOSED; From 4d06e837dc13acafb01becbeed11ade9c2d72b2e Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Wed, 8 Apr 2020 20:12:09 -0700 Subject: [PATCH 1386/1604] Remove getPartitionedRestoreSet() API Use getRestoreSet() instead for both old and new partitioned logs. --- fdbclient/BackupContainer.actor.cpp | 18 ++++++++++-------- fdbclient/BackupContainer.h | 5 ----- fdbserver/RestoreMaster.actor.cpp | 3 +-- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/fdbclient/BackupContainer.actor.cpp b/fdbclient/BackupContainer.actor.cpp index 8b405c8679..2e713b39ee 100644 --- a/fdbclient/BackupContainer.actor.cpp +++ b/fdbclient/BackupContainer.actor.cpp @@ -115,6 +115,7 @@ std::string BackupDescription::toString() const { info.append(format("URL: %s\n", url.c_str())); info.append(format("Restorable: %s\n", maxRestorableVersion.present() ? "true" : "false")); + info.append(format("Partitioned logs: %s\n", partitioned ? "true" : "false")); auto formatVersion = [&](Version v) { std::string s; @@ -169,6 +170,7 @@ std::string BackupDescription::toJSON() const { doc.setKey("SchemaVersion", "1.0.0"); doc.setKey("URL", url.c_str()); doc.setKey("Restorable", maxRestorableVersion.present()); + doc.setKey("Partitioned", partitioned); auto formatVersion = [&](Version v) { JsonBuilderObject doc; @@ -1281,7 +1283,7 @@ public: return end; } - ACTOR static Future> getRestoreSet_impl(Reference bc, Version targetVersion, bool partitioned) { + ACTOR static Future> getRestoreSet_impl(Reference bc, Version targetVersion) { // Find the most recent keyrange snapshot to end at or before targetVersion state Optional snapshot; std::vector snapshots = wait(bc->listKeyspaceSnapshots()); @@ -1305,9 +1307,13 @@ public: } // FIXME: check if there are tagged logs. for each tag, there is no version gap. - state std::vector logs = wait(bc->listLogFiles(snapshot.get().beginVersion, targetVersion, partitioned)); + state std::vector logs; + state std::vector plogs; + wait(store(logs, bc->listLogFiles(snapshot.get().beginVersion, targetVersion, false)) && + store(plogs, bc->listLogFiles(snapshot.get().beginVersion, targetVersion, true))); - if (partitioned) { + if (plogs.size() > 0) { + logs.swap(plogs); // sort by tag ID so that filterDuplicates works. std::sort(logs.begin(), logs.end(), [](const LogFile& a, const LogFile& b) { return std::tie(a.tagId, a.beginVersion, a.endVersion) < @@ -1343,11 +1349,7 @@ public: } Future> getRestoreSet(Version targetVersion) final { - return getRestoreSet_impl(Reference::addRef(this), targetVersion, false); - } - - Future> getPartitionedRestoreSet(Version targetVersion) final { - return getRestoreSet_impl(Reference::addRef(this), targetVersion, true); + return getRestoreSet_impl(Reference::addRef(this), targetVersion); } private: diff --git a/fdbclient/BackupContainer.h b/fdbclient/BackupContainer.h index 6caeaaaa64..9697d280bc 100644 --- a/fdbclient/BackupContainer.h +++ b/fdbclient/BackupContainer.h @@ -267,11 +267,6 @@ public: // restore to given version is not possible. virtual Future> getRestoreSet(Version targetVersion) = 0; - // Get exactly the files necessary to restore to targetVersion. Returns non-present if - // restore to given version is not possible. This is intended for parallel - // restore in FDB 7.0, which reads partitioned mutation logs. - virtual Future> getPartitionedRestoreSet(Version targetVersion) = 0; - // Get an IBackupContainer based on a container spec string static Reference openContainer(std::string url); static std::vector getURLFormats(); diff --git a/fdbserver/RestoreMaster.actor.cpp b/fdbserver/RestoreMaster.actor.cpp index 92b189cb10..46db838633 100644 --- a/fdbserver/RestoreMaster.actor.cpp +++ b/fdbserver/RestoreMaster.actor.cpp @@ -633,8 +633,7 @@ ACTOR static Future collectBackupFiles(Reference bc, std::cout << "Restore to version: " << request.targetVersion << "\nBackupDesc: \n" << desc.toString() << "\n\n"; } - Optional restorable = wait(desc.partitioned ? bc->getPartitionedRestoreSet(request.targetVersion) - : bc->getRestoreSet(request.targetVersion)); + Optional restorable = wait(bc->getRestoreSet(request.targetVersion)); if (!restorable.present()) { TraceEvent(SevWarn, "FastRestoreMasterPhaseCollectBackupFiles").detail("NotRestorable", request.targetVersion); From d4334256f1c4d747068967d96e464e531b501217 Mon Sep 17 00:00:00 2001 From: Steve Atherton Date: Wed, 8 Apr 2020 20:18:41 -0700 Subject: [PATCH 1387/1604] Bug fix: BTree::writePages() was not accounting for the small amount of unpredictability in node overhead sizes which could lead to serializing a tree that doesn't fit into its destination page buffer. WritePages() now skips the prefix common to all records in the input set when calling deltaSize() to estimate resulting DeltaTree serialized sizes. --- fdbserver/DeltaTree.h | 2 +- fdbserver/IPager.h | 2 +- fdbserver/VersionedBTree.actor.cpp | 45 +++++++++++++++++++++++------- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/fdbserver/DeltaTree.h b/fdbserver/DeltaTree.h index 44d061729e..639daa98fe 100644 --- a/fdbserver/DeltaTree.h +++ b/fdbserver/DeltaTree.h @@ -484,7 +484,7 @@ public: int commonPrefix = basePrev ? commonWithPrev : commonWithNext; const T *base = basePrev ? prev : next; - int deltaSize = k.deltaSize(*base, false, commonPrefix); + int deltaSize = k.deltaSize(*base, commonPrefix, false); int nodeSpace = deltaSize + Node::headerSize(tree->largeNodes); if(nodeSpace > tree->nodeBytesFree) { return false; diff --git a/fdbserver/IPager.h b/fdbserver/IPager.h index 12d23ab089..5043d315fa 100644 --- a/fdbserver/IPager.h +++ b/fdbserver/IPager.h @@ -29,7 +29,7 @@ #define REDWOOD_DEBUG 0 -#define debug_printf_stream stderr +#define debug_printf_stream stdout #define debug_printf_always(...) { fprintf(debug_printf_stream, "%s %f %04d ", g_network->getLocalAddress().toString().c_str(), now(), __LINE__); fprintf(debug_printf_stream, __VA_ARGS__); fflush(debug_printf_stream); } #define debug_printf_noop(...) diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index b064a0cb78..a3242753b1 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -2348,13 +2348,23 @@ struct RedwoodRecordRef { return compare(rhs) >= 0; } - int deltaSize(const RedwoodRecordRef &base, bool worstCase = true, int skipLen = 0) const { + // Worst case overhead means to assu + int deltaSize(const RedwoodRecordRef &base, int skipLen, bool worstCaseOverhead) const { int prefixLen = getCommonPrefixLen(base, skipLen); int keySuffixLen = key.size() - prefixLen; int valueLen = value.present() ? value.get().size() : 0; - int formatType = Delta::determineLengthFormat(prefixLen, keySuffixLen, valueLen); - int versionBytes = version == 0 ? 0 : Delta::getVersionDeltaSizeBytes(version - base.version); + int formatType; + int versionBytes; + if(worstCaseOverhead) { + formatType = Delta::determineLengthFormat(key.size(), key.size(), valueLen); + versionBytes = version == 0 ? 0 : Delta::getVersionDeltaSizeBytes(version << 1); + } + else { + formatType = Delta::determineLengthFormat(prefixLen, keySuffixLen, valueLen); + versionBytes = version == 0 ? 0 : Delta::getVersionDeltaSizeBytes(version - base.version); + } + return 1 + Delta::LengthFormatSizes[formatType] + keySuffixLen + valueLen + versionBytes; } @@ -3386,6 +3396,8 @@ private: state int start = 0; state int i = 0; + // The common prefix length between the first and last records are common to all records + state int skipLen = entries.front().getCommonPrefixLen(entries.back()); // Leaves can have just one record if it's large, but internal pages should have at least 4 state int minimumEntries = (height == 1 ? 1 : 4); @@ -3408,15 +3420,24 @@ private: continue; } - // Get delta from previous record - int deltaSize = entry.deltaSize((i == start) ? pageLowerBound : entries[i - 1]); + // Get delta from previous record or page lower boundary if this is the first item in a page + const RedwoodRecordRef &base = (i == start) ? pageLowerBound : entries[i - 1]; + + // All record pairs in entries have skipLen bytes in common with each other, but for i == 0 the base is lowerBound + int skip = i == 0 ? 0 : skipLen; + + // In a delta tree, all common prefix bytes that can be borrowed, will be, but not necessarily + // by the same records during the linear estimate of the built page size. Since the key suffix bytes + // and therefore the key prefix lengths can be distributed differently in the balanced tree, worst case + // overhead for the delta size must be assumed. + int deltaSize = entry.deltaSize(base, skip, true); + int keySize = entry.key.size(); int valueSize = entry.value.present() ? entry.value.get().size() : 0; int nodeSize = BTreePage::BinaryTree::Node::headerSize(largeTree) + deltaSize; - - debug_printf("Adding %3d of %3lu (i=%3d) klen %4d vlen %3d nodeSize %4d page usage: %d/%d (%.2f%%) record=%s\n", - i + 1, entries.size(), i, keySize, valueSize, nodeSize, compressedBytes, pageSize, (float)compressedBytes / pageSize * 100, entry.toString(height == 1).c_str()); + debug_printf("Adding %3d of %3lu (i=%3d) klen %4d vlen %5d nodeSize %5d deltaSize %5d page usage: %d/%d (%.2f%%) record=%s\n", + i + 1, entries.size(), i, keySize, valueSize, nodeSize, deltaSize, compressedBytes, pageSize, (float)compressedBytes / pageSize * 100, entry.toString(height == 1).c_str()); // While the node doesn't fit, expand the page. // This is a loop because if the page size moves into "large" range for DeltaTree @@ -3486,8 +3507,12 @@ private: btPage->height = height; btPage->kvBytes = kvBytes; + debug_printf("Building tree. start=%d i=%d count=%d page usage: %d/%d (%.2f%%) bytes\nlower: %s\nupper: %s\n", start, i, i - start, + compressedBytes, pageSize, (float)compressedBytes / pageSize * 100, pageLowerBound.toString(false).c_str(), pageUpperBound.toString(false).c_str()); + int written = btPage->tree().build(pageSize, &entries[start], &entries[i], &pageLowerBound, &pageUpperBound); if(written > pageSize) { + debug_printf("ERROR: Wrote %d bytes to %d byte page (%d blocks). recs %d kvBytes %d compressed %d\n", written, pageSize, blockCount, i - start, kvBytes, compressedBytes); fprintf(stderr, "ERROR: Wrote %d bytes to %d byte page (%d blocks). recs %d kvBytes %d compressed %d\n", written, pageSize, blockCount, i - start, kvBytes, compressedBytes); ASSERT(false); } @@ -5422,7 +5447,7 @@ struct IntIntPair { return compare(rhs) < 0; } - int deltaSize(const IntIntPair &base, bool worstcase = false, int skipLen = 0) const { + int deltaSize(const IntIntPair &base, int skipLen, bool worstcase) const { return sizeof(Delta); } @@ -5447,7 +5472,7 @@ int deltaTest(RedwoodRecordRef rec, RedwoodRecordRef base) { RedwoodRecordRef::Delta &d = *(RedwoodRecordRef::Delta *)&buf.front(); Arena mem; - int expectedSize = rec.deltaSize(base, false); + int expectedSize = rec.deltaSize(base, 0, false); int deltaSize = rec.writeDelta(d, base); RedwoodRecordRef decoded = d.apply(base, mem); From fed5c543d47c6d5639a44c586c7ce3a2d7d78408 Mon Sep 17 00:00:00 2001 From: Vishesh Yadav Date: Wed, 8 Apr 2020 22:31:34 -0700 Subject: [PATCH 1388/1604] Remove leftover TODO code around centralized healthmonitor --- fdbclient/CMakeLists.txt | 2 - fdbclient/ClusterInterface.h | 33 +------ fdbclient/HealthMonitorClient.actor.cpp | 111 ------------------------ fdbclient/HealthMonitorClient.h | 29 ------- fdbrpc/FlowTransport.actor.cpp | 52 +++++------ fdbserver/ClusterController.actor.cpp | 19 ---- fdbserver/worker.actor.cpp | 2 - 7 files changed, 25 insertions(+), 223 deletions(-) delete mode 100644 fdbclient/HealthMonitorClient.actor.cpp delete mode 100644 fdbclient/HealthMonitorClient.h diff --git a/fdbclient/CMakeLists.txt b/fdbclient/CMakeLists.txt index ebdc1808ca..f8ac1f310d 100644 --- a/fdbclient/CMakeLists.txt +++ b/fdbclient/CMakeLists.txt @@ -21,8 +21,6 @@ set(FDBCLIENT_SRCS FDBOptions.h FDBTypes.h FileBackupAgent.actor.cpp - HealthMonitorClient.h - HealthMonitorClient.actor.cpp HTTP.actor.cpp IClientApi.h JsonBuilder.cpp diff --git a/fdbclient/ClusterInterface.h b/fdbclient/ClusterInterface.h index 2b881ed16e..8e2839cfbb 100644 --- a/fdbclient/ClusterInterface.h +++ b/fdbclient/ClusterInterface.h @@ -36,7 +36,6 @@ struct ClusterInterface { RequestStream< ReplyPromise > ping; RequestStream< struct GetClientWorkersRequest > getClientWorkers; RequestStream< struct ForceRecoveryRequest > forceRecovery; - RequestStream< struct HealthMonitoringRequest > healthMonitoring; bool operator == (ClusterInterface const& r) const { return id() == r.id(); } bool operator != (ClusterInterface const& r) const { return id() != r.id(); } @@ -49,8 +48,7 @@ struct ClusterInterface { databaseStatus.getFuture().isReady() || ping.getFuture().isReady() || getClientWorkers.getFuture().isReady() || - forceRecovery.getFuture().isReady() || - healthMonitoring.getFuture().isReady(); + forceRecovery.getFuture().isReady(); } void initEndpoints() { @@ -60,13 +58,11 @@ struct ClusterInterface { ping.getEndpoint( TaskPriority::ClusterController ); getClientWorkers.getEndpoint( TaskPriority::ClusterController ); forceRecovery.getEndpoint( TaskPriority::ClusterController ); - healthMonitoring.getEndpoint( TaskPriority::FailureMonitor ); } template void serialize( Ar& ar ) { - serializer(ar, openDatabase, failureMonitoring, databaseStatus, ping, getClientWorkers, forceRecovery, - healthMonitoring); + serializer(ar, openDatabase, failureMonitoring, databaseStatus, ping, getClientWorkers, forceRecovery); } }; @@ -234,31 +230,6 @@ struct FailureMonitoringRequest { } }; -struct HealthMonitoringReply { - constexpr static FileIdentifier file_identifier = 6820326; - Version healthInformationVersion; - Arena arena; - - template - void serialize(Ar& ar) { - serializer(ar, healthInformationVersion, arena); - } -}; - -struct HealthMonitoringRequest { - constexpr static FileIdentifier file_identifier = 5867852; - Version healthInformationVersion; - int lastRequestElapsed; - std::map closedPeers; - std::map peerStatus; - ReplyPromise reply; - - template - void serialize(Ar& ar) { - serializer(ar, lastRequestElapsed, healthInformationVersion, closedPeers, peerStatus, reply); - } -}; - struct StatusReply { constexpr static FileIdentifier file_identifier = 9980504; StatusObject statusObj; diff --git a/fdbclient/HealthMonitorClient.actor.cpp b/fdbclient/HealthMonitorClient.actor.cpp deleted file mode 100644 index fe2481c2ab..0000000000 --- a/fdbclient/HealthMonitorClient.actor.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * HealthMonitorClient.actor.cpp - * - * This source file is part of the FoundationDB open source project - * - * Copyright 2013-2020 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/HealthMonitorClient.h" -#include "fdbrpc/FailureMonitor.h" -#include "fdbclient/ClusterInterface.h" -#include "flow/actorcompiler.h" // has to be last include -#include - -struct HealthMonitorClientState : ReferenceCounted { - HealthMonitorClientState() { } -}; - -ACTOR Future healthMonitorClientLoop(ClusterInterface controller, Reference hmState) { - state Version version = 0; - state Future request = Never(); - state Future nextRequest = delay(0, TaskPriority::FailureMonitor); - state Future requestTimeout = Never(); - state double before = now(); - state double waitfor = 0; - - state int CLIENT_REQUEST_FAILED_TIMEOUT_SECS = 2; /* seconds */ - try { - loop { - choose { - when(HealthMonitoringReply reply = wait(request)) { - g_network->setCurrentTask(TaskPriority::DefaultDelay); - request = Never(); - requestTimeout = Never(); - version = reply.healthInformationVersion; - - before = now(); - waitfor = FLOW_KNOBS->HEALTH_MONITOR_CLIENT_REQUEST_INTERVAL_SECS; - nextRequest = delayJittered(waitfor, TaskPriority::FailureMonitor); - } - when(wait(requestTimeout)) { - g_network->setCurrentTask(TaskPriority::DefaultDelay); - requestTimeout = Never(); - TraceEvent(SevWarn, "HealthMonitoringServerDown").detail("OldServerID", controller.id()); - } - when(wait(nextRequest)) { - g_network->setCurrentTask(TaskPriority::DefaultDelay); - nextRequest = Never(); - - double elapsed = now() - before; - double slowThreshold = .200 + waitfor + FLOW_KNOBS->MAX_BUGGIFIED_DELAY; - double warnAlwaysThreshold = CLIENT_KNOBS->FAILURE_MIN_DELAY / 2; - - if (elapsed > slowThreshold && deterministicRandom()->random01() < elapsed / warnAlwaysThreshold) { - TraceEvent(elapsed > warnAlwaysThreshold ? SevWarnAlways : SevWarn, "HealthMonitorClientSlow") - .detail("Elapsed", elapsed) - .detail("Expected", waitfor); - } - - std::map closedPeers; - for (const auto& entry : FlowTransport::transport().healthMonitor()->getPeerClosedHistory()) { - closedPeers[entry.second] += 1; - } - - HealthMonitoringRequest req; - req.healthInformationVersion = version; - req.closedPeers = closedPeers; - req.peerStatus = FlowTransport::transport().healthMonitor()->getPeerStatus(); - request = controller.healthMonitoring.getReply(req, TaskPriority::FailureMonitor); - if (!controller.healthMonitoring.getEndpoint().isLocal()) - requestTimeout = delay(CLIENT_REQUEST_FAILED_TIMEOUT_SECS, TaskPriority::FailureMonitor); - } - } - } - } catch (Error& e) { - if (e.code() == error_code_broken_promise) // broken promise from clustercontroller means it has died (and - // hopefully will be replaced) - return Void(); - TraceEvent(SevError, "HealthMonitorClientError").error(e); - throw; // goes nowhere - } -} - -ACTOR Future healthMonitorClient(Reference>> ci) { - TraceEvent("HealthMonitorStart").detail("IsClient", FlowTransport::transport().isClient()); - if (FlowTransport::transport().isClient()) { - wait(Never()); - } - - return Never(); - // TODO: Re-enable centralized health monitoring. - // state Reference hmState = - // Reference(new HealthMonitorClientState()); - // loop { - // state Future client = - // ci->get().present() ? healthMonitorClientLoop(ci->get().get(), hmState) : Void(); - // wait(ci->onChange()); - // } -} diff --git a/fdbclient/HealthMonitorClient.h b/fdbclient/HealthMonitorClient.h deleted file mode 100644 index 6190ccde5a..0000000000 --- a/fdbclient/HealthMonitorClient.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * HealthMonitorClient.h - * - * This source file is part of the FoundationDB open source project - * - * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef FDBCLIENT_HEALTHMONITORCLIENT_H -#define FDBCLIENT_HEALTHMONITORCLIENT_H -#pragma once - -#include "flow/flow.h" - -Future healthMonitorClient(Reference>> const&); - -#endif diff --git a/fdbrpc/FlowTransport.actor.cpp b/fdbrpc/FlowTransport.actor.cpp index 6eb7e319e9..a269d516c9 100644 --- a/fdbrpc/FlowTransport.actor.cpp +++ b/fdbrpc/FlowTransport.actor.cpp @@ -399,38 +399,32 @@ ACTOR Future connectionWriter( Reference self, Reference delayedHealthUpdate(NetworkAddress address) { - try { - state double start = now(); - state int count = 0; - loop { - if (FLOW_KNOBS->HEALTH_MONITOR_MARK_FAILED_UNSTABLE_CONNECTIONS && - FlowTransport::transport().healthMonitor()->tooManyConnectionsClosed(address) && address.isPublic()) { - if (count == 0) { - TraceEvent("TooManyConnectionsClosedMarkFailed") - .detail("Dest", address) - .detail("StartTime", start) - .detail("ClosedCount", - FlowTransport::transport().healthMonitor()->closedConnectionsCount(address)); - IFailureMonitor::failureMonitor().setStatus(address, FailureStatus(true)); - } - wait(delayJittered(FLOW_KNOBS->MAX_RECONNECTION_TIME * 2.0)); - } else { - if (count > 1) - TraceEvent("TooManyConnectionsClosedMarkAvailable") - .detail("Dest", address) - .detail("StartTime", start) - .detail("TimeElapsed", now() - start) - .detail("ClosedCount", - FlowTransport::transport().healthMonitor()->closedConnectionsCount(address)); - IFailureMonitor::failureMonitor().setStatus(address, FailureStatus(false)); - break; + state double start = now(); + state int count = 0; + loop { + if (FLOW_KNOBS->HEALTH_MONITOR_MARK_FAILED_UNSTABLE_CONNECTIONS && + FlowTransport::transport().healthMonitor()->tooManyConnectionsClosed(address) && address.isPublic()) { + if (count == 0) { + TraceEvent("TooManyConnectionsClosedMarkFailed") + .detail("Dest", address) + .detail("StartTime", start) + .detail("ClosedCount", FlowTransport::transport().healthMonitor()->closedConnectionsCount(address)); + IFailureMonitor::failureMonitor().setStatus(address, FailureStatus(true)); } - ++count; + wait(delayJittered(FLOW_KNOBS->MAX_RECONNECTION_TIME * 2.0)); + } else { + if (count > 1) + TraceEvent("TooManyConnectionsClosedMarkAvailable") + .detail("Dest", address) + .detail("StartTime", start) + .detail("TimeElapsed", now() - start) + .detail("ClosedCount", FlowTransport::transport().healthMonitor()->closedConnectionsCount(address)); + IFailureMonitor::failureMonitor().setStatus(address, FailureStatus(false)); + break; } - return Void(); - } catch (Error& e) { - throw e; + ++count; } + return Void(); } ACTOR Future connectionKeeper( Reference self, diff --git a/fdbserver/ClusterController.actor.cpp b/fdbserver/ClusterController.actor.cpp index d2a87fc8fb..fc00bccbf0 100644 --- a/fdbserver/ClusterController.actor.cpp +++ b/fdbserver/ClusterController.actor.cpp @@ -1957,24 +1957,6 @@ ACTOR Future failureDetectionServer( UID uniqueID, ClusterControllerData* } } -struct HealthMonitorServerState { - std::map> failureState; -}; - -ACTOR Future healthMonitoringServer(UID uniqueID, ClusterControllerData* self, - FutureStream requests) { - state Version currentVersion = 0; - - loop choose { - when(HealthMonitoringRequest req = waitNext(requests)) { - NetworkAddress reporteeAddress = req.reply.getEndpoint().getPrimaryAddress(); - TraceEvent("HealthMonitorRequestReceived") - .detail("Size", req.closedPeers.size()) - .detail("EndpointAddress", reporteeAddress); - } - } -} - ACTOR Future> requireAll( vector>>> in ) { state vector out; state int i; @@ -3070,7 +3052,6 @@ ACTOR Future clusterControllerCore( ClusterControllerFullInterface interf, state Future> error = errorOr( actorCollection( self.addActor.getFuture() ) ); self.addActor.send( failureDetectionServer( self.id, &self, interf.clientInterface.failureMonitoring.getFuture() ) ); - self.addActor.send( healthMonitoringServer( self.id, &self, interf.clientInterface.healthMonitoring.getFuture() ) ); self.addActor.send( clusterWatchDatabase( &self, &self.db ) ); // Start the master database self.addActor.send( self.updateWorkerList.init( self.db.db ) ); self.addActor.send( statusServer( interf.clientInterface.databaseStatus.getFuture(), &self, coordinators)); diff --git a/fdbserver/worker.actor.cpp b/fdbserver/worker.actor.cpp index ecab4f3545..b22015e296 100644 --- a/fdbserver/worker.actor.cpp +++ b/fdbserver/worker.actor.cpp @@ -25,7 +25,6 @@ #include "flow/TDMetric.actor.h" #include "fdbrpc/simulator.h" #include "fdbclient/NativeAPI.actor.h" -#include "fdbclient/HealthMonitorClient.h" #include "fdbclient/MetricLogger.h" #include "fdbserver/BackupInterface.h" #include "fdbserver/WorkerInterface.actor.h" @@ -1600,7 +1599,6 @@ ACTOR Future fdbd( actors.push_back(reportErrors(monitorAndWriteCCPriorityInfo(fitnessFilePath, asyncPriorityInfo), "MonitorAndWriteCCPriorityInfo")); actors.push_back( reportErrors( processClass == ProcessClass::TesterClass ? monitorLeader( connFile, cc ) : clusterController( connFile, cc , asyncPriorityInfo, recoveredDiskFiles.getFuture(), localities ), "ClusterController") ); - actors.push_back( reportErrors(healthMonitorClient( ci ), "HealthMonitorClient") ); actors.push_back( reportErrors(extractClusterInterface( cc, ci ), "ExtractClusterInterface") ); actors.push_back( reportErrorsExcept(workerServer(connFile, cc, localities, asyncPriorityInfo, processClass, dataFolder, memoryLimit, metricsConnFile, metricsPrefix, recoveredDiskFiles, memoryProfileThreshold, coordFolder, whitelistBinPaths), "WorkerServer", UID(), &normalWorkerErrors()) ); state Future firstConnect = reportErrors( printOnFirstConnected(ci), "ClusterFirstConnectedError" ); From db2cef844b326fd9e13b4a4385c8ce2235c816c5 Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Wed, 8 Apr 2020 22:52:23 -0700 Subject: [PATCH 1389/1604] Write mutation log type as a backup property This can solve the problem when listing log files returns empty results. --- fdbclient/BackupContainer.actor.cpp | 27 ++++++++++++++----- ...kupAndParallelRestoreCorrectness.actor.cpp | 1 - 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/fdbclient/BackupContainer.actor.cpp b/fdbclient/BackupContainer.actor.cpp index 2e713b39ee..27a64a53bf 100644 --- a/fdbclient/BackupContainer.actor.cpp +++ b/fdbclient/BackupContainer.actor.cpp @@ -723,10 +723,12 @@ public: state Optional metaLogEnd; state Optional metaExpiredEnd; state Optional metaUnreliableEnd; + state Optional metaLogType; std::vector> metaReads; metaReads.push_back(store(metaExpiredEnd, bc->expiredEndVersion().get())); metaReads.push_back(store(metaUnreliableEnd, bc->unreliableEndVersion().get())); + metaReads.push_back(store(metaLogType, bc->logType().get())); // Only read log begin/end versions if not doing a deep scan, otherwise scan files and recalculate them. if(!deepScan) { @@ -737,12 +739,13 @@ public: wait(waitForAll(metaReads)); TraceEvent("BackupContainerDescribe2") - .detail("URL", bc->getURL()) - .detail("LogStartVersionOverride", logStartVersionOverride) - .detail("ExpiredEndVersion", metaExpiredEnd.orDefault(invalidVersion)) - .detail("UnreliableEndVersion", metaUnreliableEnd.orDefault(invalidVersion)) - .detail("LogBeginVersion", metaLogBegin.orDefault(invalidVersion)) - .detail("LogEndVersion", metaLogEnd.orDefault(invalidVersion)); + .detail("URL", bc->getURL()) + .detail("LogStartVersionOverride", logStartVersionOverride) + .detail("ExpiredEndVersion", metaExpiredEnd.orDefault(invalidVersion)) + .detail("UnreliableEndVersion", metaUnreliableEnd.orDefault(invalidVersion)) + .detail("LogBeginVersion", metaLogBegin.orDefault(invalidVersion)) + .detail("LogEndVersion", metaLogEnd.orDefault(invalidVersion)) + .detail("LogType", metaLogType.orDefault(-1)); // If the logStartVersionOverride is positive (not relative) then ensure that unreliableEndVersion is equal or greater if(logStartVersionOverride != invalidVersion && metaUnreliableEnd.orDefault(invalidVersion) < logStartVersionOverride) { @@ -810,7 +813,7 @@ public: desc.partitioned = true; logs.swap(plogs); } else { - desc.partitioned = false; + desc.partitioned = metaLogType.present() && metaLogType.get() == PARTITIONED_MUTATION_LOG; } // List logs in version order so log continuity can be analyzed @@ -857,6 +860,11 @@ public: updates = updates && bc->logEndVersion().set(desc.contiguousLogEnd.get()); } + if (!metaLogType.present()) { + updates = updates && bc->logType().set(desc.partitioned ? PARTITIONED_MUTATION_LOG + : NON_PARTITIONED_MUTATION_LOG); + } + wait(updates); } catch(Error &e) { if(e.code() == error_code_actor_cancelled) @@ -1384,6 +1392,11 @@ public: VersionProperty expiredEndVersion() { return {Reference::addRef(this), "expired_end_version"}; } VersionProperty unreliableEndVersion() { return {Reference::addRef(this), "unreliable_end_version"}; } + // Backup log types + const static Version NON_PARTITIONED_MUTATION_LOG = 0; + const static Version PARTITIONED_MUTATION_LOG = 1; + VersionProperty logType() { return { Reference::addRef(this), "mutation_log_type" }; } + ACTOR static Future writeVersionProperty(Reference bc, std::string path, Version v) { try { state Reference f = wait(bc->writeFile(path)); diff --git a/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp b/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp index 3f00b5f781..66a07df408 100644 --- a/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp +++ b/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp @@ -218,7 +218,6 @@ struct BackupAndParallelRestoreCorrectnessWorkload : TestWorkload { if(!fdesc.isError()) { state BackupDescription desc = fdesc.get(); - ASSERT(self->usePartitionedLogs == desc.partitioned); wait(desc.resolveVersionTimes(cx)); printf("BackupDescription:\n%s\n", desc.toString().c_str()); restorable = desc.maxRestorableVersion.present(); From 01285f33740e749941201ddda2f240746101d49b Mon Sep 17 00:00:00 2001 From: tclinken Date: Thu, 9 Apr 2020 14:09:00 -0700 Subject: [PATCH 1390/1604] Delay annotation of trace batch events created before trace file is opened --- flow/Trace.cpp | 29 +++++++++++++++++++---------- flow/Trace.h | 3 +++ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/flow/Trace.cpp b/flow/Trace.cpp index ce946a2db9..6518a1757a 100644 --- a/flow/Trace.cpp +++ b/flow/Trace.cpp @@ -377,11 +377,11 @@ public: eventBuffer.clear(); } + opened = true; for(TraceEventFields &fields : eventBuffer) { annotateEvent(fields); } - opened = true; if(preopenOverflowCount > 0) { TraceEvent(SevWarn, "TraceLogPreopenOverflow").detail("OverflowEventCount", preopenOverflowCount); preopenOverflowCount = 0; @@ -389,6 +389,8 @@ public: } void annotateEvent(TraceEventFields& fields) { + if (!opened || fields.isAnnotated()) + return; MutexHolder holder(mutex); if(localAddress.present()) { fields.addField("Machine", formatIpPort(localAddress.get().ip, localAddress.get().port)); @@ -400,15 +402,13 @@ public: if(r.rolesString.size() > 0) { fields.addField("Roles", r.rolesString); } + fields.setAnnotated(); } - void writeEvent(TraceEventFields fields, std::string trackLatestKey, bool trackError, - bool alreadyAnnotated = false) { + void writeEvent(TraceEventFields fields, std::string trackLatestKey, bool trackError) { MutexHolder hold(mutex); - if (opened && !alreadyAnnotated) { - annotateEvent(fields); - } + annotateEvent(fields); if(!trackLatestKey.empty()) { fields.addField("TrackLatestType", "Original"); @@ -420,6 +420,7 @@ public: } // FIXME: What if we are using way too much memory for buffer? + ASSERT(!isOpen() || fields.isAnnotated()); eventBuffer.push_back(fields); bufferLength += fields.sizeBytes(); @@ -1230,21 +1231,21 @@ void TraceBatch::dump() { if(g_network->isSimulated()) { attachBatch[i].fields.addField("Machine", machine); } - g_traceLog.writeEvent(attachBatch[i].fields, "", false, !dumpImmediately()); + g_traceLog.writeEvent(attachBatch[i].fields, "", false); } for(int i = 0; i < eventBatch.size(); i++) { if(g_network->isSimulated()) { eventBatch[i].fields.addField("Machine", machine); } - g_traceLog.writeEvent(eventBatch[i].fields, "", false, !dumpImmediately()); + g_traceLog.writeEvent(eventBatch[i].fields, "", false); } for(int i = 0; i < buggifyBatch.size(); i++) { if(g_network->isSimulated()) { buggifyBatch[i].fields.addField("Machine", machine); } - g_traceLog.writeEvent(buggifyBatch[i].fields, "", false, !dumpImmediately()); + g_traceLog.writeEvent(buggifyBatch[i].fields, "", false); } g_traceLog.flush(); @@ -1278,7 +1279,7 @@ TraceBatch::BuggifyInfo::BuggifyInfo(double time, int activated, int line, std:: fields.addField("Line", format("%d", line)); } -TraceEventFields::TraceEventFields() : bytes(0) {} +TraceEventFields::TraceEventFields() : bytes(0), annotated(false) {} void TraceEventFields::addField(const std::string& key, const std::string& value) { bytes += key.size() + value.size(); @@ -1306,6 +1307,14 @@ TraceEventFields::FieldIterator TraceEventFields::end() const { return fields.cend(); } +bool TraceEventFields::isAnnotated() const { + return annotated; +} + +void TraceEventFields::setAnnotated() { + annotated = true; +} + const TraceEventFields::Field &TraceEventFields::operator[] (int index) const { ASSERT(index >= 0 && index < size()); return fields.at(index); diff --git a/flow/Trace.h b/flow/Trace.h index 562192f3de..2be433f2d9 100644 --- a/flow/Trace.h +++ b/flow/Trace.h @@ -71,6 +71,8 @@ public: size_t sizeBytes() const; FieldIterator begin() const; FieldIterator end() const; + bool isAnnotated() const; + void setAnnotated(); void addField(const std::string& key, const std::string& value); void addField(std::string&& key, std::string&& value); @@ -95,6 +97,7 @@ public: private: FieldContainer fields; size_t bytes; + bool annotated; }; template From 0e7592c7577de78fccc37e26ac1b9f9e5348a540 Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Thu, 9 Apr 2020 14:15:58 -0700 Subject: [PATCH 1391/1604] don't fail docker script if group already exists --- build/gen_dev_docker.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/gen_dev_docker.sh b/build/gen_dev_docker.sh index 7f23b66354..2bdaa1b8bd 100755 --- a/build/gen_dev_docker.sh +++ b/build/gen_dev_docker.sh @@ -30,7 +30,7 @@ num_groups=${#gids[@]} additional_groups="-G sudo" for ((i=0;i> Dockerfile + echo "RUN groupadd -g ${gids[$i]} ${groups[$i]} || true" >> Dockerfile if [ ${gids[i]} -ne ${gid} ] then additional_groups="${additional_groups},${gids[$i]}" From ceab4374cf383d10add4b92f5546288f3a4f33ea Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Thu, 9 Apr 2020 14:16:14 -0700 Subject: [PATCH 1392/1604] statically link libc++ --- cmake/ConfigureCompiler.cmake | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cmake/ConfigureCompiler.cmake b/cmake/ConfigureCompiler.cmake index e5d33533d1..893752be6f 100644 --- a/cmake/ConfigureCompiler.cmake +++ b/cmake/ConfigureCompiler.cmake @@ -189,13 +189,16 @@ else() add_compile_options() # Clang has link errors unless `atomic` is specifically requested. if(NOT APPLE) - add_link_options(-latomic) + #add_link_options(-latomic) endif() if (APPLE OR USE_LIBCXX) add_compile_options($<$:-stdlib=libc++>) add_compile_definitions(WITH_LIBCXX) if (NOT APPLE) - add_link_options(-lc++ -lc++abi -Wl,-build-id=sha1) + if (STATIC_LINK_LIBCXX) + add_link_options(-static-libgcc -nostdlib++ -Wl,-Bstatic -lc++ -lc++abi -Wl,-Bdynamic) + endif() + add_link_options(-stdlib=libc++ -Wl,-build-id=sha1) endif() endif() if (OPEN_FOR_IDE) From 20a2fe2785308d8d87f4a31c4ceae65a97386492 Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Thu, 9 Apr 2020 14:20:52 -0700 Subject: [PATCH 1393/1604] Add LLVM to docker file --- build/Dockerfile | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/build/Dockerfile b/build/Dockerfile index c8a84818b8..0067084843 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -51,6 +51,16 @@ RUN cd /tmp && curl -L https://www.openssl.org/source/openssl-1.1.1d.tar.gz -o o ln -sv /usr/local/lib64/lib*.so.1.1 /usr/lib64/ &&\ cd /tmp/ && rm -rf /tmp/openssl-1.1.1d /tmp/openssl.tar.gz +# install llvm +WORKDIR /tmp +RUN cd /tmp && curl -L https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/llvm-project-10.0.0.tar.xz -o llvm.tar.xz &&\ + echo "6287a85f4a6aeb07dbffe27847117fe311ada48005f2b00241b523fe7b60716e llvm.tar.xz" > llvm-sha.txt &&\ + sha256sum -c llvm-sha.txt && tar xf llvm.tar.xz --no-same-owner &&\ + mkdir /tmp/llvm-project-10.0.0/build && cd /tmp/llvm-project-10.0.0/build &&\ + scl enable devtoolset-8 rh-python36 -- cmake -G Ninja -DLLVM_ENABLE_PROJECTS='clang;clang-tools-extra;libcxx;libcxxabi;libunwind;lldb;compiler-rt;lld' -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release ../llvm &&\ + scl enable devtoolset-8 rh-python36 -- ninja install &&\ + cd / && rm -rf /tmp/llvm-project-10.0.0 + LABEL version=0.1.12 ENV DOCKER_IMAGEVER=0.1.12 ENV JAVA_HOME=/usr/lib/jvm/java-1.8.0 From 5a64dab74ff7ef3f423d5bd42bf07f0006dc7005 Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Thu, 9 Apr 2020 14:27:13 -0700 Subject: [PATCH 1394/1604] fix linker error --- contrib/monitoring/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/monitoring/CMakeLists.txt b/contrib/monitoring/CMakeLists.txt index 37aab4b0ef..4f5f2008c3 100644 --- a/contrib/monitoring/CMakeLists.txt +++ b/contrib/monitoring/CMakeLists.txt @@ -1 +1,2 @@ add_executable(actor_flamegraph actor_flamegraph.cpp) +target_link_libraries(actor_flamegraph PRIVATE Threads::Threads) From 6e5e7eea8d328785a7920db4be4080c324f92d59 Mon Sep 17 00:00:00 2001 From: Steve Atherton Date: Thu, 9 Apr 2020 17:14:25 -0700 Subject: [PATCH 1395/1604] Fixed a comment. --- fdbserver/VersionedBTree.actor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index 4f901f0820..f7895855d5 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -2067,8 +2067,8 @@ struct RedwoodRecordRef { // 1 bits - has nonzero version // 2 bits - version delta integer size code, maps to 0, 2, 4, 8 // 2 bits - length fields format - // - // Length fields using 3 to 7 bytes total depending on length fields format + // + // Length fields using 3 to 8 bytes total depending on length fields format // // Byte strings // Key suffix bytes From 549ce29bdbf259fc11fb1db87cddc52b4ea8cb4e Mon Sep 17 00:00:00 2001 From: Steve Atherton Date: Thu, 9 Apr 2020 19:12:24 -0700 Subject: [PATCH 1396/1604] VersionDelta size options are now (0, 4, 6, 8), having removed the 2-byte option as it is not very useful. --- fdbserver/VersionedBTree.actor.cpp | 44 ++++++++++++++++++------------ 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index f7895855d5..c2e8038be9 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -2056,7 +2056,14 @@ struct RedwoodRecordRef { } LengthFormat3; }; + struct int48_t { + static constexpr int64_t MASK = 0xFFFFFFFFFFFFLL; + int32_t high; + int16_t low; + }; + static constexpr int LengthFormatSizes[] = {sizeof(LengthFormat0), sizeof(LengthFormat1), sizeof(LengthFormat2), sizeof(LengthFormat3)}; + static constexpr int VersionDeltaSizes[] = {0, sizeof(int32_t), sizeof(int48_t), sizeof(int64_t)}; // Serialized Format // @@ -2065,7 +2072,7 @@ struct RedwoodRecordRef { // 1 bit - item is deleted // 1 bit - has value (different from zero-length value, if 0 value len will be 0) // 1 bits - has nonzero version - // 2 bits - version delta integer size code, maps to 0, 2, 4, 8 + // 2 bits - version delta integer size code, maps to 0, 4, 6, 8 // 2 bits - length fields format // // Length fields using 3 to 8 bytes total depending on length fields format @@ -2156,22 +2163,19 @@ struct RedwoodRecordRef { int getVersionDeltaSizeBytes() const { int code = (flags & VERSION_DELTA_SIZE) >> 2; - if(code != 0) { - return 1 << code; - } - return 0; + return VersionDeltaSizes[code]; } static int getVersionDeltaSizeBytes(Version d) { if(d == 0) { return 0; } - else if(d == (int16_t)d) { - return sizeof(uint16_t); - } else if(d == (int32_t)d) { return sizeof(int32_t); } + else if(d == (d & int48_t::MASK)) { + return sizeof(int48_t); + } return sizeof(int64_t); } @@ -2179,8 +2183,8 @@ struct RedwoodRecordRef { int code = (flags & VERSION_DELTA_SIZE) >> 2; switch(code) { case 0: return 0; - case 1: return *(int16_t *)r; - case 2: return *(int32_t *)r; + case 1: return *(int32_t *)r; + case 2: return (((int64_t)((int48_t *)r)->high) << 16) | (((int48_t *)r)->low & 0xFFFF); case 3: default: return *(int64_t *)r; } @@ -2192,15 +2196,16 @@ struct RedwoodRecordRef { if(d == 0) { return 0; } - else if(d == (int16_t)d) { - flags |= 1 << 2; - *(uint16_t *)w = d; - return sizeof(uint16_t); - } else if(d == (int32_t)d) { + flags |= 1 << 2; + *(uint32_t *)w = d; + return sizeof(uint32_t); + } + else if(d == (d & int48_t::MASK)) { flags |= 2 << 2; - *(int32_t *)w = d; - return sizeof(int32_t); + ((int48_t *)w)->high = d >> 16; + ((int48_t *)w)->low = d; + return sizeof(int48_t); } else { flags |= 3 << 2; @@ -5515,6 +5520,11 @@ TEST_CASE("!/redwood/correctness/unit/RedwoodRecordRef") { ASSERT(RedwoodRecordRef::Delta::LengthFormatSizes[2] == 6); ASSERT(RedwoodRecordRef::Delta::LengthFormatSizes[3] == 8); + ASSERT(RedwoodRecordRef::Delta::VersionDeltaSizes[0] == 0); + ASSERT(RedwoodRecordRef::Delta::VersionDeltaSizes[1] == 4); + ASSERT(RedwoodRecordRef::Delta::VersionDeltaSizes[2] == 6); + ASSERT(RedwoodRecordRef::Delta::VersionDeltaSizes[3] == 8); + // Test pageID stuff. { LogicalPageID ids[] = {1, 5}; From fe4bf3092f999b171063245761aee7e1da84cbc8 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Thu, 9 Apr 2020 20:43:09 -0700 Subject: [PATCH 1397/1604] Buggify DESIRED_TEAMS_PER_SERVER between 1 and 10 --- fdbserver/Knobs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index 4e2547cd31..8e55e23abd 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -201,7 +201,7 @@ void ServerKnobs::initialize(bool randomize, ClientKnobs* clientKnobs, bool isSi init( STORAGE_METRICS_POLLING_DELAY, 2.0 ); if( randomize && BUGGIFY ) STORAGE_METRICS_POLLING_DELAY = 15.0; init( STORAGE_METRICS_RANDOM_DELAY, 0.2 ); init( AVAILABLE_SPACE_RATIO_CUTOFF, 0.05 ); - init( DESIRED_TEAMS_PER_SERVER, 5 ); if( randomize && BUGGIFY ) DESIRED_TEAMS_PER_SERVER = 1; + init( DESIRED_TEAMS_PER_SERVER, 5 ); if( randomize && BUGGIFY ) DESIRED_TEAMS_PER_SERVER = deterministicRandom()->randomInt(1, 10); init( MAX_TEAMS_PER_SERVER, 5*DESIRED_TEAMS_PER_SERVER ); init( DD_SHARD_SIZE_GRANULARITY, 5000000 ); init( DD_SHARD_SIZE_GRANULARITY_SIM, 500000 ); if( randomize && BUGGIFY ) DD_SHARD_SIZE_GRANULARITY_SIM = 0; From f95bbc0ffa56aede08e66daed3655f431ff3b237 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Fri, 10 Apr 2020 09:48:35 -0700 Subject: [PATCH 1398/1604] Exclude Snap related tests from correctness test temporily Those tests fail with high chance in nightly test. --- tests/CMakeLists.txt | 16 ++++++++-------- .../from_6.2.0 => }/SnapCycleRestart-1.txt | 0 .../from_6.2.0 => }/SnapCycleRestart-2.txt | 0 .../from_6.2.0 => }/SnapTestAttrition-1.txt | 0 .../from_6.2.0 => }/SnapTestAttrition-2.txt | 0 .../from_6.2.0 => }/SnapTestRestart-1.txt | 0 .../from_6.2.0 => }/SnapTestRestart-2.txt | 0 .../from_6.2.0 => }/SnapTestSimpleRestart-1.txt | 0 .../from_6.2.0 => }/SnapTestSimpleRestart-2.txt | 0 9 files changed, 8 insertions(+), 8 deletions(-) rename tests/{restarting/from_6.2.0 => }/SnapCycleRestart-1.txt (100%) rename tests/{restarting/from_6.2.0 => }/SnapCycleRestart-2.txt (100%) rename tests/{restarting/from_6.2.0 => }/SnapTestAttrition-1.txt (100%) rename tests/{restarting/from_6.2.0 => }/SnapTestAttrition-2.txt (100%) rename tests/{restarting/from_6.2.0 => }/SnapTestRestart-1.txt (100%) rename tests/{restarting/from_6.2.0 => }/SnapTestRestart-2.txt (100%) rename tests/{restarting/from_6.2.0 => }/SnapTestSimpleRestart-1.txt (100%) rename tests/{restarting/from_6.2.0 => }/SnapTestSimpleRestart-2.txt (100%) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 37837543f2..5cf67ba5ae 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -163,17 +163,17 @@ if(WITH_PYTHON) TEST_FILES restarting/StorefrontTestRestart-1.txt restarting/StorefrontTestRestart-2.txt) add_fdb_test( - TEST_FILES restarting/from_6.2.0/SnapTestAttrition-1.txt - restarting/from_6.2.0/SnapTestAttrition-2.txt) + TEST_FILES SnapTestAttrition-1.txt + SnapTestAttrition-2.txt IGNORE) add_fdb_test( - TEST_FILES restarting/from_6.2.0/SnapTestSimpleRestart-1.txt - restarting/from_6.2.0/SnapTestSimpleRestart-2.txt) + TEST_FILES SnapTestSimpleRestart-1.txt + SnapTestSimpleRestart-2.txt IGNORE) add_fdb_test( - TEST_FILES restarting/from_6.2.0/SnapTestRestart-1.txt - restarting/from_6.2.0/SnapTestRestart-2.txt) + TEST_FILES SnapTestRestart-1.txt + SnapTestRestart-2.txt IGNORE) add_fdb_test( - TEST_FILES restarting/from_6.2.0/SnapCycleRestart-1.txt - restarting/from_6.2.0/SnapCycleRestart-2.txt) + TEST_FILES SnapCycleRestart-1.txt + SnapCycleRestart-2.txt IGNORE) add_fdb_test( TEST_FILES restarting/from_5.1.7/DrUpgradeRestart-1.txt restarting/from_5.1.7/DrUpgradeRestart-2.txt) diff --git a/tests/restarting/from_6.2.0/SnapCycleRestart-1.txt b/tests/SnapCycleRestart-1.txt similarity index 100% rename from tests/restarting/from_6.2.0/SnapCycleRestart-1.txt rename to tests/SnapCycleRestart-1.txt diff --git a/tests/restarting/from_6.2.0/SnapCycleRestart-2.txt b/tests/SnapCycleRestart-2.txt similarity index 100% rename from tests/restarting/from_6.2.0/SnapCycleRestart-2.txt rename to tests/SnapCycleRestart-2.txt diff --git a/tests/restarting/from_6.2.0/SnapTestAttrition-1.txt b/tests/SnapTestAttrition-1.txt similarity index 100% rename from tests/restarting/from_6.2.0/SnapTestAttrition-1.txt rename to tests/SnapTestAttrition-1.txt diff --git a/tests/restarting/from_6.2.0/SnapTestAttrition-2.txt b/tests/SnapTestAttrition-2.txt similarity index 100% rename from tests/restarting/from_6.2.0/SnapTestAttrition-2.txt rename to tests/SnapTestAttrition-2.txt diff --git a/tests/restarting/from_6.2.0/SnapTestRestart-1.txt b/tests/SnapTestRestart-1.txt similarity index 100% rename from tests/restarting/from_6.2.0/SnapTestRestart-1.txt rename to tests/SnapTestRestart-1.txt diff --git a/tests/restarting/from_6.2.0/SnapTestRestart-2.txt b/tests/SnapTestRestart-2.txt similarity index 100% rename from tests/restarting/from_6.2.0/SnapTestRestart-2.txt rename to tests/SnapTestRestart-2.txt diff --git a/tests/restarting/from_6.2.0/SnapTestSimpleRestart-1.txt b/tests/SnapTestSimpleRestart-1.txt similarity index 100% rename from tests/restarting/from_6.2.0/SnapTestSimpleRestart-1.txt rename to tests/SnapTestSimpleRestart-1.txt diff --git a/tests/restarting/from_6.2.0/SnapTestSimpleRestart-2.txt b/tests/SnapTestSimpleRestart-2.txt similarity index 100% rename from tests/restarting/from_6.2.0/SnapTestSimpleRestart-2.txt rename to tests/SnapTestSimpleRestart-2.txt From 4d64beeba34fcd322893d5809bfe54c9c4fd025b Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Fri, 10 Apr 2020 10:07:58 -0700 Subject: [PATCH 1399/1604] add clangd to ~/bin --- build/gen_dev_docker.sh | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/build/gen_dev_docker.sh b/build/gen_dev_docker.sh index 2bdaa1b8bd..37efabe4f4 100755 --- a/build/gen_dev_docker.sh +++ b/build/gen_dev_docker.sh @@ -72,9 +72,21 @@ sudo docker run --rm `# delete (temporary) image after return` \\ --security-opt seccomp=unconfined \\ -v "${HOME}:${HOME}" `# Mount home directory` \\ \${ccache_args} \\ - ${image} + ${image} "\$@" EOF +cat < Date: Fri, 10 Apr 2020 10:08:24 -0700 Subject: [PATCH 1400/1604] suppress weird ccache warning with clang --- cmake/ConfigureCompiler.cmake | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmake/ConfigureCompiler.cmake b/cmake/ConfigureCompiler.cmake index 893752be6f..5f263788b2 100644 --- a/cmake/ConfigureCompiler.cmake +++ b/cmake/ConfigureCompiler.cmake @@ -21,6 +21,10 @@ set(RELATIVE_DEBUG_PATHS OFF CACHE BOOL "Use relative file paths in debug info") set(STATIC_LINK_LIBCXX ON CACHE BOOL "Statically link libstdcpp/libc++") set(USE_WERROR OFF CACHE BOOL "Compile with -Werror. Recommended for local development and CI.") +if(USE_LIBCXX AND STATIC_LINK_LIBCXX AND NOT USE_LD STREQUAL "LLD") + message(FATAL_ERROR "Unsupported configuration: STATIC_LINK_LIBCXX with libc+++ only works if USE_LD=LLD") +endif() + set(rel_debug_paths OFF) if(RELATIVE_DEBUG_PATHS) set(rel_debug_paths ON) @@ -218,7 +222,7 @@ else() if (USE_CCACHE) add_compile_options( -Wno-register - -Wno-error=unused-command-line-argument) + -Wno-unused-command-line-argument) endif() endif() if (USE_WERROR) From 945832a0bb45480e5846588d5150a109077bddb2 Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Fri, 10 Apr 2020 10:38:24 -0700 Subject: [PATCH 1401/1604] remove llvm from docker image --- build/Dockerfile | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/build/Dockerfile b/build/Dockerfile index 0067084843..c8a84818b8 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -51,16 +51,6 @@ RUN cd /tmp && curl -L https://www.openssl.org/source/openssl-1.1.1d.tar.gz -o o ln -sv /usr/local/lib64/lib*.so.1.1 /usr/lib64/ &&\ cd /tmp/ && rm -rf /tmp/openssl-1.1.1d /tmp/openssl.tar.gz -# install llvm -WORKDIR /tmp -RUN cd /tmp && curl -L https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/llvm-project-10.0.0.tar.xz -o llvm.tar.xz &&\ - echo "6287a85f4a6aeb07dbffe27847117fe311ada48005f2b00241b523fe7b60716e llvm.tar.xz" > llvm-sha.txt &&\ - sha256sum -c llvm-sha.txt && tar xf llvm.tar.xz --no-same-owner &&\ - mkdir /tmp/llvm-project-10.0.0/build && cd /tmp/llvm-project-10.0.0/build &&\ - scl enable devtoolset-8 rh-python36 -- cmake -G Ninja -DLLVM_ENABLE_PROJECTS='clang;clang-tools-extra;libcxx;libcxxabi;libunwind;lldb;compiler-rt;lld' -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release ../llvm &&\ - scl enable devtoolset-8 rh-python36 -- ninja install &&\ - cd / && rm -rf /tmp/llvm-project-10.0.0 - LABEL version=0.1.12 ENV DOCKER_IMAGEVER=0.1.12 ENV JAVA_HOME=/usr/lib/jvm/java-1.8.0 From 8ef5a04896662f8fd9efb6494a9d7e74e0d0eff5 Mon Sep 17 00:00:00 2001 From: tclinken Date: Fri, 10 Apr 2020 13:03:15 -0700 Subject: [PATCH 1402/1604] Guard all of annotateEvent with mutex --- flow/Trace.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/Trace.cpp b/flow/Trace.cpp index 6518a1757a..41f086d425 100644 --- a/flow/Trace.cpp +++ b/flow/Trace.cpp @@ -389,9 +389,9 @@ public: } void annotateEvent(TraceEventFields& fields) { + MutexHolder holder(mutex); if (!opened || fields.isAnnotated()) return; - MutexHolder holder(mutex); if(localAddress.present()) { fields.addField("Machine", formatIpPort(localAddress.get().ip, localAddress.get().port)); } From ce4493f679c7688795812342c30dd4ff624a56b1 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Fri, 10 Apr 2020 13:45:16 -0700 Subject: [PATCH 1403/1604] many bug fixes --- fdbclient/DatabaseConfiguration.cpp | 15 +++-- fdbclient/DatabaseConfiguration.h | 2 +- fdbclient/StorageServerInterface.h | 1 + fdbrpc/FailureMonitor.actor.cpp | 9 ++- fdbrpc/FlowTransport.h | 8 ++- fdbserver/ClusterController.actor.cpp | 41 ++++++++---- fdbserver/DataDistribution.actor.cpp | 66 ++++++++++++------- fdbserver/MasterInterface.h | 1 + fdbserver/MasterProxyServer.actor.cpp | 9 +-- fdbserver/MoveKeys.actor.cpp | 19 +++++- fdbserver/Status.actor.cpp | 9 ++- fdbserver/WorkerInterface.actor.h | 2 + fdbserver/worker.actor.cpp | 43 ++++++++---- .../workloads/ConsistencyCheck.actor.cpp | 6 +- 14 files changed, 161 insertions(+), 70 deletions(-) diff --git a/fdbclient/DatabaseConfiguration.cpp b/fdbclient/DatabaseConfiguration.cpp index 11938f29fa..d7b8468f25 100644 --- a/fdbclient/DatabaseConfiguration.cpp +++ b/fdbclient/DatabaseConfiguration.cpp @@ -494,11 +494,16 @@ Optional DatabaseConfiguration::get( KeyRef key ) const { } } -bool DatabaseConfiguration::isExcludedServer( NetworkAddress a ) const { - return get( encodeExcludedServersKey( AddressExclusion(a.ip, a.port) ) ).present() || - get( encodeExcludedServersKey( AddressExclusion(a.ip) ) ).present() || - get( encodeFailedServersKey( AddressExclusion(a.ip, a.port) ) ).present() || - get( encodeFailedServersKey( AddressExclusion(a.ip) ) ).present(); +bool DatabaseConfiguration::isExcludedServer( NetworkAddressList a ) const { + return get( encodeExcludedServersKey( AddressExclusion(a.address.ip, a.address.port) ) ).present() || + get( encodeExcludedServersKey( AddressExclusion(a.address.ip) ) ).present() || + get( encodeFailedServersKey( AddressExclusion(a.address.ip, a.address.port) ) ).present() || + get( encodeFailedServersKey( AddressExclusion(a.address.ip) ) ).present() || + ( a.secondaryAddress.present() && ( + get( encodeExcludedServersKey( AddressExclusion(a.secondaryAddress.get().ip, a.secondaryAddress.get().port) ) ).present() || + get( encodeExcludedServersKey( AddressExclusion(a.secondaryAddress.get().ip) ) ).present() || + get( encodeFailedServersKey( AddressExclusion(a.secondaryAddress.get().ip, a.secondaryAddress.get().port) ) ).present() || + get( encodeFailedServersKey( AddressExclusion(a.secondaryAddress.get().ip) ) ).present() ) ); } std::set DatabaseConfiguration::getExcludedServers() const { const_cast(this)->makeConfigurationImmutable(); diff --git a/fdbclient/DatabaseConfiguration.h b/fdbclient/DatabaseConfiguration.h index c2a99de9c4..46e0fbfc1f 100644 --- a/fdbclient/DatabaseConfiguration.h +++ b/fdbclient/DatabaseConfiguration.h @@ -187,7 +187,7 @@ struct DatabaseConfiguration { std::vector regions; // Excluded servers (no state should be here) - bool isExcludedServer( NetworkAddress ) const; + bool isExcludedServer( NetworkAddressList ) const; std::set getExcludedServers() const; int32_t getDesiredProxies() const { if(masterProxyCount == -1) return autoMasterProxyCount; return masterProxyCount; } diff --git a/fdbclient/StorageServerInterface.h b/fdbclient/StorageServerInterface.h index 3ba0ea7562..439692c2d7 100644 --- a/fdbclient/StorageServerInterface.h +++ b/fdbclient/StorageServerInterface.h @@ -74,6 +74,7 @@ struct StorageServerInterface { explicit StorageServerInterface(UID uid) : uniqueID( uid ) {} StorageServerInterface() : uniqueID( deterministicRandom()->randomUniqueID() ) {} NetworkAddress address() const { return getValue.getEndpoint().getPrimaryAddress(); } + NetworkAddress stableAddress() const { return getValue.getEndpoint().getStableAddress(); } Optional secondaryAddress() const { return getValue.getEndpoint().addresses.secondaryAddress; } UID id() const { return uniqueID; } std::string toString() const { return id().shortString(); } diff --git a/fdbrpc/FailureMonitor.actor.cpp b/fdbrpc/FailureMonitor.actor.cpp index 2c5a449e44..47a6f47540 100644 --- a/fdbrpc/FailureMonitor.actor.cpp +++ b/fdbrpc/FailureMonitor.actor.cpp @@ -24,8 +24,10 @@ ACTOR Future waitForStateEqual( IFailureMonitor* monitor, Endpoint endpoint, FailureStatus status ) { loop { Future change = monitor->onStateChanged(endpoint); - if (monitor->getState(endpoint) == status) + + if (monitor->getState(endpoint) == status) { return Void(); + } wait( change ); } } @@ -34,8 +36,9 @@ ACTOR Future waitForContinuousFailure( IFailureMonitor* monitor, Endpoint state double startT = now(); loop { wait( monitor->onFailed( endpoint ) ); - if(monitor->permanentlyFailed(endpoint)) + if(monitor->permanentlyFailed(endpoint)) { return Void(); + } // X == sustainedFailureDuration + slope * (now()-startT+X) double waitDelay = (sustainedFailureDuration + slope * (now()-startT)) / (1-slope); @@ -102,7 +105,7 @@ void SimpleFailureMonitor::endpointNotFound( Endpoint const& endpoint ) { TraceEvent("WellKnownEndpointNotFound").suppressFor(1.0).detail("Address", endpoint.getPrimaryAddress()).detail("TokenFirst", endpoint.token.first()).detail("TokenSecond", endpoint.token.second()); return; } - TraceEvent("EndpointNotFound").suppressFor(1.0).detail("Address", endpoint.getPrimaryAddress()).detail("Token", endpoint.token); + TraceEvent("EndpointNotFound").detail("Addresses", endpoint.addresses.toString()).detail("Token", endpoint.token).detail("IsFailed", endpointKnownFailed.get(endpoint)).detail("ShouldSwap", endpoint.addresses.secondaryAddress.present() && !g_network->getLocalAddresses().secondaryAddress.present() && (endpoint.addresses.address.isTLS() != g_network->getLocalAddresses().address.isTLS())); endpointKnownFailed.set( endpoint, true ); } diff --git a/fdbrpc/FlowTransport.h b/fdbrpc/FlowTransport.h index e813b736c5..1a6aba2176 100644 --- a/fdbrpc/FlowTransport.h +++ b/fdbrpc/FlowTransport.h @@ -44,7 +44,9 @@ public: } void choosePrimaryAddress() { - if(addresses.secondaryAddress.present() && !g_network->getLocalAddresses().secondaryAddress.present() && (addresses.address.isTLS() != g_network->getLocalAddresses().address.isTLS())) { + if(addresses.secondaryAddress.present() && + ((!g_network->getLocalAddresses().secondaryAddress.present() && (addresses.address.isTLS() != g_network->getLocalAddresses().address.isTLS())) || + (g_network->getLocalAddresses().secondaryAddress.present() && !addresses.address.isTLS()))) { std::swap(addresses.address, addresses.secondaryAddress.get()); } } @@ -58,6 +60,10 @@ public: return addresses.address; } + NetworkAddress getStableAddress() const { + return addresses.getTLSAddress(); + } + bool operator == (Endpoint const& r) const { return getPrimaryAddress() == r.getPrimaryAddress() && token == r.token; } diff --git a/fdbserver/ClusterController.actor.cpp b/fdbserver/ClusterController.actor.cpp index 7ade50df10..f9475017a3 100644 --- a/fdbserver/ClusterController.actor.cpp +++ b/fdbserver/ClusterController.actor.cpp @@ -269,6 +269,7 @@ public: !excludedMachines.count(it.second.details.interf.locality.zoneId()) && ( includeDCs.size() == 0 || includeDCs.count(it.second.details.interf.locality.dcId()) ) && !addressExcluded(excludedAddresses, it.second.details.interf.address()) && + ( !it.second.details.interf.secondaryAddress().present() || !addressExcluded(excludedAddresses, it.second.details.interf.secondaryAddress().get()) ) && it.second.details.processClass.machineClassFitness( ProcessClass::Storage ) <= ProcessClass::UnsetFit ) { return it.second.details; } @@ -305,7 +306,7 @@ public: for( auto& it : id_worker ) { auto fitness = it.second.details.processClass.machineClassFitness( ProcessClass::Storage ); - if( workerAvailable(it.second, false) && !conf.isExcludedServer(it.second.details.interf.address()) && fitness != ProcessClass::NeverAssign && ( !dcId.present() || it.second.details.interf.locality.dcId()==dcId.get() ) ) { + if( workerAvailable(it.second, false) && !conf.isExcludedServer(it.second.details.interf.addresses()) && fitness != ProcessClass::NeverAssign && ( !dcId.present() || it.second.details.interf.locality.dcId()==dcId.get() ) ) { fitness_workers[ fitness ].push_back(it.second.details); } } @@ -350,7 +351,7 @@ public: for( auto& it : id_worker ) { if (std::find(exclusionWorkerIds.begin(), exclusionWorkerIds.end(), it.second.details.interf.id()) == exclusionWorkerIds.end()) { auto fitness = it.second.details.processClass.machineClassFitness(ProcessClass::TLog); - if (workerAvailable(it.second, checkStable) && !conf.isExcludedServer(it.second.details.interf.address()) && fitness != ProcessClass::NeverAssign && (!dcIds.size() || dcIds.count(it.second.details.interf.locality.dcId()))) { + if (workerAvailable(it.second, checkStable) && !conf.isExcludedServer(it.second.details.interf.addresses()) && fitness != ProcessClass::NeverAssign && (!dcIds.size() || dcIds.count(it.second.details.interf.locality.dcId()))) { fitness_workers[std::make_pair(fitness, it.second.details.degraded)].push_back(it.second.details); } else { @@ -506,7 +507,7 @@ public: for( auto& it : id_worker ) { auto fitness = it.second.details.processClass.machineClassFitness( role ); - if(conf.isExcludedServer(it.second.details.interf.address())) { + if(conf.isExcludedServer(it.second.details.interf.addresses())) { fitness = std::max(fitness, ProcessClass::ExcludeFit); } if( workerAvailable(it.second, checkStable) && fitness < unacceptableFitness && it.second.details.interf.locality.dcId()==dcId ) { @@ -544,7 +545,7 @@ public: for( auto& it : id_worker ) { auto fitness = it.second.details.processClass.machineClassFitness( role ); - if( workerAvailable(it.second, checkStable) && !conf.isExcludedServer(it.second.details.interf.address()) && it.second.details.interf.locality.dcId() == dcId && + if( workerAvailable(it.second, checkStable) && !conf.isExcludedServer(it.second.details.interf.addresses()) && it.second.details.interf.locality.dcId() == dcId && ( !minWorker.present() || ( it.second.details.interf.id() != minWorker.get().worker.interf.id() && ( fitness < minWorker.get().fitness || (fitness == minWorker.get().fitness && id_used[it.first] <= minWorker.get().used ) ) ) ) ) { if (isLongLivedStateless(it.first)) { fitness_workers[ std::make_pair(fitness, id_used[it.first]) ].second.push_back(it.second.details); @@ -663,7 +664,7 @@ public: std::set>> getDatacenters( DatabaseConfiguration const& conf, bool checkStable = false ) { std::set>> result; for( auto& it : id_worker ) - if( workerAvailable( it.second, checkStable ) && !conf.isExcludedServer( it.second.details.interf.address() ) ) + if( workerAvailable( it.second, checkStable ) && !conf.isExcludedServer( it.second.details.interf.addresses() ) ) result.insert(it.second.details.interf.locality.dcId()); return result; } @@ -1093,7 +1094,7 @@ public: // Check master fitness. Don't return false if master is excluded in case all the processes are excluded, we still need master for recovery. ProcessClass::Fitness oldMasterFit = masterWorker->second.details.processClass.machineClassFitness( ProcessClass::Master ); - if(db.config.isExcludedServer(dbi.master.address())) { + if(db.config.isExcludedServer(dbi.master.addresses())) { oldMasterFit = std::max(oldMasterFit, ProcessClass::ExcludeFit); } @@ -1101,7 +1102,7 @@ public: id_used[clusterControllerProcessId]++; WorkerFitnessInfo mworker = getWorkerForRoleInDatacenter(clusterControllerDcId, ProcessClass::Master, ProcessClass::NeverAssign, db.config, id_used, true); auto newMasterFit = mworker.worker.processClass.machineClassFitness( ProcessClass::Master ); - if(db.config.isExcludedServer(mworker.worker.interf.address())) { + if(db.config.isExcludedServer(mworker.worker.interf.addresses())) { newMasterFit = std::max(newMasterFit, ProcessClass::ExcludeFit); } @@ -1604,11 +1605,11 @@ void checkBetterDDOrRK(ClusterControllerData* self) { newDDWorker = self->id_worker[self->masterProcessId.get()].details; } auto bestFitnessForRK = newRKWorker.processClass.machineClassFitness(ProcessClass::Ratekeeper); - if(self->db.config.isExcludedServer(newRKWorker.interf.address())) { + if(self->db.config.isExcludedServer(newRKWorker.interf.addresses())) { bestFitnessForRK = std::max(bestFitnessForRK, ProcessClass::ExcludeFit); } auto bestFitnessForDD = newDDWorker.processClass.machineClassFitness(ProcessClass::DataDistributor); - if(self->db.config.isExcludedServer(newDDWorker.interf.address())) { + if(self->db.config.isExcludedServer(newDDWorker.interf.addresses())) { bestFitnessForDD = std::max(bestFitnessForDD, ProcessClass::ExcludeFit); } //TraceEvent("CheckBetterDDorRKNewRecruits", self->id).detail("MasterProcessId", self->masterProcessId) @@ -1738,7 +1739,7 @@ ACTOR Future workerAvailabilityWatch( WorkerInterface worker, ProcessClass (worker.address() == g_network->getLocalAddress() || startingClass.classType() == ProcessClass::TesterClass) ? Never() : waitFailureClient(worker.waitFailure, SERVER_KNOBS->WORKER_FAILURE_TIME); - cluster->updateWorkerList.set( worker.locality.processId(), ProcessData(worker.locality, startingClass, worker.address()) ); + cluster->updateWorkerList.set( worker.locality.processId(), ProcessData(worker.locality, startingClass, worker.stableAddress()) ); cluster->updateDBInfoEndpoints.push_back(worker.updateServerDBInfo.getEndpoint()); cluster->updateDBInfo.trigger(); // This switching avoids a race where the worker can be added to id_worker map after the workerAvailabilityWatch fails for the worker. @@ -2058,7 +2059,7 @@ void clusterRegisterMaster( ClusterControllerData* self, RegisterMasterRequest c self->gotFullyRecoveredConfig = true; db->fullyRecoveredConfig = req.configuration.get(); for ( auto& it : self->id_worker ) { - bool isExcludedFromConfig = db->fullyRecoveredConfig.isExcludedServer(it.second.details.interf.address()); + bool isExcludedFromConfig = db->fullyRecoveredConfig.isExcludedServer(it.second.details.interf.addresses()); if ( it.second.priorityInfo.isExcluded != isExcludedFromConfig ) { it.second.priorityInfo.isExcluded = isExcludedFromConfig; if( !it.second.reply.isSet() ) { @@ -2129,6 +2130,7 @@ void registerWorker( RegisterWorkerRequest req, ClusterControllerData *self ) { for(auto it : req.incompatiblePeers) { self->db.incompatibleConnections[it] = now() + SERVER_KNOBS->INCOMPATIBLE_PEERS_LOGGING_INTERVAL; } + self->removedDBInfoEndpoints.erase(w.updateServerDBInfo.getEndpoint()); if(info == self->id_worker.end()) { TraceEvent("ClusterControllerActualWorkers", self->id).detail("WorkerId",w.id()).detail("ProcessId", w.locality.processId()).detail("ZoneId", w.locality.zoneId()).detail("DataHall", w.locality.dataHallId()).detail("PClass", req.processClass.toString()).detail("Workers", self->id_worker.size()); @@ -2169,7 +2171,7 @@ void registerWorker( RegisterWorkerRequest req, ClusterControllerData *self ) { } if ( self->gotFullyRecoveredConfig ) { - newPriorityInfo.isExcluded = self->db.fullyRecoveredConfig.isExcludedServer(w.address()); + newPriorityInfo.isExcluded = self->db.fullyRecoveredConfig.isExcludedServer(w.addresses()); } } @@ -3063,14 +3065,25 @@ ACTOR Future dbInfoUpdater( ClusterControllerData* self ) { req.dbInfo = self->db.serverInfo->get().read(); req.broadcastInfo = self->updateDBInfoEndpoints; + for(auto &it : self->updateDBInfoEndpoints) { + TraceEvent("DBInfoAttemptUpdate", self->id).detail("Addr", it.getPrimaryAddress()).detail("Token", it.token); + } + self->updateDBInfoEndpoints.clear(); + TraceEvent("DBInfoStartBroadcast", self->id); choose { when(std::vector notUpdated = wait( broadcastDBInfoRequest(req, 2, Optional(), false) )) { + TraceEvent("DBInfoFinishBroadcast", self->id); + for(auto &it : notUpdated) { + TraceEvent("DBInfoNotUpdated", self->id).detail("Addr", it.getPrimaryAddress()); + } self->updateDBInfoEndpoints.insert(self->updateDBInfoEndpoints.end(), notUpdated.begin(), notUpdated.end()); if(notUpdated.size()) { self->updateDBInfo.trigger(); } } - when(wait(dbInfoChange)) {} + when(wait(dbInfoChange)) { + TraceEvent("DBInfoChangeBroadcast", self->id); + } } } } @@ -3134,7 +3147,7 @@ ACTOR Future clusterControllerCore( ClusterControllerFullInterface interf, vector workers; for(auto& it : self.id_worker) { - if ( (req.flags & GetWorkersRequest::NON_EXCLUDED_PROCESSES_ONLY) && self.db.config.isExcludedServer(it.second.details.interf.address()) ) { + if ( (req.flags & GetWorkersRequest::NON_EXCLUDED_PROCESSES_ONLY) && self.db.config.isExcludedServer(it.second.details.interf.addresses()) ) { continue; } diff --git a/fdbserver/DataDistribution.actor.cpp b/fdbserver/DataDistribution.actor.cpp index cbba73bea9..7360502b92 100644 --- a/fdbserver/DataDistribution.actor.cpp +++ b/fdbserver/DataDistribution.actor.cpp @@ -1038,7 +1038,7 @@ struct DDTeamCollection : ReferenceCounted { TraceEvent(SevWarnAlways, "MissingLocality") .detail("Server", i->first.uniqueID) .detail("Locality", i->first.locality.toString()); - auto addr = i->first.address(); + auto addr = i->first.stableAddress(); self->invalidLocalityAddr.insert(AddressExclusion(addr.ip, addr.port)); if (self->checkInvalidLocalities.isReady()) { self->checkInvalidLocalities = checkAndRemoveInvalidLocalityAddr(self); @@ -2856,6 +2856,14 @@ bool teamContainsFailedServer(DDTeamCollection* self, Reference team self->excludedServers.get(ipaddr) == DDTeamCollection::Status::FAILED) { return true; } + if(ssi.secondaryAddress().present()) { + AddressExclusion saddr(ssi.secondaryAddress().get().ip, ssi.secondaryAddress().get().port); + AddressExclusion sipaddr(ssi.secondaryAddress().get().ip); + if (self->excludedServers.get(saddr) == DDTeamCollection::Status::FAILED || + self->excludedServers.get(sipaddr) == DDTeamCollection::Status::FAILED) { + return true; + } + } } return false; } @@ -3567,29 +3575,41 @@ ACTOR Future storageServerTracker( // If the storage server is in the excluded servers list, it is undesired NetworkAddress a = server->lastKnownInterface.address(); - state AddressExclusion addr( a.ip, a.port ); - state AddressExclusion ipaddr( a.ip ); - state DDTeamCollection::Status addrStatus = self->excludedServers.get(addr); - state DDTeamCollection::Status ipaddrStatus = self->excludedServers.get(ipaddr); - if (addrStatus != DDTeamCollection::Status::NONE || ipaddrStatus != DDTeamCollection::Status::NONE) { + AddressExclusion worstAddr( a.ip, a.port ); + DDTeamCollection::Status worstStatus = self->excludedServers.get( worstAddr ); + otherChanges.push_back( self->excludedServers.onChange( worstAddr ) ); + + for(int i = 0; i < 3; i++) { + if(i > 0 && !server->lastKnownInterface.secondaryAddress().present()) { + break; + } + AddressExclusion testAddr; + if(i == 0) testAddr = AddressExclusion(a.ip); + else if(i == 1) testAddr = AddressExclusion(server->lastKnownInterface.secondaryAddress().get().ip, server->lastKnownInterface.secondaryAddress().get().port); + else if(i == 2) testAddr = AddressExclusion(server->lastKnownInterface.secondaryAddress().get().ip); + DDTeamCollection::Status testStatus = self->excludedServers.get(testAddr); + if(testStatus > worstStatus) { + worstStatus = testStatus; + worstAddr = testAddr; + } + otherChanges.push_back( self->excludedServers.onChange( testAddr ) ); + } + + if (worstStatus != DDTeamCollection::Status::NONE) { TraceEvent(SevWarn, "UndesiredStorageServer", self->distributorId) .detail("Server", server->id) - .detail("Excluded", - ipaddrStatus == DDTeamCollection::Status::NONE ? addr.toString() : ipaddr.toString()); + .detail("Excluded", worstAddr.toString()); status.isUndesired = true; status.isWrongConfiguration = true; - if (addrStatus == DDTeamCollection::Status::FAILED || - ipaddrStatus == DDTeamCollection::Status::FAILED) { + if (worstStatus == DDTeamCollection::Status::FAILED) { TraceEvent(SevWarn, "FailedServerRemoveKeys", self->distributorId) - .detail("Address", addr.toString()) - .detail("ServerID", server->id); + .detail("Server", server->id) + .detail("Excluded", worstAddr.toString()); wait(removeKeysFromFailedServer(cx, server->id, self->lock)); if (BUGGIFY) wait(delay(5.0)); self->shardsAffectedByTeamFailure->eraseServer(server->id); } } - otherChanges.push_back( self->excludedServers.onChange( addr ) ); - otherChanges.push_back( self->excludedServers.onChange( ipaddr ) ); failureTracker = storageServerFailureTracker(self, server, cx, &status, addedVersion); //We need to recruit new storage servers if the key value store type has changed @@ -3859,7 +3879,7 @@ ACTOR Future checkAndRemoveInvalidLocalityAddr(DDTeamCollection* self) { int numExistingSSOnAddr(DDTeamCollection* self, const AddressExclusion& addr) { int numExistingSS = 0; for (auto& server : self->server_info) { - const NetworkAddress& netAddr = server.second->lastKnownInterface.address(); + const NetworkAddress& netAddr = server.second->lastKnownInterface.stableAddress(); AddressExclusion usedAddr(netAddr.ip, netAddr.port); if (usedAddr == addr) { ++numExistingSS; @@ -3873,10 +3893,10 @@ ACTOR Future initializeStorage(DDTeamCollection* self, RecruitStorageReply // SOMEDAY: Cluster controller waits for availability, retry quickly if a server's Locality changes self->recruitingStream.set(self->recruitingStream.get() + 1); - const NetworkAddress& netAddr = candidateWorker.worker.address(); + const NetworkAddress& netAddr = candidateWorker.worker.stableAddress(); AddressExclusion workerAddr(netAddr.ip, netAddr.port); if (numExistingSSOnAddr(self, workerAddr) <= 2 && - self->recruitingLocalities.find(candidateWorker.worker.address()) == self->recruitingLocalities.end()) { + self->recruitingLocalities.find(candidateWorker.worker.stableAddress()) == self->recruitingLocalities.end()) { // Only allow at most 2 storage servers on an address, because // too many storage server on the same address (i.e., process) can cause OOM. // Ask the candidateWorker to initialize a SS only if the worker does not have a pending request @@ -3897,7 +3917,7 @@ ACTOR Future initializeStorage(DDTeamCollection* self, RecruitStorageReply .detail("RecruitingStream", self->recruitingStream.get()); self->recruitingIds.insert(interfaceId); - self->recruitingLocalities.insert(candidateWorker.worker.address()); + self->recruitingLocalities.insert(candidateWorker.worker.stableAddress()); state ErrorOr newServer = wait(candidateWorker.worker.storage.tryGetReply(isr, TaskPriority::DataDistribution)); if (newServer.isError()) { @@ -3908,7 +3928,7 @@ ACTOR Future initializeStorage(DDTeamCollection* self, RecruitStorageReply wait(delay(SERVER_KNOBS->STORAGE_RECRUITMENT_DELAY, TaskPriority::DataDistribution)); } self->recruitingIds.erase(interfaceId); - self->recruitingLocalities.erase(candidateWorker.worker.address()); + self->recruitingLocalities.erase(candidateWorker.worker.stableAddress()); TraceEvent("DDRecruiting") .detail("Primary", self->primary) @@ -3954,7 +3974,7 @@ ACTOR Future storageRecruiter( DDTeamCollection* self, Referenceprimary) .detail("Excluding", s->second->lastKnownInterface.address()); - auto addr = s->second->lastKnownInterface.address(); + auto addr = s->second->lastKnownInterface.stableAddress(); AddressExclusion addrExcl(addr.ip, addr.port); exclusions.insert(addrExcl); numSSPerAddr[addrExcl]++; // increase from 0 @@ -4005,8 +4025,8 @@ ACTOR Future storageRecruiter( DDTeamCollection* self, Reference= 2) { TraceEvent(SevWarnAlways, "StorageRecruiterTooManySSOnSameAddr", self->distributorId) @@ -4740,7 +4760,7 @@ ACTOR Future ddExclusionSafetyCheck(DistributorExclusionSafetyCheckRequest // Go through storage server interfaces and translate Address -> server ID (UID) for (const AddressExclusion& excl : req.exclusions) { for (const auto& ssi : ssis) { - if (excl.excludes(ssi.address())) { + if (excl.excludes(ssi.address()) || (ssi.secondaryAddress().present() && excl.excludes(ssi.secondaryAddress().get()))) { excludeServerIDs.push_back(ssi.id()); } } diff --git a/fdbserver/MasterInterface.h b/fdbserver/MasterInterface.h index 3129b3f8eb..61ade89cee 100644 --- a/fdbserver/MasterInterface.h +++ b/fdbserver/MasterInterface.h @@ -40,6 +40,7 @@ struct MasterInterface { RequestStream notifyBackupWorkerDone; NetworkAddress address() const { return changeCoordinators.getEndpoint().getPrimaryAddress(); } + NetworkAddressList addresses() const { return changeCoordinators.getEndpoint().addresses; } UID id() const { return changeCoordinators.getEndpoint().token; } template diff --git a/fdbserver/MasterProxyServer.actor.cpp b/fdbserver/MasterProxyServer.actor.cpp index cec54123f0..b7c4a4cdc0 100644 --- a/fdbserver/MasterProxyServer.actor.cpp +++ b/fdbserver/MasterProxyServer.actor.cpp @@ -53,11 +53,12 @@ ACTOR Future broadcastTxnRequest(TxnStateRequest req, int sendAmount, bool resetReply( req ); std::vector> replies; int currentStream = 0; - for(int i = 0; i < sendAmount && currentStream < req.broadcastInfo.size(); i++) { + std::vector broadcastEndpoints = req.broadcastInfo; + for(int i = 0; i < sendAmount && currentStream < broadcastEndpoints.size(); i++) { std::vector endpoints; - RequestStream cur(req.broadcastInfo[currentStream++]); - while(currentStream < req.broadcastInfo.size()*(i+1)/sendAmount) { - endpoints.push_back(req.broadcastInfo[currentStream++]); + RequestStream cur(broadcastEndpoints[currentStream++]); + while(currentStream < broadcastEndpoints.size()*(i+1)/sendAmount) { + endpoints.push_back(broadcastEndpoints[currentStream++]); } req.broadcastInfo = endpoints; replies.push_back(brokenPromiseToNever( cur.getReply( req ) )); diff --git a/fdbserver/MoveKeys.actor.cpp b/fdbserver/MoveKeys.actor.cpp index cec54e2bff..7ef864d47a 100644 --- a/fdbserver/MoveKeys.actor.cpp +++ b/fdbserver/MoveKeys.actor.cpp @@ -768,6 +768,7 @@ ACTOR Future> addStorageServer( Database cx, StorageServ try { state Future> fTagLocalities = tr.getRange( tagLocalityListKeys, CLIENT_KNOBS->TOO_MANY ); state Future> fv = tr.get( serverListKeyFor(server.id()) ); + state Future> fExclProc = tr.get( StringRef(encodeExcludedServersKey( AddressExclusion( server.address().ip, server.address().port ))) ); state Future> fExclIP = tr.get( @@ -776,14 +777,28 @@ ACTOR Future> addStorageServer( Database cx, StorageServ StringRef(encodeFailedServersKey( AddressExclusion( server.address().ip, server.address().port ))) ); state Future> fFailIP = tr.get( StringRef(encodeFailedServersKey( AddressExclusion( server.address().ip ))) ); + + state Future> fExclProc2 = server.secondaryAddress().present() ? tr.get( + StringRef(encodeExcludedServersKey( AddressExclusion( server.secondaryAddress().get().ip, server.secondaryAddress().get().port ))) ) : Future>( Optional() ); + state Future> fExclIP2 = server.secondaryAddress().present() ? tr.get( + StringRef(encodeExcludedServersKey( AddressExclusion( server.secondaryAddress().get().ip ))) ) : Future>( Optional() ); + state Future> fFailProc2 = server.secondaryAddress().present() ? tr.get( + StringRef(encodeFailedServersKey( AddressExclusion( server.secondaryAddress().get().ip, server.secondaryAddress().get().port ))) ) : Future>( Optional() ); + state Future> fFailIP2 = server.secondaryAddress().present() ? tr.get( + StringRef(encodeFailedServersKey( AddressExclusion( server.secondaryAddress().get().ip ))) ) : Future>( Optional() ); + state Future> fTags = tr.getRange( serverTagKeys, CLIENT_KNOBS->TOO_MANY, true); state Future> fHistoryTags = tr.getRange( serverTagHistoryKeys, CLIENT_KNOBS->TOO_MANY, true); - wait( success(fTagLocalities) && success(fv) && success(fExclProc) && success(fExclIP) && success(fFailProc) && success(fFailIP) && success(fTags) && success(fHistoryTags) ); + wait( success(fTagLocalities) && success(fv) && success(fTags) && success(fHistoryTags) && + success(fExclProc) && success(fExclIP) && success(fFailProc) && success(fFailIP) && + success(fExclProc2) && success(fExclIP2) && success(fFailProc2) && success(fFailIP2) ); // If we have been added to the excluded/failed state servers list, we have to fail - if (fExclProc.get().present() || fExclIP.get().present() || fFailProc.get().present() || fFailIP.get().present() ) + if (fExclProc.get().present() || fExclIP.get().present() || fFailProc.get().present() || fFailIP.get().present() || + fExclProc2.get().present() || fExclIP2.get().present() || fFailProc2.get().present() || fFailIP2.get().present() ) { throw recruitment_failed(); + } if(fTagLocalities.get().more || fTags.get().more || fHistoryTags.get().more) ASSERT(false); diff --git a/fdbserver/Status.actor.cpp b/fdbserver/Status.actor.cpp index 8f0fe1356e..00c0df2d51 100644 --- a/fdbserver/Status.actor.cpp +++ b/fdbserver/Status.actor.cpp @@ -342,7 +342,10 @@ static JsonBuilderObject machineStatusFetcher(WorkerEvents mMetrics, vectorfirst)) + //FIXME: this will not catch if the secondary address of the process was excluded + NetworkAddressList tempList; + tempList.address = it->first; + if (configuration.present() && !configuration.get().isExcludedServer(tempList)) notExcludedMap[machineId] = false; workerContribMap[machineId] ++; } @@ -828,7 +831,7 @@ ACTOR static Future processStatusFetcher( statusObj["roles"] = roles.getStatusForAddress(address); if (configuration.present()){ - statusObj["excluded"] = configuration.get().isExcludedServer(address); + statusObj["excluded"] = configuration.get().isExcludedServer(workerItr->interf.addresses()); } statusObj["class_type"] = workerItr->processClass.toString(); @@ -1549,7 +1552,7 @@ static int getExtraTLogEligibleZones(const vector& workers, const std::map> dcId_zone; for(auto const& worker : workers) { if(worker.processClass.machineClassFitness(ProcessClass::TLog) < ProcessClass::NeverAssign - && !configuration.isExcludedServer(worker.interf.address())) + && !configuration.isExcludedServer(worker.interf.addresses())) { allZones.insert(worker.interf.locality.zoneId().get()); if(worker.interf.locality.dcId().present()) { diff --git a/fdbserver/WorkerInterface.actor.h b/fdbserver/WorkerInterface.actor.h index 82215da51f..08824c20bf 100644 --- a/fdbserver/WorkerInterface.actor.h +++ b/fdbserver/WorkerInterface.actor.h @@ -69,7 +69,9 @@ struct WorkerInterface { UID id() const { return tLog.getEndpoint().token; } NetworkAddress address() const { return tLog.getEndpoint().getPrimaryAddress(); } + NetworkAddress stableAddress() const { return tLog.getEndpoint().getStableAddress(); } Optional secondaryAddress() const { return tLog.getEndpoint().addresses.secondaryAddress; } + NetworkAddressList addresses() const { return tLog.getEndpoint().addresses; } WorkerInterface() {} WorkerInterface( const LocalityData& locality ) : locality( locality ) {} diff --git a/fdbserver/worker.actor.cpp b/fdbserver/worker.actor.cpp index 6354eb37b4..a0169d949d 100644 --- a/fdbserver/worker.actor.cpp +++ b/fdbserver/worker.actor.cpp @@ -69,12 +69,20 @@ extern IKeyValueStore* keyValueStoreCompressTestData(IKeyValueStore* store); #endif ACTOR Future> tryDBInfoBroadcast(RequestStream stream, UpdateServerDBInfoRequest req) { - ErrorOr> rep = wait( stream.getReplyUnlessFailedFor(req, 1.0, 0) ); - if(rep.present()) { - return rep.get(); + state UID dbgid = nondeterministicRandom()->randomUniqueID(); + TraceEvent("BroadcastDBInfo", dbgid).detail("Addr", stream.getEndpoint().getPrimaryAddress()).detail("Token", stream.getEndpoint().token); + try { + ErrorOr> rep = wait( stream.getReplyUnlessFailedFor(req, 1.0, 0) ); + TraceEvent("BroadcastDBInfoReply", dbgid).detail("Addr", stream.getEndpoint().getPrimaryAddress()).detail("Present", rep.present()); + if(rep.present()) { + return rep.get(); + } + req.broadcastInfo.push_back(stream.getEndpoint()); + return req.broadcastInfo; + } catch( Error &e ) { + TraceEvent("BroadcastDBInfoError", dbgid).error(e,true).detail("Addr", stream.getEndpoint().getPrimaryAddress()); + throw; } - req.broadcastInfo.push_back(stream.getEndpoint()); - return req.broadcastInfo; } ACTOR Future> broadcastDBInfoRequest(UpdateServerDBInfoRequest req, int sendAmount, Optional sender, bool sendReply) { @@ -82,13 +90,17 @@ ACTOR Future> broadcastDBInfoRequest(UpdateServerDBInfoReq state ReplyPromise> reply = req.reply; resetReply( req ); int currentStream = 0; - for(int i = 0; i < sendAmount && currentStream < req.broadcastInfo.size(); i++) { + std::vector broadcastEndpoints = req.broadcastInfo; + for(int i = 0; i < sendAmount && currentStream < broadcastEndpoints.size(); i++) { std::vector endpoints; - RequestStream cur(req.broadcastInfo[currentStream++]); - while(currentStream < req.broadcastInfo.size()*(i+1)/sendAmount) { - endpoints.push_back(req.broadcastInfo[currentStream++]); + RequestStream cur(broadcastEndpoints[currentStream++]); + while(currentStream < broadcastEndpoints.size()*(i+1)/sendAmount) { + endpoints.push_back(broadcastEndpoints[currentStream++]); } req.broadcastInfo = endpoints; + for(auto &it : req.broadcastInfo) { + TraceEvent("BroadcastDBForward").detail("Addr", it.getPrimaryAddress()); + } replies.push_back( tryDBInfoBroadcast( cur, req ) ); resetReply( req ); } @@ -103,6 +115,9 @@ ACTOR Future> broadcastDBInfoRequest(UpdateServerDBInfoReq if(sendReply) { reply.send(notUpdated); } + for(auto &it : notUpdated) { + TraceEvent("BroadcastDBNotUpdated").detail("Addr", it.getPrimaryAddress()); + } return notUpdated; } @@ -961,6 +976,7 @@ ACTOR Future workerServer( DUMPTOKEN(recruited.setMetricsRate); DUMPTOKEN(recruited.eventLogRequest); DUMPTOKEN(recruited.traceBatchDumpRequest); + DUMPTOKEN(recruited.updateServerDBInfo); } state std::vector> recoveries; @@ -1060,11 +1076,16 @@ ACTOR Future workerServer( loop choose { when( UpdateServerDBInfoRequest req = waitNext( interf.updateServerDBInfo.getFuture() ) ) { + TraceEvent("GotServerDBInfoMsg").detail("NotUpdated", !ccInterface->get().present() || req.dbInfo.clusterInterface != ccInterface->get().get() || (req.dbInfo.infoGeneration < dbInfo->get().infoGeneration && dbInfo->get().clusterInterface == ccInterface->get().get())) + .detail("ReqInterface", ccInterface->get().present()) + .detail("InfoGeneration", req.dbInfo.infoGeneration) + .detail("Token", interf.updateServerDBInfo.getEndpoint().token); + Optional notUpdated; - if(req.dbInfo.clusterInterface != ccInterface->get().get() || (req.dbInfo.infoGeneration < dbInfo->get().infoGeneration && dbInfo->get().clusterInterface == ccInterface->get().get())) { + if(!ccInterface->get().present() || req.dbInfo.clusterInterface != ccInterface->get().get() || (req.dbInfo.infoGeneration < dbInfo->get().infoGeneration && dbInfo->get().clusterInterface == ccInterface->get().get())) { notUpdated = interf.updateServerDBInfo.getEndpoint(); } - if(req.dbInfo.clusterInterface == ccInterface->get().get() && (req.dbInfo.infoGeneration > dbInfo->get().infoGeneration || dbInfo->get().clusterInterface != ccInterface->get().get())) { + if(ccInterface->get().present() && req.dbInfo.clusterInterface == ccInterface->get().get() && (req.dbInfo.infoGeneration > dbInfo->get().infoGeneration || dbInfo->get().clusterInterface != ccInterface->get().get())) { ServerDBInfo localInfo = req.dbInfo; TraceEvent("GotServerDBInfoChange").detail("ChangeID", localInfo.id).detail("MasterID", localInfo.master.id()) .detail("RatekeeperID", localInfo.ratekeeper.present() ? localInfo.ratekeeper.get().id() : UID()) diff --git a/fdbserver/workloads/ConsistencyCheck.actor.cpp b/fdbserver/workloads/ConsistencyCheck.actor.cpp index e767b2bbb7..49749a9b3b 100644 --- a/fdbserver/workloads/ConsistencyCheck.actor.cpp +++ b/fdbserver/workloads/ConsistencyCheck.actor.cpp @@ -1139,12 +1139,12 @@ struct ConsistencyCheckWorkload : TestWorkload std::set> missingStorage; for( int i = 0; i < workers.size(); i++ ) { - NetworkAddress addr = workers[i].interf.tLog.getEndpoint().addresses.getTLSAddress(); - if( !configuration.isExcludedServer(addr) && + NetworkAddress addr = workers[i].interf.stableAddress(); + if( !configuration.isExcludedServer(workers[i].interf.addresses()) && ( workers[i].processClass == ProcessClass::StorageClass || workers[i].processClass == ProcessClass::UnsetClass ) ) { bool found = false; for( int j = 0; j < storageServers.size(); j++ ) { - if( storageServers[j].getValue.getEndpoint().addresses.getTLSAddress() == addr ) { + if( storageServers[j].stableAddress() == addr ) { found = true; break; } From ac4654b09ebc0019141888f331b9f15db2e1a97b Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Fri, 10 Apr 2020 13:50:26 -0700 Subject: [PATCH 1404/1604] re-suppress trace event --- fdbrpc/FailureMonitor.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbrpc/FailureMonitor.actor.cpp b/fdbrpc/FailureMonitor.actor.cpp index 47a6f47540..eb171e0baa 100644 --- a/fdbrpc/FailureMonitor.actor.cpp +++ b/fdbrpc/FailureMonitor.actor.cpp @@ -105,7 +105,7 @@ void SimpleFailureMonitor::endpointNotFound( Endpoint const& endpoint ) { TraceEvent("WellKnownEndpointNotFound").suppressFor(1.0).detail("Address", endpoint.getPrimaryAddress()).detail("TokenFirst", endpoint.token.first()).detail("TokenSecond", endpoint.token.second()); return; } - TraceEvent("EndpointNotFound").detail("Addresses", endpoint.addresses.toString()).detail("Token", endpoint.token).detail("IsFailed", endpointKnownFailed.get(endpoint)).detail("ShouldSwap", endpoint.addresses.secondaryAddress.present() && !g_network->getLocalAddresses().secondaryAddress.present() && (endpoint.addresses.address.isTLS() != g_network->getLocalAddresses().address.isTLS())); + TraceEvent("EndpointNotFound").suppressFor(1.0).detail("Addresses", endpoint.addresses.toString()).detail("Token", endpoint.token).detail("IsFailed", endpointKnownFailed.get(endpoint)).detail("ShouldSwap", endpoint.addresses.secondaryAddress.present() && !g_network->getLocalAddresses().secondaryAddress.present() && (endpoint.addresses.address.isTLS() != g_network->getLocalAddresses().address.isTLS())); endpointKnownFailed.set( endpoint, true ); } From 07cc0a8d7461407e04acb14ce35865ad87ef6392 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Fri, 10 Apr 2020 17:02:11 -0700 Subject: [PATCH 1405/1604] code cleanup --- fdbrpc/FailureMonitor.actor.cpp | 2 +- fdbrpc/FlowTransport.actor.cpp | 2 +- fdbserver/CMakeLists.txt | 1 - fdbserver/ClusterController.actor.cpp | 12 ++---- fdbserver/ClusterRecruitmentInterface.h | 42 ------------------- fdbserver/DataDistribution.actor.h | 1 - fdbserver/Knobs.cpp | 4 +- fdbserver/Knobs.h | 4 +- fdbserver/LeaderElection.actor.cpp | 1 - fdbserver/MasterProxyServer.actor.cpp | 2 +- fdbserver/ServerDBInfo.h | 1 - fdbserver/SimulatedCluster.actor.cpp | 1 - fdbserver/Status.actor.cpp | 1 - fdbserver/fdbserver.actor.cpp | 1 - fdbserver/masterserver.actor.cpp | 6 +-- fdbserver/tester.actor.cpp | 1 - fdbserver/worker.actor.cpp | 34 ++++----------- .../workloads/MachineAttrition.actor.cpp | 1 - fdbserver/workloads/Performance.actor.cpp | 1 - fdbserver/workloads/ReadWrite.actor.cpp | 1 - fdbserver/workloads/SnapTest.actor.cpp | 1 - flow/Knobs.cpp | 1 + flow/Knobs.h | 1 + 23 files changed, 23 insertions(+), 99 deletions(-) delete mode 100644 fdbserver/ClusterRecruitmentInterface.h diff --git a/fdbrpc/FailureMonitor.actor.cpp b/fdbrpc/FailureMonitor.actor.cpp index eb171e0baa..99c862ad6f 100644 --- a/fdbrpc/FailureMonitor.actor.cpp +++ b/fdbrpc/FailureMonitor.actor.cpp @@ -105,7 +105,7 @@ void SimpleFailureMonitor::endpointNotFound( Endpoint const& endpoint ) { TraceEvent("WellKnownEndpointNotFound").suppressFor(1.0).detail("Address", endpoint.getPrimaryAddress()).detail("TokenFirst", endpoint.token.first()).detail("TokenSecond", endpoint.token.second()); return; } - TraceEvent("EndpointNotFound").suppressFor(1.0).detail("Addresses", endpoint.addresses.toString()).detail("Token", endpoint.token).detail("IsFailed", endpointKnownFailed.get(endpoint)).detail("ShouldSwap", endpoint.addresses.secondaryAddress.present() && !g_network->getLocalAddresses().secondaryAddress.present() && (endpoint.addresses.address.isTLS() != g_network->getLocalAddresses().address.isTLS())); + TraceEvent("EndpointNotFound").suppressFor(1.0).detail("Addresses", endpoint.addresses.toString()).detail("Token", endpoint.token); endpointKnownFailed.set( endpoint, true ); } diff --git a/fdbrpc/FlowTransport.actor.cpp b/fdbrpc/FlowTransport.actor.cpp index b979dc0c60..cb011985f7 100644 --- a/fdbrpc/FlowTransport.actor.cpp +++ b/fdbrpc/FlowTransport.actor.cpp @@ -1045,7 +1045,7 @@ ACTOR static Future multiVersionCleanupWorker( TransportData* self ) { if( self->multiVersionConnections.count(it->second.first) ) { it = self->incompatiblePeers.erase(it); } else { - if( now() - it->second.second > 5.0 ) { //INCOMPATIBLE_PEER_DELAY_BEFORE_LOGGING + if( now() - it->second.second > FLOW_KNOBS->INCOMPATIBLE_PEER_DELAY_BEFORE_LOGGING ) { foundIncompatible = true; } it++; diff --git a/fdbserver/CMakeLists.txt b/fdbserver/CMakeLists.txt index 64bb6b5017..08bb67e01a 100644 --- a/fdbserver/CMakeLists.txt +++ b/fdbserver/CMakeLists.txt @@ -6,7 +6,6 @@ set(FDBSERVER_SRCS BackupProgress.actor.h BackupWorker.actor.cpp ClusterController.actor.cpp - ClusterRecruitmentInterface.h ConflictSet.h CoordinatedState.actor.cpp CoordinatedState.h diff --git a/fdbserver/ClusterController.actor.cpp b/fdbserver/ClusterController.actor.cpp index f9475017a3..e036831480 100644 --- a/fdbserver/ClusterController.actor.cpp +++ b/fdbserver/ClusterController.actor.cpp @@ -36,7 +36,6 @@ #include "fdbserver/LeaderElection.h" #include "fdbserver/LogSystemConfig.h" #include "fdbserver/WaitFailure.h" -#include "fdbserver/ClusterRecruitmentInterface.h" #include "fdbserver/RatekeeperInterface.h" #include "fdbserver/ServerDBInfo.h" #include "fdbserver/Status.h" @@ -3037,7 +3036,7 @@ ACTOR Future dbInfoUpdater( ClusterControllerData* self ) { loop { choose { when(wait(updateDBInfo)) { - wait(delay(0.1) || dbInfoChange); + wait(delay(SERVER_KNOBS->DBINFO_BATCH_DELAY) || dbInfoChange); } when(wait(dbInfoChange)) {} } @@ -3065,13 +3064,10 @@ ACTOR Future dbInfoUpdater( ClusterControllerData* self ) { req.dbInfo = self->db.serverInfo->get().read(); req.broadcastInfo = self->updateDBInfoEndpoints; - for(auto &it : self->updateDBInfoEndpoints) { - TraceEvent("DBInfoAttemptUpdate", self->id).detail("Addr", it.getPrimaryAddress()).detail("Token", it.token); - } self->updateDBInfoEndpoints.clear(); TraceEvent("DBInfoStartBroadcast", self->id); choose { - when(std::vector notUpdated = wait( broadcastDBInfoRequest(req, 2, Optional(), false) )) { + when(std::vector notUpdated = wait( broadcastDBInfoRequest(req, SERVER_KNOBS->DBINFO_SEND_AMOUNT, Optional(), false) )) { TraceEvent("DBInfoFinishBroadcast", self->id); for(auto &it : notUpdated) { TraceEvent("DBInfoNotUpdated", self->id).detail("Addr", it.getPrimaryAddress()); @@ -3081,9 +3077,7 @@ ACTOR Future dbInfoUpdater( ClusterControllerData* self ) { self->updateDBInfo.trigger(); } } - when(wait(dbInfoChange)) { - TraceEvent("DBInfoChangeBroadcast", self->id); - } + when(wait(dbInfoChange)) {} } } } diff --git a/fdbserver/ClusterRecruitmentInterface.h b/fdbserver/ClusterRecruitmentInterface.h deleted file mode 100644 index a46e1263fa..0000000000 --- a/fdbserver/ClusterRecruitmentInterface.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * ClusterRecruitmentInterface.h - * - * This source file is part of the FoundationDB open source project - * - * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef FDBSERVER_CLUSTERRECRUITMENTINTERFACE_H -#define FDBSERVER_CLUSTERRECRUITMENTINTERFACE_H -#pragma once - -#include - -#include "fdbclient/ClusterInterface.h" -#include "fdbclient/StorageServerInterface.h" -#include "fdbclient/MasterProxyInterface.h" -#include "fdbclient/DatabaseConfiguration.h" -#include "fdbserver/BackupInterface.h" -#include "fdbserver/DataDistributorInterface.h" -#include "fdbserver/MasterInterface.h" -#include "fdbserver/TLogInterface.h" -#include "fdbserver/WorkerInterface.actor.h" -#include "fdbserver/Knobs.h" - - - -#include "fdbserver/ServerDBInfo.h" // include order hack - -#endif diff --git a/fdbserver/DataDistribution.actor.h b/fdbserver/DataDistribution.actor.h index dc518f7d2f..f07a15dbfd 100644 --- a/fdbserver/DataDistribution.actor.h +++ b/fdbserver/DataDistribution.actor.h @@ -25,7 +25,6 @@ #define FDBSERVER_DATA_DISTRIBUTION_ACTOR_H #include "fdbclient/NativeAPI.actor.h" -#include "fdbserver/ClusterRecruitmentInterface.h" #include "fdbserver/MoveKeys.actor.h" #include "fdbserver/LogSystem.h" #include "flow/actorcompiler.h" // This must be the last #include. diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index 4e2547cd31..ac56be590c 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -343,6 +343,7 @@ void ServerKnobs::initialize(bool randomize, ClientKnobs* clientKnobs, bool isSi init( MAX_PROXY_COMPUTE, 2.0 ); init( PROXY_COMPUTE_BUCKETS, 20000 ); init( PROXY_COMPUTE_GROWTH_RATE, 0.01 ); + init( TXN_STATE_SEND_AMOUNT, 2 ); // Master Server // masterCommitter() in the master server will allow lower priority tasks (e.g. DataDistibution) @@ -410,6 +411,8 @@ void ServerKnobs::initialize(bool randomize, ClientKnobs* clientKnobs, bool isSi init( POLICY_RATING_TESTS, 200 ); if( randomize && BUGGIFY ) POLICY_RATING_TESTS = 20; init( POLICY_GENERATIONS, 100 ); if( randomize && BUGGIFY ) POLICY_GENERATIONS = 10; + init( DBINFO_SEND_AMOUNT, 2 ); + init( DBINFO_BATCH_DELAY, 0.1 ); //Move Keys init( SHARD_READY_DELAY, 0.25 ); @@ -521,7 +524,6 @@ void ServerKnobs::initialize(bool randomize, ClientKnobs* clientKnobs, bool isSi //Worker init( WORKER_LOGGING_INTERVAL, 5.0 ); - init( INCOMPATIBLE_PEER_DELAY_BEFORE_LOGGING, 5.0 ); init( HEAP_PROFILER_INTERVAL, 30.0 ); init( DEGRADED_RESET_INTERVAL, 24*60*60 ); if ( randomize && BUGGIFY ) DEGRADED_RESET_INTERVAL = 10; init( DEGRADED_WARNING_LIMIT, 1 ); diff --git a/fdbserver/Knobs.h b/fdbserver/Knobs.h index 9e7d8b384f..47ff8a886e 100644 --- a/fdbserver/Knobs.h +++ b/fdbserver/Knobs.h @@ -280,6 +280,7 @@ public: double MAX_PROXY_COMPUTE; int PROXY_COMPUTE_BUCKETS; double PROXY_COMPUTE_GROWTH_RATE; + int TXN_STATE_SEND_AMOUNT; // Master Server double COMMIT_SLEEP_TIME; @@ -344,6 +345,8 @@ public: int EXPECTED_PROXY_FITNESS; int EXPECTED_RESOLVER_FITNESS; double RECRUITMENT_TIMEOUT; + int DBINFO_SEND_AMOUNT; + double DBINFO_BATCH_DELAY; //Move Keys double SHARD_READY_DELAY; @@ -456,7 +459,6 @@ public: //Worker double WORKER_LOGGING_INTERVAL; - double INCOMPATIBLE_PEER_DELAY_BEFORE_LOGGING; double HEAP_PROFILER_INTERVAL; double DEGRADED_RESET_INTERVAL; double DEGRADED_WARNING_LIMIT; diff --git a/fdbserver/LeaderElection.actor.cpp b/fdbserver/LeaderElection.actor.cpp index be23f7da8e..a910a3c486 100644 --- a/fdbserver/LeaderElection.actor.cpp +++ b/fdbserver/LeaderElection.actor.cpp @@ -20,7 +20,6 @@ #include "fdbrpc/FailureMonitor.h" #include "fdbrpc/Locality.h" -#include "fdbserver/ClusterRecruitmentInterface.h" #include "fdbserver/CoordinationInterface.h" #include "fdbclient/MonitorLeader.h" #include "flow/actorcompiler.h" // This must be the last #include. diff --git a/fdbserver/MasterProxyServer.actor.cpp b/fdbserver/MasterProxyServer.actor.cpp index b7c4a4cdc0..ccaf853745 100644 --- a/fdbserver/MasterProxyServer.actor.cpp +++ b/fdbserver/MasterProxyServer.actor.cpp @@ -1986,7 +1986,7 @@ ACTOR Future masterProxyServerCore( commitData.txnStateStore->enableSnapshot(); } } - addActor.send(broadcastTxnRequest(req, 2, true)); + addActor.send(broadcastTxnRequest(req, SERVER_KNOBS->TXN_STATE_SEND_AMOUNT, true)); wait(yield()); } } diff --git a/fdbserver/ServerDBInfo.h b/fdbserver/ServerDBInfo.h index 5c1de99d40..2c45135c27 100644 --- a/fdbserver/ServerDBInfo.h +++ b/fdbserver/ServerDBInfo.h @@ -22,7 +22,6 @@ #define FDBSERVER_SERVERDBINFO_H #pragma once -#include "fdbserver/ClusterRecruitmentInterface.h" #include "fdbserver/DataDistributorInterface.h" #include "fdbserver/MasterInterface.h" #include "fdbserver/LogSystemConfig.h" diff --git a/fdbserver/SimulatedCluster.actor.cpp b/fdbserver/SimulatedCluster.actor.cpp index f89a97c59e..1a5be61699 100644 --- a/fdbserver/SimulatedCluster.actor.cpp +++ b/fdbserver/SimulatedCluster.actor.cpp @@ -25,7 +25,6 @@ #include "fdbserver/WorkerInterface.actor.h" #include "fdbclient/ClusterInterface.h" #include "fdbserver/Knobs.h" -#include "fdbserver/ClusterRecruitmentInterface.h" #include "fdbserver/CoordinationInterface.h" #include "fdbmonitor/SimpleIni.h" #include "fdbrpc/AsyncFileNonDurable.actor.h" diff --git a/fdbserver/Status.actor.cpp b/fdbserver/Status.actor.cpp index 715919f2e5..3416422167 100644 --- a/fdbserver/Status.actor.cpp +++ b/fdbserver/Status.actor.cpp @@ -25,7 +25,6 @@ #include "fdbclient/SystemData.h" #include "fdbclient/ReadYourWrites.h" #include "fdbserver/WorkerInterface.actor.h" -#include "fdbserver/ClusterRecruitmentInterface.h" #include #include "fdbserver/CoordinationInterface.h" #include "fdbserver/DataDistribution.actor.h" diff --git a/fdbserver/fdbserver.actor.cpp b/fdbserver/fdbserver.actor.cpp index 4bb0d38902..4471d90495 100644 --- a/fdbserver/fdbserver.actor.cpp +++ b/fdbserver/fdbserver.actor.cpp @@ -34,7 +34,6 @@ #include "fdbserver/CoordinationInterface.h" #include "fdbserver/WorkerInterface.actor.h" #include "fdbclient/RestoreWorkerInterface.actor.h" -#include "fdbserver/ClusterRecruitmentInterface.h" #include "fdbserver/ServerDBInfo.h" #include "fdbserver/MoveKeys.actor.h" #include "fdbserver/ConflictSet.h" diff --git a/fdbserver/masterserver.actor.cpp b/fdbserver/masterserver.actor.cpp index 5968c0c7e2..c20d8ffb71 100644 --- a/fdbserver/masterserver.actor.cpp +++ b/fdbserver/masterserver.actor.cpp @@ -33,7 +33,6 @@ #include "fdbserver/MasterInterface.h" #include "fdbserver/WaitFailure.h" #include "fdbserver/WorkerInterface.actor.h" -#include "fdbserver/ClusterRecruitmentInterface.h" #include "fdbserver/ServerDBInfo.h" #include "fdbserver/CoordinatedState.h" #include "fdbserver/CoordinationInterface.h" // copy constructors for ServerCoordinators class @@ -744,7 +743,6 @@ ACTOR Future sendInitialCommitToResolvers( Reference self ) { state int64_t dataOutstanding = 0; state std::vector endpoints; - state int sendAmount = 2; for(auto& it : self->proxies) { endpoints.push_back(it.txnState.getEndpoint()); } @@ -760,8 +758,8 @@ ACTOR Future sendInitialCommitToResolvers( Reference self ) { req.sequence = txnSequence; req.last = !nextData.size(); req.broadcastInfo = endpoints; - txnReplies.push_back(broadcastTxnRequest(req, sendAmount, false)); - dataOutstanding += sendAmount*data.arena().getSize(); + txnReplies.push_back(broadcastTxnRequest(req, SERVER_KNOBS->TXN_STATE_SEND_AMOUNT, false)); + dataOutstanding += SERVER_KNOBS->TXN_STATE_SEND_AMOUNT*data.arena().getSize(); data = nextData; txnSequence++; diff --git a/fdbserver/tester.actor.cpp b/fdbserver/tester.actor.cpp index f673b2d707..988b78d6df 100644 --- a/fdbserver/tester.actor.cpp +++ b/fdbserver/tester.actor.cpp @@ -28,7 +28,6 @@ #include "fdbclient/SystemData.h" #include "fdbserver/TesterInterface.actor.h" #include "fdbserver/WorkerInterface.actor.h" -#include "fdbserver/ClusterRecruitmentInterface.h" #include "fdbserver/workloads/workloads.actor.h" #include "fdbserver/Status.h" #include "fdbserver/QuietDatabase.h" diff --git a/fdbserver/worker.actor.cpp b/fdbserver/worker.actor.cpp index a0169d949d..e87d2a3915 100644 --- a/fdbserver/worker.actor.cpp +++ b/fdbserver/worker.actor.cpp @@ -33,7 +33,6 @@ #include "fdbserver/TesterInterface.actor.h" // for poisson() #include "fdbserver/IDiskQueue.h" #include "fdbclient/DatabaseContext.h" -#include "fdbserver/ClusterRecruitmentInterface.h" #include "fdbserver/DataDistributorInterface.h" #include "fdbserver/ServerDBInfo.h" #include "fdbserver/FDBExecHelper.actor.h" @@ -69,20 +68,12 @@ extern IKeyValueStore* keyValueStoreCompressTestData(IKeyValueStore* store); #endif ACTOR Future> tryDBInfoBroadcast(RequestStream stream, UpdateServerDBInfoRequest req) { - state UID dbgid = nondeterministicRandom()->randomUniqueID(); - TraceEvent("BroadcastDBInfo", dbgid).detail("Addr", stream.getEndpoint().getPrimaryAddress()).detail("Token", stream.getEndpoint().token); - try { - ErrorOr> rep = wait( stream.getReplyUnlessFailedFor(req, 1.0, 0) ); - TraceEvent("BroadcastDBInfoReply", dbgid).detail("Addr", stream.getEndpoint().getPrimaryAddress()).detail("Present", rep.present()); - if(rep.present()) { - return rep.get(); - } - req.broadcastInfo.push_back(stream.getEndpoint()); - return req.broadcastInfo; - } catch( Error &e ) { - TraceEvent("BroadcastDBInfoError", dbgid).error(e,true).detail("Addr", stream.getEndpoint().getPrimaryAddress()); - throw; + ErrorOr> rep = wait( stream.getReplyUnlessFailedFor(req, 1.0, 0) ); + if(rep.present()) { + return rep.get(); } + req.broadcastInfo.push_back(stream.getEndpoint()); + return req.broadcastInfo; } ACTOR Future> broadcastDBInfoRequest(UpdateServerDBInfoRequest req, int sendAmount, Optional sender, bool sendReply) { @@ -98,9 +89,6 @@ ACTOR Future> broadcastDBInfoRequest(UpdateServerDBInfoReq endpoints.push_back(broadcastEndpoints[currentStream++]); } req.broadcastInfo = endpoints; - for(auto &it : req.broadcastInfo) { - TraceEvent("BroadcastDBForward").detail("Addr", it.getPrimaryAddress()); - } replies.push_back( tryDBInfoBroadcast( cur, req ) ); resetReply( req ); } @@ -115,9 +103,6 @@ ACTOR Future> broadcastDBInfoRequest(UpdateServerDBInfoReq if(sendReply) { reply.send(notUpdated); } - for(auto &it : notUpdated) { - TraceEvent("BroadcastDBNotUpdated").detail("Addr", it.getPrimaryAddress()); - } return notUpdated; } @@ -503,7 +488,7 @@ ACTOR Future registrationClient( auto peers = FlowTransport::transport().getIncompatiblePeers(); for(auto it = peers->begin(); it != peers->end();) { - if( now() - it->second.second > SERVER_KNOBS->INCOMPATIBLE_PEER_DELAY_BEFORE_LOGGING ) { + if( now() - it->second.second > FLOW_KNOBS->INCOMPATIBLE_PEER_DELAY_BEFORE_LOGGING ) { request.incompatiblePeers.push_back(it->first); it = peers->erase(it); } else { @@ -1076,11 +1061,6 @@ ACTOR Future workerServer( loop choose { when( UpdateServerDBInfoRequest req = waitNext( interf.updateServerDBInfo.getFuture() ) ) { - TraceEvent("GotServerDBInfoMsg").detail("NotUpdated", !ccInterface->get().present() || req.dbInfo.clusterInterface != ccInterface->get().get() || (req.dbInfo.infoGeneration < dbInfo->get().infoGeneration && dbInfo->get().clusterInterface == ccInterface->get().get())) - .detail("ReqInterface", ccInterface->get().present()) - .detail("InfoGeneration", req.dbInfo.infoGeneration) - .detail("Token", interf.updateServerDBInfo.getEndpoint().token); - Optional notUpdated; if(!ccInterface->get().present() || req.dbInfo.clusterInterface != ccInterface->get().get() || (req.dbInfo.infoGeneration < dbInfo->get().infoGeneration && dbInfo->get().clusterInterface == ccInterface->get().get())) { notUpdated = interf.updateServerDBInfo.getEndpoint(); @@ -1094,7 +1074,7 @@ ACTOR Future workerServer( localInfo.myLocality = locality; dbInfo->set(localInfo); } - errorForwarders.add(success(broadcastDBInfoRequest(req, 2, notUpdated, true))); + errorForwarders.add(success(broadcastDBInfoRequest(req, SERVER_KNOBS->DBINFO_SEND_AMOUNT, notUpdated, true))); } when( RebootRequest req = waitNext( interf.clientInterface.reboot.getFuture() ) ) { state RebootRequest rebootReq = req; diff --git a/fdbserver/workloads/MachineAttrition.actor.cpp b/fdbserver/workloads/MachineAttrition.actor.cpp index 4ad7b4de84..5a666bf887 100644 --- a/fdbserver/workloads/MachineAttrition.actor.cpp +++ b/fdbserver/workloads/MachineAttrition.actor.cpp @@ -20,7 +20,6 @@ #include "fdbclient/NativeAPI.actor.h" #include "fdbclient/CoordinationInterface.h" -#include "fdbserver/ClusterRecruitmentInterface.h" #include "fdbserver/TesterInterface.actor.h" #include "fdbserver/WorkerInterface.actor.h" #include "fdbserver/workloads/workloads.actor.h" diff --git a/fdbserver/workloads/Performance.actor.cpp b/fdbserver/workloads/Performance.actor.cpp index 16c6b00528..ffb4f90e07 100644 --- a/fdbserver/workloads/Performance.actor.cpp +++ b/fdbserver/workloads/Performance.actor.cpp @@ -22,7 +22,6 @@ #include "fdbserver/TesterInterface.actor.h" #include "fdbserver/workloads/workloads.actor.h" #include "fdbserver/QuietDatabase.h" -#include "fdbserver/ClusterRecruitmentInterface.h" #include "flow/actorcompiler.h" // This must be the last #include. struct PerformanceWorkload : TestWorkload { diff --git a/fdbserver/workloads/ReadWrite.actor.cpp b/fdbserver/workloads/ReadWrite.actor.cpp index cd7cc918c3..24cbc203e5 100644 --- a/fdbserver/workloads/ReadWrite.actor.cpp +++ b/fdbserver/workloads/ReadWrite.actor.cpp @@ -28,7 +28,6 @@ #include "fdbserver/WorkerInterface.actor.h" #include "fdbserver/workloads/workloads.actor.h" #include "fdbserver/workloads/BulkSetup.actor.h" -#include "fdbserver/ClusterRecruitmentInterface.h" #include "fdbclient/ReadYourWrites.h" #include "flow/TDMetric.actor.h" #include "flow/actorcompiler.h" // This must be the last #include. diff --git a/fdbserver/workloads/SnapTest.actor.cpp b/fdbserver/workloads/SnapTest.actor.cpp index 78cd7580ae..85c5fbbd09 100644 --- a/fdbserver/workloads/SnapTest.actor.cpp +++ b/fdbserver/workloads/SnapTest.actor.cpp @@ -4,7 +4,6 @@ #include "fdbclient/ReadYourWrites.h" #include "fdbrpc/ContinuousSample.h" #include "fdbmonitor/SimpleIni.h" -#include "fdbserver/ClusterRecruitmentInterface.h" #include "fdbserver/Status.h" #include "fdbserver/TesterInterface.actor.h" #include "fdbserver/WorkerInterface.actor.h" diff --git a/flow/Knobs.cpp b/flow/Knobs.cpp index dbdec21a13..1e13a61740 100644 --- a/flow/Knobs.cpp +++ b/flow/Knobs.cpp @@ -76,6 +76,7 @@ void FlowKnobs::initialize(bool randomize, bool isSimulated) { init( TOO_MANY_CONNECTIONS_CLOSED_RESET_DELAY, 5.0 ); init( TOO_MANY_CONNECTIONS_CLOSED_TIMEOUT, 20.0 ); init( PEER_UNAVAILABLE_FOR_LONG_TIME_TIMEOUT, 3600.0 ); + init( INCOMPATIBLE_PEER_DELAY_BEFORE_LOGGING, 5.0 ); init( TLS_CERT_REFRESH_DELAY_SECONDS, 12*60*60 ); init( TLS_SERVER_CONNECTION_THROTTLE_TIMEOUT, 9.0 ); diff --git a/flow/Knobs.h b/flow/Knobs.h index c846e66281..7de4ca95b2 100644 --- a/flow/Knobs.h +++ b/flow/Knobs.h @@ -90,6 +90,7 @@ public: double RECONNECTION_TIME_GROWTH_RATE; double RECONNECTION_RESET_TIME; int ACCEPT_BATCH_SIZE; + double INCOMPATIBLE_PEER_DELAY_BEFORE_LOGGING; int TLS_CERT_REFRESH_DELAY_SECONDS; double TLS_SERVER_CONNECTION_THROTTLE_TIMEOUT; From 4b5fdef9767e728e5cc6bd1e1798e5583a443bb5 Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Fri, 10 Apr 2020 18:16:52 -0700 Subject: [PATCH 1406/1604] fix correctness dependencies --- cmake/AddFdbTest.cmake | 22 +++++++++++++--------- cmake/FlowCommands.cmake | 2 +- contrib/TestHarness/CMakeLists.txt | 3 +-- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/cmake/AddFdbTest.cmake b/cmake/AddFdbTest.cmake index 4380c4ccd7..4a73721cf2 100644 --- a/cmake/AddFdbTest.cmake +++ b/cmake/AddFdbTest.cmake @@ -189,27 +189,31 @@ function(create_test_package) add_custom_command( OUTPUT ${tar_file} DEPENDS ${out_files} - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/correctnessTest.sh ${CMAKE_BINARY_DIR}/packages/joshua_test - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/correctnessTimeout.sh ${CMAKE_BINARY_DIR}/packages/joshua_timeout + ${CMAKE_BINARY_DIR}/packages/bin/fdbserver + ${CMAKE_BINARY_DIR}/packages/bin/TestHarness.exe + ${CMAKE_BINARY_DIR}/packages/bin/TraceLogHelper.dll + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/correctnessTest.sh + ${CMAKE_BINARY_DIR}/packages/joshua_test + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/correctnessTimeout.sh + ${CMAKE_BINARY_DIR}/packages/joshua_timeout COMMAND ${CMAKE_COMMAND} -E tar cfz ${tar_file} ${CMAKE_BINARY_DIR}/packages/bin/fdbserver - ${CMAKE_BINARY_DIR}/packages/bin/TestHarness.exe - ${CMAKE_BINARY_DIR}/packages/bin/TraceLogHelper.dll - ${CMAKE_BINARY_DIR}/packages/joshua_test - ${CMAKE_BINARY_DIR}/packages/joshua_timeout - ${out_files} ${external_files} + ${CMAKE_BINARY_DIR}/packages/bin/TestHarness.exe + ${CMAKE_BINARY_DIR}/packages/bin/TraceLogHelper.dll + ${CMAKE_BINARY_DIR}/packages/joshua_test + ${CMAKE_BINARY_DIR}/packages/joshua_timeout + ${out_files} ${external_files} COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_BINARY_DIR}/packages/joshua_test ${CMAKE_BINARY_DIR}/packages/joshua_timeout WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/packages COMMENT "Package correctness archive" ) add_custom_target(package_tests ALL DEPENDS ${tar_file}) - add_dependencies(package_tests strip_only_fdbserver TestHarness) endif() if(USE_VALGRIND) set(tar_file ${CMAKE_BINARY_DIR}/packages/valgrind-${CMAKE_PROJECT_VERSION}.tar.gz) add_custom_command( OUTPUT ${tar_file} - DEPENDS ${out_files} + DEPENDS ${out_files} strip_only_fdbserver COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/valgrindTest.sh ${CMAKE_BINARY_DIR}/packages/joshua_test COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/valgrindTimeout.sh ${CMAKE_BINARY_DIR}/packages/joshua_timeout COMMAND ${CMAKE_COMMAND} -E tar cfz ${tar_file} ${CMAKE_BINARY_DIR}/packages/bin/fdbserver diff --git a/cmake/FlowCommands.cmake b/cmake/FlowCommands.cmake index 7e88d95ab3..a9546d0bcb 100644 --- a/cmake/FlowCommands.cmake +++ b/cmake/FlowCommands.cmake @@ -135,11 +135,11 @@ function(strip_debug_symbols target) add_custom_target(strip_only_${target} DEPENDS ${out_file}) if(is_exec AND NOT APPLE) add_custom_command(OUTPUT "${out_file}.debug" + DEPENDS strip_only_${target} COMMAND objcopy --verbose --only-keep-debug $ "${out_file}.debug" COMMAND objcopy --verbose --add-gnu-debuglink="${out_file}.debug" "${out_file}" COMMENT "Copy debug symbols to ${out_name}.debug") add_custom_target(strip_${target} DEPENDS "${out_file}.debug") - add_dependencies(strip_${target} ${target} strip_only_${target}) else() add_custom_target(strip_${target}) add_dependencies(strip_${target} strip_only_${target}) diff --git a/contrib/TestHarness/CMakeLists.txt b/contrib/TestHarness/CMakeLists.txt index b91079f286..3616d772d2 100644 --- a/contrib/TestHarness/CMakeLists.txt +++ b/contrib/TestHarness/CMakeLists.txt @@ -10,8 +10,7 @@ set(out_file ${CMAKE_BINARY_DIR}/packages/bin/TestHarness.exe) add_custom_command(OUTPUT ${out_file} COMMAND ${MCS_EXECUTABLE} ARGS ${TEST_HARNESS_REFERENCES} ${SRCS} "-target:exe" "-out:${out_file}" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - DEPENDS ${SRCS} + DEPENDS ${SRCS} TraceLogHelper COMMENT "Compile TestHarness" VERBATIM) add_custom_target(TestHarness DEPENDS ${out_file}) -add_dependencies(TestHarness TraceLogHelper) set(TestHarnesExe "${out_file}" PARENT_SCOPE) From e004d912bd4f39d7062039e802c710aed32e4359 Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Fri, 10 Apr 2020 18:23:29 -0700 Subject: [PATCH 1407/1604] Also depend on Joshua scripts --- cmake/AddFdbTest.cmake | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/cmake/AddFdbTest.cmake b/cmake/AddFdbTest.cmake index 4a73721cf2..cbd77479b8 100644 --- a/cmake/AddFdbTest.cmake +++ b/cmake/AddFdbTest.cmake @@ -192,6 +192,9 @@ function(create_test_package) ${CMAKE_BINARY_DIR}/packages/bin/fdbserver ${CMAKE_BINARY_DIR}/packages/bin/TestHarness.exe ${CMAKE_BINARY_DIR}/packages/bin/TraceLogHelper.dll + ${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/correctnessTest.sh + ${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/correctnessTimeout.sh + ${external_files} COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/correctnessTest.sh ${CMAKE_BINARY_DIR}/packages/joshua_test COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/correctnessTimeout.sh @@ -201,7 +204,8 @@ function(create_test_package) ${CMAKE_BINARY_DIR}/packages/bin/TraceLogHelper.dll ${CMAKE_BINARY_DIR}/packages/joshua_test ${CMAKE_BINARY_DIR}/packages/joshua_timeout - ${out_files} ${external_files} + ${out_files} + ${external_files} COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_BINARY_DIR}/packages/joshua_test ${CMAKE_BINARY_DIR}/packages/joshua_timeout WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/packages COMMENT "Package correctness archive" @@ -213,15 +217,25 @@ function(create_test_package) set(tar_file ${CMAKE_BINARY_DIR}/packages/valgrind-${CMAKE_PROJECT_VERSION}.tar.gz) add_custom_command( OUTPUT ${tar_file} - DEPENDS ${out_files} strip_only_fdbserver - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/valgrindTest.sh ${CMAKE_BINARY_DIR}/packages/joshua_test - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/valgrindTimeout.sh ${CMAKE_BINARY_DIR}/packages/joshua_timeout - COMMAND ${CMAKE_COMMAND} -E tar cfz ${tar_file} ${CMAKE_BINARY_DIR}/packages/bin/fdbserver - ${CMAKE_BINARY_DIR}/packages/bin/TestHarness.exe - ${CMAKE_BINARY_DIR}/packages/bin/TraceLogHelper.dll - ${CMAKE_BINARY_DIR}/packages/joshua_test - ${CMAKE_BINARY_DIR}/packages/joshua_timeout - ${out_files} ${external_files} + DEPENDS ${out_files} + ${CMAKE_BINARY_DIR}/packages/bin/fdbserver + ${CMAKE_BINARY_DIR}/packages/bin/TestHarness.exe + ${CMAKE_BINARY_DIR}/packages/bin/TraceLogHelper.dll + ${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/valgrindTest.sh + ${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/valgrindTimeout.sh + ${external_files} + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/valgrindTest.sh + ${CMAKE_BINARY_DIR}/packages/joshua_test + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/contrib/Joshua/scripts/valgrindTimeout.sh + ${CMAKE_BINARY_DIR}/packages/joshua_timeout + COMMAND ${CMAKE_COMMAND} -E tar cfz ${tar_file} + ${CMAKE_BINARY_DIR}/packages/bin/fdbserver + ${CMAKE_BINARY_DIR}/packages/bin/TestHarness.exe + ${CMAKE_BINARY_DIR}/packages/bin/TraceLogHelper.dll + ${CMAKE_BINARY_DIR}/packages/joshua_test + ${CMAKE_BINARY_DIR}/packages/joshua_timeout + ${out_files} + ${external_files} COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_BINARY_DIR}/packages/joshua_test ${CMAKE_BINARY_DIR}/packages/joshua_timeout WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/packages COMMENT "Package correctness archive" From 7e5551ea1942c4beb97839058d254bc5f2fc1310 Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Fri, 10 Apr 2020 21:19:37 -0700 Subject: [PATCH 1408/1604] Avoid overlapping version ranges for backup workers Sometimes, an epoch's begin version is lower than the previous epoch's end version. In some rare casse, the master ends up recruiting backup workers for both epoch and have overlapping ranges of [epochBeginVersion, prevEpochEndVersion]. Since the popping order is by epoch. Previous epoch can pop the mutation and save to a log file. Then this epoch will miss these popped mutation in the overlapping range, causing corrupted mutation logs. --- fdbserver/BackupProgress.actor.cpp | 23 ++++++++++++++++++----- fdbserver/BackupProgress.actor.h | 3 ++- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/fdbserver/BackupProgress.actor.cpp b/fdbserver/BackupProgress.actor.cpp index e292b1171f..e64a44bfdc 100644 --- a/fdbserver/BackupProgress.actor.cpp +++ b/fdbserver/BackupProgress.actor.cpp @@ -45,17 +45,21 @@ void BackupProgress::addBackupStatus(const WorkerBackupStatus& status) { } void BackupProgress::updateTagVersions(std::map* tagVersions, std::set* tags, - const std::map& progress, Version endVersion, LogEpoch epoch) { + const std::map& progress, Version endVersion, + Version adjustedBeginVersion, LogEpoch epoch) { for (const auto& [tag, savedVersion] : progress) { // If tag is not in "tags", it means the old epoch has more tags than // new epoch's tags. Just ignore the tag here. auto n = tags->erase(tag); if (n > 0 && savedVersion < endVersion - 1) { - tagVersions->insert({ tag, savedVersion + 1 }); + const Version beginVersion = + (savedVersion + 1 > adjustedBeginVersion) ? (savedVersion + 1) : adjustedBeginVersion; + tagVersions->insert({ tag, beginVersion }); TraceEvent("BackupVersionRange", dbgid) .detail("OldEpoch", epoch) .detail("Tag", tag.toString()) .detail("BeginVersion", savedVersion + 1) + .detail("AdjustedBeginVersion", beginVersion) .detail("EndVersion", endVersion); } } @@ -66,12 +70,20 @@ std::map, std::map> BackupProgr if (!backupStartedValue.present()) return toRecruit; // No active backups + Version lastEnd = invalidVersion; for (const auto& [epoch, info] : epochInfos) { std::set tags = enumerateLogRouterTags(info.logRouterTags); std::map tagVersions; + + // Sometimes, an epoch's begin version is lower than the previous epoch's + // end version. In this case, adjust the epoch's begin version to be the + // same as previous end version. + Version adjustedBeginVersion = lastEnd > info.epochBegin ? lastEnd : info.epochBegin; + lastEnd = info.epochEnd; + auto progressIt = progress.lower_bound(epoch); if (progressIt != progress.end() && progressIt->first == epoch) { - updateTagVersions(&tagVersions, &tags, progressIt->second, info.epochEnd, epoch); + updateTagVersions(&tagVersions, &tags, progressIt->second, info.epochEnd, adjustedBeginVersion, epoch); } else { auto rit = std::find_if( progress.rbegin(), progress.rend(), @@ -90,17 +102,18 @@ std::map, std::map> BackupProgr // The logRouterTags are the same // ASSERT(info.logRouterTags == epochTags[rit->first]); - updateTagVersions(&tagVersions, &tags, rit->second, info.epochEnd, epoch); + updateTagVersions(&tagVersions, &tags, rit->second, info.epochEnd, adjustedBeginVersion, epoch); } } } for (const Tag tag : tags) { // tags without progress data - tagVersions.insert({ tag, info.epochBegin }); + tagVersions.insert({ tag, adjustedBeginVersion }); TraceEvent("BackupVersionRange", dbgid) .detail("OldEpoch", epoch) .detail("Tag", tag.toString()) .detail("BeginVersion", info.epochBegin) + .detail("AdjustedBeginVersion", adjustedBeginVersion) .detail("EndVersion", info.epochEnd); } if (!tagVersions.empty()) { diff --git a/fdbserver/BackupProgress.actor.h b/fdbserver/BackupProgress.actor.h index d17d2c9a15..64400c10e7 100644 --- a/fdbserver/BackupProgress.actor.h +++ b/fdbserver/BackupProgress.actor.h @@ -81,7 +81,8 @@ private: // For each tag in progress, the saved version is smaller than endVersion - 1, // add {tag, savedVersion+1} to tagVersions and remove the tag from "tags". void updateTagVersions(std::map* tagVersions, std::set* tags, - const std::map& progress, Version endVersion, LogEpoch epoch); + const std::map& progress, Version endVersion, Version adjustedBeginVersion, + LogEpoch epoch); const UID dbgid; From 4e128328f77f89cd5e60f9b3f8eca58574abd379 Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Sat, 11 Apr 2020 10:23:53 -0700 Subject: [PATCH 1409/1604] Stop backup workers before clearing DB in parallel restore workload This is because the clearing of DB can be picked up by backup workers and be applied during restore, causing restore failures. --- fdbserver/BackupWorker.actor.cpp | 4 +++- ...kupAndParallelRestoreCorrectness.actor.cpp | 19 +++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/fdbserver/BackupWorker.actor.cpp b/fdbserver/BackupWorker.actor.cpp index 20f6e363f3..481bc2e20e 100644 --- a/fdbserver/BackupWorker.actor.cpp +++ b/fdbserver/BackupWorker.actor.cpp @@ -179,7 +179,9 @@ struct BackupData { config.startedBackupWorkers().set(tr, workers.get()); } for (auto p : workers.get()) { - TraceEvent("BackupWorkerDebug", self->myId).detail("Epoch", p.first).detail("TagID", p.second); + TraceEvent("BackupWorkerDebugTag", self->myId) + .detail("Epoch", p.first) + .detail("TagID", p.second); } wait(tr->commit()); diff --git a/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp b/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp index 66a07df408..aa94d9c2b7 100644 --- a/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp +++ b/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp @@ -21,6 +21,7 @@ #include "fdbrpc/simulator.h" #include "fdbclient/BackupAgent.actor.h" #include "fdbclient/BackupContainer.h" +#include "fdbclient/ManagementAPI.actor.h" #include "fdbserver/workloads/workloads.actor.h" #include "fdbserver/workloads/BulkSetup.actor.h" #include "fdbclient/RestoreWorkerInterface.actor.h" @@ -421,6 +422,11 @@ struct BackupAndParallelRestoreCorrectnessWorkload : TestWorkload { // wait(attemptDirtyRestore(self, cx, &backupAgent, StringRef(lastBackupContainer->getURL()), // randomID)); } + + // We must ensure no backup workers are running, otherwise the clear DB + // below can be picked up by backup workers and applied during restore. + wait(success(changeConfig(cx, "backup_worker_enabled:=0", true))); + // Clear DB before restore wait(runRYWTransaction(cx, [=](Reference tr) -> Future { for (auto& kvrange : self->backupRanges) tr->clear(kvrange); @@ -437,12 +443,6 @@ struct BackupAndParallelRestoreCorrectnessWorkload : TestWorkload { BackupDescription desc = wait(container->describeBackup()); ASSERT(self->usePartitionedLogs == desc.partitioned); - TraceEvent("BAFRW_Restore", randomID) - .detail("LastBackupContainer", lastBackupContainer->getURL()) - .detail("MinRestorableVersion", desc.minRestorableVersion.get()) - .detail("MaxRestorableVersion", desc.maxRestorableVersion.get()) - .detail("ContiguousLogEnd", desc.contiguousLogEnd.get()); - state Version targetVersion = -1; if (desc.maxRestorableVersion.present()) { if (deterministicRandom()->random01() < 0.1) { @@ -461,6 +461,13 @@ struct BackupAndParallelRestoreCorrectnessWorkload : TestWorkload { } } + TraceEvent("BAFRW_Restore", randomID) + .detail("LastBackupContainer", lastBackupContainer->getURL()) + .detail("MinRestorableVersion", desc.minRestorableVersion.get()) + .detail("MaxRestorableVersion", desc.maxRestorableVersion.get()) + .detail("ContiguousLogEnd", desc.contiguousLogEnd.get()) + .detail("TargetVersion", targetVersion); + state std::vector> restores; state std::vector> restoreTags; From 1476057996e42be39c43e5d0debb6857f2061634 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Sat, 11 Apr 2020 19:30:05 -0700 Subject: [PATCH 1410/1604] properly cache serialization of serverDBInfo --- fdbserver/ClusterController.actor.cpp | 136 ++++++++++++-------------- fdbserver/ServerDBInfo.h | 6 +- fdbserver/Status.actor.cpp | 62 ++++++------ fdbserver/Status.h | 2 +- fdbserver/WorkerInterface.actor.h | 1 - fdbserver/tester.actor.cpp | 4 +- fdbserver/worker.actor.cpp | 27 ++--- 7 files changed, 106 insertions(+), 132 deletions(-) diff --git a/fdbserver/ClusterController.actor.cpp b/fdbserver/ClusterController.actor.cpp index e036831480..912afe61cd 100644 --- a/fdbserver/ClusterController.actor.cpp +++ b/fdbserver/ClusterController.actor.cpp @@ -99,7 +99,7 @@ class ClusterControllerData { public: struct DBInfo { Reference> clientInfo; - Reference>> serverInfo; + Reference> serverInfo; std::map incompatibleConnections; AsyncTrigger forceMasterFailure; int64_t masterRegistrationCount; @@ -117,32 +117,29 @@ public: DBInfo() : masterRegistrationCount(0), recoveryStalled(false), forceRecovery(false), unfinishedRecoveries(0), logGenerations(0), cachePopulated(false), clientInfo( new AsyncVar( ClientDBInfo() ) ), dbInfoCount(0), - serverInfo( new AsyncVar>( CachedSerialization() ) ), + serverInfo( new AsyncVar( ServerDBInfo() ) ), db( DatabaseContext::create( clientInfo, Future(), LocalityData(), true, TaskPriority::DefaultEndpoint, true ) ) // SOMEDAY: Locality! { } void setDistributor(const DataDistributorInterface& interf) { - CachedSerialization newInfoCache = serverInfo->get(); - auto& newInfo = newInfoCache.mutate(); + auto newInfo = serverInfo->get(); newInfo.id = deterministicRandom()->randomUniqueID(); newInfo.infoGeneration = ++dbInfoCount; newInfo.distributor = interf; - serverInfo->set( newInfoCache ); + serverInfo->set( newInfo ); } void setRatekeeper(const RatekeeperInterface& interf) { - CachedSerialization newInfoCache = serverInfo->get(); - auto& newInfo = newInfoCache.mutate(); + auto newInfo = serverInfo->get(); newInfo.id = deterministicRandom()->randomUniqueID(); newInfo.infoGeneration = ++dbInfoCount; newInfo.ratekeeper = interf; - serverInfo->set( newInfoCache ); + serverInfo->set( newInfo ); } void setStorageCache(uint16_t id, const StorageServerInterface& interf) { - CachedSerialization newInfoCache = serverInfo->get(); - auto& newInfo = newInfoCache.mutate(); + auto newInfo = serverInfo->get(); bool found = false; for(auto& it : newInfo.storageCaches) { if(it.first == id) { @@ -160,12 +157,11 @@ public: newInfo.infoGeneration = ++dbInfoCount; newInfo.storageCaches.push_back(std::make_pair(id, interf)); } - serverInfo->set( newInfoCache ); + serverInfo->set( newInfo ); } void clearInterf(ProcessClass::ClassType t) { - CachedSerialization newInfoCache = serverInfo->get(); - auto& newInfo = newInfoCache.mutate(); + auto newInfo = serverInfo->get(); newInfo.id = deterministicRandom()->randomUniqueID(); newInfo.infoGeneration = ++dbInfoCount; if (t == ProcessClass::DataDistributorClass) { @@ -173,12 +169,11 @@ public: } else if (t == ProcessClass::RatekeeperClass) { newInfo.ratekeeper = Optional(); } - serverInfo->set( newInfoCache ); + serverInfo->set( newInfo ); } void clearStorageCache(uint16_t id) { - CachedSerialization newInfoCache = serverInfo->get(); - auto& newInfo = newInfoCache.mutate(); + auto newInfo = serverInfo->get(); for(auto it = newInfo.storageCaches.begin(); it != newInfo.storageCaches.end(); ++it) { if(it->first == id) { newInfo.id = deterministicRandom()->randomUniqueID(); @@ -187,7 +182,7 @@ public: break; } } - serverInfo->set( newInfoCache ); + serverInfo->set( newInfo ); } }; @@ -254,8 +249,8 @@ public: } bool isLongLivedStateless( Optional const& processId ) { - return (db.serverInfo->get().read().distributor.present() && db.serverInfo->get().read().distributor.get().locality.processId() == processId) || - (db.serverInfo->get().read().ratekeeper.present() && db.serverInfo->get().read().ratekeeper.get().locality.processId() == processId); + return (db.serverInfo->get().distributor.present() && db.serverInfo->get().distributor.get().locality.processId() == processId) || + (db.serverInfo->get().ratekeeper.present() && db.serverInfo->get().ratekeeper.get().locality.processId() == processId); } WorkerDetails getStorageWorker( RecruitStorageRequest const& req ) { @@ -983,7 +978,7 @@ public: } void checkRecoveryStalled() { - if( (db.serverInfo->get().read().recoveryState == RecoveryState::RECRUITING || db.serverInfo->get().read().recoveryState == RecoveryState::ACCEPTING_COMMITS || db.serverInfo->get().read().recoveryState == RecoveryState::ALL_LOGS_RECRUITED) && db.recoveryStalled ) { + if( (db.serverInfo->get().recoveryState == RecoveryState::RECRUITING || db.serverInfo->get().recoveryState == RecoveryState::ACCEPTING_COMMITS || db.serverInfo->get().recoveryState == RecoveryState::ALL_LOGS_RECRUITED) && db.recoveryStalled ) { if (db.config.regions.size() > 1) { auto regions = db.config.regions; if(clusterControllerDcId.get() == regions[0].dcId) { @@ -997,7 +992,7 @@ public: //FIXME: determine when to fail the cluster controller when a primaryDC has not been set bool betterMasterExists() { - const ServerDBInfo dbi = db.serverInfo->get().read(); + const ServerDBInfo dbi = db.serverInfo->get(); if(dbi.recoveryState < RecoveryState::ACCEPTING_COMMITS) { return false; @@ -1262,7 +1257,7 @@ public: ASSERT(masterProcessId.present()); if (processId == masterProcessId) return false; - auto& dbInfo = db.serverInfo->get().read(); + auto& dbInfo = db.serverInfo->get(); for (const auto& tlogset : dbInfo.logSystemConfig.tLogs) { for (const auto& tlog: tlogset.tLogs) { if (tlog.present() && tlog.interf().locality.processId() == processId) return true; @@ -1292,7 +1287,7 @@ public: std::map>, int> idUsed; updateKnownIds(&idUsed); - auto& dbInfo = db.serverInfo->get().read(); + auto& dbInfo = db.serverInfo->get(); for (const auto& tlogset : dbInfo.logSystemConfig.tLogs) { for (const auto& tlog: tlogset.tLogs) { if (tlog.present()) { @@ -1376,14 +1371,13 @@ public: serversFailed("ServersFailed", clusterControllerMetrics), serversUnfailed("ServersUnfailed", clusterControllerMetrics) { - CachedSerialization newInfoCache; - auto& serverInfo = newInfoCache.mutate(); + auto serverInfo = ServerDBInfo(); serverInfo.id = deterministicRandom()->randomUniqueID(); serverInfo.infoGeneration = ++db.dbInfoCount; serverInfo.masterLifetime.ccID = id; serverInfo.clusterInterface = ccInterface; serverInfo.myLocality = locality; - db.serverInfo->set( newInfoCache ); + db.serverInfo->set( serverInfo ); cx = openDBOnServer(db.serverInfo, TaskPriority::DefaultEndpoint, true, true); } @@ -1418,7 +1412,7 @@ ACTOR Future clusterWatchDatabase( ClusterControllerData* cluster, Cluster continue; } RecruitMasterRequest rmq; - rmq.lifetime = db->serverInfo->get().read().masterLifetime; + rmq.lifetime = db->serverInfo->get().masterLifetime; rmq.forceRecovery = db->forceRecovery; cluster->masterProcessId = masterWorker.worker.interf.locality.processId(); @@ -1438,21 +1432,20 @@ ACTOR Future clusterWatchDatabase( ClusterControllerData* cluster, Cluster db->masterRegistrationCount = 0; db->recoveryStalled = false; - CachedSerialization newInfoCache; - auto& dbInfo = newInfoCache.mutate(); + auto dbInfo = ServerDBInfo(); dbInfo.master = iMaster; dbInfo.id = deterministicRandom()->randomUniqueID(); dbInfo.infoGeneration = ++db->dbInfoCount; - dbInfo.masterLifetime = db->serverInfo->get().read().masterLifetime; + dbInfo.masterLifetime = db->serverInfo->get().masterLifetime; ++dbInfo.masterLifetime; - dbInfo.clusterInterface = db->serverInfo->get().read().clusterInterface; - dbInfo.distributor = db->serverInfo->get().read().distributor; - dbInfo.ratekeeper = db->serverInfo->get().read().ratekeeper; - dbInfo.storageCaches = db->serverInfo->get().read().storageCaches; - dbInfo.latencyBandConfig = db->serverInfo->get().read().latencyBandConfig; + dbInfo.clusterInterface = db->serverInfo->get().clusterInterface; + dbInfo.distributor = db->serverInfo->get().distributor; + dbInfo.ratekeeper = db->serverInfo->get().ratekeeper; + dbInfo.storageCaches = db->serverInfo->get().storageCaches; + dbInfo.latencyBandConfig = db->serverInfo->get().latencyBandConfig; TraceEvent("CCWDB", cluster->id).detail("Lifetime", dbInfo.masterLifetime.toString()).detail("ChangeID", dbInfo.id); - db->serverInfo->set( newInfoCache ); + db->serverInfo->set( dbInfo ); state Future spinDelay = delay(SERVER_KNOBS->MASTER_SPIN_DELAY); // Don't retry master recovery more than once per second, but don't delay the "first" recovery after more than a second of normal operation @@ -1487,8 +1480,8 @@ ACTOR Future clusterWatchDatabase( ClusterControllerData* cluster, Cluster } ACTOR Future clusterGetServerInfo(ClusterControllerData::DBInfo* db, UID knownServerInfoID, - ReplyPromise> reply) { - while(db->serverInfo->get().read().id == knownServerInfoID) { + ReplyPromise reply) { + while(db->serverInfo->get().id == knownServerInfoID) { choose { when (wait( yieldedFuture(db->serverInfo->onChange()) )) {} when (wait( delayJittered( 300 ) )) { break; } // The server might be long gone! @@ -1585,7 +1578,7 @@ void checkOutstandingStorageRequests( ClusterControllerData* self ) { } void checkBetterDDOrRK(ClusterControllerData* self) { - if (!self->masterProcessId.present() || self->db.serverInfo->get().read().recoveryState < RecoveryState::ACCEPTING_COMMITS) { + if (!self->masterProcessId.present() || self->db.serverInfo->get().recoveryState < RecoveryState::ACCEPTING_COMMITS) { return; } @@ -1617,7 +1610,7 @@ void checkBetterDDOrRK(ClusterControllerData* self) { Optional> currentRKProcessId; Optional> currentDDProcessId; - auto& db = self->db.serverInfo->get().read(); + auto& db = self->db.serverInfo->get(); bool ratekeeperHealthy = false; if (db.ratekeeper.present() && self->id_worker.count(db.ratekeeper.get().locality.processId()) && (!self->recruitingRatekeeperID.present() || (self->recruitingRatekeeperID.get() == db.ratekeeper.get().id()))) { @@ -1676,7 +1669,7 @@ ACTOR Future doCheckOutstandingRequests( ClusterControllerData* self ) { self->checkRecoveryStalled(); if (self->betterMasterExists()) { self->db.forceMasterFailure.trigger(); - TraceEvent("MasterRegistrationKill", self->id).detail("MasterId", self->db.serverInfo->get().read().master.id()); + TraceEvent("MasterRegistrationKill", self->id).detail("MasterId", self->db.serverInfo->get().master.id()); } } catch( Error &e ) { if(e.code() != error_code_no_more_servers) { @@ -2036,8 +2029,8 @@ void clusterRegisterMaster( ClusterControllerData* self, RegisterMasterRequest c //make sure the request comes from an active database auto db = &self->db; - if ( db->serverInfo->get().read().master.id() != req.id || req.registrationCount <= db->masterRegistrationCount ) { - TraceEvent("MasterRegistrationNotFound", self->id).detail("MasterId", req.id).detail("ExistingId", db->serverInfo->get().read().master.id()).detail("RegCount", req.registrationCount).detail("ExistingRegCount", db->masterRegistrationCount); + if ( db->serverInfo->get().master.id() != req.id || req.registrationCount <= db->masterRegistrationCount ) { + TraceEvent("MasterRegistrationNotFound", self->id).detail("MasterId", req.id).detail("ExistingId", db->serverInfo->get().master.id()).detail("RegCount", req.registrationCount).detail("ExistingRegCount", db->masterRegistrationCount); return; } @@ -2070,8 +2063,7 @@ void clusterRegisterMaster( ClusterControllerData* self, RegisterMasterRequest c } bool isChanged = false; - auto cachedInfo = self->db.serverInfo->get(); - auto& dbInfo = cachedInfo.mutate(); + auto dbInfo = self->db.serverInfo->get(); if (dbInfo.recoveryState != req.recoveryState) { dbInfo.recoveryState = req.recoveryState; @@ -2113,7 +2105,7 @@ void clusterRegisterMaster( ClusterControllerData* self, RegisterMasterRequest c if( isChanged ) { dbInfo.id = deterministicRandom()->randomUniqueID(); dbInfo.infoGeneration = ++self->db.dbInfoCount; - self->db.serverInfo->set( cachedInfo ); + self->db.serverInfo->set( dbInfo ); } checkOutstandingRequests(self); @@ -2176,7 +2168,7 @@ void registerWorker( RegisterWorkerRequest req, ClusterControllerData *self ) { if( info == self->id_worker.end() ) { self->id_worker[w.locality.processId()] = WorkerInfo( workerAvailabilityWatch( w, newProcessClass, self ), req.reply, req.generation, w, req.initialClass, newProcessClass, newPriorityInfo, req.degraded, req.issues ); - if (!self->masterProcessId.present() && w.locality.processId() == self->db.serverInfo->get().read().master.locality.processId()) { + if (!self->masterProcessId.present() && w.locality.processId() == self->db.serverInfo->get().master.locality.processId()) { self->masterProcessId = w.locality.processId(); } checkOutstandingRequests( self ); @@ -2202,7 +2194,7 @@ void registerWorker( RegisterWorkerRequest req, ClusterControllerData *self ) { TEST(true); // Received an old worker registration request. } - if (req.distributorInterf.present() && !self->db.serverInfo->get().read().distributor.present() && + if (req.distributorInterf.present() && !self->db.serverInfo->get().distributor.present() && self->clusterControllerDcId == req.distributorInterf.get().locality.dcId() && !self->recruitingDistributor) { const DataDistributorInterface& di = req.distributorInterf.get(); @@ -2222,7 +2214,7 @@ void registerWorker( RegisterWorkerRequest req, ClusterControllerData *self ) { req.ratekeeperInterf.get().haltRatekeeper.getReply(HaltRatekeeperRequest(self->id))); } else if (!self->recruitingRatekeeperID.present()) { const RatekeeperInterface& rki = req.ratekeeperInterf.get(); - const auto& ratekeeper = self->db.serverInfo->get().read().ratekeeper; + const auto& ratekeeper = self->db.serverInfo->get().ratekeeper; TraceEvent("CCRegisterRatekeeper", self->id).detail("RKID", rki.id()); if (ratekeeper.present() && ratekeeper.get().id() != rki.id() && self->id_worker.count(ratekeeper.get().locality.processId())) { TraceEvent("CCHaltPreviousRatekeeper", self->id).detail("RKID", ratekeeper.get().id()) @@ -2549,14 +2541,13 @@ ACTOR Future monitorServerInfoConfig(ClusterControllerData::DBInfo* db) { config = LatencyBandConfig::parse(configVal.get()); } - auto cachedInfo = db->serverInfo->get(); - auto& serverInfo = cachedInfo.mutate(); + auto serverInfo = db->serverInfo->get(); if(config != serverInfo.latencyBandConfig) { TraceEvent("LatencyBandConfigChanged").detail("Present", config.present()); serverInfo.id = deterministicRandom()->randomUniqueID(); serverInfo.infoGeneration = ++db->dbInfoCount; serverInfo.latencyBandConfig = config; - db->serverInfo->set(cachedInfo); + db->serverInfo->set(serverInfo); } state Future configChangeFuture = tr.watch(latencyBandConfigKey); @@ -2784,7 +2775,7 @@ ACTOR Future updateDatacenterVersionDifference( ClusterControllerData *sel state double lastLogTime = 0; loop { self->versionDifferenceUpdated = false; - if(self->db.serverInfo->get().read().recoveryState >= RecoveryState::ACCEPTING_COMMITS && self->db.config.usableRegions == 1) { + if(self->db.serverInfo->get().recoveryState >= RecoveryState::ACCEPTING_COMMITS && self->db.config.usableRegions == 1) { bool oldDifferenceTooLarge = !self->versionDifferenceUpdated || self->datacenterVersionDifference >= SERVER_KNOBS->MAX_VERSION_DIFFERENCE; self->versionDifferenceUpdated = true; self->datacenterVersionDifference = 0; @@ -2799,8 +2790,8 @@ ACTOR Future updateDatacenterVersionDifference( ClusterControllerData *sel state Optional primaryLog; state Optional remoteLog; - if(self->db.serverInfo->get().read().recoveryState >= RecoveryState::ALL_LOGS_RECRUITED) { - for(auto& logSet : self->db.serverInfo->get().read().logSystemConfig.tLogs) { + if(self->db.serverInfo->get().recoveryState >= RecoveryState::ALL_LOGS_RECRUITED) { + for(auto& logSet : self->db.serverInfo->get().logSystemConfig.tLogs) { if(logSet.isLocal && logSet.locality != tagLocalitySatellite) { for(auto& tLog : logSet.tLogs) { if(tLog.present()) { @@ -2901,12 +2892,12 @@ ACTOR Future startDataDistributor( ClusterControllerDa TraceEvent("CCStartDataDistributor", self->id); loop { try { - state bool no_distributor = !self->db.serverInfo->get().read().distributor.present(); - while (!self->masterProcessId.present() || self->masterProcessId != self->db.serverInfo->get().read().master.locality.processId() || self->db.serverInfo->get().read().recoveryState < RecoveryState::ACCEPTING_COMMITS) { + state bool no_distributor = !self->db.serverInfo->get().distributor.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_distributor && self->db.serverInfo->get().read().distributor.present()) { - return self->db.serverInfo->get().read().distributor.get(); + if (no_distributor && self->db.serverInfo->get().distributor.present()) { + return self->db.serverInfo->get().distributor.get(); } std::map>, int> id_used = self->getUsedIds(); @@ -2936,15 +2927,15 @@ ACTOR Future startDataDistributor( ClusterControllerDa } ACTOR Future monitorDataDistributor(ClusterControllerData *self) { - while(self->db.serverInfo->get().read().recoveryState < RecoveryState::ACCEPTING_COMMITS) { + while(self->db.serverInfo->get().recoveryState < RecoveryState::ACCEPTING_COMMITS) { wait(self->db.serverInfo->onChange()); } loop { - if ( self->db.serverInfo->get().read().distributor.present() ) { - wait( waitFailureClient( self->db.serverInfo->get().read().distributor.get().waitFailure, SERVER_KNOBS->DD_FAILURE_TIME ) ); + if ( self->db.serverInfo->get().distributor.present() ) { + wait( waitFailureClient( self->db.serverInfo->get().distributor.get().waitFailure, SERVER_KNOBS->DD_FAILURE_TIME ) ); TraceEvent("CCDataDistributorDied", self->id) - .detail("DistributorId", self->db.serverInfo->get().read().distributor.get().id()); + .detail("DistributorId", self->db.serverInfo->get().distributor.get().id()); self->db.clearInterf(ProcessClass::DataDistributorClass); } else { self->recruitingDistributor = true; @@ -2961,11 +2952,11 @@ ACTOR Future startRatekeeper(ClusterControllerData *self) { TraceEvent("CCStartRatekeeper", self->id); loop { try { - state bool no_ratekeeper = !self->db.serverInfo->get().read().ratekeeper.present(); - while (!self->masterProcessId.present() || self->masterProcessId != self->db.serverInfo->get().read().master.locality.processId() || self->db.serverInfo->get().read().recoveryState < RecoveryState::ACCEPTING_COMMITS) { + state bool no_ratekeeper = !self->db.serverInfo->get().ratekeeper.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_ratekeeper && self->db.serverInfo->get().read().ratekeeper.present()) { + if (no_ratekeeper && self->db.serverInfo->get().ratekeeper.present()) { // Existing ratekeeper registers while waiting, so skip. return Void(); } @@ -2985,7 +2976,7 @@ ACTOR Future startRatekeeper(ClusterControllerData *self) { if (interf.present()) { self->recruitRatekeeper.set(false); self->recruitingRatekeeperID = interf.get().id(); - const auto& ratekeeper = self->db.serverInfo->get().read().ratekeeper; + const auto& ratekeeper = self->db.serverInfo->get().ratekeeper; TraceEvent("CCRatekeeperRecruited", self->id).detail("Addr", worker.interf.address()).detail("RKID", interf.get().id()); if (ratekeeper.present() && ratekeeper.get().id() != interf.get().id() && self->id_worker.count(ratekeeper.get().locality.processId())) { TraceEvent("CCHaltRatekeeperAfterRecruit", self->id).detail("RKID", ratekeeper.get().id()) @@ -3010,16 +3001,16 @@ ACTOR Future startRatekeeper(ClusterControllerData *self) { } ACTOR Future monitorRatekeeper(ClusterControllerData *self) { - while(self->db.serverInfo->get().read().recoveryState < RecoveryState::ACCEPTING_COMMITS) { + while(self->db.serverInfo->get().recoveryState < RecoveryState::ACCEPTING_COMMITS) { wait(self->db.serverInfo->onChange()); } loop { - if ( self->db.serverInfo->get().read().ratekeeper.present() && !self->recruitRatekeeper.get() ) { + if ( self->db.serverInfo->get().ratekeeper.present() && !self->recruitRatekeeper.get() ) { choose { - when(wait(waitFailureClient( self->db.serverInfo->get().read().ratekeeper.get().waitFailure, SERVER_KNOBS->RATEKEEPER_FAILURE_TIME ))) { + when(wait(waitFailureClient( self->db.serverInfo->get().ratekeeper.get().waitFailure, SERVER_KNOBS->RATEKEEPER_FAILURE_TIME ))) { TraceEvent("CCRatekeeperDied", self->id) - .detail("RKID", self->db.serverInfo->get().read().ratekeeper.get().id()); + .detail("RKID", self->db.serverInfo->get().ratekeeper.get().id()); self->db.clearInterf(ProcessClass::RatekeeperClass); } when(wait(self->recruitRatekeeper.onChange())) {} @@ -3060,8 +3051,7 @@ ACTOR Future dbInfoUpdater( ClusterControllerData* self ) { updateDBInfo = self->updateDBInfo.onTrigger(); UpdateServerDBInfoRequest req; - //FIXME: cache serialization - req.dbInfo = self->db.serverInfo->get().read(); + req.serializedDbInfo = BinaryWriter::toValue(self->db.serverInfo->get(), AssumeVersion(currentProtocolVersion)); req.broadcastInfo = self->updateDBInfoEndpoints; self->updateDBInfoEndpoints.clear(); diff --git a/fdbserver/ServerDBInfo.h b/fdbserver/ServerDBInfo.h index 2c45135c27..a28c6323ae 100644 --- a/fdbserver/ServerDBInfo.h +++ b/fdbserver/ServerDBInfo.h @@ -66,20 +66,20 @@ struct ServerDBInfo { struct UpdateServerDBInfoRequest { constexpr static FileIdentifier file_identifier = 9467438; - ServerDBInfo dbInfo; + Standalone serializedDbInfo; std::vector broadcastInfo; ReplyPromise> reply; template void serialize(Ar& ar) { - serializer(ar, dbInfo, broadcastInfo, reply); + serializer(ar, serializedDbInfo, broadcastInfo, reply); } }; struct GetServerDBInfoRequest { constexpr static FileIdentifier file_identifier = 9467439; UID knownServerInfoID; - ReplyPromise< CachedSerialization > reply; + ReplyPromise reply; template void serialize(Ar& ar) { diff --git a/fdbserver/Status.actor.cpp b/fdbserver/Status.actor.cpp index 3416422167..5ad0312c36 100644 --- a/fdbserver/Status.actor.cpp +++ b/fdbserver/Status.actor.cpp @@ -549,7 +549,7 @@ struct RolesInfo { }; ACTOR static Future processStatusFetcher( - Reference>> db, std::vector workers, WorkerEvents pMetrics, + Reference> db, std::vector workers, WorkerEvents pMetrics, WorkerEvents mMetrics, WorkerEvents nMetrics, WorkerEvents errors, WorkerEvents traceFileOpenErrors, WorkerEvents programStarts, std::map> processIssues, vector> storageServers, @@ -607,18 +607,18 @@ ACTOR static Future processStatusFetcher( state RolesInfo roles; - roles.addRole("master", db->get().read().master); - roles.addRole("cluster_controller", db->get().read().clusterInterface.clientInterface); + roles.addRole("master", db->get().master); + roles.addRole("cluster_controller", db->get().clusterInterface.clientInterface); - if (db->get().read().distributor.present()) { - roles.addRole("data_distributor", db->get().read().distributor.get()); + if (db->get().distributor.present()) { + roles.addRole("data_distributor", db->get().distributor.get()); } - if (db->get().read().ratekeeper.present()) { - roles.addRole("ratekeeper", db->get().read().ratekeeper.get()); + if (db->get().ratekeeper.present()) { + roles.addRole("ratekeeper", db->get().ratekeeper.get()); } - for(auto& tLogSet : db->get().read().logSystemConfig.tLogs) { + for(auto& tLogSet : db->get().logSystemConfig.tLogs) { for(auto& it : tLogSet.logRouters) { if(it.present()) { roles.addRole("router", it.interf()); @@ -626,7 +626,7 @@ ACTOR static Future processStatusFetcher( } } - for(auto& old : db->get().read().logSystemConfig.oldTLogs) { + for(auto& old : db->get().logSystemConfig.oldTLogs) { for(auto& tLogSet : old.tLogs) { for(auto& it : tLogSet.logRouters) { if(it.present()) { @@ -669,7 +669,7 @@ ACTOR static Future processStatusFetcher( } state std::vector::const_iterator res; - state std::vector resolvers = db->get().read().resolvers; + state std::vector resolvers = db->get().resolvers; for(res = resolvers.begin(); res != resolvers.end(); ++res) { roles.addRole( "resolver", *res ); wait(yield()); @@ -1531,17 +1531,17 @@ ACTOR static Future>> getStor return results; } -ACTOR static Future>> getTLogsAndMetrics(Reference>> db, std::unordered_map address_workers) { - vector servers = db->get().read().logSystemConfig.allPresentLogs(); +ACTOR static Future>> getTLogsAndMetrics(Reference> db, std::unordered_map address_workers) { + vector servers = db->get().logSystemConfig.allPresentLogs(); vector> results = wait(getServerMetrics(servers, address_workers, std::vector{ "TLogMetrics" })); return results; } -ACTOR static Future>> getProxiesAndMetrics(Reference>> db, std::unordered_map address_workers) { +ACTOR static Future>> getProxiesAndMetrics(Reference> db, std::unordered_map address_workers) { vector> results = wait(getServerMetrics( - db->get().read().client.proxies, address_workers, std::vector{ "GRVLatencyMetrics", "CommitLatencyMetrics" })); + db->get().client.proxies, address_workers, std::vector{ "GRVLatencyMetrics", "CommitLatencyMetrics" })); return results; } @@ -1609,7 +1609,7 @@ JsonBuilderObject getPerfLimit(TraceEventFields const& ratekeeper, double transP return perfLimit; } -ACTOR static Future workloadStatusFetcher(Reference>> db, vector workers, WorkerDetails mWorker, WorkerDetails rkWorker, +ACTOR static Future workloadStatusFetcher(Reference> db, vector workers, WorkerDetails mWorker, WorkerDetails rkWorker, JsonBuilderObject *qos, JsonBuilderObject *data_overlay, std::set *incomplete_reasons, Future>>> storageServerFuture) { state JsonBuilderObject statusObj; @@ -1624,7 +1624,7 @@ ACTOR static Future workloadStatusFetcher(Referenceget().read().client.proxies) { + for (auto &p : db->get().client.proxies) { auto worker = getWorker(workersMap, p.address()); if (worker.present()) proxyStatFutures.push_back(timeoutError(worker.get().interf.eventLogRequest.getReply(EventLogRequest(LiteralStringRef("ProxyMetrics"))), 1.0)); @@ -1839,11 +1839,11 @@ ACTOR static Future clusterSummaryStatisticsFetcher(WorkerEve return statusObj; } -static JsonBuilderArray oldTlogFetcher(int* oldLogFaultTolerance, Reference>> db, std::unordered_map const& address_workers) { +static JsonBuilderArray oldTlogFetcher(int* oldLogFaultTolerance, Reference> db, std::unordered_map const& address_workers) { JsonBuilderArray oldTlogsArray; - if(db->get().read().recoveryState >= RecoveryState::ACCEPTING_COMMITS) { - for(auto it : db->get().read().logSystemConfig.oldTLogs) { + if(db->get().recoveryState >= RecoveryState::ACCEPTING_COMMITS) { + for(auto it : db->get().logSystemConfig.oldTLogs) { JsonBuilderObject statusObj; JsonBuilderArray logsObj; Optional sat_log_replication_factor, sat_log_write_anti_quorum, sat_log_fault_tolerance, log_replication_factor, log_write_anti_quorum, log_fault_tolerance, remote_log_replication_factor, remote_log_fault_tolerance; @@ -2088,7 +2088,7 @@ ACTOR Future layerStatusFetcher(Database cx, JsonBuilderArray return statusObj; } -ACTOR Future lockedStatusFetcher(Reference>> db, JsonBuilderArray *messages, std::set *incomplete_reasons) { +ACTOR Future lockedStatusFetcher(Reference> db, JsonBuilderArray *messages, std::set *incomplete_reasons) { state JsonBuilderObject statusObj; state Database cx = openDBOnServer(db, TaskPriority::DefaultEndpoint, true, false); // Open a new database connection that isn't lock-aware @@ -2160,7 +2160,7 @@ ACTOR Future> getActivePrimaryDC(Database cx, JsonBuilderArray* // constructs the cluster section of the json status output ACTOR Future clusterGetStatus( - Reference>> db, + Reference> db, Database cx, vector workers, std::vector>>> workerIssues, @@ -2180,7 +2180,7 @@ ACTOR Future clusterGetStatus( try { // Get the master Worker interface - Optional _mWorker = getWorker( workers, db->get().read().master.address() ); + Optional _mWorker = getWorker( workers, db->get().master.address() ); if (_mWorker.present()) { mWorker = _mWorker.get(); } else { @@ -2188,11 +2188,11 @@ ACTOR Future clusterGetStatus( } // Get the DataDistributor worker interface Optional _ddWorker; - if (db->get().read().distributor.present()) { - _ddWorker = getWorker( workers, db->get().read().distributor.get().address() ); + if (db->get().distributor.present()) { + _ddWorker = getWorker( workers, db->get().distributor.get().address() ); } - if (!db->get().read().distributor.present() || !_ddWorker.present()) { + if (!db->get().distributor.present() || !_ddWorker.present()) { messages.push_back(JsonString::makeMessage("unreachable_dataDistributor_worker", "Unable to locate the data distributor worker.")); } else { ddWorker = _ddWorker.get(); @@ -2200,11 +2200,11 @@ ACTOR Future clusterGetStatus( // Get the Ratekeeper worker interface Optional _rkWorker; - if (db->get().read().ratekeeper.present()) { - _rkWorker = getWorker( workers, db->get().read().ratekeeper.get().address() ); + if (db->get().ratekeeper.present()) { + _rkWorker = getWorker( workers, db->get().ratekeeper.get().address() ); } - if (!db->get().read().ratekeeper.present() || !_rkWorker.present()) { + if (!db->get().ratekeeper.present() || !_rkWorker.present()) { messages.push_back(JsonString::makeMessage("unreachable_ratekeeper_worker", "Unable to locate the ratekeeper worker.")); } else { rkWorker = _rkWorker.get(); @@ -2262,8 +2262,8 @@ ACTOR Future clusterGetStatus( state WorkerEvents programStarts = workerEventsVec[5].present() ? workerEventsVec[5].get().first : WorkerEvents(); state JsonBuilderObject statusObj; - if(db->get().read().recoveryCount > 0) { - statusObj["generation"] = db->get().read().recoveryCount; + if(db->get().recoveryCount > 0) { + statusObj["generation"] = db->get().recoveryCount; } state std::map> processIssues = @@ -2346,7 +2346,7 @@ ACTOR Future clusterGetStatus( state std::vector workerStatuses = wait(getAll(futures2)); int oldLogFaultTolerance = 100; - if(db->get().read().recoveryState >= RecoveryState::ACCEPTING_COMMITS && db->get().read().logSystemConfig.oldTLogs.size() > 0) { + if(db->get().recoveryState >= RecoveryState::ACCEPTING_COMMITS && db->get().logSystemConfig.oldTLogs.size() > 0) { statusObj["old_logs"] = oldTlogFetcher(&oldLogFaultTolerance, db, address_workers); } diff --git a/fdbserver/Status.h b/fdbserver/Status.h index 15c8e38025..95be0e55d4 100644 --- a/fdbserver/Status.h +++ b/fdbserver/Status.h @@ -27,7 +27,7 @@ #include "fdbserver/MasterInterface.h" #include "fdbclient/ClusterInterface.h" -Future clusterGetStatus( Reference>> const& db, Database const& cx, vector const& workers, std::vector>>> const& workerIssues, +Future clusterGetStatus( Reference> const& db, Database const& cx, vector const& workers, std::vector>>> const& workerIssues, std::map>* const& clientStatus, ServerCoordinators const& coordinators, std::vector const& incompatibleConnections, Version const& datacenterVersionDifference ); #endif diff --git a/fdbserver/WorkerInterface.actor.h b/fdbserver/WorkerInterface.actor.h index 08824c20bf..e94badb4ff 100644 --- a/fdbserver/WorkerInterface.actor.h +++ b/fdbserver/WorkerInterface.actor.h @@ -691,7 +691,6 @@ void endRole(const Role &role, UID id, std::string reason, bool ok = true, Error struct ServerDBInfo; class Database openDBOnServer( Reference> const& db, TaskPriority taskID = TaskPriority::DefaultEndpoint, bool enableLocalityLoadBalance = true, bool lockAware = false ); -class Database openDBOnServer( Reference>> const& db, TaskPriority taskID = TaskPriority::DefaultEndpoint, bool enableLocalityLoadBalance = true, bool lockAware = false ); ACTOR Future extractClusterInterface(Reference>> a, Reference>> b); diff --git a/fdbserver/tester.actor.cpp b/fdbserver/tester.actor.cpp index 988b78d6df..f675acebc9 100644 --- a/fdbserver/tester.actor.cpp +++ b/fdbserver/tester.actor.cpp @@ -1030,8 +1030,8 @@ ACTOR Future monitorServerDBInfo(Referenceget().id; choose { - when( CachedSerialization ni = wait( ccInterface->get().present() ? brokenPromiseToNever( ccInterface->get().get().getServerDBInfo.getReply( req ) ) : Never() ) ) { - ServerDBInfo localInfo = ni.read(); + when( ServerDBInfo _localInfo = wait( ccInterface->get().present() ? brokenPromiseToNever( ccInterface->get().get().getServerDBInfo.getReply( req ) ) : Never() ) ) { + ServerDBInfo localInfo = _localInfo; TraceEvent("GotServerDBInfoChange").detail("ChangeID", localInfo.id).detail("MasterID", localInfo.master.id()) .detail("RatekeeperID", localInfo.ratekeeper.present() ? localInfo.ratekeeper.get().id() : UID()) .detail("DataDistributorID", localInfo.distributor.present() ? localInfo.distributor.get().id() : UID()); diff --git a/fdbserver/worker.actor.cpp b/fdbserver/worker.actor.cpp index e87d2a3915..1d04e5a6e4 100644 --- a/fdbserver/worker.actor.cpp +++ b/fdbserver/worker.actor.cpp @@ -117,27 +117,11 @@ ACTOR static Future extractClientInfo( Reference> d } } -ACTOR static Future extractClientInfo( Reference>> db, Reference> info ) { - state std::vector lastProxyUIDs; - state std::vector lastProxies; - loop { - ClientDBInfo ni = db->get().read().client; - shrinkProxyList(ni, lastProxyUIDs, lastProxies); - info->set( ni ); - wait( db->onChange() ); - } -} - Database openDBOnServer( Reference> const& db, TaskPriority taskID, bool enableLocalityLoadBalance, bool lockAware ) { Reference> info( new AsyncVar ); return DatabaseContext::create( info, extractClientInfo(db, info), enableLocalityLoadBalance ? db->get().myLocality : LocalityData(), enableLocalityLoadBalance, taskID, lockAware ); } -Database openDBOnServer( Reference>> const& db, TaskPriority taskID, bool enableLocalityLoadBalance, bool lockAware ) { - Reference> info( new AsyncVar ); - return DatabaseContext::create( info, extractClientInfo(db, info), enableLocalityLoadBalance ? db->get().read().myLocality : LocalityData(), enableLocalityLoadBalance, taskID, lockAware ); -} - struct ErrorInfo { Error error; const Role &role; @@ -1061,17 +1045,18 @@ ACTOR Future workerServer( loop choose { when( UpdateServerDBInfoRequest req = waitNext( interf.updateServerDBInfo.getFuture() ) ) { + ServerDBInfo localInfo = BinaryReader::fromStringRef(req.serializedDbInfo, AssumeVersion(currentProtocolVersion)); + localInfo.myLocality = locality; + Optional notUpdated; - if(!ccInterface->get().present() || req.dbInfo.clusterInterface != ccInterface->get().get() || (req.dbInfo.infoGeneration < dbInfo->get().infoGeneration && dbInfo->get().clusterInterface == ccInterface->get().get())) { + if(!ccInterface->get().present() || localInfo.clusterInterface != ccInterface->get().get() || (localInfo.infoGeneration < dbInfo->get().infoGeneration && dbInfo->get().clusterInterface == ccInterface->get().get())) { notUpdated = interf.updateServerDBInfo.getEndpoint(); } - if(ccInterface->get().present() && req.dbInfo.clusterInterface == ccInterface->get().get() && (req.dbInfo.infoGeneration > dbInfo->get().infoGeneration || dbInfo->get().clusterInterface != ccInterface->get().get())) { - ServerDBInfo localInfo = req.dbInfo; + if(ccInterface->get().present() && localInfo.clusterInterface == ccInterface->get().get() && (localInfo.infoGeneration > dbInfo->get().infoGeneration || dbInfo->get().clusterInterface != ccInterface->get().get())) { + TraceEvent("GotServerDBInfoChange").detail("ChangeID", localInfo.id).detail("MasterID", localInfo.master.id()) .detail("RatekeeperID", localInfo.ratekeeper.present() ? localInfo.ratekeeper.get().id() : UID()) .detail("DataDistributorID", localInfo.distributor.present() ? localInfo.distributor.get().id() : UID()); - - localInfo.myLocality = locality; dbInfo->set(localInfo); } errorForwarders.add(success(broadcastDBInfoRequest(req, SERVER_KNOBS->DBINFO_SEND_AMOUNT, notUpdated, true))); From e5ec7f28003307aeacdad750077e1b70b4b44a44 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Sat, 11 Apr 2020 20:05:03 -0700 Subject: [PATCH 1411/1604] do not broadcast obsolete serverDBInfo --- fdbserver/worker.actor.cpp | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/fdbserver/worker.actor.cpp b/fdbserver/worker.actor.cpp index 1d04e5a6e4..21d22e7114 100644 --- a/fdbserver/worker.actor.cpp +++ b/fdbserver/worker.actor.cpp @@ -1048,18 +1048,24 @@ ACTOR Future workerServer( ServerDBInfo localInfo = BinaryReader::fromStringRef(req.serializedDbInfo, AssumeVersion(currentProtocolVersion)); localInfo.myLocality = locality; - Optional notUpdated; - if(!ccInterface->get().present() || localInfo.clusterInterface != ccInterface->get().get() || (localInfo.infoGeneration < dbInfo->get().infoGeneration && dbInfo->get().clusterInterface == ccInterface->get().get())) { - notUpdated = interf.updateServerDBInfo.getEndpoint(); + if(ccInterface->get().present() && localInfo.infoGeneration < dbInfo->get().infoGeneration && dbInfo->get().clusterInterface == ccInterface->get().get()) { + std::vector rep = req.broadcastInfo; + rep.push_back(interf.updateServerDBInfo.getEndpoint()); + req.reply.send(rep); + } else { + Optional notUpdated; + if(!ccInterface->get().present() || localInfo.clusterInterface != ccInterface->get().get()) { + notUpdated = interf.updateServerDBInfo.getEndpoint(); + } + if(ccInterface->get().present() && localInfo.clusterInterface == ccInterface->get().get() && (localInfo.infoGeneration > dbInfo->get().infoGeneration || dbInfo->get().clusterInterface != ccInterface->get().get())) { + + TraceEvent("GotServerDBInfoChange").detail("ChangeID", localInfo.id).detail("MasterID", localInfo.master.id()) + .detail("RatekeeperID", localInfo.ratekeeper.present() ? localInfo.ratekeeper.get().id() : UID()) + .detail("DataDistributorID", localInfo.distributor.present() ? localInfo.distributor.get().id() : UID()); + dbInfo->set(localInfo); + } + errorForwarders.add(success(broadcastDBInfoRequest(req, SERVER_KNOBS->DBINFO_SEND_AMOUNT, notUpdated, true))); } - if(ccInterface->get().present() && localInfo.clusterInterface == ccInterface->get().get() && (localInfo.infoGeneration > dbInfo->get().infoGeneration || dbInfo->get().clusterInterface != ccInterface->get().get())) { - - TraceEvent("GotServerDBInfoChange").detail("ChangeID", localInfo.id).detail("MasterID", localInfo.master.id()) - .detail("RatekeeperID", localInfo.ratekeeper.present() ? localInfo.ratekeeper.get().id() : UID()) - .detail("DataDistributorID", localInfo.distributor.present() ? localInfo.distributor.get().id() : UID()); - dbInfo->set(localInfo); - } - errorForwarders.add(success(broadcastDBInfoRequest(req, SERVER_KNOBS->DBINFO_SEND_AMOUNT, notUpdated, true))); } when( RebootRequest req = waitNext( interf.clientInterface.reboot.getFuture() ) ) { state RebootRequest rebootReq = req; From 8f78912483b7da08825240408f64f6e057e6fc56 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Sat, 11 Apr 2020 20:54:17 -0700 Subject: [PATCH 1412/1604] knobified parameter --- fdbserver/Knobs.cpp | 1 + fdbserver/Knobs.h | 1 + fdbserver/worker.actor.cpp | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index 298c3500ff..463d3bd601 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -530,6 +530,7 @@ void ServerKnobs::initialize(bool randomize, ClientKnobs* clientKnobs, bool isSi init( DEGRADED_WARNING_RESET_DELAY, 7*24*60*60 ); init( TRACE_LOG_FLUSH_FAILURE_CHECK_INTERVAL_SECONDS, 10 ); init( TRACE_LOG_PING_TIMEOUT_SECONDS, 5.0 ); + init( DBINFO_FAILED_DELAY, 1.0 ); // Test harness init( WORKER_POLL_DELAY, 1.0 ); diff --git a/fdbserver/Knobs.h b/fdbserver/Knobs.h index 47ff8a886e..246276d175 100644 --- a/fdbserver/Knobs.h +++ b/fdbserver/Knobs.h @@ -465,6 +465,7 @@ public: double DEGRADED_WARNING_RESET_DELAY; int64_t TRACE_LOG_FLUSH_FAILURE_CHECK_INTERVAL_SECONDS; double TRACE_LOG_PING_TIMEOUT_SECONDS; + double DBINFO_FAILED_DELAY; // Test harness double WORKER_POLL_DELAY; diff --git a/fdbserver/worker.actor.cpp b/fdbserver/worker.actor.cpp index 21d22e7114..bf926bcd3f 100644 --- a/fdbserver/worker.actor.cpp +++ b/fdbserver/worker.actor.cpp @@ -68,7 +68,7 @@ extern IKeyValueStore* keyValueStoreCompressTestData(IKeyValueStore* store); #endif ACTOR Future> tryDBInfoBroadcast(RequestStream stream, UpdateServerDBInfoRequest req) { - ErrorOr> rep = wait( stream.getReplyUnlessFailedFor(req, 1.0, 0) ); + ErrorOr> rep = wait( stream.getReplyUnlessFailedFor(req, SERVER_KNOBS->DBINFO_FAILED_DELAY, 0) ); if(rep.present()) { return rep.get(); } From 9b5130194d4b8f9c589fd6990a3cc101f40e9251 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Sat, 11 Apr 2020 21:05:30 -0700 Subject: [PATCH 1413/1604] avoid updating the same endpoint multiple times --- fdbserver/ClusterController.actor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/fdbserver/ClusterController.actor.cpp b/fdbserver/ClusterController.actor.cpp index 912afe61cd..dcd2a8211c 100644 --- a/fdbserver/ClusterController.actor.cpp +++ b/fdbserver/ClusterController.actor.cpp @@ -3038,6 +3038,7 @@ ACTOR Future dbInfoUpdater( ClusterControllerData* self ) { self->updateDBInfoEndpoints.push_back(it.second.details.interf.updateServerDBInfo.getEndpoint()); } } else { + uniquify(self->updateDBInfoEndpoints); for(int i = 0; i < self->updateDBInfoEndpoints.size(); i++) { if(self->removedDBInfoEndpoints.count(self->updateDBInfoEndpoints[i])) { self->updateDBInfoEndpoints[i] = self->updateDBInfoEndpoints.back(); From 87b8aae3e55a71d85c871942f279c3cf7ff44b38 Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Sat, 11 Apr 2020 22:36:11 -0700 Subject: [PATCH 1414/1604] Buggify master recruit backup worker delay --- fdbserver/Knobs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index 8e55e23abd..d4b915992f 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -359,7 +359,7 @@ void ServerKnobs::initialize(bool randomize, ClientKnobs* clientKnobs, bool isSi init( PROVISIONAL_START_DELAY, 1.0 ); init( PROVISIONAL_MAX_DELAY, 60.0 ); init( PROVISIONAL_DELAY_GROWTH, 1.5 ); - init( SECONDS_BEFORE_RECRUIT_BACKUP_WORKER, 4.0 ); + init( SECONDS_BEFORE_RECRUIT_BACKUP_WORKER, 4.0 ); if( randomize && BUGGIFY ) SECONDS_BEFORE_RECRUIT_BACKUP_WORKER = deterministicRandom()->random01() * 8; // Resolver init( SAMPLE_OFFSET_PER_KEY, 100 ); From dbc9c231936c0f5dc3e1366ff894fd37dc98e581 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Sat, 11 Apr 2020 22:31:55 -0700 Subject: [PATCH 1415/1604] FastRestore:Loader:Send mutations at different versions in the same message to appliers This increases the bandwidth sent from loaders to appliers. --- fdbclient/FDBTypes.h | 5 ++++ fdbclient/RestoreWorkerInterface.actor.h | 24 ++++++++--------- fdbserver/Knobs.cpp | 3 ++- fdbserver/Knobs.h | 1 + fdbserver/RestoreApplier.actor.cpp | 23 +++++++--------- fdbserver/RestoreApplier.actor.h | 5 ++-- fdbserver/RestoreLoader.actor.cpp | 34 ++++++++++++------------ fdbserver/RestoreUtil.h | 2 +- 8 files changed, 50 insertions(+), 47 deletions(-) diff --git a/fdbclient/FDBTypes.h b/fdbclient/FDBTypes.h index 3f1e8ffb51..435a5e7ca7 100644 --- a/fdbclient/FDBTypes.h +++ b/fdbclient/FDBTypes.h @@ -820,6 +820,11 @@ struct LogMessageVersion { explicit LogMessageVersion(Version version) : version(version), sub(0) {} LogMessageVersion() : version(0), sub(0) {} bool empty() const { return (version == 0) && (sub == 0); } + + template + void serialize(Ar& ar) { + serializer(ar, version, sub); + } }; struct AddressExclusion { diff --git a/fdbclient/RestoreWorkerInterface.actor.h b/fdbclient/RestoreWorkerInterface.actor.h index 8bbf4d10a4..8c8efea343 100644 --- a/fdbclient/RestoreWorkerInterface.actor.h +++ b/fdbclient/RestoreWorkerInterface.actor.h @@ -460,31 +460,31 @@ struct RestoreSendVersionedMutationsRequest : TimedRequest { int batchIndex; // version batch index RestoreAsset asset; // Unique identifier for the current restore asset - Version prevVersion, version; // version is the commitVersion of the mutation vector. + Version msgIndex; // Monitonically increasing index of mutation messages bool isRangeFile; - MutationsVec mutations; // All mutations at the same version parsed by one loader - SubSequenceVec subs; // Sub-sequence number for mutations + MutationsVec mutations; // Mutations that may be at different versions parsed by one loader + LogMessageVersionVec mVersions; // (version, subversion) of each mutation in mutations field ReplyPromise reply; RestoreSendVersionedMutationsRequest() = default; - explicit RestoreSendVersionedMutationsRequest(int batchIndex, const RestoreAsset& asset, Version prevVersion, - Version version, bool isRangeFile, MutationsVec mutations, - SubSequenceVec subs) - : batchIndex(batchIndex), asset(asset), prevVersion(prevVersion), version(version), isRangeFile(isRangeFile), - mutations(mutations), subs(subs) {} + explicit RestoreSendVersionedMutationsRequest(int batchIndex, const RestoreAsset& asset, Version msgIndex, + bool isRangeFile, MutationsVec mutations, + LogMessageVersionVec mVersions) + : batchIndex(batchIndex), asset(asset), msgIndex(msgIndex), isRangeFile(isRangeFile), mutations(mutations), + mVersions(mVersions) {} std::string toString() { std::stringstream ss; - ss << "VersionBatchIndex:" << batchIndex << "RestoreAsset:" << asset.toString() - << " prevVersion:" << prevVersion << " version:" << version << " isRangeFile:" << isRangeFile - << " mutations.size:" << mutations.size() << " subs.size:" << subs.size(); + ss << "VersionBatchIndex:" << batchIndex << "RestoreAsset:" << asset.toString() << " msgIndex:" << msgIndex + << " isRangeFile:" << isRangeFile << " mutations.size:" << mutations.size() + << " mVersions.size:" << mVersions.size(); return ss.str(); } template void serialize(Ar& ar) { - serializer(ar, batchIndex, asset, prevVersion, version, isRangeFile, mutations, subs, reply); + serializer(ar, batchIndex, asset, msgIndex, isRangeFile, mutations, mVersions, reply); } }; diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index 8e55e23abd..54fea98a9c 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -201,7 +201,7 @@ void ServerKnobs::initialize(bool randomize, ClientKnobs* clientKnobs, bool isSi init( STORAGE_METRICS_POLLING_DELAY, 2.0 ); if( randomize && BUGGIFY ) STORAGE_METRICS_POLLING_DELAY = 15.0; init( STORAGE_METRICS_RANDOM_DELAY, 0.2 ); init( AVAILABLE_SPACE_RATIO_CUTOFF, 0.05 ); - init( DESIRED_TEAMS_PER_SERVER, 5 ); if( randomize && BUGGIFY ) DESIRED_TEAMS_PER_SERVER = deterministicRandom()->randomInt(1, 10); + init( DESIRED_TEAMS_PER_SERVER, 5 ); DESIRED_TEAMS_PER_SERVER = deterministicRandom()->randomInt(1, 10); init( MAX_TEAMS_PER_SERVER, 5*DESIRED_TEAMS_PER_SERVER ); init( DD_SHARD_SIZE_GRANULARITY, 5000000 ); init( DD_SHARD_SIZE_GRANULARITY_SIM, 500000 ); if( randomize && BUGGIFY ) DD_SHARD_SIZE_GRANULARITY_SIM = 0; @@ -581,6 +581,7 @@ void ServerKnobs::initialize(bool randomize, ClientKnobs* clientKnobs, bool isSi init( FASTRESTORE_HEARTBEAT_DELAY, 10 ); if( randomize && BUGGIFY ) { FASTRESTORE_HEARTBEAT_DELAY = deterministicRandom()->random01() * 120 + 2; } init( FASTRESTORE_HEARTBEAT_MAX_DELAY, 10 ); if( randomize && BUGGIFY ) { FASTRESTORE_HEARTBEAT_MAX_DELAY = FASTRESTORE_HEARTBEAT_DELAY * 10; } init( FASTRESTORE_APPLIER_FETCH_KEYS_SIZE, 100 ); if( randomize && BUGGIFY ) { FASTRESTORE_APPLIER_FETCH_KEYS_SIZE = deterministicRandom()->random01() * 10240 + 1; } + init( FASTRESTORE_LOADER_SEND_MUTATION_MSG_BYTES, 1.0 * 1024.0 * 1024.0 ); if( randomize && BUGGIFY ) { FASTRESTORE_LOADER_SEND_MUTATION_MSG_BYTES = deterministicRandom()->random01() * 10.0 * 1024.0 * 1024.0 + 1; } // clang-format on diff --git a/fdbserver/Knobs.h b/fdbserver/Knobs.h index 9e7d8b384f..062824c666 100644 --- a/fdbserver/Knobs.h +++ b/fdbserver/Knobs.h @@ -519,6 +519,7 @@ public: int64_t FASTRESTORE_HEARTBEAT_DELAY; // interval for master to ping loaders and appliers int64_t FASTRESTORE_HEARTBEAT_MAX_DELAY; // master claim a node is down if no heart beat from the node for this delay int64_t FASTRESTORE_APPLIER_FETCH_KEYS_SIZE; // number of keys to fetch in a txn on applier + int64_t FASTRESTORE_LOADER_SEND_MUTATION_MSG_BYTES; // desired size of mutation message sent from loader to appliers ServerKnobs(); void initialize(bool randomize = false, ClientKnobs* clientKnobs = NULL, bool isSimulated = false); diff --git a/fdbserver/RestoreApplier.actor.cpp b/fdbserver/RestoreApplier.actor.cpp index 2a2d190036..263a71a505 100644 --- a/fdbserver/RestoreApplier.actor.cpp +++ b/fdbserver/RestoreApplier.actor.cpp @@ -110,35 +110,29 @@ ACTOR static Future handleSendMutationVectorRequest(RestoreSendVersionedMu state Reference batchData = self->batch[req.batchIndex]; // Assume: processedFileState[req.asset] will not be erased while the actor is active. // Note: Insert new items into processedFileState will not invalidate the reference. - state NotifiedVersion& curFilePos = batchData->processedFileState[req.asset]; + state NotifiedVersion& curMsgIndex = batchData->processedFileState[req.asset]; TraceEvent(SevDebug, "FastRestoreApplierPhaseReceiveMutations", self->id()) .detail("BatchIndex", req.batchIndex) .detail("RestoreAsset", req.asset.toString()) - .detail("ProcessedFileVersion", curFilePos.get()) + .detail("RestoreAssetMesssageIndex", curMsgIndex.get()) .detail("Request", req.toString()) .detail("CurrentMemory", getSystemStatistics().processMemory) .detail("PreviousVersionBatchState", batchData->vbState.get()); wait(isSchedulable(self, req.batchIndex, __FUNCTION__)); - wait(curFilePos.whenAtLeast(req.prevVersion)); + wait(curMsgIndex.whenAtLeast(req.msgIndex - 1)); batchData->vbState = ApplierVersionBatchState::RECEIVE_MUTATIONS; state bool isDuplicated = true; - if (curFilePos.get() == req.prevVersion) { + if (curMsgIndex.get() == req.msgIndex - 1) { isDuplicated = false; - const Version commitVersion = req.version; - // Sanity check: mutations in range file is in [beginVersion, endVersion); - // mutations in log file is in [beginVersion, endVersion], both inclusive. - ASSERT(commitVersion >= req.asset.beginVersion); - // Loader sends the endVersion to ensure all useful versions are sent - ASSERT(commitVersion <= req.asset.endVersion); - ASSERT(req.mutations.size() == req.subs.size()); + ASSERT(req.mutations.size() == req.mVersions.size()); for (int mIndex = 0; mIndex < req.mutations.size(); mIndex++) { const MutationRef& mutation = req.mutations[mIndex]; - const LogMessageVersion mutationVersion(commitVersion, req.subs[mIndex]); + const LogMessageVersion mutationVersion(req.mVersions[mIndex]); TraceEvent(SevFRMutationInfo, "FastRestoreApplierPhaseReceiveMutations", self->id()) .detail("RestoreAsset", req.asset.toString()) .detail("Version", mutationVersion.toString()) @@ -149,6 +143,7 @@ ACTOR static Future handleSendMutationVectorRequest(RestoreSendVersionedMu batchData->counters.receivedMutations += 1; batchData->counters.receivedAtomicOps += isAtomicOp((MutationRef::Type)mutation.type) ? 1 : 0; // Sanity check + ASSERT_WE_THINK(req.asset.isInVersionRange(mutationVersion.version)); ASSERT_WE_THINK(req.asset.isInKeyRange(mutation)); // Note: Log and range mutations may be delivered out of order. Can we handle it? @@ -157,14 +152,14 @@ ACTOR static Future handleSendMutationVectorRequest(RestoreSendVersionedMu ASSERT(mutation.type != MutationRef::SetVersionstampedKey && mutation.type != MutationRef::SetVersionstampedValue); } - curFilePos.set(req.version); + curMsgIndex.set(req.msgIndex); } req.reply.send(RestoreCommonReply(self->id(), isDuplicated)); TraceEvent(SevDebug, "FastRestoreApplierPhaseReceiveMutationsDone", self->id()) .detail("BatchIndex", req.batchIndex) .detail("RestoreAsset", req.asset.toString()) - .detail("ProcessedFileVersion", curFilePos.get()) + .detail("ProcessedMessageIndex", curMsgIndex.get()) .detail("Request", req.toString()); return Void(); } diff --git a/fdbserver/RestoreApplier.actor.h b/fdbserver/RestoreApplier.actor.h index 06b65181ba..813867b1d1 100644 --- a/fdbserver/RestoreApplier.actor.h +++ b/fdbserver/RestoreApplier.actor.h @@ -107,8 +107,9 @@ struct StagingKey { // TODO: Add SevError here TraceEvent("SameVersion") .detail("Version", version.toString()) - .detail("Mutation", m.toString()) - .detail("NewVersion", newVersion.toString()); + .detail("NewVersion", newVersion.toString()) + .detail("OldMutation", it->second.toString()) + .detail("NewMutation", m.toString()); ASSERT(it->second.type == m.type && it->second.param1 == m.param1 && it->second.param2 == m.param2); } } diff --git a/fdbserver/RestoreLoader.actor.cpp b/fdbserver/RestoreLoader.actor.cpp index 664391ae42..9ce047af97 100644 --- a/fdbserver/RestoreLoader.actor.cpp +++ b/fdbserver/RestoreLoader.actor.cpp @@ -413,8 +413,9 @@ ACTOR Future sendMutationsToApplier(VersionedMutationsMap* pkvOps, int bat state int kvCount = 0; state int splitMutationIndex = 0; state std::vector> requests; - state Version prevVersion = 0; // startVersion + state Version msgIndex = 1; // Monotonically increased index for send message, must start at 1 state std::vector applierIDs = getApplierIDs(*pRangeToApplier); + state double msgSize = 0; // size of mutations in the message TraceEvent("FastRestoreLoaderSendMutationToApplier") .detail("IsRangeFile", isRangeFile) @@ -439,11 +440,11 @@ ACTOR Future sendMutationsToApplier(VersionedMutationsMap* pkvOps, int bat // applierMutationsBuffer is the mutation vector to be sent to each applier // applierMutationsSize is buffered mutation vector size for each applier state std::map applierMutationsBuffer; - state std::map applierSubsBuffer; + state std::map applierVersionsBuffer; state std::map applierMutationsSize; for (auto& applierID : applierIDs) { applierMutationsBuffer[applierID] = MutationsVec(); - applierSubsBuffer[applierID] = SubSequenceVec(); + applierVersionsBuffer[applierID] = LogMessageVersionVec(); applierMutationsSize[applierID] = 0.0; } for (kvOp = kvOps.begin(); kvOp != kvOps.end(); kvOp++) { @@ -458,7 +459,6 @@ ACTOR Future sendMutationsToApplier(VersionedMutationsMap* pkvOps, int bat Standalone> nodeIDs; // Because using a vector of mutations causes overhead, and the range mutation should happen rarely; // We handle the range mutation and key mutation differently for the benefit of avoiding memory copy - // WARNING: The splitMutation() may have bugs splitMutation(pRangeToApplier, kvm, mvector.arena(), mvector.contents(), nodeIDs.arena(), nodeIDs.contents()); ASSERT(mvector.size() == nodeIDs.size()); @@ -475,16 +475,15 @@ ACTOR Future sendMutationsToApplier(VersionedMutationsMap* pkvOps, int bat for (splitMutationIndex = 0; splitMutationIndex < mvector.size(); splitMutationIndex++) { MutationRef mutation = mvector[splitMutationIndex]; UID applierID = nodeIDs[splitMutationIndex]; - // printf("SPLITTED MUTATION: %d: mutation:%s applierID:%s\n", splitMutationIndex, - // mutation.toString().c_str(), applierID.toString().c_str()); if (debugMutation("RestoreLoader", commitVersion.version, mutation)) { TraceEvent("SplittedMutation") .detail("Version", commitVersion.toString()) .detail("Mutation", mutation.toString()); } applierMutationsBuffer[applierID].push_back_deep(applierMutationsBuffer[applierID].arena(), mutation); - applierSubsBuffer[applierID].push_back(applierSubsBuffer[applierID].arena(), commitVersion.sub); + applierVersionsBuffer[applierID].push_back(applierVersionsBuffer[applierID].arena(), commitVersion); applierMutationsSize[applierID] += mutation.expectedSize(); + msgSize += mutation.expectedSize(); kvCount++; } @@ -502,8 +501,9 @@ ACTOR Future sendMutationsToApplier(VersionedMutationsMap* pkvOps, int bat .detail("Mutation", kvm.toString()); } applierMutationsBuffer[applierID].push_back_deep(applierMutationsBuffer[applierID].arena(), kvm); - applierSubsBuffer[applierID].push_back(applierSubsBuffer[applierID].arena(), commitVersion.sub); + applierVersionsBuffer[applierID].push_back(applierVersionsBuffer[applierID].arena(), commitVersion); applierMutationsSize[applierID] += kvm.expectedSize(); + msgSize += kvm.expectedSize(); } } // Mutations at the same LogMessageVersion @@ -511,26 +511,27 @@ ACTOR Future sendMutationsToApplier(VersionedMutationsMap* pkvOps, int bat // changing the version comparison below. auto next = std::next(kvOp, 1); if (next == kvOps.end() || commitVersion.version < next->first.version) { + // if (next == kvOps.end() || msgSize >= SERVER_KNOBS->FASTRESTORE_LOADER_SEND_MUTATION_MSG_BYTES) { // TODO: Sanity check each asset has been received exactly once! // Send the mutations to appliers for each version for (const UID& applierID : applierIDs) { - requests.emplace_back(applierID, RestoreSendVersionedMutationsRequest( - batchIndex, asset, prevVersion, commitVersion.version, isRangeFile, - applierMutationsBuffer[applierID], applierSubsBuffer[applierID])); + requests.emplace_back(applierID, + RestoreSendVersionedMutationsRequest(batchIndex, asset, msgIndex, isRangeFile, + applierMutationsBuffer[applierID], + applierVersionsBuffer[applierID])); } TraceEvent(SevDebug, "FastRestoreLoaderSendMutationToApplier") - .detail("PrevVersion", prevVersion) - .detail("CommitVersion", commitVersion.toString()) + .detail("MessageIndex", msgIndex) .detail("RestoreAsset", asset.toString()) .detail("Requests", requests.size()); - ASSERT(prevVersion < commitVersion.version); - prevVersion = commitVersion.version; wait(sendBatchRequests(&RestoreApplierInterface::sendMutationVector, *pApplierInterfaces, requests, TaskPriority::RestoreLoaderSendMutations)); + msgIndex++; + msgSize = 0; requests.clear(); for (auto& applierID : applierIDs) { applierMutationsBuffer[applierID] = MutationsVec(); - applierSubsBuffer[applierID] = SubSequenceVec(); + applierVersionsBuffer[applierID] = LogMessageVersionVec(); applierMutationsSize[applierID] = 0.0; } } @@ -540,7 +541,6 @@ ACTOR Future sendMutationsToApplier(VersionedMutationsMap* pkvOps, int bat return Void(); } -// TODO: Add a unit test for this function void splitMutation(std::map* pRangeToApplier, MutationRef m, Arena& mvector_arena, VectorRef& mvector, Arena& nodeIDs_arena, VectorRef& nodeIDs) { TraceEvent(SevWarn, "FastRestoreSplitMutation").detail("Mutation", m.toString()); diff --git a/fdbserver/RestoreUtil.h b/fdbserver/RestoreUtil.h index 1018f787ad..733083d7b8 100644 --- a/fdbserver/RestoreUtil.h +++ b/fdbserver/RestoreUtil.h @@ -39,7 +39,7 @@ #define SevFRMutationInfo SevInfo using MutationsVec = Standalone>; -using SubSequenceVec = Standalone>; +using LogMessageVersionVec = Standalone>; enum class RestoreRole { Invalid = 0, Master = 1, Loader, Applier }; BINARY_SERIALIZABLE(RestoreRole); From befcfcd3954dd3495f68cf38b3c071640fd287be Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sun, 12 Apr 2020 17:51:21 -0700 Subject: [PATCH 1416/1604] Add design doc for TLog forward compatibility. --- design/tlog-forward-compatibility.md.html | 217 ++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 design/tlog-forward-compatibility.md.html diff --git a/design/tlog-forward-compatibility.md.html b/design/tlog-forward-compatibility.md.html new file mode 100644 index 0000000000..9ba8f4ba6d --- /dev/null +++ b/design/tlog-forward-compatibility.md.html @@ -0,0 +1,217 @@ + + +# Forward Compatibility for Transaction Logs + +## Background + +A repeated concern with adopting FoundationDB has been that upgrades are one +way, with no supported rollback. If one were to upgrade a cluster running 6.0 +to a 6.1, then there's no way to roll back to 6.0 if the new version results in +worse client application performance or unavailability. In the interest of +increasing adoption, work has begun on supporting on-disk forward +compatibility, which allows for upgrades to be rolled back. + +The traditional way of allowing roll backs is to have one version, `N`, that +introduces a feature, but is left as disabled. `N+1` enables the feature, and +then `N+2` removes whatever was deprecated in `N`. However, FDB currently has +a 6 month release cadence, and waiting 6 months to be able to use a new feature +in production is unacceptably long. Thus, the goal is to have a way to be able +to have a sane and user-friendly, rollback-supporting upgrade path, but still +allow features to be used immediately if desired. + +This document also carries two specific restrictions to the scope of what it covers: + +1. This document specifically is **not** a discussion of network protocol + compatibility nor supporting rolling upgrades. Rolling upgrades of FDB are + still discouraged, and minor versions are still protocol incompatible with + each other. +2. This only covers the proposed design of how forward compatibility for + transaction logs will be handled, and not forward compatibility for + FoundationDB as a whole. There are other parts of the system that durably + store data, the coordinators and storage servers, that will not be discussed. + +## Overview + +A new configuration option, `log_version`, will be introduced to allow a user +to control which on-disk format the transaction logs are allowed to use. Not +every release will affect the on-disk format of the transaction logs, so +`log_version` is an opaque integer that is incremented by one whenever the +on-disk format of the transaction log is changed. + +`log_version` is set by from `fdbcli`, with an invocation looking like +`$ fdbcli -C cluster.file --exec "configure log_version:=2"`. Note that `:=` +is used instead of `=`, to keep the convention in `fdbcli` that configuration +options that users aren't expected to need (or wish) to modify are set with +`:=`. + +Right now, FDB releases and `log_version` values are as follows: + +| Release | Log Version | +| ------- | ----------- | +| pre-5.2 | 1 | +| 5.2-6.0 | 2 | +| 6.1+ | 3 | +| 6.2 | 4 | +| 6.3 | 5 | + +If a user does not specify any configuration for `log_version`, then +`log_version` will be set so that rolling back to the previous minor version of +FDB will be possible. FDB will always support loading files generated by +default from the next minor version. It will be possible to configure +`log_version` to a higher value on the release that introduces it, it the user +is willing to sacrifice the ability to roll back. + +This means FDB's releases will work like the following: + +| | 6.0 | 6.1 | 6.2 | 6.3 | +|--------------|-----|-----|-------|---------| +| Configurable | 2 | 2,3 | 3,4 | 4,5 | +| Default | 2 | 2 | 3 | 4 | +| Recoverable | 2 | 2,3 | 2,3,4 | 2,3,4,5 | + +Where... + +* "configurable" means values considered an acceptable configuration setting for `fdbcli> configure log_version:=N`. +* "default" means what `log_version` will be if you don't configure it. +* "recoverable" means that FDB can load files that were generated from the specified `log_version`. + +Configuring to a `log_version` will cause FDB to use the maximum of that +`log_version` and default `log_version`. The default `log_version` will always +be the minimum configurable log version. This is done so that manually setting +`log_version` once, and then upgrading FDB multiple times, will eventually +cause a low `log_version` left in the database configuration to act as a +request for the default. Configuring `log_version` to a very high number (e.g. 9999) +will cause FDB to always use the highest available log version. + +As a concrete example, 6.1 will introduce a new transaction log feature with +on-disk format implications. If you wish to use it, you'll first have to +`configure log_version:=3`. Otherwise, after upgrading to FDB6.2, it will +become the default. If problems are discovered when upgrading to FDB6.2, then +roll back to FDB6.1. (Theoretically. See scope restrictions above.) + +## Detailed Implementation + +`fdbcli> configure log_version:=3` sets `\xff/conf/log_version` to `3`. This +version is also persisted as part of the `LogSystemConfig` and thus +`DBCoreState`, so that any code handling the log system will have access to the +`log_version` that was used to create it. + +Changing `log_version` will result in a recovery, and FoundationDB will recover +into the requested transaction log implementation. This involves locking the +previous generation of transaction logs, and then recruiting a new generation +of transaction logs. FDB will load `\xff/conf/log_version` as the requested +`log_version`, and when sending a `InitializeTLogRequest` to recruit a new +transaction log, it uses the maximum of the requested log version and the +default `log_version`. + +A worker, when receiving an `InitializeTLogRequest`, will initialize a +transaction log corresponding to the requested `log_version`. Transaction logs +can pack multiple generations of transaction logs into the same shared entity, +a `SharedTLog`. `SharedTLog` instances correspond to one set of files, and +will only contain transaction log generations of the same `log_version`. + +This allows us to have multiple generations of transaction logs running within +one worker that have different `log_version`s, and if the worker crashes and +restarts, we need to be able to recreate those transaction log instances. + +Transaction logs maintain two types of files, one is a pair files prefixed with +`logqueue-` that are the DiskQueue, and the other is the metadata store, which +is normally a mini `ssd-2` storage engine running within the transaction log. + +When a worker first starts, it scans its data directory for any files that were +instances of a transaction log. It then needs to construct a transaction log +instance that can read the format of the file to be able to reconnect the data +in the files back to the FDB cluster, so that it can be used in a recovery if +needed. + +This presents a problem that the worker needs to know all the configuration +options that were used to decide the file format of the transaction log +*before* it can rejoin a cluster and get far enough through a recovery to find +out what that configuration was. To get around this, the relevant +configuration options have been added to the file name so that they're +available when scanning the list of files. + +Currently, FDB identifies a transaction log instance via seeing a file that starts +with `log-`, which represents the metadata store. This filename has the format +of `log-.` where UUID is the `logId`, and SUFFIX tells us if the +metadata store is a memory or ssd storage engine file. + +This format is being changed to `log2-<KV PAIRS>-.`, where KV +PAIRS is a small amount of information encoded into the file name to give us +the metadata *about* the file that is required. According to POSIX, the +characters allowed for "fully portable filenames" are `A–Z a–z 0–9 . _ -` and +the filename length should stay under 255 characters. This leaves only `_` as +the only character not already used. Therefore, the KV pair encoding +`K1_V1_K2_V2_...`, so keys and values separated by an `_`, and kv pairs are +also separated by an `_`. + +The currently supported keys are: + +V +: A copy of `log_version` + +LS +: `log_spill`, a new configuration option in 6.1 + +and any unrecognized keys are ignored, which will likely help forward compatibility. + +An example file name is `log2-V_3_LS_2-46a5f353ac18d787852d44c3a2e51527-0.fdq`. + +### Testing + +`SimulationConfig` has been changed to randomly set `log_version` according to +what is supported. This means that with restarting upgrade tests that simulate +upgrading from `N` to `N+1`, the `N+1` version will see files that came from an +FDB running with any `log_version` value that was previously supported. If +`N+1` can't handle the files correctly, then the simulation test will fail. + +`ConfigureTest` tries randomly toggling `log_version` up and down in a live +database, along with all the other log related options. Some are valid, some +are invalid and should be rejected, or will cause ASSERTs in later parts of the +code. + +I've added a new test, `ConfigureTestRestart` that tests changing +configurations and then upgrading FDB, to cover testing that upgrades still +happen correctly when `log_version` has been changed. This also verifies that +on-disk formats for those `log_version`s are still loadable by future FDB +versions. + +There are no tests that mix the `ConfigureDatabase` and `Attrition` workloads. +It would be good to do so, to cover the case of `log_version` changes in the +presence of failures, but one cannot be added easily. The simulator calculates +what processes/machines are safe to kill by looking at the current +configuration. For `ConfigureTest`, this isn't good enough, because `triple` +could mean that there are three replicas, or that the FDB cluster just changed +from `single` to `triple` and only have one replica of data until data +distribution finishes. It would be good to add a `ConfigureKillTest` sometime +in the future. + +For FDB to actually announce that rolling back from `N+1` to `N` is supported, +there will need to be downgrade tests from `N+1` to `N` also. The default in +`N+1` should always be recoverable within `N`. As FDB isn't promising forward +compatibility yet, these tests haven't been implemented. + +# Transaction Log Forward Compatibility Operational Guide + +## Notable Behavior Changes + +When release notes mention a new `log_version` is available, after deploying +that release, it's worth considering upgrading `log_version`. Doing so will +allow a controlled upgrade, and reduce the number of new changes that will +take effect when upgrading to the next release. + +## Observability + +* When running with a non-default `log_version`, the setting will appear in `fdbcli> status`. + +## Monitoring and Alerting + +If anyone is doing anything that relies on the file names the transaction log uses, they'll be changing. + + + + + + + + From 1439de37b5c69634b0f748e86de31f6c656f0815 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sun, 12 Apr 2020 18:23:14 -0700 Subject: [PATCH 1417/1604] Convert GetRangeLimits() -> TOO_MANY + ASSERT(). --- fdbclient/NativeAPI.actor.cpp | 3 ++- fdbserver/DataDistribution.actor.cpp | 3 ++- fdbserver/DataDistributionQueue.actor.cpp | 3 ++- fdbserver/MoveKeys.actor.cpp | 12 ++++++++---- fdbserver/workloads/ConsistencyCheck.actor.cpp | 3 ++- 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index 59aff7b4e3..c29b2b6859 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -2233,7 +2233,8 @@ ACTOR Future>> getAddressesForKeyActor(Key key // If key >= allKeys.end, then getRange will return a kv-pair with an empty value. This will result in our serverInterfaces vector being empty, which will cause us to return an empty addresses list. state Key ksKey = keyServersKey(key); - state Standalone serverTagResult = wait( getRange(cx, ver, lastLessOrEqual(serverTagKeys.begin), firstGreaterThan(serverTagKeys.end), GetRangeLimits(), false, info ) ); + state Standalone serverTagResult = wait( getRange(cx, ver, lastLessOrEqual(serverTagKeys.begin), firstGreaterThan(serverTagKeys.end), GetRangeLimits(CLIENT_KNOBS->TOO_MANY), false, info ) ); + ASSERT( !serverTagResult.more && serverTagResult.size() < CLIENT_KNOBS->TOO_MANY ); Future> futureServerUids = getRange(cx, ver, lastLessOrEqual(ksKey), firstGreaterThan(ksKey), GetRangeLimits(1), false, info); Standalone serverUids = wait( futureServerUids ); diff --git a/fdbserver/DataDistribution.actor.cpp b/fdbserver/DataDistribution.actor.cpp index 209e77af5f..569b8e1ceb 100644 --- a/fdbserver/DataDistribution.actor.cpp +++ b/fdbserver/DataDistribution.actor.cpp @@ -474,7 +474,8 @@ ACTOR Future> getInitialDataDistribution( Dat try { tr.setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); wait(checkMoveKeysLockReadOnly(&tr, moveKeysLock)); - state Standalone UIDtoTagMap = wait(tr.getRange(serverTagKeys, GetRangeLimits())); + state Standalone UIDtoTagMap = wait(tr.getRange(serverTagKeys, CLIENT_KNOBS->TOO_MANY)); + ASSERT( !UIDtoTagMap.more && UIDtoTagMap.size() < CLIENT_KNOBS->TOO_MANY ); Standalone keyServers = wait(krmGetRanges(&tr, keyServersPrefix, KeyRangeRef(beginKey, allKeys.end), SERVER_KNOBS->MOVE_KEYS_KRM_LIMIT, SERVER_KNOBS->MOVE_KEYS_KRM_LIMIT_BYTES)); succeeded = true; diff --git a/fdbserver/DataDistributionQueue.actor.cpp b/fdbserver/DataDistributionQueue.actor.cpp index 66057603bc..59ad021c04 100644 --- a/fdbserver/DataDistributionQueue.actor.cpp +++ b/fdbserver/DataDistributionQueue.actor.cpp @@ -546,7 +546,8 @@ struct DDQueueData { servers.clear(); tr.setOption( FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE ); try { - state Standalone UIDtoTagMap = wait( tr.getRange( serverTagKeys, GetRangeLimits() ) ); + state Standalone UIDtoTagMap = wait( tr.getRange( serverTagKeys, CLIENT_KNOBS->TOO_MANY ) ); + ASSERT( !UIDtoTagMap.more && UIDtoTagMap.size() < CLIENT_KNOBS->TOO_MANY ); Standalone keyServersEntries = wait( tr.getRange( lastLessOrEqual( keyServersKey( input.keys.begin ) ), firstGreaterOrEqual( keyServersKey( input.keys.end ) ), SERVER_KNOBS->DD_QUEUE_MAX_KEY_SERVERS ) ); diff --git a/fdbserver/MoveKeys.actor.cpp b/fdbserver/MoveKeys.actor.cpp index dfb6631632..aad9a11e6a 100644 --- a/fdbserver/MoveKeys.actor.cpp +++ b/fdbserver/MoveKeys.actor.cpp @@ -211,7 +211,8 @@ ACTOR Future> addReadWriteDestinations(KeyRangeRef shard, vector>> additionalSources(Standalone shards, Transaction* tr, int desiredHealthy, int maxServers) { - state Standalone UIDtoTagMap = wait( tr->getRange(serverTagKeys, GetRangeLimits()) ); + state Standalone UIDtoTagMap = wait( tr->getRange(serverTagKeys, CLIENT_KNOBS->TOO_MANY) ); + ASSERT( !UIDtoTagMap.more && UIDtoTagMap.size() < CLIENT_KNOBS->TOO_MANY ); vector>> serverListEntries; std::set fetching; for(int i = 0; i < shards.size() - 1; ++i) { @@ -357,7 +358,8 @@ ACTOR Future startMoveKeys( Database occ, KeyRange keys, vector serve // printf("'%s': '%s'\n", old[i].key.toString().c_str(), old[i].value.toString().c_str()); //Check that enough servers for each shard are in the correct state - state Standalone UIDtoTagMap = wait(tr.getRange(serverTagKeys, GetRangeLimits())); + state Standalone UIDtoTagMap = wait(tr.getRange(serverTagKeys, CLIENT_KNOBS->TOO_MANY)); + ASSERT( !UIDtoTagMap.more && UIDtoTagMap.size() < CLIENT_KNOBS->TOO_MANY ); vector> addAsSource = wait(additionalSources(old, &tr, servers.size(), SERVER_KNOBS->MAX_ADDED_SOURCES_MULTIPLIER*servers.size())); // For each intersecting range, update keyServers[range] dest to be servers and clear existing dest servers from serverKeys @@ -557,7 +559,8 @@ ACTOR Future finishMoveKeys( Database occ, KeyRange keys, vector dest wait( checkMoveKeysLock(&tr, lock) ); state KeyRange currentKeys = KeyRangeRef(begin, keys.end); - state Standalone UIDtoTagMap = wait( tr.getRange(serverTagKeys, GetRangeLimits()) ); + state Standalone UIDtoTagMap = wait( tr.getRange(serverTagKeys, CLIENT_KNOBS->TOO_MANY) ); + ASSERT( !UIDtoTagMap.more && UIDtoTagMap.size() < CLIENT_KNOBS->TOO_MANY ); state Standalone keyServers = wait( krmGetRanges( &tr, keyServersPrefix, currentKeys, SERVER_KNOBS->MOVE_KEYS_KRM_LIMIT, SERVER_KNOBS->MOVE_KEYS_KRM_LIMIT_BYTES ) ); //Determine the last processed key (which will be the beginning for the next iteration) @@ -977,7 +980,8 @@ ACTOR Future removeKeysFromFailedServer(Database cx, UID serverID, MoveKey // Get all values of keyServers and remove serverID from every occurrence // Very inefficient going over every entry in keyServers // No shortcut because keyServers and serverKeys are not guaranteed same shard boundaries - state Standalone UIDtoTagMap = wait( tr.getRange(serverTagKeys, GetRangeLimits()) ); + state Standalone UIDtoTagMap = wait( tr.getRange(serverTagKeys, CLIENT_KNOBS->TOO_MANY) ); + ASSERT( !UIDtoTagMap.more && UIDtoTagMap.size() < CLIENT_KNOBS->TOO_MANY ); state Standalone keyServers = wait(krmGetRanges(&tr, keyServersPrefix, KeyRangeRef(begin, allKeys.end), SERVER_KNOBS->MOVE_KEYS_KRM_LIMIT, SERVER_KNOBS->MOVE_KEYS_KRM_LIMIT_BYTES)); diff --git a/fdbserver/workloads/ConsistencyCheck.actor.cpp b/fdbserver/workloads/ConsistencyCheck.actor.cpp index 08dbdce8e9..434f24a39f 100644 --- a/fdbserver/workloads/ConsistencyCheck.actor.cpp +++ b/fdbserver/workloads/ConsistencyCheck.actor.cpp @@ -667,7 +667,8 @@ struct ConsistencyCheckWorkload : TestWorkload tr.setOption(FDBTransactionOptions::LOCK_AWARE); state int bytesReadInRange = 0; - Standalone UIDtoTagMap = wait( tr.getRange( serverTagKeys, GetRangeLimits() ) ); + Standalone 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); //If the destStorageServers is non-empty, then this shard is being relocated From c8ab69cce6251f3cd10d2e4ae447fd1a8af7fece Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sun, 12 Apr 2020 19:38:51 -0700 Subject: [PATCH 1418/1604] Fix incorrect if/ASSERT logic from last minute cleanups. My test code serialized UIDs and Tags, so that I could compare and verify I got the same thing, and I undid this right before committing, but forgot to also change the `if` to not rely on UIDs being first also. --- fdbclient/SystemData.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fdbclient/SystemData.cpp b/fdbclient/SystemData.cpp index 722ab05208..c2c4672520 100644 --- a/fdbclient/SystemData.cpp +++ b/fdbclient/SystemData.cpp @@ -83,14 +83,12 @@ void decodeKeyServersValue( Standalone result, const ValueRef& v rd >> destLen; rd.rewind(); - if (value.size() == sizeof(ProtocolVersion) + sizeof(int) + srcLen * sizeof(UID) + sizeof(int) + destLen * sizeof(UID)) { + if (value.size() != sizeof(ProtocolVersion) + sizeof(int) + srcLen * sizeof(Tag) + sizeof(int) + destLen * sizeof(Tag)) { rd >> src >> dest; + rd.assertEnd(); return; } - // If this is not true, then our math was wrong. - ASSERT(value.size() == sizeof(ProtocolVersion) + sizeof(int) + srcLen * sizeof(Tag) + sizeof(int) + destLen * sizeof(Tag)); - std::vector srcTag, destTag; rd >> srcTag >> destTag; From 7dc348d0774623a929fed7eff4146fbab10edf81 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 13 Apr 2020 02:22:25 -0700 Subject: [PATCH 1419/1604] Remove `-isystem flow/-lpthread` from INCLUDES/CXXFLAGS This cmake line generated a bogus and nonsensical include path, so as the entire line isn't necessary, just remove it. --- flow/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/flow/CMakeLists.txt b/flow/CMakeLists.txt index 27b61b768f..a7f68db07c 100644 --- a/flow/CMakeLists.txt +++ b/flow/CMakeLists.txt @@ -87,7 +87,6 @@ set(FLOW_SRCS configure_file(${CMAKE_CURRENT_SOURCE_DIR}/SourceVersion.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/SourceVersion.h) add_flow_target(STATIC_LIBRARY NAME flow SRCS ${FLOW_SRCS}) -target_include_directories(flow SYSTEM PUBLIC ${CMAKE_THREAD_LIBS_INIT}) target_include_directories(flow PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) if (NOT APPLE AND NOT WIN32) set (FLOW_LIBS ${FLOW_LIBS} rt) From 2eec3bb9b16864e7cf23940beb8dee9449e9aeef Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Mon, 13 Apr 2020 13:09:21 -0700 Subject: [PATCH 1420/1604] fixed logic for skipping broadcast --- fdbserver/worker.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/worker.actor.cpp b/fdbserver/worker.actor.cpp index bf926bcd3f..0f365d88cc 100644 --- a/fdbserver/worker.actor.cpp +++ b/fdbserver/worker.actor.cpp @@ -1048,7 +1048,7 @@ ACTOR Future workerServer( ServerDBInfo localInfo = BinaryReader::fromStringRef(req.serializedDbInfo, AssumeVersion(currentProtocolVersion)); localInfo.myLocality = locality; - if(ccInterface->get().present() && localInfo.infoGeneration < dbInfo->get().infoGeneration && dbInfo->get().clusterInterface == ccInterface->get().get()) { + if(localInfo.infoGeneration < dbInfo->get().infoGeneration && localInfo.clusterInterface == dbInfo->get().clusterInterface) { std::vector rep = req.broadcastInfo; rep.push_back(interf.updateServerDBInfo.getEndpoint()); req.reply.send(rep); From ffc8b60bf8bca4f72804c03d3b748b0129859ea5 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Mon, 13 Apr 2020 15:15:19 -0700 Subject: [PATCH 1421/1604] add an assertion that all getRange results are returned in test special-key-range-impl --- fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp b/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp index 5535b985ed..10251a3382 100644 --- a/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp +++ b/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp @@ -34,6 +34,8 @@ public: auto resultFuture = ryw->getRange(kr, CLIENT_KNOBS->TOO_MANY); // all keys are written to RYW, since GRV is set, the read should happen locally ASSERT(resultFuture.isReady()); + auto result = resultFuture.getValue(); + ASSERT(!result.more); return resultFuture.getValue(); } }; From 54813e2d46ffbafa9f83a0d12d789874a461efbe Mon Sep 17 00:00:00 2001 From: Alvin Moore Date: Mon, 13 Apr 2020 17:20:25 -0700 Subject: [PATCH 1422/1604] Added support for copying fdb java tests to package directory --- bindings/java/CMakeLists.txt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/bindings/java/CMakeLists.txt b/bindings/java/CMakeLists.txt index 80ff1d1388..f97e12d51b 100644 --- a/bindings/java/CMakeLists.txt +++ b/bindings/java/CMakeLists.txt @@ -169,8 +169,6 @@ file(WRITE ${MANIFEST_FILE} ${MANIFEST_TEXT}) add_jar(fdb-java ${JAVA_BINDING_SRCS} ${GENERATED_JAVA_FILES} ${CMAKE_SOURCE_DIR}/LICENSE OUTPUT_DIR ${PROJECT_BINARY_DIR}/lib VERSION ${CMAKE_PROJECT_VERSION} MANIFEST ${MANIFEST_FILE}) add_dependencies(fdb-java fdb_java_options fdb_java) -add_jar(foundationdb-tests SOURCES ${JAVA_TESTS_SRCS} INCLUDE_JARS fdb-java) -add_dependencies(foundationdb-tests fdb_java_options) # TODO[mpilman]: The java RPM will require some more effort (mostly on debian). However, # most people will use the fat-jar, so it is not clear how high this priority is. @@ -237,6 +235,16 @@ if(NOT OPEN_FOR_IDE) WORKING_DIRECTORY ${unpack_dir} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/lib_copied COMMENT "Build ${target_jar}") + add_jar(foundationdb-tests SOURCES ${JAVA_TESTS_SRCS} INCLUDE_JARS fdb-java) + add_dependencies(foundationdb-tests fdb_java_options) + set(tests_jar ${jar_destination}/fdb-java-${CMAKE_PROJECT_VERSION}${prerelease_string}-tests.jar) + add_custom_command(OUTPUT ${tests_jar} + COMMAND ${CMAKE_COMMAND} -E copy foundationdb-tests.jar "${tests_jar}" + WORKING_DIRECTORY . + DEPENDS foundationdb-tests + COMMENT "Build ${tests_jar}") + add_custom_target(fdb-java-tests ALL DEPENDS ${tests_jar}) + add_dependencies(fdb-java-tests foundationdb-tests) add_custom_target(fat-jar ALL DEPENDS ${target_jar}) add_dependencies(fat-jar fdb-java) add_dependencies(fat-jar copy_lib) From e493ced88f5eb2c177ada5bfb292913c2d66c57d Mon Sep 17 00:00:00 2001 From: mpilman Date: Mon, 13 Apr 2020 20:06:33 -0700 Subject: [PATCH 1423/1604] removed unused variable --- contrib/TestHarness/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/contrib/TestHarness/CMakeLists.txt b/contrib/TestHarness/CMakeLists.txt index 3616d772d2..265ba847d2 100644 --- a/contrib/TestHarness/CMakeLists.txt +++ b/contrib/TestHarness/CMakeLists.txt @@ -13,4 +13,3 @@ add_custom_command(OUTPUT ${out_file} DEPENDS ${SRCS} TraceLogHelper COMMENT "Compile TestHarness" VERBATIM) add_custom_target(TestHarness DEPENDS ${out_file}) -set(TestHarnesExe "${out_file}" PARENT_SCOPE) From f0ab168368dedfbfeeada48e9eab8f778df823e6 Mon Sep 17 00:00:00 2001 From: mpilman Date: Mon, 13 Apr 2020 20:34:09 -0700 Subject: [PATCH 1424/1604] attempt on fixing make bug --- cmake/AddFdbTest.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/AddFdbTest.cmake b/cmake/AddFdbTest.cmake index cbd77479b8..1424d65c1e 100644 --- a/cmake/AddFdbTest.cmake +++ b/cmake/AddFdbTest.cmake @@ -211,6 +211,8 @@ function(create_test_package) COMMENT "Package correctness archive" ) add_custom_target(package_tests ALL DEPENDS ${tar_file}) + # seems make needs this dependency while this does nothing with ninja + add_dependencies(package_valgrind_tests strip_only_fdbserver TestHarness) endif() if(USE_VALGRIND) From 7aa030a850e4d46f1893756c3d377ba1b0f4c322 Mon Sep 17 00:00:00 2001 From: mpilman Date: Mon, 13 Apr 2020 21:08:48 -0700 Subject: [PATCH 1425/1604] fix weird typo --- cmake/AddFdbTest.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/AddFdbTest.cmake b/cmake/AddFdbTest.cmake index 1424d65c1e..a8fae7837b 100644 --- a/cmake/AddFdbTest.cmake +++ b/cmake/AddFdbTest.cmake @@ -212,7 +212,7 @@ function(create_test_package) ) add_custom_target(package_tests ALL DEPENDS ${tar_file}) # seems make needs this dependency while this does nothing with ninja - add_dependencies(package_valgrind_tests strip_only_fdbserver TestHarness) + add_dependencies(package_tests strip_only_fdbserver TestHarness) endif() if(USE_VALGRIND) From ae1de060c2a237ffa48efee6c46f81966c74f916 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Tue, 14 Apr 2020 09:10:40 -0700 Subject: [PATCH 1426/1604] Reformat SpecialKeyRangeBaseImpl constructor --- fdbclient/NativeAPI.actor.cpp | 2 +- fdbclient/SpecialKeySpace.actor.cpp | 14 +++++++------- fdbclient/SpecialKeySpace.actor.h | 6 ++---- .../workloads/SpecialKeySpaceCorrectness.actor.cpp | 4 ++-- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index b1a6376ca2..9b3251d25e 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -539,7 +539,7 @@ DatabaseContext::DatabaseContext(Reference(normalKeys.begin, specialKeys.end)), - cKImpl(std::make_shared(conflictingKeysRange.begin, conflictingKeysRange.end)) { + cKImpl(std::make_shared(conflictingKeysRange)) { dbId = deterministicRandom()->randomUniqueID(); connected = clientInfo->get().proxies.size() ? Void() : clientInfo->onChange(); diff --git a/fdbclient/SpecialKeySpace.actor.cpp b/fdbclient/SpecialKeySpace.actor.cpp index 9c5e4227c2..23b8b2f027 100644 --- a/fdbclient/SpecialKeySpace.actor.cpp +++ b/fdbclient/SpecialKeySpace.actor.cpp @@ -243,7 +243,7 @@ Future> SpecialKeySpace::get(Reference> ConflictingKeysImpl::getRange(Reference ryw, KeyRangeRef kr) const { @@ -267,8 +267,8 @@ Future> ConflictingKeysImpl::getRange(Reference 0); for (int i = 0; i < size; ++i) { kvs.push_back_deep(kvs.arena(), @@ -299,9 +299,9 @@ private: TEST_CASE("/fdbclient/SpecialKeySpace/Unittest") { SpecialKeySpace pks(normalKeys.begin, normalKeys.end); - SpecialKeyRangeTestImpl pkr1(LiteralStringRef("/cat/"), LiteralStringRef("/cat/\xff"), "small", 10); - SpecialKeyRangeTestImpl pkr2(LiteralStringRef("/dog/"), LiteralStringRef("/dog/\xff"), "medium", 100); - SpecialKeyRangeTestImpl pkr3(LiteralStringRef("/pig/"), LiteralStringRef("/pig/\xff"), "large", 1000); + SpecialKeyRangeTestImpl pkr1(KeyRangeRef(LiteralStringRef("/cat/"), LiteralStringRef("/cat/\xff")), "small", 10); + SpecialKeyRangeTestImpl pkr2(KeyRangeRef(LiteralStringRef("/dog/"), LiteralStringRef("/dog/\xff")), "medium", 100); + SpecialKeyRangeTestImpl pkr3(KeyRangeRef(LiteralStringRef("/pig/"), LiteralStringRef("/pig/\xff")), "large", 1000); pks.registerKeyRange(pkr1.getKeyRange(), &pkr1); pks.registerKeyRange(pkr2.getKeyRange(), &pkr2); pks.registerKeyRange(pkr3.getKeyRange(), &pkr3); @@ -374,4 +374,4 @@ TEST_CASE("/fdbclient/SpecialKeySpace/Unittest") { ASSERT(result[i + pkr3.getSize()] == pkr2.getKeyValueForIndex(pkr2.getSize() - 1 - i)); } return Void(); -} +} \ No newline at end of file diff --git a/fdbclient/SpecialKeySpace.actor.h b/fdbclient/SpecialKeySpace.actor.h index e2cc0a417b..c0ae6dbdf6 100644 --- a/fdbclient/SpecialKeySpace.actor.h +++ b/fdbclient/SpecialKeySpace.actor.h @@ -39,9 +39,7 @@ public: virtual Future> getRange(Reference ryw, KeyRangeRef kr) const = 0; - explicit SpecialKeyRangeBaseImpl(KeyRef start, KeyRef end) { - range = KeyRangeRef(range.arena(), KeyRangeRef(start, end)); - } + explicit SpecialKeyRangeBaseImpl(KeyRangeRef kr) : range(kr) {} KeyRangeRef getKeyRange() const { return range; } ACTOR Future normalizeKeySelectorActor(const SpecialKeyRangeBaseImpl* pkrImpl, Reference ryw, KeySelector* ks); @@ -92,7 +90,7 @@ private: // Currently, the conflicting keyranges returned are original read_conflict_ranges or union of them. class ConflictingKeysImpl : public SpecialKeyRangeBaseImpl { public: - explicit ConflictingKeysImpl(KeyRef start, KeyRef end); + explicit ConflictingKeysImpl(KeyRangeRef kr); Future> getRange(Reference ryw, KeyRangeRef kr) const override; }; diff --git a/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp b/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp index 10251a3382..63c9bd0145 100644 --- a/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp +++ b/fdbserver/workloads/SpecialKeySpaceCorrectness.actor.cpp @@ -27,7 +27,7 @@ class SKSCTestImpl : public SpecialKeyRangeBaseImpl { public: - explicit SKSCTestImpl(KeyRef start, KeyRef end) : SpecialKeyRangeBaseImpl(start, end) {} + explicit SKSCTestImpl(KeyRangeRef kr) : SpecialKeyRangeBaseImpl(kr) {} virtual Future> getRange(Reference ryw, KeyRangeRef kr) const { ASSERT(range.contains(kr)); @@ -82,7 +82,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload { Key startKey(baseKey + "/"); Key endKey(baseKey + "/\xff"); self->keys.push_back_deep(self->keys.arena(), KeyRangeRef(startKey, endKey)); - self->impls.push_back(std::make_shared(startKey, endKey)); + self->impls.push_back(std::make_shared(KeyRangeRef(startKey, endKey))); // Although there are already ranges registered, the testing range will replace them cx->specialKeySpace->registerKeyRange(self->keys.back(), self->impls.back().get()); // generate keys in each key range From f959af8228851dfbe6f963b62bd16c2a68dd849c Mon Sep 17 00:00:00 2001 From: Vishesh Yadav Date: Tue, 14 Apr 2020 11:30:40 -0700 Subject: [PATCH 1427/1604] Refactor per review comments --- fdbrpc/FlowTransport.actor.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/fdbrpc/FlowTransport.actor.cpp b/fdbrpc/FlowTransport.actor.cpp index a269d516c9..11ac552bcb 100644 --- a/fdbrpc/FlowTransport.actor.cpp +++ b/fdbrpc/FlowTransport.actor.cpp @@ -400,20 +400,21 @@ ACTOR Future connectionWriter( Reference self, Reference delayedHealthUpdate(NetworkAddress address) { state double start = now(); - state int count = 0; + state bool delayed = false; loop { if (FLOW_KNOBS->HEALTH_MONITOR_MARK_FAILED_UNSTABLE_CONNECTIONS && FlowTransport::transport().healthMonitor()->tooManyConnectionsClosed(address) && address.isPublic()) { - if (count == 0) { + if (!delayed) { TraceEvent("TooManyConnectionsClosedMarkFailed") .detail("Dest", address) .detail("StartTime", start) .detail("ClosedCount", FlowTransport::transport().healthMonitor()->closedConnectionsCount(address)); IFailureMonitor::failureMonitor().setStatus(address, FailureStatus(true)); } + delayed = true; wait(delayJittered(FLOW_KNOBS->MAX_RECONNECTION_TIME * 2.0)); } else { - if (count > 1) + if (delayed) TraceEvent("TooManyConnectionsClosedMarkAvailable") .detail("Dest", address) .detail("StartTime", start) @@ -422,7 +423,6 @@ ACTOR Future delayedHealthUpdate(NetworkAddress address) { IFailureMonitor::failureMonitor().setStatus(address, FailureStatus(false)); break; } - ++count; } return Void(); } @@ -465,6 +465,7 @@ ACTOR Future connectionKeeper( Reference self, .detail("PeerAddr", self->destination) .detail("PeerReferences", self->peerReferences); + state Future delayedHealthUpdateF = Future(); try { choose { when(Reference _conn = @@ -472,9 +473,9 @@ ACTOR Future connectionKeeper( Reference self, conn = _conn; wait(conn->connectHandshake()); if (self->unsent.empty()) { - state Future statusUpdate = delayedHealthUpdate(self->destination); + delayedHealthUpdateF = delayedHealthUpdate(self->destination); choose { - when(wait(statusUpdate)) { + when(wait(delayedHealthUpdateF)) { conn->close(); conn = Reference(); continue; @@ -511,7 +512,8 @@ ACTOR Future connectionKeeper( Reference self, firstConnFailedTime.reset(); try { self->transport->countConnEstablished++; - state Future delayedHealthUpdateF = delayedHealthUpdate(self->destination); + if (!delayedHealthUpdateF.isValid()) + delayedHealthUpdateF = delayedHealthUpdate(self->destination); wait(connectionWriter(self, conn) || reader || connectionMonitor(self)); } catch (Error& e) { if (e.code() == error_code_connection_failed) From 541c81a92af33437125fc7bf3e87dad2604dd038 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Tue, 14 Apr 2020 14:10:12 -0700 Subject: [PATCH 1428/1604] Fix merge related issue. --- fdbserver/MasterProxyServer.actor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fdbserver/MasterProxyServer.actor.cpp b/fdbserver/MasterProxyServer.actor.cpp index cb80c49073..af9823a1fe 100644 --- a/fdbserver/MasterProxyServer.actor.cpp +++ b/fdbserver/MasterProxyServer.actor.cpp @@ -1488,8 +1488,8 @@ ACTOR static Future transactionStarter( transactionCount += transactionsStarted[0] + transactionsStarted[1]; batchTransactionCount += batchTotalStarted; - normalRateInfo.updateBudget(systemTotalStarted + normalTotalStarted, transactionQueue.empty() || transactionQueue.top().first.priority() < GetReadVersionRequest::PRIORITY_DEFAULT, elapsed); - batchRateInfo.updateBudget(systemTotalStarted + normalTotalStarted + batchTotalStarted, transactionQueue.empty(), elapsed); + normalRateInfo.updateBudget(systemTotalStarted + normalTotalStarted, systemQueue.empty() && defaultQueue.empty(), elapsed); + batchRateInfo.updateBudget(systemTotalStarted + normalTotalStarted + batchTotalStarted, systemQueue.empty() && defaultQueue.empty() && batchQueue.empty(), elapsed); if (debugID.present()) { g_traceBatch.addEvent("TransactionDebug", debugID.get().first(), "MasterProxyServer.masterProxyServerCore.Broadcast"); From 37e2b0d353300011a662356b046ba2c529b86407 Mon Sep 17 00:00:00 2001 From: Evan Tschannen <36455792+etschannen@users.noreply.github.com> Date: Tue, 14 Apr 2020 17:12:07 -0700 Subject: [PATCH 1429/1604] Update flow/network.cpp --- flow/network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/network.cpp b/flow/network.cpp index 2afd264375..86fbd78438 100644 --- a/flow/network.cpp +++ b/flow/network.cpp @@ -167,7 +167,7 @@ Future> INetworkConnections::connect( std::string host, s }); } -const std::vector NetworkMetrics::starvationBins = { 1, 2500, 3500, 5000, 7000, 7500, 8500, 9000, 10500 }; +const std::vector NetworkMetrics::starvationBins = { 1, 3500, 7000, 7500, 8500, 8900, 10500 }; TEST_CASE("/flow/network/ipaddress") { ASSERT(NetworkAddress::parse("[::1]:4800").toString() == "[::1]:4800"); From ed8e5bc64f416186e3bf41327dcae7b8825d4c8a Mon Sep 17 00:00:00 2001 From: Alvin Moore Date: Wed, 15 Apr 2020 08:46:37 -0700 Subject: [PATCH 1430/1604] Added support for getting go dependencies before install source --- bindings/go/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bindings/go/CMakeLists.txt b/bindings/go/CMakeLists.txt index 793089a3f7..bcae44440f 100644 --- a/bindings/go/CMakeLists.txt +++ b/bindings/go/CMakeLists.txt @@ -99,6 +99,8 @@ function(build_go_package) endif() add_custom_command(OUTPUT ${outfile} COMMAND ${CMAKE_COMMAND} -E env ${go_env} + ${GO_EXECUTABLE} get ${GO_IMPORT_PATH}/${BGP_PATH} && + ${CMAKE_COMMAND} -E env ${go_env} ${GO_EXECUTABLE} install ${GO_IMPORT_PATH}/${BGP_PATH} DEPENDS ${fdb_options_file} COMMENT "Building ${BGP_NAME}") From d555fbb8535fc65dd5573112ed4036b81353de43 Mon Sep 17 00:00:00 2001 From: Alvin Moore Date: Wed, 15 Apr 2020 09:07:12 -0700 Subject: [PATCH 1431/1604] Changed the get to only get the dependencies --- bindings/go/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/go/CMakeLists.txt b/bindings/go/CMakeLists.txt index bcae44440f..701fa49ca8 100644 --- a/bindings/go/CMakeLists.txt +++ b/bindings/go/CMakeLists.txt @@ -99,7 +99,7 @@ function(build_go_package) endif() add_custom_command(OUTPUT ${outfile} COMMAND ${CMAKE_COMMAND} -E env ${go_env} - ${GO_EXECUTABLE} get ${GO_IMPORT_PATH}/${BGP_PATH} && + ${GO_EXECUTABLE} get -d ${GO_IMPORT_PATH}/${BGP_PATH} && ${CMAKE_COMMAND} -E env ${go_env} ${GO_EXECUTABLE} install ${GO_IMPORT_PATH}/${BGP_PATH} DEPENDS ${fdb_options_file} From 456b794d24e3de9fd54b3ccc6f070afce4a7c30c Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Wed, 15 Apr 2020 12:24:44 -0700 Subject: [PATCH 1432/1604] Reformat the file --- fdbclient/SpecialKeySpace.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbclient/SpecialKeySpace.actor.cpp b/fdbclient/SpecialKeySpace.actor.cpp index 23b8b2f027..762d97281c 100644 --- a/fdbclient/SpecialKeySpace.actor.cpp +++ b/fdbclient/SpecialKeySpace.actor.cpp @@ -374,4 +374,4 @@ TEST_CASE("/fdbclient/SpecialKeySpace/Unittest") { ASSERT(result[i + pkr3.getSize()] == pkr2.getKeyValueForIndex(pkr2.getSize() - 1 - i)); } return Void(); -} \ No newline at end of file +} From 022c53de9dcf248d46b8ce79e08a73b2219cfe9c Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Wed, 15 Apr 2020 12:26:16 -0700 Subject: [PATCH 1433/1604] Documentation for special-key-space --- design/special-key-space.md | 81 +++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 design/special-key-space.md diff --git a/design/special-key-space.md b/design/special-key-space.md new file mode 100644 index 0000000000..dfbb0033c3 --- /dev/null +++ b/design/special-key-space.md @@ -0,0 +1,81 @@ +# Special-Key-Space +This document discusses why we need the proposed special-key-space framwork. And for what problems the framework aims to solve and in what scenarios a developer should use it. + +## Motivation +Currently, there are several client functions implemented as FDB calls by passing through special keys(`prefixed with \xff\xff`). Below are all existing features: +- **status/json**: `get("\xff\xff/status/json")` +- **cluster_file_path**: `get("\xff\xff/cluster_file_path)` +- **connection_string**: `get("\xff\xff/connection_string)` +- **worker_interfaces**: `getRange("\xff\xff/worker_interfaces", )` +- **conflicting-keys**: `getRange("\xff\xff/transaction/conflicting_keys/", "\xff\xff/transaction/conflicting_keys/\xff")` + +At present, implementions are hard-coded and the pain points are obvious: +- **Maintainability**: As more features added, the hard-coded snippets are hard to maintain +- **Granularity**: It is impossible to scale up and down. For example, you want a cheap call like `get("\xff\xff/status/json/")` instead of calling `status/json` and parsing the results. In the constrast, sometime you want to aggregate results from several similiar features like `getRange("\xff\xff/transaction/, \xff\xff/transaction/\xff")` to get all transaction related info. Both of them are not achievable at present. +- **Consistency**: While using FDB calls like `get` or `getRange`, the behavior that the result of `get("\xff\xff/B")` is not included in `getRange("\xff\xff/A", "\xff\xff/C")` is inconsistent with general FDB calls. + +Consequently, the special-key-space framework wants to integrate all client functions using special keys(`prefixed with \xff`) and solve the pain points listed above. + +## When +If your feature is exposing information to clients and the results are easily formatted as key-value pairs, then you can use special-key-space to implement your client function. + +## How +If you choose to use, you need to implement a function class that inherits from `SpecialKeyRangeBaseImpl`, which has an abstract method `Future> getRange(Reference ryw, KeyRangeRef kr)`. +This method can be treated as a callback, whose implementation details are determined by the developer. +Once you fill out the method, register the function class to the corresponding key range. +Below is a detailed example. +```c++ +// Implement the function class, +// the corresponding key range is [\xff\xff/example/, \xff\xff/example/\xff) +class SKRExampleImpl : public SpecialKeyRangeBaseImpl { +public: + explicit SKRExampleImpl(KeyRangeRef kr): SpecialKeyRangeBaseImpl(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"); + } + // Implement the getRange interface + Future> getRange(Reference ryw, + KeyRangeRef kr) const override { + + Standalone result; + for (auto const& country : CountryToCapitalCity) { + // the registered range here: [\xff\xff/example/, \xff\xff/example/\xff] + Key keyWithPrefix = country.first.withPrefix(range.begin); + // check if any valid keys are given in the range + if (kr.contains(keyWithPrefix)) { + result.push_back(result.arena(), KeyValueRef(keyWithPrefix, country.second)); + result.arena().dependsOn(keyWithPrefix.arena()); + } + } + return result; + } +private: + std::map CountryToCapitalCity; +}; +// 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")); +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) +cx->specialKeySpace->registerKeyRange(exampleRange, &exampleImpl); +// Now any ReadYourWritesTransaction associated with `cx` is able to query the info +state ReadYourWritesTransaction tr(cx); +// get +Optional res1 = wait(tr.get("\xff\xff/example/Japan")); +ASSERT(res1.present() && res.getValue() == LiteralStringRef("Tokyo")); +// 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 +Standalone res2 = wait(tr.getRange(LiteralStringRef("\xff\xff/example/U"), LiteralStringRef("\xff\xff/example/U\xff"))); +// res2 should contain USA and UK +ASSERT( + res2.size() == 2 && + res2[0].value == LiteralStringRef("London") && + res2[1].value == LiteralStringRef("Washington, D.C.") +); +``` \ No newline at end of file From 8219a8ea714626db51357d943854d91c82f63776 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Wed, 15 Apr 2020 12:50:37 -0700 Subject: [PATCH 1434/1604] fix typo --- design/special-key-space.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/design/special-key-space.md b/design/special-key-space.md index dfbb0033c3..15386de508 100644 --- a/design/special-key-space.md +++ b/design/special-key-space.md @@ -11,7 +11,7 @@ Currently, there are several client functions implemented as FDB calls by passin At present, implementions are hard-coded and the pain points are obvious: - **Maintainability**: As more features added, the hard-coded snippets are hard to maintain -- **Granularity**: It is impossible to scale up and down. For example, you want a cheap call like `get("\xff\xff/status/json/")` instead of calling `status/json` and parsing the results. In the constrast, sometime you want to aggregate results from several similiar features like `getRange("\xff\xff/transaction/, \xff\xff/transaction/\xff")` to get all transaction related info. Both of them are not achievable at present. +- **Granularity**: It is impossible to scale up and down. For example, you want a cheap call like `get("\xff\xff/status/json/")` instead of calling `status/json` and parsing the results. On the contrary, sometime you want to aggregate results from several similiar features like `getRange("\xff\xff/transaction/, \xff\xff/transaction/\xff")` to get all transaction related info. Both of them are not achievable at present. - **Consistency**: While using FDB calls like `get` or `getRange`, the behavior that the result of `get("\xff\xff/B")` is not included in `getRange("\xff\xff/A", "\xff\xff/C")` is inconsistent with general FDB calls. Consequently, the special-key-space framework wants to integrate all client functions using special keys(`prefixed with \xff`) and solve the pain points listed above. From 0df2a4d7f9eebf835d88a837694cf8db9a3be33d Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Wed, 15 Apr 2020 15:39:28 -0700 Subject: [PATCH 1435/1604] FastRestore:Report error when parsing file has exception --- fdbserver/RestoreCommon.actor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fdbserver/RestoreCommon.actor.cpp b/fdbserver/RestoreCommon.actor.cpp index e5776e97d8..08de6034d4 100644 --- a/fdbserver/RestoreCommon.actor.cpp +++ b/fdbserver/RestoreCommon.actor.cpp @@ -343,7 +343,7 @@ ACTOR Future>> decodeRangeFileBlock(Reference< return results; } catch (Error& e) { - TraceEvent(SevWarn, "FileRestoreCorruptRangeFileBlock") + TraceEvent(SevError, "FileRestoreCorruptRangeFileBlock") .error(e) .detail("Filename", file->getFilename()) .detail("BlockOffset", offset) @@ -388,7 +388,7 @@ ACTOR Future>> decodeLogFileBlock(ReferencegetFilename()) .detail("BlockOffset", offset) From d6c1baa7846d0370810fa0264e1440ec4bcbf544 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Wed, 15 Apr 2020 13:32:52 -0700 Subject: [PATCH 1436/1604] FastRestore:Filter out log mutations whose version is smaller than range mutation version --- fdbclient/RestoreWorkerInterface.actor.h | 10 ++- fdbrpc/RangeMap.h | 1 + fdbserver/RestoreCommon.actor.cpp | 4 +- fdbserver/RestoreCommon.actor.h | 2 + fdbserver/RestoreLoader.actor.cpp | 63 +++++++++++++-- fdbserver/RestoreLoader.actor.h | 2 + fdbserver/RestoreMaster.actor.cpp | 99 ++++++++++++++++++++++-- 7 files changed, 161 insertions(+), 20 deletions(-) diff --git a/fdbclient/RestoreWorkerInterface.actor.h b/fdbclient/RestoreWorkerInterface.actor.h index 8c8efea343..a1fc942b28 100644 --- a/fdbclient/RestoreWorkerInterface.actor.h +++ b/fdbclient/RestoreWorkerInterface.actor.h @@ -362,20 +362,24 @@ struct RestoreSysInfoRequest : TimedRequest { constexpr static FileIdentifier file_identifier = 75960741; RestoreSysInfo sysInfo; + Standalone>> rangeVersions; ReplyPromise reply; RestoreSysInfoRequest() = default; - explicit RestoreSysInfoRequest(RestoreSysInfo sysInfo) : sysInfo(sysInfo) {} + explicit RestoreSysInfoRequest(RestoreSysInfo sysInfo, + Standalone>> rangeVersions) + : sysInfo(sysInfo), rangeVersions(rangeVersions) {} template void serialize(Ar& ar) { - serializer(ar, sysInfo, reply); + serializer(ar, sysInfo, rangeVersions, reply); } std::string toString() { std::stringstream ss; - ss << "RestoreSysInfoRequest"; + ss << "RestoreSysInfoRequest " + << "rangeVersions.size:" << rangeVersions.size(); return ss.str(); } }; diff --git a/fdbrpc/RangeMap.h b/fdbrpc/RangeMap.h index 310a578b57..61404d8a93 100644 --- a/fdbrpc/RangeMap.h +++ b/fdbrpc/RangeMap.h @@ -112,6 +112,7 @@ public: Val const& operator[]( const Key& k ) { return rangeContaining(k).value(); } Ranges ranges() { return Ranges( Iterator(map.begin()), Iterator(map.lastItem()) ); } + // intersectingRanges returns [begin, end] where begin <= r.begin and end >= r.end Ranges intersectingRanges( const Range& r ) { return Ranges(rangeContaining(r.begin), Iterator(map.lower_bound(r.end))); } // containedRanges() will return all ranges that are fully contained by the passed range (note that a range fully contains itself) Ranges containedRanges( const Range& r ) { diff --git a/fdbserver/RestoreCommon.actor.cpp b/fdbserver/RestoreCommon.actor.cpp index e5776e97d8..08de6034d4 100644 --- a/fdbserver/RestoreCommon.actor.cpp +++ b/fdbserver/RestoreCommon.actor.cpp @@ -343,7 +343,7 @@ ACTOR Future>> decodeRangeFileBlock(Reference< return results; } catch (Error& e) { - TraceEvent(SevWarn, "FileRestoreCorruptRangeFileBlock") + TraceEvent(SevError, "FileRestoreCorruptRangeFileBlock") .error(e) .detail("Filename", file->getFilename()) .detail("BlockOffset", offset) @@ -388,7 +388,7 @@ ACTOR Future>> decodeLogFileBlock(ReferencegetFilename()) .detail("BlockOffset", offset) diff --git a/fdbserver/RestoreCommon.actor.h b/fdbserver/RestoreCommon.actor.h index 268fbf26d2..b4cca10f73 100644 --- a/fdbserver/RestoreCommon.actor.h +++ b/fdbserver/RestoreCommon.actor.h @@ -38,6 +38,8 @@ #include "flow/actorcompiler.h" // has to be last include +#define MAX_VERSION (std::numeric_limits::max()) + // RestoreConfig copied from FileBackupAgent.actor.cpp // We copy RestoreConfig instead of using (and potentially changing) it in place // to avoid conflict with the existing code. diff --git a/fdbserver/RestoreLoader.actor.cpp b/fdbserver/RestoreLoader.actor.cpp index 9ce047af97..734ce62da6 100644 --- a/fdbserver/RestoreLoader.actor.cpp +++ b/fdbserver/RestoreLoader.actor.cpp @@ -38,7 +38,8 @@ typedef std::map, uint32_t> SerializedMutationPartMap; std::vector getApplierIDs(std::map& rangeToApplier); void splitMutation(std::map* pRangeToApplier, MutationRef m, Arena& mvector_arena, VectorRef& mvector, Arena& nodeIDs_arena, VectorRef& nodeIDs); -void _parseSerializedMutation(std::map::iterator kvOpsIter, +void _parseSerializedMutation(KeyRangeMap* pRangeVersions, + std::map::iterator kvOpsIter, SerializedMutationListMap* mutationMap, std::map::iterator samplesIter, LoaderCounters* cc, const RestoreAsset& asset); @@ -126,6 +127,21 @@ ACTOR Future restoreLoaderCore(RestoreLoaderInterface loaderInterf, int no return Void(); } +static __inline__ bool _logMutationTooOld(KeyRangeMap* pRangeVersions, KeyRangeRef keyRange, Version v) { + auto ranges = pRangeVersions->intersectingRanges(keyRange); + Version minVersion = MAX_VERSION; + for (auto r = ranges.begin(); r != ranges.end(); ++r) { + minVersion = std::min(minVersion, r->value()); + } + return minVersion >= v; +} + +static __inline__ bool logMutationTooOld(KeyRangeMap* pRangeVersions, MutationRef mutation, Version v) { + return isRangeMutation(mutation) + ? _logMutationTooOld(pRangeVersions, KeyRangeRef(mutation.param1, mutation.param2), v) + : _logMutationTooOld(pRangeVersions, KeyRangeRef(singleKeyRange(mutation.param1)), v); +} + // Assume: Only update the local data if it (applierInterf) has not been set void handleRestoreSysInfoRequest(const RestoreSysInfoRequest& req, Reference self) { TraceEvent("FastRestoreLoader", self->id()).detail("HandleRestoreSysInfoRequest", self->id()); @@ -138,6 +154,22 @@ void handleRestoreSysInfoRequest(const RestoreSysInfoRequest& req, ReferenceappliersInterf = req.sysInfo.appliers; + // Update rangeVersions + ASSERT(self->rangeVersions.size() == 1); // rangeVersions has not been set + for (auto rv = req.rangeVersions.begin(); rv != req.rangeVersions.end(); ++rv) { + self->rangeVersions.insert(rv->first, rv->second); + } + + // Debug message for range version in each loader + auto ranges = self->rangeVersions.ranges(); + int i = 0; + for (auto r = ranges.begin(); r != ranges.end(); ++r) { + TraceEvent("FastRestoreLoader", self->id()) + .detail("RangeIndex", i++) + .detail("RangeBegin", r->begin()) + .detail("RangeEnd", r->end()) + .detail("Version", r->value()); + } req.reply.send(RestoreCommonReply(self->id())); } @@ -145,7 +177,8 @@ void handleRestoreSysInfoRequest(const RestoreSysInfoRequest& req, Reference _parsePartitionedLogFileOnLoader( - NotifiedVersion* processedFileOffset, std::map::iterator kvOpsIter, + KeyRangeMap* pRangeVersions, NotifiedVersion* processedFileOffset, + std::map::iterator kvOpsIter, std::map::iterator samplesIter, Reference bc, RestoreAsset asset) { state Standalone buf = makeString(asset.len); state Reference file = wait(bc->readFile(asset.filename)); @@ -190,6 +223,11 @@ ACTOR static Future _parsePartitionedLogFileOnLoader( MutationRef mutation; rd >> mutation; + // Skip mutation whose commitVesion < range kv's version + if (logMutationTooOld(pRangeVersions, mutation, msgVersion.version)) { + continue; + } + // Should this mutation be skipped? if (mutation.param1 >= asset.range.end || (isRangeMutation(mutation) && mutation.param2 < asset.range.begin) || @@ -228,7 +266,8 @@ ACTOR static Future _parsePartitionedLogFileOnLoader( return Void(); } -ACTOR Future _processLoadingParam(LoadingParam param, Reference batchData, UID loaderID, +ACTOR Future _processLoadingParam(KeyRangeMap* pRangeVersions, LoadingParam param, + Reference batchData, UID loaderID, Reference bc) { // Temporary data structure for parsing log files into (version, ) // Must use StandAlone to save mutations, otherwise, the mutationref memory will be corrupted @@ -263,8 +302,8 @@ ACTOR Future _processLoadingParam(LoadingParam param, Reference _processLoadingParam(LoadingParam param, Referencecounters, param.asset); + _parseSerializedMutation(pRangeVersions, kvOpsPerLPIter, &mutationMap, samplesIter, &batchData->counters, + param.asset); } TraceEvent("FastRestoreLoaderProcessLoadingParamDone", loaderID).detail("LoadingParam", param.toString()); @@ -304,7 +344,8 @@ ACTOR Future handleLoadFileRequest(RestoreLoadFileRequest req, ReferencesampleMutations.find(req.param) == batchData->sampleMutations.end()); - batchData->processedFileParams[req.param] = _processLoadingParam(req.param, batchData, self->id(), self->bc); + batchData->processedFileParams[req.param] = + _processLoadingParam(&self->rangeVersions, req.param, batchData, self->id(), self->bc); isDuplicated = false; } else { TraceEvent("FastRestoreLoadFile", self->id()) @@ -669,7 +710,8 @@ bool concatenateBackupMutationForLogFile(std::map, Standal // we may not get the entire mutation list for the version encoded_list_of_mutations: // [mutation1][mutation2]...[mutationk], where // a mutation is encoded as [type:uint32_t][keyLength:uint32_t][valueLength:uint32_t][keyContent][valueContent] -void _parseSerializedMutation(std::map::iterator kvOpsIter, +void _parseSerializedMutation(KeyRangeMap* pRangeVersions, + std::map::iterator kvOpsIter, SerializedMutationListMap* pmutationMap, std::map::iterator samplesIter, LoaderCounters* cc, const RestoreAsset& asset) { @@ -709,6 +751,11 @@ void _parseSerializedMutation(std::map::ite MutationRef mutation((MutationRef::Type)type, KeyRef(k, kLen), KeyRef(v, vLen)); // Should this mutation be skipped? + // Skip mutation whose commitVesion < range kv's version + if (logMutationTooOld(pRangeVersions, mutation, commitVersion)) { + continue; + } + if (mutation.param1 >= asset.range.end || (isRangeMutation(mutation) && mutation.param2 < asset.range.begin) || (!isRangeMutation(mutation) && mutation.param1 < asset.range.begin)) { diff --git a/fdbserver/RestoreLoader.actor.h b/fdbserver/RestoreLoader.actor.h index 536fe2b9b9..d28321cc58 100644 --- a/fdbserver/RestoreLoader.actor.h +++ b/fdbserver/RestoreLoader.actor.h @@ -129,6 +129,8 @@ struct RestoreLoaderData : RestoreRoleData, public ReferenceCounted> batch; std::map> status; + KeyRangeMap rangeVersions; + Reference bc; // Backup container is used to read backup files Key bcUrl; // The url used to get the bc diff --git a/fdbserver/RestoreMaster.actor.cpp b/fdbserver/RestoreMaster.actor.cpp index 46db838633..05e6f8d68d 100644 --- a/fdbserver/RestoreMaster.actor.cpp +++ b/fdbserver/RestoreMaster.actor.cpp @@ -20,6 +20,7 @@ // This file implements the functions for RestoreMaster role +#include "fdbrpc/RangeMap.h" #include "fdbclient/NativeAPI.actor.h" #include "fdbclient/SystemData.h" #include "fdbclient/BackupAgent.actor.h" @@ -39,6 +40,8 @@ ACTOR static Future clearDB(Database cx); ACTOR static Future collectBackupFiles(Reference bc, std::vector* rangeFiles, std::vector* logFiles, Database cx, RestoreRequest request); +ACTOR static Future buildRangeVersions(KeyRangeMap* pRangeVersions, + std::vector* pRangeFiles, Key url); ACTOR static Future processRestoreRequest(Reference self, Database cx, RestoreRequest request); ACTOR static Future startProcessRestoreRequests(Reference self, Database cx); @@ -48,8 +51,8 @@ ACTOR static Future distributeWorkloadPerVersionBatch(Reference recruitRestoreRoles(Reference masterWorker, Reference masterData); -ACTOR static Future distributeRestoreSysInfo(Reference masterWorker, - Reference masterData); +ACTOR static Future distributeRestoreSysInfo(Reference masterData, + KeyRangeMap* pRangeVersions); ACTOR static Future>> collectRestoreRequests(Database cx); ACTOR static Future initializeVersionBatch(std::map appliersInterf, @@ -79,7 +82,7 @@ ACTOR Future startRestoreMaster(Reference masterWorker, actors.add(updateHeartbeatTime(self)); actors.add(checkRolesLiveness(self)); - wait(distributeRestoreSysInfo(masterWorker, self)); + // wait(distributeRestoreSysInfo(masterWorker, self)); wait(startProcessRestoreRequests(self, cx)); } catch (Error& e) { @@ -148,14 +151,27 @@ ACTOR Future recruitRestoreRoles(Reference masterWorker return Void(); } -ACTOR Future distributeRestoreSysInfo(Reference masterWorker, - Reference masterData) { +ACTOR Future distributeRestoreSysInfo(Reference masterData, + KeyRangeMap* pRangeVersions) { ASSERT(masterData.isValid()); ASSERT(!masterData->loadersInterf.empty()); RestoreSysInfo sysInfo(masterData->appliersInterf); + // Construct serializable KeyRange versions + Standalone>> rangeVersionsVec; + auto ranges = pRangeVersions->ranges(); + int i = 0; + for (auto r = ranges.begin(); r != ranges.end(); ++r) { + rangeVersionsVec.push_back(rangeVersionsVec.arena(), + std::make_pair(KeyRangeRef(r->begin(), r->end()), r->value())); + TraceEvent(SevDebug, "DistributeRangeVersions") + .detail("RangeIndex", i++) + .detail("RangeBegin", r->begin()) + .detail("RangeEnd", r->end()) + .detail("RangeVersion", r->value()); + } std::vector> requests; for (auto& loader : masterData->loadersInterf) { - requests.emplace_back(loader.first, RestoreSysInfoRequest(sysInfo)); + requests.emplace_back(loader.first, RestoreSysInfoRequest(sysInfo, rangeVersionsVec)); } TraceEvent("FastRestoreDistributeRestoreSysInfoToLoaders", masterData->id()) @@ -233,12 +249,13 @@ ACTOR static Future processRestoreRequest(Reference state std::vector rangeFiles; state std::vector logFiles; state std::vector allFiles; + state KeyRangeMap rangeVersions(MAX_VERSION, allKeys.end); state ActorCollection actors(false); self->initBackupContainer(request.url); // Get all backup files' description and save them to files - Version targetVersion = wait(collectBackupFiles(self->bc, &rangeFiles, &logFiles, cx, request)); + state Version targetVersion = wait(collectBackupFiles(self->bc, &rangeFiles, &logFiles, cx, request)); ASSERT(targetVersion > 0); std::sort(rangeFiles.begin(), rangeFiles.end()); @@ -247,6 +264,11 @@ ACTOR static Future processRestoreRequest(Reference std::tie(f2.endVersion, f2.beginVersion, f2.fileIndex, f2.fileName); }); + // Build range versions: version of key ranges in range file + wait(buildRangeVersions(&rangeVersions, &rangeFiles, request.url)); + + wait(distributeRestoreSysInfo(self, &rangeVersions)); + // Divide files into version batches. self->buildVersionBatches(rangeFiles, logFiles, &self->versionBatches, targetVersion); self->dumpVersionBatches(self->versionBatches); @@ -675,6 +697,69 @@ ACTOR static Future collectBackupFiles(Reference bc, return request.targetVersion; } +ACTOR static Future insertRangeVersion(KeyRangeMap* pRangeVersions, RestoreFileFR* file, + Reference bc) { + TraceEvent("FastRestoreMasterDecodeRangeVersion").detail("File", file->toString()); + Reference inFile = wait(bc->readFile(file->fileName)); + state bool beginKeySet = false; + Key beginKey; + Key endKey; + for (int64_t j = 0; j < file->fileSize; j += file->blockSize) { + int64_t len = std::min(file->blockSize, file->fileSize - j); + Standalone> blockData = wait(parallelFileRestore::decodeRangeFileBlock(inFile, j, len)); + if (!beginKeySet) { + beginKey = blockData.front().key; + } + endKey = blockData.back().key; + } + + // First and last key are the range for this file: endKey is exclusive + KeyRange fileRange = KeyRangeRef(beginKey.contents(), endKey.contents()); + TraceEvent("FastRestoreMasterInsertRangeVersion") + .detail("DecodedRangeFile", file->fileName) + .detail("KeyRange", fileRange) + .detail("Version", file->version) + .detail("DataSize", blockData.contents().size()); + // Update version for pRangeVersions's ranges in fileRange + auto ranges = pRangeVersions->modify(fileRange); + for (auto r = ranges.begin(); r != ranges.end(); ++r) { + r->value() = r->value() == MAX_VERSION ? file->version : std::max(r->value(), file->version); + } + + // Dump the new key ranges + ranges = pRangeVersions->ranges(); + int i = 0; + for (auto r = ranges.begin(); r != ranges.end(); ++r) { + TraceEvent(SevDebug, "RangeVersionsAfterUpdate") + .detail("File", file->toString()) + .detail("FileRange", fileRange.toString()) + .detail("FileVersion", file->version) + .detail("RangeIndex", i++) + .detail("RangeBegin", r->begin()) + .detail("RangeEnd", r->end()) + .detail("RangeVersion", r->value()); + } + + return Void(); +} + +ACTOR static Future buildRangeVersions(KeyRangeMap* pRangeVersions, + std::vector* pRangeFiles, Key url) { + Reference bc = IBackupContainer::openContainer(url.toString()); + + // Key ranges not in range files are empty; + // Assign highest version to avoid applying any mutation in these ranges + state int fileIndex = 0; + state std::vector> fInsertRangeVersions; + for (; fileIndex < pRangeFiles->size(); ++fileIndex) { + fInsertRangeVersions.push_back(insertRangeVersion(pRangeVersions, &pRangeFiles->at(fileIndex), bc)); + } + + wait(waitForAll(fInsertRangeVersions)); + + return Void(); +} + ACTOR static Future clearDB(Database cx) { wait(runRYWTransaction(cx, [](Reference tr) -> Future { tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); From 023372a2265fd0c8592d016557314123f8df2b04 Mon Sep 17 00:00:00 2001 From: Vishesh Yadav Date: Wed, 15 Apr 2020 19:39:56 -0700 Subject: [PATCH 1437/1604] FailMon: Mark peer failed after retrying --- fdbrpc/FlowTransport.actor.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/fdbrpc/FlowTransport.actor.cpp b/fdbrpc/FlowTransport.actor.cpp index 11ac552bcb..a85a5ca238 100644 --- a/fdbrpc/FlowTransport.actor.cpp +++ b/fdbrpc/FlowTransport.actor.cpp @@ -414,12 +414,13 @@ ACTOR Future delayedHealthUpdate(NetworkAddress address) { delayed = true; wait(delayJittered(FLOW_KNOBS->MAX_RECONNECTION_TIME * 2.0)); } else { - if (delayed) - TraceEvent("TooManyConnectionsClosedMarkAvailable") + if (delayed) { + TraceEvent("TooManyConnectionsClosedMarkAvailable") .detail("Dest", address) .detail("StartTime", start) .detail("TimeElapsed", now() - start) .detail("ClosedCount", FlowTransport::transport().healthMonitor()->closedConnectionsCount(address)); + } IFailureMonitor::failureMonitor().setStatus(address, FailureStatus(false)); break; } @@ -433,10 +434,7 @@ ACTOR Future connectionKeeper( Reference self, TraceEvent(SevDebug, "ConnectionKeeper", conn ? conn->getDebugID() : UID()) .detail("PeerAddr", self->destination) .detail("ConnSet", (bool)conn); - - if (FlowTransport::transport().getLocalAddress() == self->destination) { - return Never(); - } + ASSERT_WE_THINK(FlowTransport::transport().getLocalAddress() != self->destination); state Optional firstConnFailedTime = Optional(); loop { @@ -502,7 +500,6 @@ ACTOR Future connectionKeeper( Reference self, .suppressFor(1.0) .detail("PeerAddr", self->destination); - IFailureMonitor::failureMonitor().setStatus(self->destination, FailureStatus(true)); throw; } } else { @@ -590,6 +587,8 @@ ACTOR Future connectionKeeper( Reference self, conn->close(); conn = Reference(); + } else { + IFailureMonitor::failureMonitor().setStatus(self->destination, FailureStatus(true)); } // Clients might send more packets in response, which needs to go out on the next connection From da7d0093ee686a06a07264d186f1b328948ed59b Mon Sep 17 00:00:00 2001 From: Vishesh Yadav Date: Wed, 15 Apr 2020 19:40:48 -0700 Subject: [PATCH 1438/1604] Cleanup unused code --- fdbrpc/FlowTransport.actor.cpp | 8 -------- fdbrpc/FlowTransport.h | 1 - fdbrpc/HealthMonitor.actor.cpp | 14 -------------- fdbrpc/HealthMonitor.h | 2 -- 4 files changed, 25 deletions(-) diff --git a/fdbrpc/FlowTransport.actor.cpp b/fdbrpc/FlowTransport.actor.cpp index a85a5ca238..59a58e5b46 100644 --- a/fdbrpc/FlowTransport.actor.cpp +++ b/fdbrpc/FlowTransport.actor.cpp @@ -1372,11 +1372,3 @@ void FlowTransport::createInstance(bool isClient, uint64_t transportId) { HealthMonitor* FlowTransport::healthMonitor() { return &self->healthMonitor; } - -std::set FlowTransport::getPeers() const { - std::set result; - for (const auto& it : self->peers) { - result.insert(it.first); - } - return result; -} diff --git a/fdbrpc/FlowTransport.h b/fdbrpc/FlowTransport.h index a469034ff4..3fd39cadeb 100644 --- a/fdbrpc/FlowTransport.h +++ b/fdbrpc/FlowTransport.h @@ -207,7 +207,6 @@ public: Endpoint loadedEndpoint(const UID& token); HealthMonitor* healthMonitor(); - std::set getPeers() const; private: class TransportData* self; diff --git a/fdbrpc/HealthMonitor.actor.cpp b/fdbrpc/HealthMonitor.actor.cpp index 10841b11ad..bf03370fd2 100644 --- a/fdbrpc/HealthMonitor.actor.cpp +++ b/fdbrpc/HealthMonitor.actor.cpp @@ -40,20 +40,6 @@ void HealthMonitor::purgeOutdatedHistory() { } } -const std::deque>& HealthMonitor::getPeerClosedHistory() { - purgeOutdatedHistory(); - return peerClosedHistory; -} - -std::map HealthMonitor::getPeerStatus() { - purgeOutdatedHistory(); - std::map result; - for (const auto& peer : FlowTransport::transport().getPeers()) { - result[peer] = IFailureMonitor::failureMonitor().getState(peer).isAvailable(); - } - return result; -} - bool HealthMonitor::tooManyConnectionsClosed(const NetworkAddress& peerAddress) { purgeOutdatedHistory(); return peerClosedNum[peerAddress] > FLOW_KNOBS->HEALTH_MONITOR_CONNECTION_MAX_CLOSED; diff --git a/fdbrpc/HealthMonitor.h b/fdbrpc/HealthMonitor.h index 1c481c83e4..ef301cc7e1 100644 --- a/fdbrpc/HealthMonitor.h +++ b/fdbrpc/HealthMonitor.h @@ -29,10 +29,8 @@ class HealthMonitor { public: void reportPeerClosed(const NetworkAddress& peerAddress); - const std::deque>& getPeerClosedHistory(); bool tooManyConnectionsClosed(const NetworkAddress& peerAddress); int closedConnectionsCount(const NetworkAddress& peerAddress); - std::map getPeerStatus(); private: void purgeOutdatedHistory(); From 93400d25c839488d3ac6b84c89b7b63645790d37 Mon Sep 17 00:00:00 2001 From: tclinken Date: Wed, 15 Apr 2020 20:01:01 -0700 Subject: [PATCH 1439/1604] Added advanceversion command to fdbcli --- .../sphinx/source/command-line-interface.rst | 5 +++++ fdbcli/fdbcli.actor.cpp | 22 +++++++++++++++++++ fdbclient/ManagementAPI.actor.cpp | 20 +++++++++++++++++ fdbclient/ManagementAPI.actor.h | 2 ++ 4 files changed, 49 insertions(+) diff --git a/documentation/sphinx/source/command-line-interface.rst b/documentation/sphinx/source/command-line-interface.rst index ec49c4d517..0259525a7c 100644 --- a/documentation/sphinx/source/command-line-interface.rst +++ b/documentation/sphinx/source/command-line-interface.rst @@ -167,6 +167,11 @@ getversion The ``getversion`` command fetches the current read version of the cluster or currently running transaction. +advanceversion +-------------- + +Forces the cluster to recover at the specified version. If the specified version is larger than the current version of the cluster, the cluster version is advanced to the specified version via a forced recovery. + help ---- diff --git a/fdbcli/fdbcli.actor.cpp b/fdbcli/fdbcli.actor.cpp index fffb018c20..bd2ac38913 100644 --- a/fdbcli/fdbcli.actor.cpp +++ b/fdbcli/fdbcli.actor.cpp @@ -525,6 +525,11 @@ void initHelp() { helpMap["getversion"] = CommandHelp("getversion", "Fetch the current read version", "Displays the current read version of the database or currently running transaction."); + helpMap["advanceversion"] = CommandHelp( + "advanceversion ", "Force the cluster to recover at the specified version", + "Forces the cluster to recover at the specified version. If the specified version is larger than the current " + "version of the cluster, the cluster version is advanced " + "to the specified version via a forced recovery."); helpMap["reset"] = CommandHelp( "reset", "reset the current transaction", @@ -3217,6 +3222,23 @@ ACTOR Future cli(CLIOptions opt, LineNoise* plinenoise) { continue; } + if (tokencmp(tokens[0], "advanceversion")) { + if (tokens.size() != 2) { + printUsage(tokens[0]); + is_error = true; + } else { + Version v; + int n = 0; + if (sscanf(tokens[1].toString().c_str(), "%ld%n", &v, &n) != 1 || n != tokens[1].size()) { + printUsage(tokens[0]); + is_error = true; + } else { + wait(makeInterruptable(advanceVersion(db, v))); + } + } + continue; + } + if (tokencmp(tokens[0], "kill")) { getTransaction(db, tr, options, intrans); if (tokens.size() == 1) { diff --git a/fdbclient/ManagementAPI.actor.cpp b/fdbclient/ManagementAPI.actor.cpp index 77f2cc7a86..06aff74ff2 100644 --- a/fdbclient/ManagementAPI.actor.cpp +++ b/fdbclient/ManagementAPI.actor.cpp @@ -1803,6 +1803,26 @@ ACTOR Future checkDatabaseLock( Reference tr, U return Void(); } +ACTOR Future advanceVersion(Database cx, Version v) { + state Transaction tr(cx); + loop { + tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); + tr.setOption(FDBTransactionOptions::LOCK_AWARE); + try { + Version rv = wait(tr.getReadVersion()); + if (rv <= v) { + tr.set(minRequiredCommitVersionKey, BinaryWriter::toValue(v + 1, Unversioned())); + wait(tr.commit()); + } else { + printf("Current read version is %ld\n", rv); + return Void(); + } + } catch (Error& e) { + wait(tr.onError(e)); + } + } +} + ACTOR Future forceRecovery( Reference clusterFile, Key dcId ) { state Reference>> clusterInterface(new AsyncVar>); state Future leaderMon = monitorLeader(clusterFile, clusterInterface); diff --git a/fdbclient/ManagementAPI.actor.h b/fdbclient/ManagementAPI.actor.h index fe18d42717..a024f596c8 100644 --- a/fdbclient/ManagementAPI.actor.h +++ b/fdbclient/ManagementAPI.actor.h @@ -178,6 +178,8 @@ ACTOR Future unlockDatabase( Database cx, UID id ); ACTOR Future checkDatabaseLock( Transaction* tr, UID id ); ACTOR Future checkDatabaseLock( Reference tr, UID id ); +ACTOR Future advanceVersion(Database cx, Version v); + ACTOR Future setDDMode( Database cx, int mode ); ACTOR Future forceRecovery( Reference clusterFile, Standalone dcId ); From 1901f49b976494e1ab9f30f0b28e38e0031c669f Mon Sep 17 00:00:00 2001 From: Vishesh Yadav Date: Fri, 27 Mar 2020 01:49:35 -0700 Subject: [PATCH 1440/1604] Net2FileSystem: Add guards to honor DISABLE_POSIX_KERNEL_AIO - Adds some asserts in KAIO to ensure that when knob is set, we don't end up using KAIO in any case. - Fixes a bug where we initialize AsyncFileKAIO on Linux builds even when KAIO is disabled. This can cause problems in systems such as Windows Subsystem for Linux where KAIO is not supported. FIXES #2382 --- fdbrpc/AsyncFileKAIO.actor.h | 4 +++- fdbrpc/Net2FileSystem.cpp | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/fdbrpc/AsyncFileKAIO.actor.h b/fdbrpc/AsyncFileKAIO.actor.h index b38baf5da8..6f9f781862 100644 --- a/fdbrpc/AsyncFileKAIO.actor.h +++ b/fdbrpc/AsyncFileKAIO.actor.h @@ -97,6 +97,7 @@ public: #endif static Future> open( std::string filename, int flags, int mode, void* ignore ) { + ASSERT( !FLOW_KNOBS->DISABLE_POSIX_KERNEL_AIO ); ASSERT( flags & OPEN_UNBUFFERED ); if (flags & OPEN_LOCK) @@ -153,6 +154,7 @@ 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")); @@ -578,7 +580,7 @@ private: static Context ctx; explicit AsyncFileKAIO(int fd, int flags, std::string const& filename) : fd(fd), flags(flags), filename(filename), failed(false) { - + ASSERT( !FLOW_KNOBS->DISABLE_POSIX_KERNEL_AIO ); if( !g_network->isSimulated() ) { countFileLogicalWrites.init(LiteralStringRef("AsyncFile.CountFileLogicalWrites"), filename); countFileLogicalReads.init( LiteralStringRef("AsyncFile.CountFileLogicalReads"), filename); diff --git a/fdbrpc/Net2FileSystem.cpp b/fdbrpc/Net2FileSystem.cpp index d348edfa17..0331d6413a 100644 --- a/fdbrpc/Net2FileSystem.cpp +++ b/fdbrpc/Net2FileSystem.cpp @@ -93,6 +93,9 @@ Net2FileSystem::Net2FileSystem(double ioTimeout, std::string fileSystemPath) { Net2AsyncFile::init(); #ifdef __linux__ + if (FLOW_KNOBS->DISABLE_POSIX_KERNEL_AIO) + return; + AsyncFileKAIO::init( Reference(N2::ASIOReactor::getEventFD()), ioTimeout ); if (fileSystemPath.empty()) { From 27dd4a3f4201f4784840fd531bdaf2992888f32a Mon Sep 17 00:00:00 2001 From: Vishesh Yadav Date: Thu, 16 Apr 2020 01:21:24 -0700 Subject: [PATCH 1441/1604] Fix syntax errors in release notes --- documentation/sphinx/source/release-notes.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/sphinx/source/release-notes.rst b/documentation/sphinx/source/release-notes.rst index c48a0896bc..1ab89c2b6c 100644 --- a/documentation/sphinx/source/release-notes.rst +++ b/documentation/sphinx/source/release-notes.rst @@ -28,8 +28,8 @@ Bindings * Java: Introduced ``keyAfter`` utility function that can be used to create the immediate next key for a given byte array. `(PR #2458) `_ * C: The ``FDBKeyValue`` struct's ``key`` and ``value`` members have changed type from ``void*`` to ``uint8_t*``. `(PR #2622) `_ * Deprecated ``enable_slow_task_profiling`` transaction option and replaced it with ``enable_run_loop_profiling``. `(PR #2608) `_ -* Go: Added a `Close` function to `RangeIterator` which **must** be called to free resources returned from `Transaction.GetRange`. `(PR #1910) `_. -* Go: Finalizers are no longer used to clean up native resources. `Future` results are now copied from the native heap to the Go heap, and native resources are freed immediately. `(PR #1910) `_. +* Go: Added a ``Close`` function to ``RangeIterator`` which **must** be called to free resources returned from ``Transaction.GetRange``. `(PR #1910) `_. +* Go: Finalizers are no longer used to clean up native resources. ``Future`` results are now copied from the native heap to the Go heap, and native resources are freed immediately. `(PR #1910) `_. Other Changes From 841af731c558c4bf64674634595a9c3703bfb8b7 Mon Sep 17 00:00:00 2001 From: Vishesh Yadav Date: Thu, 16 Apr 2020 09:58:50 -0700 Subject: [PATCH 1442/1604] go: Rename deprecated fdb_future_get_version to fdb_future_get_int64 --- bindings/go/src/fdb/futures.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/go/src/fdb/futures.go b/bindings/go/src/fdb/futures.go index 7a0b869c58..aa58e7c81b 100644 --- a/bindings/go/src/fdb/futures.go +++ b/bindings/go/src/fdb/futures.go @@ -368,7 +368,7 @@ func (f *futureInt64) Get() (int64, error) { f.BlockUntilReady() var ver C.int64_t - if err := C.fdb_future_get_version(f.ptr, &ver); err != 0 { + if err := C.fdb_future_get_int64(f.ptr, &ver); err != 0 { f.v = 0 f.e = Error{int(err)} return From 43f19bd463a844e9bcad6582ed7fdd31af5f7741 Mon Sep 17 00:00:00 2001 From: Vishesh Yadav Date: Thu, 16 Apr 2020 10:56:01 -0700 Subject: [PATCH 1443/1604] Don't skip filesystem check on when KAIO is disabled WSL the known Linux system where KAIO is not supported, can run these checks. --- fdbrpc/Net2FileSystem.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fdbrpc/Net2FileSystem.cpp b/fdbrpc/Net2FileSystem.cpp index 0331d6413a..2377ddfd7e 100644 --- a/fdbrpc/Net2FileSystem.cpp +++ b/fdbrpc/Net2FileSystem.cpp @@ -93,10 +93,8 @@ Net2FileSystem::Net2FileSystem(double ioTimeout, std::string fileSystemPath) { Net2AsyncFile::init(); #ifdef __linux__ - if (FLOW_KNOBS->DISABLE_POSIX_KERNEL_AIO) - return; - - AsyncFileKAIO::init( Reference(N2::ASIOReactor::getEventFD()), ioTimeout ); + if (!FLOW_KNOBS->DISABLE_POSIX_KERNEL_AIO) + AsyncFileKAIO::init( Reference(N2::ASIOReactor::getEventFD()), ioTimeout ); if (fileSystemPath.empty()) { checkFileSystem = false; From 992002cf3415654e0d683f3501c69e3f927fb836 Mon Sep 17 00:00:00 2001 From: Vishesh Yadav Date: Thu, 16 Apr 2020 14:04:32 -0700 Subject: [PATCH 1444/1604] FlowTransport: don't start connectionKeeper for local peer getOrOpenPeer() is used for local addresses sometimes, which ends up starting connectionKeeper() which is unnecessary. --- fdbrpc/FlowTransport.actor.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/fdbrpc/FlowTransport.actor.cpp b/fdbrpc/FlowTransport.actor.cpp index 59a58e5b46..52be77561a 100644 --- a/fdbrpc/FlowTransport.actor.cpp +++ b/fdbrpc/FlowTransport.actor.cpp @@ -451,7 +451,7 @@ ACTOR Future connectionKeeper( Reference self, wait (self->dataToSend.onTrigger()); } - ASSERT( self->destination.isPublic() ); + ASSERT(self->destination.isPublic()); self->outgoingConnectionIdle = false; wait(delayJittered( std::max(0.0, self->lastConnectTime + self->reconnectionDelay - @@ -1074,7 +1074,7 @@ Reference TransportData::getOrOpenPeer( NetworkAddress const& address, boo auto peer = getPeer(address); if(!peer) { peer = Reference( new Peer(this, address) ); - if(startConnectionKeeper) { + if(startConnectionKeeper && !isLocalAddress(address)) { peer->connect = connectionKeeper(peer); } peers[address] = peer; @@ -1367,6 +1367,13 @@ void FlowTransport::createInstance(bool isClient, uint64_t transportId) { g_network->setGlobal(INetwork::enFlowTransport, (flowGlobalType) new FlowTransport(transportId)); g_network->setGlobal(INetwork::enNetworkAddressFunc, (flowGlobalType) &FlowTransport::getGlobalLocalAddress); g_network->setGlobal(INetwork::enNetworkAddressesFunc, (flowGlobalType) &FlowTransport::getGlobalLocalAddresses); + + // Mark ourselves as avaiable in FailureMonitor + const auto& localAddresses = FlowTransport::transport().getLocalAddresses(); + IFailureMonitor::failureMonitor().setStatus(localAddresses.address, FailureStatus(false)); + if (localAddresses.secondaryAddress.present()) { + IFailureMonitor::failureMonitor().setStatus(localAddresses.secondaryAddress.get(), FailureStatus(false)); + } } HealthMonitor* FlowTransport::healthMonitor() { From 9afa3545b52d8dbd587e44b1185695bf97641727 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Thu, 16 Apr 2020 21:55:15 +0000 Subject: [PATCH 1445/1604] Prevent server and clients rpm's from conflicting Before, the file /etc/foundationdb conflicted between the server and clients package, and /usr/lib64/cmake and /usr/lib64/pkgconfig conflicted with some system packages. --- cmake/InstallLayout.cmake | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmake/InstallLayout.cmake b/cmake/InstallLayout.cmake index 75da8d5c64..b1fb33c68e 100644 --- a/cmake/InstallLayout.cmake +++ b/cmake/InstallLayout.cmake @@ -320,9 +320,14 @@ set(CPACK_RPM_SERVER-EL7_USER_FILELIST "%config(noreplace) /etc/foundationdb/foundationdb.conf" "%attr(0700,foundationdb,foundationdb) /var/log/foundationdb" "%attr(0700, foundationdb, foundationdb) /var/lib/foundationdb") +set(CPACK_RPM_CLIENTS-EL6_USER_FILELIST "%dir /etc/foundationdb") +set(CPACK_RPM_CLIENTS-EL7_USER_FILELIST "%dir /etc/foundationdb") set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION "/usr/sbin" "/usr/share/java" + "/usr/lib64/cmake" + "/etc/foundationdb" + "/usr/lib64/pkgconfig" "/usr/lib64/python2.7" "/usr/lib64/python2.7/site-packages" "/var" From 7fa2a538a648c4aef1b9b2a79a7ddf823df8af00 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Thu, 16 Apr 2020 22:03:56 +0000 Subject: [PATCH 1446/1604] Actually redirect to $HOME/bin/clangd --- build/gen_dev_docker.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/gen_dev_docker.sh b/build/gen_dev_docker.sh index 37efabe4f4..03b171f969 100755 --- a/build/gen_dev_docker.sh +++ b/build/gen_dev_docker.sh @@ -75,7 +75,7 @@ sudo docker run --rm `# delete (temporary) image after return` \\ ${image} "\$@" EOF -cat < $HOME/bin/clangd #!/usr/bin/bash fdb-dev scl enable devtoolset-8 rh-python36 rh-ruby24 -- clangd From 9750d471d298184a2d8b00ea14c558ee963555f6 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Wed, 15 Apr 2020 19:45:59 -0700 Subject: [PATCH 1447/1604] Design:Fix error in backup data format example --- design/backup-dataFormat.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/design/backup-dataFormat.md b/design/backup-dataFormat.md index c3e13def0c..5847c3a98b 100644 --- a/design/backup-dataFormat.md +++ b/design/backup-dataFormat.md @@ -44,7 +44,7 @@ A data block is encoded as follows: `Header startKey k1v1 k2v2 Padding`. H = header P = padding a...z = keys v = value | = block boundary - Encoded file: H a cv dv ev P | H e ev fv gv hv P | H h hv iv jv z + Encoded file: H a cv dv P | H e ev fv gv hv P | H h hv iv jv z Decoded in blocks yields: Block 1: range [a, e) with kv pairs cv, dv Block 2: range [e, h) with kv pairs ev, fv, gv From 0342a046bb128a17204e975c9181622e8e029205 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Fri, 17 Apr 2020 09:30:36 -0700 Subject: [PATCH 1448/1604] FastRestore:Master:Use minRangeVersion to initialize rangeVersions --- design/backup-dataFormat.md | 46 +++++++++++++++---------------- fdbserver/RestoreMaster.actor.cpp | 28 +++++++++++-------- 2 files changed, 39 insertions(+), 35 deletions(-) diff --git a/design/backup-dataFormat.md b/design/backup-dataFormat.md index 5847c3a98b..73942e41ef 100644 --- a/design/backup-dataFormat.md +++ b/design/backup-dataFormat.md @@ -1,10 +1,10 @@ ## FDB Backup Data Format ### Introduction -This document describes the data format of the files generated by FoundationDB (FDB) backup procedure. -The target readers who may benefit from reading this document are: -* who make changes on the current backup or restore procedure; -* who writes tools to digest the backup data for analytical purpose; +This document describes the data format of the files generated by FoundationDB (FDB) backup procedure. +The target readers who may benefit from reading this document are: +* who make changes on the current backup or restore procedure; +* who writes tools to digest the backup data for analytical purpose; * who wants to understand the internals of how backup and restore works. The description of the backup data format is based on FDB 5.2 to FDB 6.1. The backup data format may (although unlikely) change after FDB 6.1. @@ -12,27 +12,27 @@ The description of the backup data format is based on FDB 5.2 to FDB 6.1. The ba ### Files generated by backup The backup procedure generates two types of files: range files and log files. -* A range file describes key-value pairs in a range at the version when the backup process takes a snapshot of the range. Different range files have data for different ranges at different versions. -* A log file describes the mutations taken from a version v1 to v2 during the backup procedure. +* A range file describes key-value pairs in a range at the version when the backup process takes a snapshot of the range. Different range files have data for different ranges at different versions. +* A log file describes the mutations taken from a version v1 to v2 during the backup procedure. With the key-value pairs in range file and the mutations in log file, the restore procedure can restore the database into a consistent state at a user-provided version vk if the backup data is claimed by the restore as restorable at vk. (The details of determining if a set of backup data is restorable at a version is out of scope of this document and can be found at [backup.md](https://github.com/xumengpanda/foundationdb/blob/cd873831ecd18653c5bf459d6f72d14a99b619c4/design/backup.md). ### Filename conventions -The backup files will be saved in a directory (i.e., url) specified by users. Under the directory, the range files are in the `snapshots` folder. The log files are in the `logs` folder. +The backup files will be saved in a directory (i.e., url) specified by users. Under the directory, the range files are in the `snapshots` folder. The log files are in the `logs` folder. The convention of the range filename is ` snapshots/snapshot,beginVersion,beginVersion,blockSize`, where `beginVersion` is the version when the key-values in the range file are recorded, and blockSize is the size of data blocks in the range file. The convention of the log filename is `logs/,versionPrefix/log,beginVersion,endVersion,randomUID, blockSize`, where the versionPrefix is a 2-level path (`x/y`) where beginVersion should go such that `x/y/*` contains (10^smallestBucket) possible versions; the randomUID is a random UID, the `beginVersion` and `endVersion` are the version range (left inclusive, right exclusive) when the mutations are recorded; and the `blockSize` is the data block size in the log file. We will use an example to explain what each field in the range and log filename means. -Suppose under the backup directory, we have a range file `snapshots/snapshot,78994177,78994177,97` and a log file `logs/0000/0000/log,78655645,98655645,149a0bdfedecafa2f648219d5eba816e,1048576`. +Suppose under the backup directory, we have a range file `snapshots/snapshot,78994177,78994177,97` and a log file `logs/0000/0000/log,78655645,98655645,149a0bdfedecafa2f648219d5eba816e,1048576`. The range file’s filename tells us that all key-value pairs decoded from the file are the KV value in DB at the version `78994177`. The data block size is `97` bytes. -The log file’s filename tells us that the mutations in the log file were the mutations in the DB during the version range `[78655645,98655645)`, and the data block size is `1048576` bytes. +The log file’s filename tells us that the mutations in the log file were the mutations in the DB during the version range `[78655645,98655645)`, and the data block size is `1048576` bytes. -### Data format in a range file -A range file can have one to many data blocks. Each data block has a set of key-value pairs. +### Data format in a range file +A range file can have one to many data blocks. Each data block has a set of key-value pairs. A data block is encoded as follows: `Header startKey k1v1 k2v2 Padding`. @@ -58,19 +58,19 @@ The code that decodes a range block is in `ACTOR Future>> decodeLogFileBlock(Reference file, int64_t offset, int len)`. ### Endianness -When the restore decodes a serialized integer from the backup file, it needs to convert the serialized value from big endian to little endian. +When the restore decodes a serialized integer from the backup file, it needs to convert the serialized value from big endian to little endian. -The reason is as follows: When the backup procedure transfers the data to remote blob store, the backup data is encoded in big endian. However, FoundationDB currently only run on little endian machines. The endianness affects the interpretation of an integer, so we must perform the endianness convertion. \ No newline at end of file +The reason is as follows: When the backup procedure transfers the data to remote blob store, the backup data is encoded in big endian. However, FoundationDB currently only run on little endian machines. The endianness affects the interpretation of an integer, so we must perform the endianness convertion. \ No newline at end of file diff --git a/fdbserver/RestoreMaster.actor.cpp b/fdbserver/RestoreMaster.actor.cpp index 05e6f8d68d..901b96cfeb 100644 --- a/fdbserver/RestoreMaster.actor.cpp +++ b/fdbserver/RestoreMaster.actor.cpp @@ -38,8 +38,8 @@ ACTOR static Future clearDB(Database cx); ACTOR static Future collectBackupFiles(Reference bc, std::vector* rangeFiles, - std::vector* logFiles, Database cx, - RestoreRequest request); + std::vector* logFiles, Version* minRangeVersion, + Database cx, RestoreRequest request); ACTOR static Future buildRangeVersions(KeyRangeMap* pRangeVersions, std::vector* pRangeFiles, Key url); @@ -249,13 +249,14 @@ ACTOR static Future processRestoreRequest(Reference state std::vector rangeFiles; state std::vector logFiles; state std::vector allFiles; - state KeyRangeMap rangeVersions(MAX_VERSION, allKeys.end); + state Version minRangeVersion = MAX_VERSION; state ActorCollection actors(false); self->initBackupContainer(request.url); // Get all backup files' description and save them to files - state Version targetVersion = wait(collectBackupFiles(self->bc, &rangeFiles, &logFiles, cx, request)); + state Version targetVersion = + wait(collectBackupFiles(self->bc, &rangeFiles, &logFiles, &minRangeVersion, cx, request)); ASSERT(targetVersion > 0); std::sort(rangeFiles.begin(), rangeFiles.end()); @@ -265,6 +266,7 @@ ACTOR static Future processRestoreRequest(Reference }); // Build range versions: version of key ranges in range file + state KeyRangeMap rangeVersions(minRangeVersion, allKeys.end); wait(buildRangeVersions(&rangeVersions, &rangeFiles, request.url)); wait(distributeRestoreSysInfo(self, &rangeVersions)); @@ -637,8 +639,8 @@ ACTOR static Future>> collectRestoreRequest // Collect the backup files' description into output_files by reading the backupContainer bc. // Returns the restore target version. ACTOR static Future collectBackupFiles(Reference bc, std::vector* rangeFiles, - std::vector* logFiles, Database cx, - RestoreRequest request) { + std::vector* logFiles, Version* minRangeVersion, + Database cx, RestoreRequest request) { state BackupDescription desc = wait(bc->describeBackup()); // Convert version to real time for operators to read the BackupDescription desc. @@ -667,6 +669,7 @@ ACTOR static Future collectBackupFiles(Reference bc, std::set uniqueRangeFiles; std::set uniqueLogFiles; + *minRangeVersion = MAX_VERSION; for (const RangeFile& f : restorable.get().ranges) { TraceEvent("FastRestoreMasterPhaseCollectBackupFiles").detail("RangeFile", f.toString()); if (f.fileSize <= 0) { @@ -675,6 +678,7 @@ ACTOR static Future collectBackupFiles(Reference bc, RestoreFileFR file(f); TraceEvent("FastRestoreMasterPhaseCollectBackupFiles").detail("RangeFileFR", file.toString()); uniqueRangeFiles.insert(file); + *minRangeVersion = std::min(*minRangeVersion, file.version); } for (const LogFile& f : restorable.get().logs) { TraceEvent("FastRestoreMasterPhaseCollectBackupFiles").detail("LogFile", f.toString()); @@ -700,11 +704,12 @@ ACTOR static Future collectBackupFiles(Reference bc, ACTOR static Future insertRangeVersion(KeyRangeMap* pRangeVersions, RestoreFileFR* file, Reference bc) { TraceEvent("FastRestoreMasterDecodeRangeVersion").detail("File", file->toString()); - Reference inFile = wait(bc->readFile(file->fileName)); + state Reference inFile = wait(bc->readFile(file->fileName)); state bool beginKeySet = false; - Key beginKey; - Key endKey; - for (int64_t j = 0; j < file->fileSize; j += file->blockSize) { + state Key beginKey; + state Key endKey; + state int64_t j = 0; + for (; j < file->fileSize; j += file->blockSize) { int64_t len = std::min(file->blockSize, file->fileSize - j); Standalone> blockData = wait(parallelFileRestore::decodeRangeFileBlock(inFile, j, len)); if (!beginKeySet) { @@ -718,8 +723,7 @@ ACTOR static Future insertRangeVersion(KeyRangeMap* pRangeVersion TraceEvent("FastRestoreMasterInsertRangeVersion") .detail("DecodedRangeFile", file->fileName) .detail("KeyRange", fileRange) - .detail("Version", file->version) - .detail("DataSize", blockData.contents().size()); + .detail("Version", file->version); // Update version for pRangeVersions's ranges in fileRange auto ranges = pRangeVersions->modify(fileRange); for (auto r = ranges.begin(); r != ranges.end(); ++r) { From 2d9e9a050292a75c2e6626656ac26f3e53ae0dd8 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Fri, 17 Apr 2020 10:02:53 -0700 Subject: [PATCH 1449/1604] FastRestore:Use knob to guard the expensive way to get range versions --- fdbserver/Knobs.cpp | 5 +++-- fdbserver/Knobs.h | 1 + fdbserver/RestoreMaster.actor.cpp | 13 ++++++++++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index 59d2469a1e..2827bff3dc 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -581,8 +581,9 @@ void ServerKnobs::initialize(bool randomize, ClientKnobs* clientKnobs, bool isSi init( FASTRESTORE_WAIT_FOR_MEMORY_LATENCY, 10 ); if( randomize && BUGGIFY ) { FASTRESTORE_WAIT_FOR_MEMORY_LATENCY = 60; } init( FASTRESTORE_HEARTBEAT_DELAY, 10 ); if( randomize && BUGGIFY ) { FASTRESTORE_HEARTBEAT_DELAY = deterministicRandom()->random01() * 120 + 2; } init( FASTRESTORE_HEARTBEAT_MAX_DELAY, 10 ); if( randomize && BUGGIFY ) { FASTRESTORE_HEARTBEAT_MAX_DELAY = FASTRESTORE_HEARTBEAT_DELAY * 10; } - init( FASTRESTORE_APPLIER_FETCH_KEYS_SIZE, 100 ); if( randomize && BUGGIFY ) { FASTRESTORE_APPLIER_FETCH_KEYS_SIZE = deterministicRandom()->random01() * 10240 + 1; } - init( FASTRESTORE_LOADER_SEND_MUTATION_MSG_BYTES, 1.0 * 1024.0 * 1024.0 ); if( randomize && BUGGIFY ) { FASTRESTORE_LOADER_SEND_MUTATION_MSG_BYTES = deterministicRandom()->random01() * 10.0 * 1024.0 * 1024.0 + 1; } + init( FASTRESTORE_APPLIER_FETCH_KEYS_SIZE, 100 ); if( randomize && BUGGIFY ) { FASTRESTORE_APPLIER_FETCH_KEYS_SIZE = deterministicRandom()->random01() * 10240 + 1; } + init( FASTRESTORE_LOADER_SEND_MUTATION_MSG_BYTES, 1.0 * 1024.0 * 1024.0 ); if( randomize && BUGGIFY ) { FASTRESTORE_LOADER_SEND_MUTATION_MSG_BYTES = deterministicRandom()->random01() * 10.0 * 1024.0 * 1024.0 + 1; } + init( FASTRESTORE_GET_RANGE_VERSIONS_EXPENSIVE, false ); if( randomize && BUGGIFY ) { FASTRESTORE_GET_RANGE_VERSIONS_EXPENSIVE = true; } // clang-format on diff --git a/fdbserver/Knobs.h b/fdbserver/Knobs.h index 042e232e10..37a907782a 100644 --- a/fdbserver/Knobs.h +++ b/fdbserver/Knobs.h @@ -521,6 +521,7 @@ public: int64_t FASTRESTORE_HEARTBEAT_MAX_DELAY; // master claim a node is down if no heart beat from the node for this delay int64_t FASTRESTORE_APPLIER_FETCH_KEYS_SIZE; // number of keys to fetch in a txn on applier int64_t FASTRESTORE_LOADER_SEND_MUTATION_MSG_BYTES; // desired size of mutation message sent from loader to appliers + bool FASTRESTORE_GET_RANGE_VERSIONS_EXPENSIVE; // parse each range file to get (range, version) it has? ServerKnobs(); void initialize(bool randomize = false, ClientKnobs* clientKnobs = NULL, bool isSimulated = false); diff --git a/fdbserver/RestoreMaster.actor.cpp b/fdbserver/RestoreMaster.actor.cpp index 901b96cfeb..18c085d42e 100644 --- a/fdbserver/RestoreMaster.actor.cpp +++ b/fdbserver/RestoreMaster.actor.cpp @@ -267,7 +267,9 @@ ACTOR static Future processRestoreRequest(Reference // Build range versions: version of key ranges in range file state KeyRangeMap rangeVersions(minRangeVersion, allKeys.end); - wait(buildRangeVersions(&rangeVersions, &rangeFiles, request.url)); + if (SERVER_KNOBS->FASTRESTORE_GET_RANGE_VERSIONS_EXPENSIVE) { + wait(buildRangeVersions(&rangeVersions, &rangeFiles, request.url)); + } wait(distributeRestoreSysInfo(self, &rangeVersions)); @@ -701,6 +703,8 @@ ACTOR static Future collectBackupFiles(Reference bc, return request.targetVersion; } +// By the first and last block of *file to get (beginKey, endKey); +// set (beginKey, endKey) and file->version to pRangeVersions ACTOR static Future insertRangeVersion(KeyRangeMap* pRangeVersions, RestoreFileFR* file, Reference bc) { TraceEvent("FastRestoreMasterDecodeRangeVersion").detail("File", file->toString()); @@ -747,8 +751,15 @@ ACTOR static Future insertRangeVersion(KeyRangeMap* pRangeVersion return Void(); } +// Build the version skyline of snapshot ranges by parsing range files; +// Expensive and slow operation that should not run in real prod. ACTOR static Future buildRangeVersions(KeyRangeMap* pRangeVersions, std::vector* pRangeFiles, Key url) { + if (!g_network->isSimulated()) { + TraceEvent(SevError, "ExpensiveBuildRangeVersions") + .detail("Reason", "Parsing all range files is slow and memory intensive"); + return Void(); + } Reference bc = IBackupContainer::openContainer(url.toString()); // Key ranges not in range files are empty; From 4a05910c6ce10ff52d02d1a72aef5415325da0a9 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Fri, 17 Apr 2020 11:20:57 -0700 Subject: [PATCH 1450/1604] Buggify FASTRESTORE_GET_RANGE_VERSIONS_EXPENSIVE knob for true and false --- fdbserver/Knobs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index 2827bff3dc..fa7b9389c3 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -583,7 +583,7 @@ void ServerKnobs::initialize(bool randomize, ClientKnobs* clientKnobs, bool isSi init( FASTRESTORE_HEARTBEAT_MAX_DELAY, 10 ); if( randomize && BUGGIFY ) { FASTRESTORE_HEARTBEAT_MAX_DELAY = FASTRESTORE_HEARTBEAT_DELAY * 10; } init( FASTRESTORE_APPLIER_FETCH_KEYS_SIZE, 100 ); if( randomize && BUGGIFY ) { FASTRESTORE_APPLIER_FETCH_KEYS_SIZE = deterministicRandom()->random01() * 10240 + 1; } init( FASTRESTORE_LOADER_SEND_MUTATION_MSG_BYTES, 1.0 * 1024.0 * 1024.0 ); if( randomize && BUGGIFY ) { FASTRESTORE_LOADER_SEND_MUTATION_MSG_BYTES = deterministicRandom()->random01() * 10.0 * 1024.0 * 1024.0 + 1; } - init( FASTRESTORE_GET_RANGE_VERSIONS_EXPENSIVE, false ); if( randomize && BUGGIFY ) { FASTRESTORE_GET_RANGE_VERSIONS_EXPENSIVE = true; } + init( FASTRESTORE_GET_RANGE_VERSIONS_EXPENSIVE, false ); if( randomize && BUGGIFY ) { FASTRESTORE_GET_RANGE_VERSIONS_EXPENSIVE = deterministicRandom()->random01() < 0.5 ? true : false; } // clang-format on From 52dba96f070513c72bd2abf2601dbf05ca75902a Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Fri, 17 Apr 2020 13:38:09 -0700 Subject: [PATCH 1451/1604] Fix:Segmentation fault when get status in BackupAndParallelRestore workload --- fdbserver/RestoreMaster.actor.cpp | 11 +++++++++++ .../BackupAndParallelRestoreCorrectness.actor.cpp | 8 +++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/fdbserver/RestoreMaster.actor.cpp b/fdbserver/RestoreMaster.actor.cpp index 18c085d42e..00973ac069 100644 --- a/fdbserver/RestoreMaster.actor.cpp +++ b/fdbserver/RestoreMaster.actor.cpp @@ -269,6 +269,17 @@ ACTOR static Future processRestoreRequest(Reference state KeyRangeMap rangeVersions(minRangeVersion, allKeys.end); if (SERVER_KNOBS->FASTRESTORE_GET_RANGE_VERSIONS_EXPENSIVE) { wait(buildRangeVersions(&rangeVersions, &rangeFiles, request.url)); + } else { + // Debug purpose, dump range versions + auto ranges = rangeVersions.ranges(); + int i = 0; + for (auto r = ranges.begin(); r != ranges.end(); ++r) { + TraceEvent(SevDebug, "SingleRangeVersion") + .detail("RangeIndex", i++) + .detail("RangeBegin", r->begin()) + .detail("RangeEnd", r->end()) + .detail("RangeVersion", r->value()); + } } wait(distributeRestoreSysInfo(self, &rangeVersions)); diff --git a/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp b/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp index aa94d9c2b7..b941d2e920 100644 --- a/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp +++ b/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp @@ -463,9 +463,11 @@ struct BackupAndParallelRestoreCorrectnessWorkload : TestWorkload { TraceEvent("BAFRW_Restore", randomID) .detail("LastBackupContainer", lastBackupContainer->getURL()) - .detail("MinRestorableVersion", desc.minRestorableVersion.get()) - .detail("MaxRestorableVersion", desc.maxRestorableVersion.get()) - .detail("ContiguousLogEnd", desc.contiguousLogEnd.get()) + .detail("MinRestorableVersion", + desc.minRestorableVersion.present() ? desc.minRestorableVersion.get() : -1) + .detail("MaxRestorableVersion", + desc.maxRestorableVersion.present() ? desc.maxRestorableVersion.get() : -1) + .detail("ContiguousLogEnd", desc.contiguousLogEnd.present() ? desc.contiguousLogEnd.get() : -1) .detail("TargetVersion", targetVersion); state std::vector> restores; From b667d5442f24ab29ccbfa144e344755cd8bfb3f7 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Fri, 17 Apr 2020 13:47:54 -0700 Subject: [PATCH 1452/1604] fix: not all removed endpoints were actually removed --- fdbserver/ClusterController.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/ClusterController.actor.cpp b/fdbserver/ClusterController.actor.cpp index dcd2a8211c..ca6dfca5d7 100644 --- a/fdbserver/ClusterController.actor.cpp +++ b/fdbserver/ClusterController.actor.cpp @@ -3041,7 +3041,7 @@ ACTOR Future dbInfoUpdater( ClusterControllerData* self ) { uniquify(self->updateDBInfoEndpoints); for(int i = 0; i < self->updateDBInfoEndpoints.size(); i++) { if(self->removedDBInfoEndpoints.count(self->updateDBInfoEndpoints[i])) { - self->updateDBInfoEndpoints[i] = self->updateDBInfoEndpoints.back(); + self->updateDBInfoEndpoints[i--] = self->updateDBInfoEndpoints.back(); self->updateDBInfoEndpoints.pop_back(); } } From 14406ad940e138f7cafb33183bc3316c122085d3 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Fri, 17 Apr 2020 13:51:39 -0700 Subject: [PATCH 1453/1604] BackupAndParallelRestoreCorrectness:Assert on backup validity --- .../workloads/BackupAndParallelRestoreCorrectness.actor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp b/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp index b941d2e920..5f3b056354 100644 --- a/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp +++ b/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp @@ -442,6 +442,7 @@ struct BackupAndParallelRestoreCorrectnessWorkload : TestWorkload { auto container = IBackupContainer::openContainer(lastBackupContainer->getURL()); BackupDescription desc = wait(container->describeBackup()); ASSERT(self->usePartitionedLogs == desc.partitioned); + ASSERT(desc.minRestorableVersion.present()); // We must have a valid backup now. state Version targetVersion = -1; if (desc.maxRestorableVersion.present()) { From 4c51e0a05b6ce579f30a4d99d651510188e1b355 Mon Sep 17 00:00:00 2001 From: Evan Tschannen <36455792+etschannen@users.noreply.github.com> Date: Fri, 17 Apr 2020 14:44:58 -0700 Subject: [PATCH 1454/1604] Update fdbserver/worker.actor.cpp Co-Authored-By: A.J. Beamon --- fdbserver/worker.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/worker.actor.cpp b/fdbserver/worker.actor.cpp index 0f365d88cc..9beffc5ece 100644 --- a/fdbserver/worker.actor.cpp +++ b/fdbserver/worker.actor.cpp @@ -1057,7 +1057,7 @@ ACTOR Future workerServer( if(!ccInterface->get().present() || localInfo.clusterInterface != ccInterface->get().get()) { notUpdated = interf.updateServerDBInfo.getEndpoint(); } - if(ccInterface->get().present() && localInfo.clusterInterface == ccInterface->get().get() && (localInfo.infoGeneration > dbInfo->get().infoGeneration || dbInfo->get().clusterInterface != ccInterface->get().get())) { + else if(localInfo.infoGeneration > dbInfo->get().infoGeneration || dbInfo->get().clusterInterface != ccInterface->get().get()) { TraceEvent("GotServerDBInfoChange").detail("ChangeID", localInfo.id).detail("MasterID", localInfo.master.id()) .detail("RatekeeperID", localInfo.ratekeeper.present() ? localInfo.ratekeeper.get().id() : UID()) From 7e890cb6be7e2751997523be5b054e0e0cfe7392 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Fri, 17 Apr 2020 15:00:07 -0700 Subject: [PATCH 1455/1604] FastRestore:Minor simplify code --- fdbserver/RestoreMaster.actor.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fdbserver/RestoreMaster.actor.cpp b/fdbserver/RestoreMaster.actor.cpp index 00973ac069..657862b71d 100644 --- a/fdbserver/RestoreMaster.actor.cpp +++ b/fdbserver/RestoreMaster.actor.cpp @@ -82,8 +82,6 @@ ACTOR Future startRestoreMaster(Reference masterWorker, actors.add(updateHeartbeatTime(self)); actors.add(checkRolesLiveness(self)); - // wait(distributeRestoreSysInfo(masterWorker, self)); - wait(startProcessRestoreRequests(self, cx)); } catch (Error& e) { if (e.code() != error_code_operation_cancelled) { @@ -163,7 +161,7 @@ ACTOR Future distributeRestoreSysInfo(Reference masterD for (auto r = ranges.begin(); r != ranges.end(); ++r) { rangeVersionsVec.push_back(rangeVersionsVec.arena(), std::make_pair(KeyRangeRef(r->begin(), r->end()), r->value())); - TraceEvent(SevDebug, "DistributeRangeVersions") + TraceEvent("DistributeRangeVersions") .detail("RangeIndex", i++) .detail("RangeBegin", r->begin()) .detail("RangeEnd", r->end()) @@ -742,7 +740,7 @@ ACTOR static Future insertRangeVersion(KeyRangeMap* pRangeVersion // Update version for pRangeVersions's ranges in fileRange auto ranges = pRangeVersions->modify(fileRange); for (auto r = ranges.begin(); r != ranges.end(); ++r) { - r->value() = r->value() == MAX_VERSION ? file->version : std::max(r->value(), file->version); + r->value() = std::max(r->value(), file->version); } // Dump the new key ranges From 33efb9ec97e9177b676f0f7bfef19be6be99fb83 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Fri, 17 Apr 2020 15:05:01 -0700 Subject: [PATCH 1456/1604] code cleanup based on review comments --- fdbserver/ClusterController.actor.cpp | 28 +++++++++++---------------- fdbserver/DataDistribution.actor.cpp | 6 +++--- fdbserver/Status.actor.cpp | 8 ++++---- fdbserver/Status.h | 9 ++++++++- fdbserver/WorkerInterface.actor.h | 2 +- 5 files changed, 27 insertions(+), 26 deletions(-) diff --git a/fdbserver/ClusterController.actor.cpp b/fdbserver/ClusterController.actor.cpp index ca6dfca5d7..f7601c5f98 100644 --- a/fdbserver/ClusterController.actor.cpp +++ b/fdbserver/ClusterController.actor.cpp @@ -1326,7 +1326,7 @@ public: Future outstandingRequestChecker; Future outstandingRemoteRequestChecker; AsyncTrigger updateDBInfo; - std::vector updateDBInfoEndpoints; + std::set updateDBInfoEndpoints; std::set removedDBInfoEndpoints; DBInfo db; @@ -1732,7 +1732,7 @@ ACTOR Future workerAvailabilityWatch( WorkerInterface worker, ProcessClass ? Never() : waitFailureClient(worker.waitFailure, SERVER_KNOBS->WORKER_FAILURE_TIME); cluster->updateWorkerList.set( worker.locality.processId(), ProcessData(worker.locality, startingClass, worker.stableAddress()) ); - cluster->updateDBInfoEndpoints.push_back(worker.updateServerDBInfo.getEndpoint()); + cluster->updateDBInfoEndpoints.insert(worker.updateServerDBInfo.getEndpoint()); cluster->updateDBInfo.trigger(); // This switching avoids a race where the worker can be added to id_worker map after the workerAvailabilityWatch fails for the worker. wait(delay(0)); @@ -2395,12 +2395,12 @@ ACTOR Future statusServer(FutureStream< StatusRequest> requests, // Get status but trap errors to send back to client. vector workers; - std::vector>>> workerIssues; + std::vector workerIssues; for(auto& it : self->id_worker) { workers.push_back(it.second.details); if(it.second.issues.size()) { - workerIssues.push_back(std::make_pair(it.second.details.interf.address(), it.second.issues)); + workerIssues.push_back(ProcessIssues(it.second.details.interf.address(), it.second.issues)); } } @@ -3032,30 +3032,24 @@ ACTOR Future dbInfoUpdater( ClusterControllerData* self ) { when(wait(dbInfoChange)) {} } + UpdateServerDBInfoRequest req; if(dbInfoChange.isReady()) { - self->updateDBInfoEndpoints.clear(); for(auto &it : self->id_worker) { - self->updateDBInfoEndpoints.push_back(it.second.details.interf.updateServerDBInfo.getEndpoint()); + req.broadcastInfo.push_back(it.second.details.interf.updateServerDBInfo.getEndpoint()); } } else { - uniquify(self->updateDBInfoEndpoints); - for(int i = 0; i < self->updateDBInfoEndpoints.size(); i++) { - if(self->removedDBInfoEndpoints.count(self->updateDBInfoEndpoints[i])) { - self->updateDBInfoEndpoints[i--] = self->updateDBInfoEndpoints.back(); - self->updateDBInfoEndpoints.pop_back(); - } - } + self->updateDBInfoEndpoints.erase(self->removedDBInfoEndpoints.begin(), self->removedDBInfoEndpoints.end()); + req.broadcastInfo = std::vector(self->updateDBInfoEndpoints.begin(), self->updateDBInfoEndpoints.end()); } + self->updateDBInfoEndpoints.clear(); self->removedDBInfoEndpoints.clear(); + dbInfoChange = self->db.serverInfo->onChange(); updateDBInfo = self->updateDBInfo.onTrigger(); - UpdateServerDBInfoRequest req; req.serializedDbInfo = BinaryWriter::toValue(self->db.serverInfo->get(), AssumeVersion(currentProtocolVersion)); - req.broadcastInfo = self->updateDBInfoEndpoints; - self->updateDBInfoEndpoints.clear(); TraceEvent("DBInfoStartBroadcast", self->id); choose { when(std::vector notUpdated = wait( broadcastDBInfoRequest(req, SERVER_KNOBS->DBINFO_SEND_AMOUNT, Optional(), false) )) { @@ -3063,7 +3057,7 @@ ACTOR Future dbInfoUpdater( ClusterControllerData* self ) { for(auto &it : notUpdated) { TraceEvent("DBInfoNotUpdated", self->id).detail("Addr", it.getPrimaryAddress()); } - self->updateDBInfoEndpoints.insert(self->updateDBInfoEndpoints.end(), notUpdated.begin(), notUpdated.end()); + self->updateDBInfoEndpoints.insert(notUpdated.begin(), notUpdated.end()); if(notUpdated.size()) { self->updateDBInfo.trigger(); } diff --git a/fdbserver/DataDistribution.actor.cpp b/fdbserver/DataDistribution.actor.cpp index 7360502b92..6a02a1125f 100644 --- a/fdbserver/DataDistribution.actor.cpp +++ b/fdbserver/DataDistribution.actor.cpp @@ -3597,14 +3597,14 @@ ACTOR Future storageServerTracker( if (worstStatus != DDTeamCollection::Status::NONE) { TraceEvent(SevWarn, "UndesiredStorageServer", self->distributorId) - .detail("Server", server->id) - .detail("Excluded", worstAddr.toString()); + .detail("Server", server->id) + .detail("Excluded", worstAddr.toString()); status.isUndesired = true; status.isWrongConfiguration = true; if (worstStatus == DDTeamCollection::Status::FAILED) { TraceEvent(SevWarn, "FailedServerRemoveKeys", self->distributorId) .detail("Server", server->id) - .detail("Excluded", worstAddr.toString()); + .detail("Excluded", worstAddr.toString()); wait(removeKeysFromFailedServer(cx, server->id, self->lock)); if (BUGGIFY) wait(delay(5.0)); self->shardsAffectedByTeamFailure->eraseServer(server->id); diff --git a/fdbserver/Status.actor.cpp b/fdbserver/Status.actor.cpp index 5ad0312c36..c524ed946f 100644 --- a/fdbserver/Status.actor.cpp +++ b/fdbserver/Status.actor.cpp @@ -1966,14 +1966,14 @@ static std::string getIssueDescription(std::string name) { } static std::map> getProcessIssuesAsMessages( - std::vector>>> const& issues) { + std::vector const& issues) { std::map> issuesMap; try { for (auto processIssues : issues) { - for (auto issue : processIssues.second) { + for (auto issue : processIssues.issues) { std::string issueStr = issue.toString(); - issuesMap[processIssues.first.toString()].push_back( + issuesMap[processIssues.address.toString()].push_back( JsonString::makeMessage(issueStr.c_str(), getIssueDescription(issueStr).c_str())); } } @@ -2163,7 +2163,7 @@ ACTOR Future clusterGetStatus( Reference> db, Database cx, vector workers, - std::vector>>> workerIssues, + std::vector workerIssues, std::map>* clientStatus, ServerCoordinators coordinators, std::vector incompatibleConnections, diff --git a/fdbserver/Status.h b/fdbserver/Status.h index 95be0e55d4..ac863b9c39 100644 --- a/fdbserver/Status.h +++ b/fdbserver/Status.h @@ -27,7 +27,14 @@ #include "fdbserver/MasterInterface.h" #include "fdbclient/ClusterInterface.h" -Future clusterGetStatus( Reference> const& db, Database const& cx, vector const& workers, std::vector>>> const& workerIssues, +struct ProcessIssues { + NetworkAddress address; + Standalone> issues; + + ProcessIssues(NetworkAddress address, Standalone> issues) : address(address), issues(issues) {} +}; + +Future clusterGetStatus( Reference> const& db, Database const& cx, vector const& workers, std::vector const& workerIssues, std::map>* const& clientStatus, ServerCoordinators const& coordinators, std::vector const& incompatibleConnections, Version const& datacenterVersionDifference ); #endif diff --git a/fdbserver/WorkerInterface.actor.h b/fdbserver/WorkerInterface.actor.h index e94badb4ff..4f32177f04 100644 --- a/fdbserver/WorkerInterface.actor.h +++ b/fdbserver/WorkerInterface.actor.h @@ -120,7 +120,7 @@ struct ClusterControllerFullInterface { RequestStream< struct RegisterWorkerRequest > registerWorker; RequestStream< struct GetWorkersRequest > getWorkers; RequestStream< struct RegisterMasterRequest > registerMaster; - RequestStream< struct GetServerDBInfoRequest > getServerDBInfo; + RequestStream< struct GetServerDBInfoRequest > getServerDBInfo; //only used by testers; the cluster controller will send the serverDBInfo to workers UID id() const { return clientInterface.id(); } bool operator == (ClusterControllerFullInterface const& r) const { return id() == r.id(); } From c8d049d0bb26f78e35f7c4f1b1cbe8584e8f193a Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Fri, 17 Apr 2020 15:21:59 -0700 Subject: [PATCH 1457/1604] FastRestore:Loader:Add counter oldLogMutations --- fdbserver/RestoreLoader.actor.cpp | 9 ++++++--- fdbserver/RestoreLoader.actor.h | 4 +++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/fdbserver/RestoreLoader.actor.cpp b/fdbserver/RestoreLoader.actor.cpp index 734ce62da6..d8a5b8491c 100644 --- a/fdbserver/RestoreLoader.actor.cpp +++ b/fdbserver/RestoreLoader.actor.cpp @@ -178,7 +178,7 @@ void handleRestoreSysInfoRequest(const RestoreSysInfoRequest& req, Reference _parsePartitionedLogFileOnLoader( KeyRangeMap* pRangeVersions, NotifiedVersion* processedFileOffset, - std::map::iterator kvOpsIter, + std::map::iterator kvOpsIter, LoaderCounters* cc, std::map::iterator samplesIter, Reference bc, RestoreAsset asset) { state Standalone buf = makeString(asset.len); state Reference file = wait(bc->readFile(asset.filename)); @@ -225,6 +225,7 @@ ACTOR static Future _parsePartitionedLogFileOnLoader( // Skip mutation whose commitVesion < range kv's version if (logMutationTooOld(pRangeVersions, mutation, msgVersion.version)) { + cc->oldLogMutations++; continue; } @@ -302,8 +303,9 @@ ACTOR Future _processLoadingParam(KeyRangeMap* pRangeVersions, Lo } else { // TODO: Sanity check the log file's range is overlapped with the restored version range if (param.isPartitionedLog()) { - fileParserFutures.push_back(_parsePartitionedLogFileOnLoader( - pRangeVersions, &processedFileOffset, kvOpsPerLPIter, samplesIter, bc, subAsset)); + fileParserFutures.push_back(_parsePartitionedLogFileOnLoader(pRangeVersions, &processedFileOffset, + kvOpsPerLPIter, samplesIter, + &batchData->counters, bc, subAsset)); } else { fileParserFutures.push_back(_parseLogFileToMutationsOnLoader(&processedFileOffset, &mutationMap, &mutationPartMap, bc, subAsset)); @@ -753,6 +755,7 @@ void _parseSerializedMutation(KeyRangeMap* pRangeVersions, // Should this mutation be skipped? // Skip mutation whose commitVesion < range kv's version if (logMutationTooOld(pRangeVersions, mutation, commitVersion)) { + cc->oldLogMutations++; continue; } diff --git a/fdbserver/RestoreLoader.actor.h b/fdbserver/RestoreLoader.actor.h index d28321cc58..e7832e0bb0 100644 --- a/fdbserver/RestoreLoader.actor.h +++ b/fdbserver/RestoreLoader.actor.h @@ -82,11 +82,13 @@ struct LoaderBatchData : public ReferenceCounted { CounterCollection cc; Counter loadedRangeBytes, loadedLogBytes, sentBytes; Counter sampledRangeBytes, sampledLogBytes; + Counter oldLogMutations; Counters(LoaderBatchData* self, UID loaderInterfID, int batchIndex) : cc("LoaderBatch", loaderInterfID.toString() + ":" + std::to_string(batchIndex)), loadedRangeBytes("LoadedRangeBytes", cc), loadedLogBytes("LoadedLogBytes", cc), sentBytes("SentBytes", cc), - sampledRangeBytes("SampledRangeBytes", cc), sampledLogBytes("SampledLogBytes", cc) {} + sampledRangeBytes("SampledRangeBytes", cc), sampledLogBytes("SampledLogBytes", cc), + oldLogMutations("OldLogMutations", cc) {} } counters; explicit LoaderBatchData(UID nodeID, int batchIndex) : counters(this, nodeID, batchIndex), vbState(LoaderVersionBatchState::NOT_INIT) { From cb6389d42d0b1652f8d2ca56b461fc99e9e43a42 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Fri, 17 Apr 2020 23:34:28 +0000 Subject: [PATCH 1458/1604] Prevent main thread from destroying flatbuffers globals We recently witnessed (using tsan) the main thread exiting without first joining the network thread, and this caused data races and heap-use-after-free's Now the lifetime of these globals will be tied to the network thread itself (and I guess every thread, but the one that actually uses memory will be owned by the network thread.) --- flow/flat_buffers.cpp | 6 ++++-- flow/flat_buffers.h | 11 ++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/flow/flat_buffers.cpp b/flow/flat_buffers.cpp index 1cb4b1099d..8871fa438e 100644 --- a/flow/flat_buffers.cpp +++ b/flow/flat_buffers.cpp @@ -31,10 +31,12 @@ namespace detail { namespace { -std::vector mWriteToOffsetsMemoy; +thread_local std::vector gWriteToOffsetsMemory; } -std::vector* writeToOffsetsMemory = &mWriteToOffsetsMemoy; +void swapWithThreadLocalGlobal(std::vector& writeToOffsets) { + gWriteToOffsetsMemory.swap(writeToOffsets); +} VTable generate_vtable(size_t numMembers, const std::vector& sizesAlignments) { if (numMembers == 0) { diff --git a/flow/flat_buffers.h b/flow/flat_buffers.h index ff8f7ccceb..193813edd6 100644 --- a/flow/flat_buffers.h +++ b/flow/flat_buffers.h @@ -343,15 +343,16 @@ struct _SizeOf { static constexpr unsigned int align = fb_align; }; -extern std::vector* writeToOffsetsMemory; +// Re-use this intermediate memory to avoid frequent new/delete +void swapWithThreadLocalGlobal(std::vector& writeToOffsets); template struct PrecomputeSize : Context { PrecomputeSize(const Context& context) : Context(context) { - writeToOffsets.swap(*writeToOffsetsMemory); + swapWithThreadLocalGlobal(writeToOffsets); writeToOffsets.clear(); } - ~PrecomputeSize() { writeToOffsets.swap(*writeToOffsetsMemory); } + ~PrecomputeSize() { swapWithThreadLocalGlobal(writeToOffsets); } // |offset| is measured from the end of the buffer. Precondition: len <= // offset. void write(const void*, int offset, int /*len*/) { current_buffer_size = std::max(current_buffer_size, offset); } @@ -491,7 +492,7 @@ extern VTable generate_vtable(size_t numMembers, const std::vector& si template const VTable* gen_vtable3() { - static VTable table = + static thread_local VTable table = generate_vtable(sizeof...(MembersAndAlignments) / 2, std::vector{ MembersAndAlignments... }); return &table; } @@ -619,7 +620,7 @@ VTableSet get_vtableset_impl(const Root& root, const Context& context) { template const VTableSet* get_vtableset(const Root& root, const Context& context) { - static VTableSet result = get_vtableset_impl(root, context); + static thread_local VTableSet result = get_vtableset_impl(root, context); return &result; } From b04478704e1844795b020ab319d60a9b80cfaa6c Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Fri, 17 Apr 2020 16:45:22 -0700 Subject: [PATCH 1459/1604] fixed improper use of std::set erase --- fdbserver/ClusterController.actor.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fdbserver/ClusterController.actor.cpp b/fdbserver/ClusterController.actor.cpp index f7601c5f98..3cf16b2ec6 100644 --- a/fdbserver/ClusterController.actor.cpp +++ b/fdbserver/ClusterController.actor.cpp @@ -3038,7 +3038,9 @@ ACTOR Future dbInfoUpdater( ClusterControllerData* self ) { req.broadcastInfo.push_back(it.second.details.interf.updateServerDBInfo.getEndpoint()); } } else { - self->updateDBInfoEndpoints.erase(self->removedDBInfoEndpoints.begin(), self->removedDBInfoEndpoints.end()); + for(auto it : self->removedDBInfoEndpoints) { + self->updateDBInfoEndpoints.erase(it); + } req.broadcastInfo = std::vector(self->updateDBInfoEndpoints.begin(), self->updateDBInfoEndpoints.end()); } @@ -3057,8 +3059,8 @@ ACTOR Future dbInfoUpdater( ClusterControllerData* self ) { for(auto &it : notUpdated) { TraceEvent("DBInfoNotUpdated", self->id).detail("Addr", it.getPrimaryAddress()); } - self->updateDBInfoEndpoints.insert(notUpdated.begin(), notUpdated.end()); if(notUpdated.size()) { + self->updateDBInfoEndpoints.insert(notUpdated.begin(), notUpdated.end()); self->updateDBInfo.trigger(); } } From 916d361587615afd9c72a5d6a290674fdc6d7504 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Fri, 17 Apr 2020 18:32:14 -0700 Subject: [PATCH 1460/1604] BackupAndParallelRestoreCorrectness:Remove unnecessary checking optional variable --- fdbserver/RestoreLoader.actor.cpp | 9 +++++---- .../BackupAndParallelRestoreCorrectness.actor.cpp | 8 +++----- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/fdbserver/RestoreLoader.actor.cpp b/fdbserver/RestoreLoader.actor.cpp index d8a5b8491c..bcdbe0c25d 100644 --- a/fdbserver/RestoreLoader.actor.cpp +++ b/fdbserver/RestoreLoader.actor.cpp @@ -178,8 +178,9 @@ void handleRestoreSysInfoRequest(const RestoreSysInfoRequest& req, Reference _parsePartitionedLogFileOnLoader( KeyRangeMap* pRangeVersions, NotifiedVersion* processedFileOffset, - std::map::iterator kvOpsIter, LoaderCounters* cc, - std::map::iterator samplesIter, Reference bc, RestoreAsset asset) { + std::map::iterator kvOpsIter, + std::map::iterator samplesIter, LoaderCounters* cc, Reference bc, + RestoreAsset asset) { state Standalone buf = makeString(asset.len); state Reference file = wait(bc->readFile(asset.filename)); int rLen = wait(file->read(mutateString(buf), asset.len, asset.offset)); @@ -225,7 +226,7 @@ ACTOR static Future _parsePartitionedLogFileOnLoader( // Skip mutation whose commitVesion < range kv's version if (logMutationTooOld(pRangeVersions, mutation, msgVersion.version)) { - cc->oldLogMutations++; + cc->oldLogMutations += 1; continue; } @@ -755,7 +756,7 @@ void _parseSerializedMutation(KeyRangeMap* pRangeVersions, // Should this mutation be skipped? // Skip mutation whose commitVesion < range kv's version if (logMutationTooOld(pRangeVersions, mutation, commitVersion)) { - cc->oldLogMutations++; + cc->oldLogMutations += 1; continue; } diff --git a/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp b/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp index 5f3b056354..987a394cbb 100644 --- a/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp +++ b/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp @@ -464,11 +464,9 @@ struct BackupAndParallelRestoreCorrectnessWorkload : TestWorkload { TraceEvent("BAFRW_Restore", randomID) .detail("LastBackupContainer", lastBackupContainer->getURL()) - .detail("MinRestorableVersion", - desc.minRestorableVersion.present() ? desc.minRestorableVersion.get() : -1) - .detail("MaxRestorableVersion", - desc.maxRestorableVersion.present() ? desc.maxRestorableVersion.get() : -1) - .detail("ContiguousLogEnd", desc.contiguousLogEnd.present() ? desc.contiguousLogEnd.get() : -1) + .detail("MinRestorableVersion", desc.minRestorableVersion.get()) + .detail("MaxRestorableVersion", desc.maxRestorableVersion.get()) + .detail("ContiguousLogEnd", desc.contiguousLogEnd.get()) .detail("TargetVersion", targetVersion); state std::vector> restores; From 82ae82c98f9c3cacfab2cf18f3c9b36ea1f5605c Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Fri, 17 Apr 2020 18:38:11 -0700 Subject: [PATCH 1461/1604] Move MAX_VERSION to FDBTypes.h --- fdbclient/FDBTypes.h | 2 +- fdbserver/RestoreCommon.actor.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/fdbclient/FDBTypes.h b/fdbclient/FDBTypes.h index 435a5e7ca7..7d765392fd 100644 --- a/fdbclient/FDBTypes.h +++ b/fdbclient/FDBTypes.h @@ -416,7 +416,7 @@ typedef Standalone KeyRange; typedef Standalone KeyValue; typedef Standalone KeySelector; -enum { invalidVersion = -1, latestVersion = -2 }; +enum { invalidVersion = -1, latestVersion = -2, MAX_VERSION = std::numeric_limits::max() }; inline Key keyAfter( const KeyRef& key ) { if(key == LiteralStringRef("\xff\xff")) diff --git a/fdbserver/RestoreCommon.actor.h b/fdbserver/RestoreCommon.actor.h index b4cca10f73..268fbf26d2 100644 --- a/fdbserver/RestoreCommon.actor.h +++ b/fdbserver/RestoreCommon.actor.h @@ -38,8 +38,6 @@ #include "flow/actorcompiler.h" // has to be last include -#define MAX_VERSION (std::numeric_limits::max()) - // RestoreConfig copied from FileBackupAgent.actor.cpp // We copy RestoreConfig instead of using (and potentially changing) it in place // to avoid conflict with the existing code. From 10a6461d1348fb85074bf75376d5e75223bc5aec Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Fri, 17 Apr 2020 22:31:40 -0700 Subject: [PATCH 1462/1604] FastRestore:Change __inline__ to inline __inline__ is compiler specific while inline is the standard keyword --- fdbserver/RestoreLoader.actor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fdbserver/RestoreLoader.actor.cpp b/fdbserver/RestoreLoader.actor.cpp index bcdbe0c25d..7e65a1c19b 100644 --- a/fdbserver/RestoreLoader.actor.cpp +++ b/fdbserver/RestoreLoader.actor.cpp @@ -127,7 +127,7 @@ ACTOR Future restoreLoaderCore(RestoreLoaderInterface loaderInterf, int no return Void(); } -static __inline__ bool _logMutationTooOld(KeyRangeMap* pRangeVersions, KeyRangeRef keyRange, Version v) { +static inline bool _logMutationTooOld(KeyRangeMap* pRangeVersions, KeyRangeRef keyRange, Version v) { auto ranges = pRangeVersions->intersectingRanges(keyRange); Version minVersion = MAX_VERSION; for (auto r = ranges.begin(); r != ranges.end(); ++r) { @@ -136,7 +136,7 @@ static __inline__ bool _logMutationTooOld(KeyRangeMap* pRangeVersions, return minVersion >= v; } -static __inline__ bool logMutationTooOld(KeyRangeMap* pRangeVersions, MutationRef mutation, Version v) { +static inline bool logMutationTooOld(KeyRangeMap* pRangeVersions, MutationRef mutation, Version v) { return isRangeMutation(mutation) ? _logMutationTooOld(pRangeVersions, KeyRangeRef(mutation.param1, mutation.param2), v) : _logMutationTooOld(pRangeVersions, KeyRangeRef(singleKeyRange(mutation.param1)), v); From 94b4f78ea9f46936b984681759d9068c5a0e813c Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 18 Apr 2020 15:48:02 -0700 Subject: [PATCH 1463/1604] Fix clients crashing in TLS code on exit. If client code initiates an FDB operation to a TLS cluster, and then immediately exits the main thread, then OpenSSL's atexit handler would potentially run while the network thread is attempting to do TLS operations, and thus crash. This commit removes the OpenSSL atexit hander, and instead relies on a client intentionally ending the network thread to do TLS cleanup. If the client code exits without stopping the network thread, then we'll never free OpenSSL data structures, which is the safer thing to do. --- fdbclient/NativeAPI.actor.cpp | 2 ++ flow/TLSConfig.actor.cpp | 26 ++++++++++++++++++++++++++ flow/TLSConfig.actor.h | 16 ++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index 5f379c4f3d..ad61994bdb 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -994,6 +994,7 @@ void setupNetwork(uint64_t transportId, bool useMetrics) { if (!networkOptions.logClientInfo.present()) networkOptions.logClientInfo = true; + TLS::DisableOpenSSLAtExitHandler(); g_network = newNet2(tlsConfig, false, useMetrics || networkOptions.traceDirectory.present()); FlowTransport::createInstance(true, transportId); Net2FileSystem::newFileSystem(); @@ -1019,6 +1020,7 @@ void stopNetwork() { g_network->stop(); closeTraceFile(); + TLS::DestroyOpenSSLGlobalState(); } Reference DatabaseContext::getMasterProxies(bool useProvisionalProxies) { diff --git a/flow/TLSConfig.actor.cpp b/flow/TLSConfig.actor.cpp index f432229ec9..73a336e38a 100644 --- a/flow/TLSConfig.actor.cpp +++ b/flow/TLSConfig.actor.cpp @@ -25,6 +25,32 @@ // To force typeinfo to only be emitted once. TLSPolicy::~TLSPolicy() {} +namespace TLS { + +void DisableOpenSSLAtExitHandler() { +#ifdef TLS_DISABLED + return; +#else + static bool once = false; + if (!once) { + once = true; + int success = OPENSSL_init_crypto(OPENSSL_INIT_NO_ATEXIT, nullptr); + if (!success) { + throw tls_error(); + } + } +#endif +} + +void DestroyOpenSSLGlobalState() { +#ifdef TLS_DISABLED + return; +#else + OPENSSL_cleanup(); +#endif +} + +} // namespace TLS #ifdef TLS_DISABLED void LoadedTLSConfig::print(FILE *fp) { diff --git a/flow/TLSConfig.actor.h b/flow/TLSConfig.actor.h index 820c90d5c9..aa07e27fde 100644 --- a/flow/TLSConfig.actor.h +++ b/flow/TLSConfig.actor.h @@ -36,6 +36,22 @@ #include "flow/Knobs.h" #include "flow/flow.h" +namespace TLS { + +// Force OpenSSL to not register an atexit handler to clean up global state before process exit. +// If you call this, you must also call DestroyOpenSSLGlobalState() before the program exits. +// Calls OPENSSL_init_crypto with OPENSSL_INIT_NO_ATEXIT. +// Must be called before any other OpenSSL function. +void DisableOpenSSLAtExitHandler(); + +// Frees all global state maintained by OpenSSL. +// Calls OPENSSL_cleanup. +// Must be called before program exit if using DisableOpenSSLAtExitHandler. +// No OpenSSL code may be run after calling this function. +void DestroyOpenSSLGlobalState(); + +} // namespace TLS + #ifndef TLS_DISABLED #include From 1398e9a82edb08271e27601933cb10324800c07c Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 18 Apr 2020 19:40:55 -0700 Subject: [PATCH 1464/1604] Stop background eio threads on Net2::stop(). This will stop eio threads for both the client (`fdb_stop_network()`) and the server. This change is being done more for the former, but I don't see any harm in doing the latter as well. --- fdbrpc/AsyncFileEIO.actor.h | 4 ++++ fdbrpc/AsyncFileWinASIO.actor.h | 2 ++ fdbrpc/Net2FileSystem.cpp | 4 ++++ fdbrpc/Net2FileSystem.h | 1 + flow/Net2.actor.cpp | 2 ++ 5 files changed, 13 insertions(+) diff --git a/fdbrpc/AsyncFileEIO.actor.h b/fdbrpc/AsyncFileEIO.actor.h index f3450af847..512a6c95aa 100644 --- a/fdbrpc/AsyncFileEIO.actor.h +++ b/fdbrpc/AsyncFileEIO.actor.h @@ -52,6 +52,10 @@ public: } } + static void stop() { + eio_set_max_parallel(0); + } + static bool should_poll() { return want_poll; } static bool lock_fd( int fd ) { diff --git a/fdbrpc/AsyncFileWinASIO.actor.h b/fdbrpc/AsyncFileWinASIO.actor.h index 961d5a62f4..19b6ffcb9c 100644 --- a/fdbrpc/AsyncFileWinASIO.actor.h +++ b/fdbrpc/AsyncFileWinASIO.actor.h @@ -39,6 +39,8 @@ class AsyncFileWinASIO : public IAsyncFile, public ReferenceCounted lastWriteTime( std::string filename ); //void init(); + static void stop(); Net2FileSystem(double ioTimeout=0.0, std::string fileSystemPath = ""); diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index 2a53b097be..9bab7e4508 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -42,6 +42,7 @@ // See the comment in TLSConfig.actor.h for the explanation of why this module breaking include was done. #include "fdbrpc/IAsyncFile.h" +#include "fdbrpc/Net2FileSystem.h" #ifdef WIN32 #include @@ -200,6 +201,7 @@ public: void trackMinPriority( TaskPriority minTaskID, double now ); void stopImmediately() { stopped=true; decltype(ready) _1; ready.swap(_1); decltype(timers) _2; timers.swap(_2); + Net2FileSystem::stop(); } Future timeOffsetLogger; From 11eebc4a48ba720e49655c3b3d53abfcf5fa5b2e Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 18 Apr 2020 20:21:10 -0700 Subject: [PATCH 1465/1604] Log Net2TLSConfig with paths and settings when using TLS. There were similar TraceEvents in the FDBLibTLS/LibreSSL TLS implementaiton that were accidentally dropped in the TLS rewrite. This makes it so that one does not have to use magic to figure out if a process was configued with TLS correctly when some of the settings come from environment variables. --- flow/Net2.actor.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index 2a53b097be..801675e408 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -23,12 +23,13 @@ #define BOOST_SYSTEM_NO_LIB #define BOOST_DATE_TIME_NO_LIB #define BOOST_REGEX_NO_LIB -#include "boost/asio.hpp" -#include "boost/bind.hpp" -#include "boost/date_time/posix_time/posix_time_types.hpp" +#include +#include +#include +#include +#include #include "flow/network.h" #include "flow/IThreadPool.h" -#include "boost/range.hpp" #include "flow/ActorCollection.h" #include "flow/ThreadSafeQueue.h" @@ -963,6 +964,13 @@ void Net2::initTLS() { try { boost::asio::ssl::context newContext(boost::asio::ssl::context::tls); auto onPolicyFailure = [this]() { this->countTLSPolicyFailures++; }; + const LoadedTLSConfig& loaded = tlsConfig.loadSync(); + TraceEvent("Net2TLSConfig") + .detail("CAPath", tlsConfig.getCAPathSync()) + .detail("CertificatePath", tlsConfig.getCertificatePathSync()) + .detail("KeyPath", tlsConfig.getKeyPathSync()) + .detail("HasPassword", !loaded.getPassword().empty()) + .detail("VerifyPeers", boost::algorithm::join(loaded.getVerifyPeers(), "|")); ConfigureSSLContext( tlsConfig.loadSync(), &newContext, onPolicyFailure ); sslContextVar.set(ReferencedObject::from(std::move(newContext))); backgroundCertRefresh = reloadCertificatesOnChange( tlsConfig, onPolicyFailure, &sslContextVar ); From cbb6ffb4310b1e2d8ba928bbc26d3ae082c21042 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 18 Apr 2020 20:39:02 -0700 Subject: [PATCH 1466/1604] Only log OpenSSL error strings for OpenSSL errors. Normal "connection refused" messages would show up with a long verbose string that doesn't really provide any useful information otherwise. --- flow/Net2.actor.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index 2a53b097be..5256d2b61c 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -260,11 +260,19 @@ public: try { if (error) { // Log the error... - TraceEvent(SevWarn, errContext, errID).suppressFor(1.0).detail("ErrorCode", error.value()).detail("Message", error.message()) + { + TraceEvent evt(SevWarn, errContext, errID); + evt.suppressFor(1.0).detail("ErrorCode", error.value()).detail("Message", error.message()); #ifndef TLS_DISABLED - .detail("WhichMeans", TLSPolicy::ErrorString(error)) + // There is no function in OpenSSL to use to check if an error code is from OpenSSL, + // but all OpenSSL errors have a non-zero "library" code set in bits 24-32, and linux + // error codes should never go that high. + if (error.value() >= (1 << 24L)) { + evt.detail("WhichMeans", TLSPolicy::ErrorString(error)); + } #endif - ; + } + p.sendError( connection_failed() ); } else p.send( Void() ); From 719eda94215215b113befccf3e10287ea5a978e5 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Sat, 18 Apr 2020 22:42:42 -0700 Subject: [PATCH 1467/1604] FastRestore:Add an assertion in handleRestoreSysInfoRequest as suggested in code review. --- fdbserver/RestoreLoader.actor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/fdbserver/RestoreLoader.actor.cpp b/fdbserver/RestoreLoader.actor.cpp index 7e65a1c19b..da741a5b5d 100644 --- a/fdbserver/RestoreLoader.actor.cpp +++ b/fdbserver/RestoreLoader.actor.cpp @@ -155,6 +155,7 @@ void handleRestoreSysInfoRequest(const RestoreSysInfoRequest& req, ReferenceappliersInterf = req.sysInfo.appliers; // Update rangeVersions + ASSERT(req.rangeVersions.size() > 0); // At least the min version of range files will be used ASSERT(self->rangeVersions.size() == 1); // rangeVersions has not been set for (auto rv = req.rangeVersions.begin(); rv != req.rangeVersions.end(); ++rv) { self->rangeVersions.insert(rv->first, rv->second); From 7b23c6f640e7a96ccb67664d08ef22e4d5642650 Mon Sep 17 00:00:00 2001 From: Steve Atherton Date: Mon, 20 Apr 2020 01:50:37 -0700 Subject: [PATCH 1468/1604] Future constructor to avoid a copy when Future is initialized from an rvalue reference to T. --- flow/flow.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/flow/flow.h b/flow/flow.h index 4d55f49b8b..290e6b392e 100644 --- a/flow/flow.h +++ b/flow/flow.h @@ -687,6 +687,11 @@ public: { sav->send(presentValue); } + Future(T&& presentValue) + : sav(new SAV(1, 0)) + { + sav->send(std::forward(presentValue)); + } Future(Never) : sav(new SAV(1, 0)) { From 2ce539ef6d55b42892ecc126c832e24dad95c3b1 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 20 Apr 2020 02:53:07 -0700 Subject: [PATCH 1469/1604] Respect flow<->fdbrpc module boundaries. Which fixes a compilation error due to a circular dependency between flow.a and fdbrpc.a. However, this is now done at the cost of newNet2 users have to remember to add Net2FileSystem::stop() as a callback. --- fdbclient/NativeAPI.actor.cpp | 1 + fdbrpc/FlowTests.actor.cpp | 1 + fdbrpc/sim2.actor.cpp | 13 ++++++++++++- fdbserver/fdbserver.actor.cpp | 1 + flow/Net2.actor.cpp | 13 +++++++++++-- flow/network.h | 4 ++++ 6 files changed, 30 insertions(+), 3 deletions(-) diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index 5f379c4f3d..afe5220b67 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -995,6 +995,7 @@ void setupNetwork(uint64_t transportId, bool useMetrics) { networkOptions.logClientInfo = true; g_network = newNet2(tlsConfig, false, useMetrics || networkOptions.traceDirectory.present()); + g_network->addStopCallback( Net2FileSystem::stop ); FlowTransport::createInstance(true, transportId); Net2FileSystem::newFileSystem(); } diff --git a/fdbrpc/FlowTests.actor.cpp b/fdbrpc/FlowTests.actor.cpp index b6d38c8fe2..53ea27c8cd 100644 --- a/fdbrpc/FlowTests.actor.cpp +++ b/fdbrpc/FlowTests.actor.cpp @@ -194,6 +194,7 @@ struct YieldMockNetwork : INetwork, ReferenceCounted { virtual double now() { return baseNetwork->now(); } virtual double timer() { return baseNetwork->timer(); } virtual void stop() { return baseNetwork->stop(); } + virtual void addStopCallback( std::function fn ) { ASSERT(false); return; } virtual bool isSimulated() const { return baseNetwork->isSimulated(); } virtual void onMainThread(Promise&& signal, TaskPriority taskID) { return baseNetwork->onMainThread(std::move(signal), taskID); } bool isOnMainThread() const override { return baseNetwork->isOnMainThread(); } diff --git a/fdbrpc/sim2.actor.cpp b/fdbrpc/sim2.actor.cpp index 845a166a9f..9b5697d53d 100644 --- a/fdbrpc/sim2.actor.cpp +++ b/fdbrpc/sim2.actor.cpp @@ -870,7 +870,15 @@ public: return emptyConfig; } - virtual void stop() { isStopped = true; } + virtual void stop() { + isStopped = true; + for ( auto& fn : stopCallbacks ) { + fn(); + } + } + virtual void addStopCallback( std::function fn ) { + stopCallbacks.emplace_back(std::move(fn)); + } virtual bool isSimulated() const { return true; } struct SimThreadArgs { @@ -1605,6 +1613,7 @@ public: // Not letting currentProcess be NULL eliminates some annoying special cases currentProcess = new ProcessInfo("NoMachine", LocalityData(Optional>(), StringRef(), StringRef(), StringRef()), ProcessClass(), {NetworkAddress()}, this, "", ""); g_network = net2 = newNet2(TLSConfig(), false, true); + g_network->addStopCallback( Net2FileSystem::stop ); Net2FileSystem::newFileSystem(); check_yield(TaskPriority::Zero); } @@ -1703,6 +1712,8 @@ public: //tasks is guarded by ISimulator::mutex std::priority_queue> tasks; + std::vector> stopCallbacks; + //Sim2Net network; INetwork *net2; diff --git a/fdbserver/fdbserver.actor.cpp b/fdbserver/fdbserver.actor.cpp index 48dfb320f5..64006eb67b 100644 --- a/fdbserver/fdbserver.actor.cpp +++ b/fdbserver/fdbserver.actor.cpp @@ -1551,6 +1551,7 @@ int main(int argc, char* argv[]) { openTraceFile(NetworkAddress(), rollsize, maxLogsSize, logFolder, "trace", logGroup); } else { g_network = newNet2(tlsConfig, useThreadPool, true); + g_network->addStopCallback( Net2FileSystem::stop ); FlowTransport::createInstance(false, 1); const bool expectsPublicAddress = (role == FDBD || role == NetworkTestServer || role == Restore); diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index 9bab7e4508..2af69a4a28 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -42,7 +42,6 @@ // See the comment in TLSConfig.actor.h for the explanation of why this module breaking include was done. #include "fdbrpc/IAsyncFile.h" -#include "fdbrpc/Net2FileSystem.h" #ifdef WIN32 #include @@ -145,6 +144,13 @@ public: // SOMEDAY: NULL for deferred error, no analysis of correctness (itp) onMainThreadVoid( [this] { this->stopImmediately(); }, NULL ); } + virtual void addStopCallback( std::function fn ) { + if ( thread_network == this ) + stopCallbacks.emplace_back(std::move(fn)); + else + // SOMEDAY: NULL for deferred error, no analysis of correctness (itp) + onMainThreadVoid( [this, fn] { this->stopCallbacks.emplace_back(std::move(fn)); }, NULL ); + } virtual bool isSimulated() const { return false; } virtual THREAD_HANDLE startThread( THREAD_FUNC_RETURN (*func) (void*), void *arg); @@ -201,7 +207,9 @@ public: void trackMinPriority( TaskPriority minTaskID, double now ); void stopImmediately() { stopped=true; decltype(ready) _1; ready.swap(_1); decltype(timers) _2; timers.swap(_2); - Net2FileSystem::stop(); + for ( auto& fn : stopCallbacks ) { + fn(); + } } Future timeOffsetLogger; @@ -233,6 +241,7 @@ public: EventMetricHandle slowTaskMetric; std::vector blobCredentialFiles; + std::vector> stopCallbacks; }; static boost::asio::ip::address tcpAddress(IPAddress const& n) { diff --git a/flow/network.h b/flow/network.h index 0fc7b25f2e..4257f47465 100644 --- a/flow/network.h +++ b/flow/network.h @@ -463,6 +463,10 @@ public: virtual void stop() = 0; // Terminate the program + virtual void addStopCallback( std::function fn ) = 0; + // Calls `fn` when stop() is called. + // addStopCallback can be called more than once, and each added `fn` will be run once. + virtual bool isSimulated() const = 0; // Returns true if this network is a local simulation From 022b77e2888a995a853bb7ff41f77d7a8525d218 Mon Sep 17 00:00:00 2001 From: Steve Atherton Date: Mon, 20 Apr 2020 04:19:33 -0700 Subject: [PATCH 1470/1604] Actor compiler will std::move() return expressions that exactly match a state variable. --- flow/actorcompiler/ActorCompiler.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/flow/actorcompiler/ActorCompiler.cs b/flow/actorcompiler/ActorCompiler.cs index c95c9bf7f2..175ceb7b0a 100644 --- a/flow/actorcompiler/ActorCompiler.cs +++ b/flow/actorcompiler/ActorCompiler.cs @@ -953,7 +953,15 @@ namespace actorcompiler // if it has side effects cx.target.WriteLine("if (!{0}->SAV<{1}>::futures) {{ (void)({2}); this->~{3}(); {0}->destroy(); return 0; }}", This, actor.returnType, stmt.expression, stateClassName); // Build the return value directly in SAV::value_storage - cx.target.WriteLine("new (&{0}->SAV< {1} >::value()) {1}({2});", This, actor.returnType, stmt.expression); + // If the expression is exactly the name of a state variable, std::move() it + if (state.Exists(s => s.name == stmt.expression)) + { + cx.target.WriteLine("new (&{0}->SAV< {1} >::value()) {1}(std::move({2})); // state_var_RVO", This, actor.returnType, stmt.expression); + } + else + { + cx.target.WriteLine("new (&{0}->SAV< {1} >::value()) {1}({2});", This, actor.returnType, stmt.expression); + } // Destruct state cx.target.WriteLine("this->~{0}();", stateClassName); // Tell SAV to return the value we already constructed in value_storage From ba1b0a1d96d3e92782c20f2283b029f397e25922 Mon Sep 17 00:00:00 2001 From: Steve Atherton Date: Mon, 20 Apr 2020 11:01:01 -0700 Subject: [PATCH 1471/1604] Use std::move() instead of forward. --- flow/flow.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/flow.h b/flow/flow.h index 290e6b392e..1823a0a6ee 100644 --- a/flow/flow.h +++ b/flow/flow.h @@ -690,7 +690,7 @@ public: Future(T&& presentValue) : sav(new SAV(1, 0)) { - sav->send(std::forward(presentValue)); + sav->send(std::move(presentValue)); } Future(Never) : sav(new SAV(1, 0)) From 4c66c8c377703f393405236bbdc5aacaab751128 Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Wed, 15 Apr 2020 14:06:27 -0700 Subject: [PATCH 1472/1604] Fix backup progress calculation The oldest epoch the master gets can assume its begin version is 1, which can be wrong. In this case, we use the saved backup progress to "true-up" the real begin version. --- fdbclient/BackupContainer.actor.cpp | 6 +++++- fdbserver/BackupProgress.actor.cpp | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/fdbclient/BackupContainer.actor.cpp b/fdbclient/BackupContainer.actor.cpp index 27a64a53bf..5b6a21d5da 100644 --- a/fdbclient/BackupContainer.actor.cpp +++ b/fdbclient/BackupContainer.actor.cpp @@ -1193,7 +1193,10 @@ public: std::vector filtered; int i = 0; for (int j = 1; j < logs.size(); j++) { - if (logs[j].isSubset(logs[i])) continue; + if (logs[j].isSubset(logs[i])) { + ASSERT(logs[j].fileSize <= logs[i].fileSize); + continue; + } if (!logs[i].isSubset(logs[j])) { filtered.push_back(logs[i]); @@ -1249,6 +1252,7 @@ public: // filter out if indices.back() is subset of files[i] or vice versa if (!indices.empty()) { if (logs[indices.back()].isSubset(logs[i])) { + ASSERT(logs[indices.back()].fileSize <= logs[i].fileSize); indices.back() = i; } else if (!logs[i].isSubset(logs[indices.back()])) { indices.push_back(i); diff --git a/fdbserver/BackupProgress.actor.cpp b/fdbserver/BackupProgress.actor.cpp index e64a44bfdc..fac69d6ee3 100644 --- a/fdbserver/BackupProgress.actor.cpp +++ b/fdbserver/BackupProgress.actor.cpp @@ -83,6 +83,15 @@ std::map, std::map> BackupProgr auto progressIt = progress.lower_bound(epoch); if (progressIt != progress.end() && progressIt->first == epoch) { + if (progressIt != progress.begin() && info.epochBegin == 1) { + // Previous epoch is gone, consolidate the progress. + auto prev = std::prev(progressIt); + for (auto [tag, version] : prev->second) { + if (tags.count(tag) > 0) { + progressIt->second[tag] = std::max(version, progressIt->second[tag]); + } + } + } updateTagVersions(&tagVersions, &tags, progressIt->second, info.epochEnd, adjustedBeginVersion, epoch); } else { auto rit = std::find_if( From 552885793490cd647e133441a1aaedf18174b7a8 Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Fri, 17 Apr 2020 20:20:42 -0700 Subject: [PATCH 1473/1604] Remove epoch's begin version check Turns out the begin version can be a valid previous epoch's begin version, not specificly 1. --- fdbserver/BackupProgress.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/BackupProgress.actor.cpp b/fdbserver/BackupProgress.actor.cpp index fac69d6ee3..985fcb7f93 100644 --- a/fdbserver/BackupProgress.actor.cpp +++ b/fdbserver/BackupProgress.actor.cpp @@ -83,7 +83,7 @@ std::map, std::map> BackupProgr auto progressIt = progress.lower_bound(epoch); if (progressIt != progress.end() && progressIt->first == epoch) { - if (progressIt != progress.begin() && info.epochBegin == 1) { + if (progressIt != progress.begin()) { // Previous epoch is gone, consolidate the progress. auto prev = std::prev(progressIt); for (auto [tag, version] : prev->second) { From 76d90ac6d7bd8ae37bb9bd0ccf24897ffba2e173 Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Fri, 17 Apr 2020 22:55:09 -0700 Subject: [PATCH 1474/1604] Limit the version range for old epochs When the Master recruits a backup worker for previous epochs, the Master may set the begin version to a very low number, because the backup progress for that epoch is not saved. This can cause problem for the log file, since these low versions have been popped. The fix here is to advance savedVersion to the minimum of backup's starting version if it is higher than the begin version set by the Master. This is safe because these versions are not popped. If they are popped, their progress should already be recorded and Master would use a higher version than the backup's starting version. --- fdbserver/BackupWorker.actor.cpp | 44 ++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/fdbserver/BackupWorker.actor.cpp b/fdbserver/BackupWorker.actor.cpp index 481bc2e20e..2d2a6fe259 100644 --- a/fdbserver/BackupWorker.actor.cpp +++ b/fdbserver/BackupWorker.actor.cpp @@ -74,7 +74,8 @@ struct BackupData { const LogEpoch backupEpoch; // the epoch workers should pull mutations LogEpoch oldestBackupEpoch = 0; // oldest epoch that still has data on tLogs for backup to pull Version minKnownCommittedVersion; - Version savedVersion; + Version savedVersion; // Largest version saved to blob storage + Version popVersion; // Largest version popped. Can be larger than savedVersion in NOOP mode. AsyncVar> logSystem; Database cx; std::vector messages; @@ -225,7 +226,7 @@ struct BackupData { explicit BackupData(UID id, Reference> db, const InitializeBackupRequest& req) : myId(id), tag(req.routerTag), totalTags(req.totalTags), startVersion(req.startVersion), endVersion(req.endVersion), recruitedEpoch(req.recruitedEpoch), backupEpoch(req.backupEpoch), - minKnownCommittedVersion(invalidVersion), savedVersion(req.startVersion - 1), + minKnownCommittedVersion(invalidVersion), savedVersion(req.startVersion - 1), popVersion(req.startVersion - 1), cc("BackupWorker", myId.toString()), pulledVersion(0), paused(false) { cx = openDBOnServer(db, TaskPriority::DefaultEndpoint, true, true); @@ -291,7 +292,7 @@ struct BackupData { } ASSERT_WE_THINK(backupEpoch == oldestBackupEpoch); const Tag popTag = logSystem.get()->getPseudoPopTag(tag, ProcessClass::BackupClass); - logSystem.get()->pop(savedVersion, popTag); + logSystem.get()->pop(popVersion, popTag); } void stop() { @@ -326,11 +327,13 @@ struct BackupData { } bool modified = false; + Version minVersion = std::numeric_limits::max(); for (const auto [uid, version] : uidVersions) { auto it = backups.find(uid); if (it == backups.end()) { modified = true; backups.emplace(uid, BackupData::PerBackupInfo(this, uid, version)); + minVersion = std::min(minVersion, version); } else { stopList.erase(uid); } @@ -342,6 +345,14 @@ struct BackupData { it->second.stop(); modified = true; } + if (backupEpoch < recruitedEpoch && savedVersion + 1 == startVersion) { + // Advance savedVersion to minimize version ranges in case backupEpoch's + // progress is not saved. Master may set a very low startVersion that + // is already popped. Advance the version is safe because these + // versions are not popped -- if they are popped, their progress should + // be already recorded and Master would use a higher version than minVersion. + savedVersion = std::max(minVersion, savedVersion); + } if (modified) changedTrigger.trigger(); } @@ -390,10 +401,10 @@ struct BackupData { Future getMinKnownCommittedVersion() { return _getMinKnownCommittedVersion(this); } }; -// Monitors "backupStartedKey". If "started" is true, wait until the key is set; +// Monitors "backupStartedKey". If "present" is true, wait until the key is set; // otherwise, wait until the key is cleared. If "watch" is false, do not perform // the wait for key set/clear events. Returns if key present. -ACTOR Future monitorBackupStartedKeyChanges(BackupData* self, bool started, bool watch) { +ACTOR Future monitorBackupStartedKeyChanges(BackupData* self, bool present, bool watch) { loop { state ReadYourWritesTransaction tr(self->cx); @@ -418,13 +429,13 @@ ACTOR Future monitorBackupStartedKeyChanges(BackupData* self, bool started } self->exitEarly = shouldExit; self->onBackupChanges(uidVersions); - if (started || !watch) return true; + if (present || !watch) return true; } else { TraceEvent("BackupWorkerEmptyStartKey", self->myId); self->onBackupChanges(uidVersions); self->exitEarly = shouldExit; - if (!started || !watch) { + if (!present || !watch) { return false; } } @@ -762,6 +773,7 @@ ACTOR Future uploadData(BackupData* self) { if (((numMsg > 0 || popVersion > lastPopVersion) && self->pulling) || self->pullFinished()) { TraceEvent("BackupWorkerSave", self->myId) .detail("Version", popVersion) + .detail("SavedVersion", self->savedVersion) .detail("MsgQ", self->messages.size()); // save an empty file for old epochs so that log file versions are continuous wait(saveMutationsToFile(self, popVersion, numMsg)); @@ -769,7 +781,14 @@ ACTOR Future uploadData(BackupData* self) { } // If transition into NOOP mode, should clear messages - if (!self->pulling) self->messages.clear(); + if (!self->pulling) { + self->messages.clear(); + // Update popVersion so that save progress below can + // indicate ranges not used for future epochs. + if (self->popVersion > self->savedVersion && self->backupEpoch == self->recruitedEpoch) { + popVersion = std::max(popVersion, self->popVersion); + } + } if (popVersion > self->savedVersion) { wait(saveProgress(self, popVersion)); @@ -778,6 +797,7 @@ ACTOR Future uploadData(BackupData* self) { .detail("Version", popVersion) .detail("MsgQ", self->messages.size()); self->savedVersion = std::max(popVersion, self->savedVersion); + self->popVersion = std::max(self->savedVersion, self->popVersion); self->pop(); } @@ -872,10 +892,13 @@ ACTOR Future monitorBackupKeyOrPullData(BackupData* self, bool keyPresent) when(wait(success(present))) { break; } when(wait(success(committedVersion) || delay(SERVER_KNOBS->BACKUP_NOOP_POP_DELAY, self->cx->taskID))) { if (committedVersion.isReady()) { - self->savedVersion = std::max(committedVersion.get(), self->savedVersion); + self->popVersion = + std::max(self->popVersion, std::max(committedVersion.get(), self->savedVersion)); self->minKnownCommittedVersion = std::max(committedVersion.get(), self->minKnownCommittedVersion); - TraceEvent("BackupWorkerNoopPop", self->myId).detail("SavedVersion", self->savedVersion); + TraceEvent("BackupWorkerNoopPop", self->myId) + .detail("SavedVersion", self->savedVersion) + .detail("PopVersion", self->popVersion); self->pop(); // Pop while the worker is in this NOOP state. committedVersion = Never(); } else { @@ -884,6 +907,7 @@ ACTOR Future monitorBackupKeyOrPullData(BackupData* self, bool keyPresent) } } } + ASSERT(!keyPresent == present.get()); keyPresent = !keyPresent; } } From cdc911a6ae5a9c59b669aa31d2f93b5e793f1e38 Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Sat, 18 Apr 2020 09:38:14 -0700 Subject: [PATCH 1475/1604] Fix inadvertent savedVersion update --- fdbserver/BackupWorker.actor.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fdbserver/BackupWorker.actor.cpp b/fdbserver/BackupWorker.actor.cpp index 2d2a6fe259..b420b1c94e 100644 --- a/fdbserver/BackupWorker.actor.cpp +++ b/fdbserver/BackupWorker.actor.cpp @@ -327,6 +327,7 @@ struct BackupData { } bool modified = false; + bool minVersionChanged = false; Version minVersion = std::numeric_limits::max(); for (const auto [uid, version] : uidVersions) { auto it = backups.find(uid); @@ -334,6 +335,7 @@ struct BackupData { modified = true; backups.emplace(uid, BackupData::PerBackupInfo(this, uid, version)); minVersion = std::min(minVersion, version); + minVersionChanged = true; } else { stopList.erase(uid); } @@ -345,7 +347,7 @@ struct BackupData { it->second.stop(); modified = true; } - if (backupEpoch < recruitedEpoch && savedVersion + 1 == startVersion) { + if (minVersionChanged && backupEpoch < recruitedEpoch && savedVersion + 1 == startVersion) { // Advance savedVersion to minimize version ranges in case backupEpoch's // progress is not saved. Master may set a very low startVersion that // is already popped. Advance the version is safe because these From 8245f12091a49d9b9fde9f84c595bd2a54808768 Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Sat, 18 Apr 2020 10:24:08 -0700 Subject: [PATCH 1476/1604] Backup worker doesn't save progress in NOOP mode This fixes the consistency check failure, where saving progress commits new transactions. Pop is performed by the NOOP loop in monitorBackupKeyOrPullData. --- fdbserver/BackupWorker.actor.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/fdbserver/BackupWorker.actor.cpp b/fdbserver/BackupWorker.actor.cpp index b420b1c94e..ee7c26757d 100644 --- a/fdbserver/BackupWorker.actor.cpp +++ b/fdbserver/BackupWorker.actor.cpp @@ -785,21 +785,15 @@ ACTOR Future uploadData(BackupData* self) { // If transition into NOOP mode, should clear messages if (!self->pulling) { self->messages.clear(); - // Update popVersion so that save progress below can - // indicate ranges not used for future epochs. - if (self->popVersion > self->savedVersion && self->backupEpoch == self->recruitedEpoch) { - popVersion = std::max(popVersion, self->popVersion); - } } - if (popVersion > self->savedVersion) { + if (popVersion > self->savedVersion && popVersion > self->popVersion) { wait(saveProgress(self, popVersion)); TraceEvent("BackupWorkerSavedProgress", self->myId) .detail("Tag", self->tag.toString()) .detail("Version", popVersion) .detail("MsgQ", self->messages.size()); self->savedVersion = std::max(popVersion, self->savedVersion); - self->popVersion = std::max(self->savedVersion, self->popVersion); self->pop(); } From 70221a25d7e6406da7db5076681296e7facef53a Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Sun, 19 Apr 2020 10:03:47 -0700 Subject: [PATCH 1477/1604] True-up a backup's begin version For the first mutation log of a backup, we need to true-up its begin version to the exact version of the first mutation. This is needed to ensure the strict less than relationship between two mutation logs, if one's version range is within the other. A problematic scenario is as follows: Epoch 1: a mutation log A [200, 900] is saved, but its progress is NOT saved. Epoch 2: master recruits a worker for [1, 1000], 1000 is epoch 1's end version. New worker saves a mutation log B [100, 1000] A's range is strict within B's range, but A's size is larger than B. This happens because B's start version is true-up to the backup's begin version, which is not the actual version of the first mutation. After B's begin version is true-up to 300, we won't have this issue. --- fdbserver/BackupWorker.actor.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fdbserver/BackupWorker.actor.cpp b/fdbserver/BackupWorker.actor.cpp index ee7c26757d..9bd65b2434 100644 --- a/fdbserver/BackupWorker.actor.cpp +++ b/fdbserver/BackupWorker.actor.cpp @@ -75,7 +75,7 @@ struct BackupData { LogEpoch oldestBackupEpoch = 0; // oldest epoch that still has data on tLogs for backup to pull Version minKnownCommittedVersion; Version savedVersion; // Largest version saved to blob storage - Version popVersion; // Largest version popped. Can be larger than savedVersion in NOOP mode. + Version popVersion; // Largest version popped in NOOP mode, can be larger than savedVersion. AsyncVar> logSystem; Database cx; std::vector messages; @@ -663,8 +663,13 @@ ACTOR Future saveMutationsToFile(BackupData* self, Version popVersion, int activeUids.push_back(it->first); self->insertRanges(keyRangeMap, it->second.ranges.get(), index); if (it->second.lastSavedVersion == invalidVersion) { - it->second.lastSavedVersion = - self->savedVersion > self->startVersion ? self->savedVersion : self->startVersion; + if (it->second.startVersion > self->startVersion && !self->messages.empty()) { + // True-up first mutation log's begin version + it->second.lastSavedVersion = self->messages[0].getVersion(); + } else { + it->second.lastSavedVersion = + std::max(self->popVersion, std::max(self->savedVersion, self->startVersion)); + } } logFileFutures.push_back(it->second.container.get().get()->writeTaggedLogFile( it->second.lastSavedVersion, popVersion + 1, blockSize, self->tag.id, self->totalTags)); From 082309142305feb5a53f60824cc4138a7247ceca Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Sun, 19 Apr 2020 21:39:47 -0700 Subject: [PATCH 1478/1604] Fix backup worker removal races with setting The master waits for all backup worker recruitment done and then set them in a batch. However, a backup worker could remove itself before the master sets it. As a result, the worker is not removed and oldest backup epoch can't advance, and TLog can't be popped. --- fdbserver/TagPartitionedLogSystem.actor.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fdbserver/TagPartitionedLogSystem.actor.cpp b/fdbserver/TagPartitionedLogSystem.actor.cpp index 9a6184d042..0b65cbdb4e 100644 --- a/fdbserver/TagPartitionedLogSystem.actor.cpp +++ b/fdbserver/TagPartitionedLogSystem.actor.cpp @@ -188,6 +188,7 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted removedBackupWorkers; // Workers that are removed before setting them. Optional recoverAt; Optional recoveredAt; @@ -1399,6 +1400,10 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCountedepoch; oldestBackupEpoch = this->epoch; for (const auto& reply : replies) { + if (removedBackupWorkers.count(reply.interf.id()) > 0) { + removedBackupWorkers.erase(reply.interf.id()); + continue; + } Reference>> worker(new AsyncVar>(OptionalInterface(reply.interf))); if (reply.backupEpoch != logsetEpoch) { // find the logset from oldLogData @@ -1408,6 +1413,9 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCountedbackupWorkers.push_back(worker); + TraceEvent("AddBackupWorker", dbgid) + .detail("Epoch", logsetEpoch) + .detail("BackupWorkerID", reply.interf.id()); } TraceEvent("SetOldestBackupEpoch", dbgid).detail("Epoch", oldestBackupEpoch); backupWorkerChanged.trigger(); @@ -1434,6 +1442,8 @@ struct TagPartitionedLogSystem : ILogSystem, ReferenceCounted Date: Mon, 20 Apr 2020 11:05:50 -0700 Subject: [PATCH 1479/1604] Backup worker pops max of savedVersion or NOOP's popVersion --- fdbserver/BackupWorker.actor.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fdbserver/BackupWorker.actor.cpp b/fdbserver/BackupWorker.actor.cpp index 9bd65b2434..066ccc0143 100644 --- a/fdbserver/BackupWorker.actor.cpp +++ b/fdbserver/BackupWorker.actor.cpp @@ -68,7 +68,8 @@ struct BackupData { const UID myId; const Tag tag; // LogRouter tag for this worker, i.e., (-2, i) const int totalTags; // Total log router tags - const Version startVersion; + // Backup request's commit version. Mutations are logged at some version after this. + const Version startVersion; // This worker's start version const Optional endVersion; // old epoch's end version (inclusive), or empty for current epoch const LogEpoch recruitedEpoch; // current epoch whose tLogs are receiving mutations const LogEpoch backupEpoch; // the epoch workers should pull mutations @@ -292,7 +293,7 @@ struct BackupData { } ASSERT_WE_THINK(backupEpoch == oldestBackupEpoch); const Tag popTag = logSystem.get()->getPseudoPopTag(tag, ProcessClass::BackupClass); - logSystem.get()->pop(popVersion, popTag); + logSystem.get()->pop(std::max(popVersion, savedVersion), popTag); } void stop() { From 5c399bf725f9753abaaadf45be79640b4f8a07d2 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 20 Apr 2020 13:14:19 -0700 Subject: [PATCH 1480/1604] Move the callbacks into ::run() right before it exits. stopped=true doesn't cause the run loop to immediately exit. --- flow/Net2.actor.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index 2af69a4a28..d862dd03bc 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -207,9 +207,6 @@ public: void trackMinPriority( TaskPriority minTaskID, double now ); void stopImmediately() { stopped=true; decltype(ready) _1; ready.swap(_1); decltype(timers) _2; timers.swap(_2); - for ( auto& fn : stopCallbacks ) { - fn(); - } } Future timeOffsetLogger; @@ -1189,6 +1186,10 @@ void Net2::run() { TraceEvent("SomewhatSlowRunLoopBottom").detail("Elapsed", nnow - now); // This includes the time spent running tasks } + for ( auto& fn : stopCallbacks ) { + fn(); + } + #ifdef WIN32 timeEndPeriod(1); #endif From e51d0365cf645084041a83b5f58cb69f1ee2cbd9 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 20 Apr 2020 13:16:16 -0700 Subject: [PATCH 1481/1604] Cleanup: Use the shutdown callback for destroying TLS state. --- fdbclient/NativeAPI.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index 64691a8813..909d0abeaa 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -997,6 +997,7 @@ void setupNetwork(uint64_t transportId, bool useMetrics) { TLS::DisableOpenSSLAtExitHandler(); g_network = newNet2(tlsConfig, false, useMetrics || networkOptions.traceDirectory.present()); g_network->addStopCallback( Net2FileSystem::stop ); + g_network->addStopCallback( TLS::DestroyOpenSSLGlobalState ); FlowTransport::createInstance(true, transportId); Net2FileSystem::newFileSystem(); } @@ -1021,7 +1022,6 @@ void stopNetwork() { g_network->stop(); closeTraceFile(); - TLS::DestroyOpenSSLGlobalState(); } Reference DatabaseContext::getMasterProxies(bool useProvisionalProxies) { From 75a4f3b7c9da0040f9b4d011fc3729d2076b912b Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 20 Apr 2020 13:19:42 -0700 Subject: [PATCH 1482/1604] Remove comment about ignoring runOnMainThread errors. If we got an exception, it wouldn't be of type `Error` anyway, so it seems like things would crash regardless. --- flow/Net2.actor.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index f99f7ebfd7..b100f75e1b 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -141,14 +141,12 @@ public: if ( thread_network == this ) stopImmediately(); else - // SOMEDAY: NULL for deferred error, no analysis of correctness (itp) onMainThreadVoid( [this] { this->stopImmediately(); }, NULL ); } virtual void addStopCallback( std::function fn ) { if ( thread_network == this ) stopCallbacks.emplace_back(std::move(fn)); else - // SOMEDAY: NULL for deferred error, no analysis of correctness (itp) onMainThreadVoid( [this, fn] { this->stopCallbacks.emplace_back(std::move(fn)); }, NULL ); } From 61f0f44ab3479aae7a120404394825bf534c4354 Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Mon, 20 Apr 2020 17:07:50 -0700 Subject: [PATCH 1483/1604] Fix comments on startVersion in BackupWorker --- fdbserver/BackupWorker.actor.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fdbserver/BackupWorker.actor.cpp b/fdbserver/BackupWorker.actor.cpp index 066ccc0143..077e4963d4 100644 --- a/fdbserver/BackupWorker.actor.cpp +++ b/fdbserver/BackupWorker.actor.cpp @@ -68,7 +68,6 @@ struct BackupData { const UID myId; const Tag tag; // LogRouter tag for this worker, i.e., (-2, i) const int totalTags; // Total log router tags - // Backup request's commit version. Mutations are logged at some version after this. const Version startVersion; // This worker's start version const Optional endVersion; // old epoch's end version (inclusive), or empty for current epoch const LogEpoch recruitedEpoch; // current epoch whose tLogs are receiving mutations @@ -209,8 +208,12 @@ struct BackupData { } BackupData* self = nullptr; + + // Backup request's commit version. Mutations are logged at some version after this. Version startVersion = invalidVersion; + // The last mutation log's saved version (not inclusive), i.e., next log's begin version. Version lastSavedVersion = invalidVersion; + Future>> container; Future>> ranges; // Key ranges of this backup Future updateWorker; From 0ae0a81edff499e7b24a75f0ff31b0717644500c Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Mon, 20 Apr 2020 20:22:04 -0700 Subject: [PATCH 1484/1604] Ensure mutation logs save complete version's data I.e., do not allow the same version's mutations saved in different files. Otherwise, we may have a file only contain a version's partial data, causing continuity analysis of mutation logs to fail. This could also cause restore failures, if the target version's mutations are stored in two files. In the above description, all mutation logs refer to the same tag's logs. --- fdbserver/BackupWorker.actor.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/fdbserver/BackupWorker.actor.cpp b/fdbserver/BackupWorker.actor.cpp index 077e4963d4..c6d0ba8d10 100644 --- a/fdbserver/BackupWorker.actor.cpp +++ b/fdbserver/BackupWorker.actor.cpp @@ -765,6 +765,10 @@ ACTOR Future uploadData(BackupData* self) { state int numMsg = 0; Version lastPopVersion = popVersion; + // index of last version's end position in self->messages + int lastVersionIndex = 0; + Version lastVersion = invalidVersion; + if (self->messages.empty()) { // Even though messages is empty, we still want to advance popVersion. if (!self->endVersion.present()) { @@ -773,18 +777,30 @@ ACTOR Future uploadData(BackupData* self) { } else { for (const auto& message : self->messages) { // message may be prefetched in peek; uncommitted message should not be uploaded. - if (message.getVersion() > self->maxPopVersion()) break; - popVersion = std::max(popVersion, message.getVersion()); + const Version version = message.getVersion(); + if (version > self->maxPopVersion()) break; + if (version > popVersion) { + lastVersionIndex = numMsg; + lastVersion = popVersion; + popVersion = version; + } numMsg++; } } if (self->pullFinished()) { popVersion = self->endVersion.get(); + } else { + // make sure file is saved on version boundary + popVersion = lastVersion; + numMsg = lastVersionIndex; } if (((numMsg > 0 || popVersion > lastPopVersion) && self->pulling) || self->pullFinished()) { TraceEvent("BackupWorkerSave", self->myId) .detail("Version", popVersion) + .detail("LastPopVersion", lastPopVersion) + .detail("Pulling", self->pulling) .detail("SavedVersion", self->savedVersion) + .detail("NumMsg", numMsg) .detail("MsgQ", self->messages.size()); // save an empty file for old epochs so that log file versions are continuous wait(saveMutationsToFile(self, popVersion, numMsg)); From a51746b307dc2c6bc88a52139fe3b9cf53caee37 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 20 Apr 2020 21:38:04 -0700 Subject: [PATCH 1485/1604] Match 6.2.15's behavior in how invalid/unreadable/non-existent certs are handled. Which is to proceed past Net2 creation, and allow certificate refresh to try and eventually load valid certs. Additionally, fix certificate refeshing dieing if the certificate is not readable when first called. In testing, I also found and fixed an issue where if a cert went from unreadable to readable, we wouldn't reload the TLS context, due to not considering it as a file change. --- flow/Net2.actor.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index cb9829dfb2..4dc4106e9d 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -907,13 +907,19 @@ ACTOR static Future watchFileForChanges( std::string filename, AsyncTrigge if (filename == "") { return Never(); } - state std::time_t lastModTime = wait(IAsyncFileSystem::filesystem()->lastWriteTime(filename)); + state bool firstRun = true; + state bool statError = false; + state std::time_t lastModTime = 0; loop { - wait(delay(FLOW_KNOBS->TLS_CERT_REFRESH_DELAY_SECONDS)); try { std::time_t modtime = wait(IAsyncFileSystem::filesystem()->lastWriteTime(filename)); - if (lastModTime != modtime) { + if (firstRun) { lastModTime = modtime; + firstRun = false; + } + if (lastModTime != modtime || statError) { + lastModTime = modtime; + statError = false; fileChanged->trigger(); } } catch (Error& e) { @@ -923,10 +929,12 @@ ACTOR static Future watchFileForChanges( std::string filename, AsyncTrigge // certificates, then there's no point in crashing, but we should complain // loudly. IAsyncFile will log the error, but not necessarily as a warning. TraceEvent(SevWarnAlways, "TLSCertificateRefreshStatError").detail("File", filename); + statError = true; } else { throw; } } + wait(delay(FLOW_KNOBS->TLS_CERT_REFRESH_DELAY_SECONDS)); } } @@ -975,9 +983,9 @@ void Net2::initTLS() { return; } #ifndef TLS_DISABLED + auto onPolicyFailure = [this]() { this->countTLSPolicyFailures++; }; try { boost::asio::ssl::context newContext(boost::asio::ssl::context::tls); - auto onPolicyFailure = [this]() { this->countTLSPolicyFailures++; }; const LoadedTLSConfig& loaded = tlsConfig.loadSync(); TraceEvent("Net2TLSConfig") .detail("CAPath", tlsConfig.getCAPathSync()) @@ -987,11 +995,10 @@ void Net2::initTLS() { .detail("VerifyPeers", boost::algorithm::join(loaded.getVerifyPeers(), "|")); ConfigureSSLContext( tlsConfig.loadSync(), &newContext, onPolicyFailure ); sslContextVar.set(ReferencedObject::from(std::move(newContext))); - backgroundCertRefresh = reloadCertificatesOnChange( tlsConfig, onPolicyFailure, &sslContextVar ); } catch (Error& e) { TraceEvent("Net2TLSInitError").error(e); - throw tls_error(); } + backgroundCertRefresh = reloadCertificatesOnChange( tlsConfig, onPolicyFailure, &sslContextVar ); #endif tlsInitialized = true; } From 3063611355235ac73d33e52095ffd97c686568c7 Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Wed, 15 Apr 2020 23:08:19 -0700 Subject: [PATCH 1486/1604] Write range files' begin & end keys to manifest file This information can be very useful in knowing the content in these files, especially for restores. --- fdbclient/BackupContainer.actor.cpp | 25 ++++++++++++++++++++----- fdbclient/BackupContainer.h | 4 +++- fdbclient/FileBackupAgent.actor.cpp | 6 +++++- fdbclient/JSONDoc.h | 2 +- 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/fdbclient/BackupContainer.actor.cpp b/fdbclient/BackupContainer.actor.cpp index 5b6a21d5da..a9e8b96770 100644 --- a/fdbclient/BackupContainer.actor.cpp +++ b/fdbclient/BackupContainer.actor.cpp @@ -489,8 +489,11 @@ public: return readKeyspaceSnapshot_impl(Reference::addRef(this), snapshot); } - ACTOR static Future writeKeyspaceSnapshotFile_impl(Reference bc, std::vector fileNames, int64_t totalBytes) { - ASSERT(!fileNames.empty()); + ACTOR static Future writeKeyspaceSnapshotFile_impl(Reference bc, + std::vector fileNames, + std::vector> beginEndKeys, + int64_t totalBytes) { + ASSERT(!fileNames.empty() && fileNames.size() == beginEndKeys.size()); state Version minVer = std::numeric_limits::max(); state Version maxVer = 0; @@ -521,6 +524,13 @@ public: doc.create("beginVersion") = minVer; doc.create("endVersion") = maxVer; + auto ranges = doc.subDoc("keyRanges"); + for (int i = 0; i < beginEndKeys.size(); i++) { + auto fileDoc = ranges.subDoc(fileNames[i], /*split=*/false); + fileDoc.create("beginKey") = printable(beginEndKeys[i].first); + fileDoc.create("endKey") = printable(beginEndKeys[i].second); + } + wait(yield()); state std::string docString = json_spirit::write_string(json); @@ -531,8 +541,11 @@ public: return Void(); } - Future writeKeyspaceSnapshotFile(std::vector fileNames, int64_t totalBytes) final { - return writeKeyspaceSnapshotFile_impl(Reference::addRef(this), fileNames, totalBytes); + Future writeKeyspaceSnapshotFile(const std::vector& fileNames, + const std::vector>& beginEndKeys, + int64_t totalBytes) final { + return writeKeyspaceSnapshotFile_impl(Reference::addRef(this), fileNames, + beginEndKeys, totalBytes); }; // List log files, unsorted, which contain data at any version >= beginVersion and <= targetVersion. @@ -2085,6 +2098,7 @@ ACTOR Future testBackupContainer(std::string url) { state Version logStart = v; state int kvfiles = deterministicRandom()->randomInt(0, 3); + state std::vector> beginEndKeys; while(kvfiles > 0) { if(snapshots.empty()) { snapshots[v] = {}; @@ -2097,13 +2111,14 @@ ACTOR Future testBackupContainer(std::string url) { ++nRangeFiles; v = nextVersion(v); snapshots.rbegin()->second.push_back(range->getFileName()); + beginEndKeys.emplace_back(LiteralStringRef(""), LiteralStringRef("")); int size = chooseFileSize(fileSizes); snapshotSizes.rbegin()->second += size; writes.push_back(writeAndVerifyFile(c, range, size)); if(deterministicRandom()->random01() < .2) { - writes.push_back(c->writeKeyspaceSnapshotFile(snapshots.rbegin()->second, snapshotSizes.rbegin()->second)); + writes.push_back(c->writeKeyspaceSnapshotFile(snapshots.rbegin()->second, beginEndKeys, snapshotSizes.rbegin()->second)); snapshots[v] = {}; snapshotSizes[v] = 0; break; diff --git a/fdbclient/BackupContainer.h b/fdbclient/BackupContainer.h index 9697d280bc..c843fb65e7 100644 --- a/fdbclient/BackupContainer.h +++ b/fdbclient/BackupContainer.h @@ -231,7 +231,9 @@ public: // Write a KeyspaceSnapshotFile of range file names representing a full non overlapping // snapshot of the key ranges this backup is targeting. - virtual Future writeKeyspaceSnapshotFile(std::vector fileNames, int64_t totalBytes) = 0; + virtual Future writeKeyspaceSnapshotFile(const std::vector& fileNames, + const std::vector>& beginEndKeys, + int64_t totalBytes) = 0; // Open a file for read by name virtual Future> readFile(std::string name) = 0; diff --git a/fdbclient/FileBackupAgent.actor.cpp b/fdbclient/FileBackupAgent.actor.cpp index e16863791f..59f1837374 100644 --- a/fdbclient/FileBackupAgent.actor.cpp +++ b/fdbclient/FileBackupAgent.actor.cpp @@ -2257,6 +2257,7 @@ namespace fileBackup { } std::vector files; + std::vector> beginEndKeys; state Version maxVer = 0; state Version minVer = std::numeric_limits::max(); state int64_t totalBytes = 0; @@ -2272,6 +2273,9 @@ namespace fileBackup { // Add file to final file list files.push_back(r.fileName); + // Add (beginKey, endKey) pairs to the list + beginEndKeys.emplace_back(i->second.begin, i->first); + // Update version range seen if(r.version < minVer) minVer = r.version; @@ -2293,7 +2297,7 @@ namespace fileBackup { } Params.endVersion().set(task, maxVer); - wait(bc->writeKeyspaceSnapshotFile(files, totalBytes)); + wait(bc->writeKeyspaceSnapshotFile(files, beginEndKeys, totalBytes)); TraceEvent(SevInfo, "FileBackupWroteSnapshotManifest") .detail("BackupUID", config.getUid()) diff --git a/fdbclient/JSONDoc.h b/fdbclient/JSONDoc.h index 70c05375aa..aafd1bb87f 100644 --- a/fdbclient/JSONDoc.h +++ b/fdbclient/JSONDoc.h @@ -193,7 +193,7 @@ struct JSONDoc { return v.get_value(); } - // Ensures that a an Object exists at path and returns a JSONDoc that writes to it. + // Ensures that an Object exists at path and returns a JSONDoc that writes to it. JSONDoc subDoc(std::string path, bool split=true) { json_spirit::mValue &v = create(path, split); if(v.type() != json_spirit::obj_type) From a2b867c6f95c76006c85c665667684f571fb976f Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Thu, 16 Apr 2020 12:33:24 -0700 Subject: [PATCH 1487/1604] Fix a unit test failure --- fdbclient/BackupContainer.actor.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/fdbclient/BackupContainer.actor.cpp b/fdbclient/BackupContainer.actor.cpp index a9e8b96770..fe2e1b79f0 100644 --- a/fdbclient/BackupContainer.actor.cpp +++ b/fdbclient/BackupContainer.actor.cpp @@ -527,8 +527,8 @@ public: auto ranges = doc.subDoc("keyRanges"); for (int i = 0; i < beginEndKeys.size(); i++) { auto fileDoc = ranges.subDoc(fileNames[i], /*split=*/false); - fileDoc.create("beginKey") = printable(beginEndKeys[i].first); - fileDoc.create("endKey") = printable(beginEndKeys[i].second); + fileDoc.create("beginKey") = beginEndKeys[i].first.toString(); + fileDoc.create("endKey") = beginEndKeys[i].second.toString(); } wait(yield()); @@ -1323,7 +1323,7 @@ public: restorable.targetVersion = targetVersion; std::vector ranges = wait(bc->readKeyspaceSnapshot(snapshot.get())); - restorable.ranges = ranges; + restorable.ranges = std::move(ranges); // No logs needed if there is a complete key space snapshot at the target version. if (snapshot.get().beginVersion == snapshot.get().endVersion && @@ -2087,6 +2087,7 @@ ACTOR Future testBackupContainer(std::string url) { state std::vector> writes; state std::map> snapshots; state std::map snapshotSizes; + state std::map>> snapshotBeginEndKeys; state int nRangeFiles = 0; state std::map logs; state Version v = deterministicRandom()->randomInt64(0, std::numeric_limits::max() / 2); @@ -2098,10 +2099,10 @@ ACTOR Future testBackupContainer(std::string url) { state Version logStart = v; state int kvfiles = deterministicRandom()->randomInt(0, 3); - state std::vector> beginEndKeys; while(kvfiles > 0) { if(snapshots.empty()) { snapshots[v] = {}; + snapshotBeginEndKeys[v] = {}; snapshotSizes[v] = 0; if(deterministicRandom()->coinflip()) { v = nextVersion(v); @@ -2111,15 +2112,17 @@ ACTOR Future testBackupContainer(std::string url) { ++nRangeFiles; v = nextVersion(v); snapshots.rbegin()->second.push_back(range->getFileName()); - beginEndKeys.emplace_back(LiteralStringRef(""), LiteralStringRef("")); + snapshotBeginEndKeys.rbegin()->second.emplace_back(LiteralStringRef(""), LiteralStringRef("")); int size = chooseFileSize(fileSizes); snapshotSizes.rbegin()->second += size; writes.push_back(writeAndVerifyFile(c, range, size)); if(deterministicRandom()->random01() < .2) { - writes.push_back(c->writeKeyspaceSnapshotFile(snapshots.rbegin()->second, beginEndKeys, snapshotSizes.rbegin()->second)); + writes.push_back(c->writeKeyspaceSnapshotFile( + snapshots.rbegin()->second, snapshotBeginEndKeys.rbegin()->second, snapshotSizes.rbegin()->second)); snapshots[v] = {}; + snapshotBeginEndKeys[v] = {}; snapshotSizes[v] = 0; break; } From 930b175c4cb3ece3f82a8ec79dd4989ca688d9e1 Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Thu, 16 Apr 2020 15:11:09 -0700 Subject: [PATCH 1488/1604] Add range files' key ranges to RestorableFileSet Also add continuous logs' begin and end version in RestorableFileSet. --- fdbclient/BackupContainer.actor.cpp | 41 ++++++++++++++++++++++++----- fdbclient/BackupContainer.h | 7 +++++ 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/fdbclient/BackupContainer.actor.cpp b/fdbclient/BackupContainer.actor.cpp index fe2e1b79f0..965042f627 100644 --- a/fdbclient/BackupContainer.actor.cpp +++ b/fdbclient/BackupContainer.actor.cpp @@ -20,6 +20,7 @@ #include "fdbclient/BackupContainer.h" #include "fdbclient/BackupAgent.actor.h" +#include "fdbclient/FDBTypes.h" #include "fdbclient/JsonBuilder.h" #include "flow/Trace.h" #include "flow/UnitTest.h" @@ -424,9 +425,11 @@ public: } // TODO: Do this more efficiently, as the range file list for a snapshot could potentially be hundreds of megabytes. - ACTOR static Future> readKeyspaceSnapshot_impl(Reference bc, KeyspaceSnapshotFile snapshot) { + ACTOR static Future, std::map>> readKeyspaceSnapshot_impl( + Reference bc, KeyspaceSnapshotFile snapshot) { // Read the range file list for the specified version range, and then index them by fileName. - // This is so we can verify that each of the files listed in the manifest file are also in the container at this time. + // This is so we can verify that each of the files listed in the manifest file are also in the container at this + // time. std::vector files = wait(bc->listRangeFiles(snapshot.beginVersion, snapshot.endVersion)); state std::map rangeIndex; for(auto &f : files) @@ -482,10 +485,30 @@ public: throw restore_missing_data(); } - return results; + // Check key ranges for files + std::map fileKeyRanges; + JSONDoc ranges = doc.subDoc("keyRanges"); // Create an empty doc if not existed + for (auto i : ranges.obj()) { + const std::string& filename = i.first; + JSONDoc fields(i.second); + std::string begin, end; + if (fields.tryGet("beginKey", begin) && fields.tryGet("endKey", end)) { + TraceEvent("ManifestFields") + .detail("File", filename) + .detail("Begin", printable(StringRef(begin))) + .detail("End", printable(StringRef(end))); + fileKeyRanges.emplace(filename, KeyRange(KeyRangeRef(StringRef(begin), StringRef(end)))); + } else { + TraceEvent("MalFormattedManifest").detail("Key", filename); + throw restore_corrupted_data(); + } + } + + return std::make_pair(results, fileKeyRanges); } - Future> readKeyspaceSnapshot(KeyspaceSnapshotFile snapshot) { + Future, std::map>> readKeyspaceSnapshot( + KeyspaceSnapshotFile snapshot) { return readKeyspaceSnapshot_impl(Reference::addRef(this), snapshot); } @@ -1322,8 +1345,10 @@ public: restorable.snapshot = snapshot.get(); restorable.targetVersion = targetVersion; - std::vector ranges = wait(bc->readKeyspaceSnapshot(snapshot.get())); - restorable.ranges = std::move(ranges); + std::pair, std::map> results = + wait(bc->readKeyspaceSnapshot(snapshot.get())); + restorable.ranges = std::move(results.first); + restorable.keyRanges = std::move(results.second); // No logs needed if there is a complete key space snapshot at the target version. if (snapshot.get().beginVersion == snapshot.get().endVersion && @@ -1352,6 +1377,8 @@ public: // sort by version order again for continuous analysis std::sort(restorable.logs.begin(), restorable.logs.end()); if (isPartitionedLogsContinuous(restorable.logs, snapshot.get().beginVersion, targetVersion)) { + restorable.continuousBeginVersion = snapshot.get().beginVersion; + restorable.continuousEndVersion = targetVersion + 1; // not inclusive return Optional(restorable); } return Optional(); @@ -1365,6 +1392,8 @@ public: Version end = logs.begin()->endVersion; computeRestoreEndVersion(logs, &restorable.logs, &end, targetVersion); if (end >= targetVersion) { + restorable.continuousBeginVersion = logs.begin()->beginVersion; + restorable.continuousEndVersion = end; return Optional(restorable); } } diff --git a/fdbclient/BackupContainer.h b/fdbclient/BackupContainer.h index c843fb65e7..e68e29b014 100644 --- a/fdbclient/BackupContainer.h +++ b/fdbclient/BackupContainer.h @@ -193,6 +193,13 @@ struct RestorableFileSet { Version targetVersion; std::vector logs; std::vector ranges; + + // Range file's key ranges. Can be empty for backups generated before 6.3. + std::map keyRanges; + + // Mutation logs continuous range [begin, end) + Version continuousBeginVersion, continuousEndVersion; + KeyspaceSnapshotFile snapshot; // Info. for debug purposes }; From 0938e45c6ab1bb7607c582745f26d190cf142b6c Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Thu, 16 Apr 2020 15:52:20 -0700 Subject: [PATCH 1489/1604] Set continuous version to invalidVersion when snapshot version is the target version --- fdbclient/BackupContainer.actor.cpp | 1 + fdbclient/BackupContainer.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/fdbclient/BackupContainer.actor.cpp b/fdbclient/BackupContainer.actor.cpp index 965042f627..1719d7837e 100644 --- a/fdbclient/BackupContainer.actor.cpp +++ b/fdbclient/BackupContainer.actor.cpp @@ -1353,6 +1353,7 @@ public: // No logs needed if there is a complete key space snapshot at the target version. if (snapshot.get().beginVersion == snapshot.get().endVersion && snapshot.get().endVersion == targetVersion) { + restorable.continuousBeginVersion = restorable.continuousEndVersion = invalidVersion; return Optional(restorable); } diff --git a/fdbclient/BackupContainer.h b/fdbclient/BackupContainer.h index e68e29b014..7c6f96d38c 100644 --- a/fdbclient/BackupContainer.h +++ b/fdbclient/BackupContainer.h @@ -197,7 +197,8 @@ struct RestorableFileSet { // Range file's key ranges. Can be empty for backups generated before 6.3. std::map keyRanges; - // Mutation logs continuous range [begin, end) + // Mutation logs continuous range [begin, end). Both can be invalidVersion + // when the entire key space snapshot is at the target version. Version continuousBeginVersion, continuousEndVersion; KeyspaceSnapshotFile snapshot; // Info. for debug purposes From 0e54f1ed31c3be93732c2adfef41e7b45b8b80bc Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Thu, 16 Apr 2020 20:47:05 -0700 Subject: [PATCH 1490/1604] Fix a test failure minRestorableVersion may be decided by snapshot version and is larger than contiguousLogEnd version. --- .../workloads/BackupAndParallelRestoreCorrectness.actor.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp b/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp index 987a394cbb..55a235b86c 100644 --- a/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp +++ b/fdbserver/workloads/BackupAndParallelRestoreCorrectness.actor.cpp @@ -450,7 +450,8 @@ struct BackupAndParallelRestoreCorrectnessWorkload : TestWorkload { targetVersion = desc.minRestorableVersion.get(); } else if (deterministicRandom()->random01() < 0.1) { targetVersion = desc.maxRestorableVersion.get(); - } else if (deterministicRandom()->random01() < 0.5) { + } else if (deterministicRandom()->random01() < 0.5 && + desc.minRestorableVersion.get() < desc.contiguousLogEnd.get()) { // The assertion may fail because minRestorableVersion may be decided by snapshot version. // ASSERT_WE_THINK(desc.minRestorableVersion.get() <= desc.contiguousLogEnd.get()); // This assertion can fail when contiguousLogEnd < maxRestorableVersion and From c80d28ac7e66e658df88ed58b16fcdf12bcd6d8d Mon Sep 17 00:00:00 2001 From: Steve Atherton Date: Tue, 21 Apr 2020 04:12:39 -0700 Subject: [PATCH 1491/1604] Added several assertions and removed some checks for situations in commitSubtree() which should no longer be possible due to recent changes. This is mainly to test assumptions being made in the commitSubtree() refactor in progress. Set simulation to always choose Redwood, will revert for PR. --- fdbserver/SimulatedCluster.actor.cpp | 2 +- fdbserver/VersionedBTree.actor.cpp | 47 +++++++--------------------- 2 files changed, 12 insertions(+), 37 deletions(-) diff --git a/fdbserver/SimulatedCluster.actor.cpp b/fdbserver/SimulatedCluster.actor.cpp index f89a97c59e..15ea6504d6 100644 --- a/fdbserver/SimulatedCluster.actor.cpp +++ b/fdbserver/SimulatedCluster.actor.cpp @@ -737,7 +737,7 @@ void SimulationConfig::generateNormalConfig(int minimumReplication, int minimumR if (deterministicRandom()->random01() < 0.25) db.desiredTLogCount = deterministicRandom()->randomInt(1,7); if (deterministicRandom()->random01() < 0.25) db.masterProxyCount = deterministicRandom()->randomInt(1,7); if (deterministicRandom()->random01() < 0.25) db.resolverCount = deterministicRandom()->randomInt(1,7); - int storage_engine_type = deterministicRandom()->randomInt(0, 3); + int storage_engine_type = 3; // deterministicRandom()->randomInt(0, 4); switch (storage_engine_type) { case 0: { TEST(true); // Simulated cluster using ssd storage engine diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index c2e8038be9..2713ad44d6 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -3835,28 +3835,6 @@ private: // iMutationBoundary is greatest boundary <= lowerBound->key // iMutationBoundaryEnd is least boundary >= upperBound->key - // If the boundary range iterators are the same then this subtree only has one unique key, which is the same key as the boundary - // record the iterators are pointing to. There only two outcomes possible: Clearing the subtree or leaving it alone. - // If there are any changes to the one key then the entire subtree should be deleted as the changes for the key - // do not go into this subtree. - if(iMutationBoundary == iMutationBoundaryEnd) { - if(iMutationBoundary.mutation().boundaryChanged) { - debug_printf("%s lower and upper bound key/version match and key is modified so deleting page, returning %s\n", context.c_str(), toString(result).c_str()); - if(isLeaf) { - self->freeBtreePage(rootID, writeVersion); - } - else { - self->m_lazyDeleteQueue.pushBack(LazyDeleteQueueEntry{writeVersion, rootID}); - } - return result; - } - - // Otherwise, no changes to this subtree - result.contents() = ChildLinksRef(decodeLowerBound, decodeUpperBound); - debug_printf("%s page contains a single key '%s' which is not changing, returning %s\n", context.c_str(), lowerBound->key.toString().c_str(), toString(result).c_str()); - return result; - } - // If one mutation range covers the entire subtree, then check if the entire subtree is modified, // unmodified, or possibly/partially modified. MutationBuffer::const_iterator iMutationBoundaryNext = iMutationBoundary; @@ -4185,28 +4163,25 @@ private: const RedwoodRecordRef &childLowerBound = first ? *lowerBound : cursor.get(); first = false; - // Skip over any children that do not link to a page. They exist to preserve the ancestors from - // which adjacent children can borrow prefix bytes. - // If there are any, then the first valid child page will incur a boundary change to move - // its lower bound to the left so we can delete the non-linking entry from this page to free up space. - while(!cursor.get().value.present()) { - // There should never be an internal page written that has no valid child pages. This loop will find - // the first valid child link, and if there are no more then execution will not return to this loop. - ASSERT(cursor.moveNext()); - } - - ASSERT(cursor.valid()); + // At this point we should never be at a null child page entry because the first entry of a page + // can't be null and this loop will skip over null entries that come after non-null entries. + ASSERT(cursor.get().value.present()); + // The decode lower bound is always the key of the child link record const RedwoodRecordRef &decodeChildLowerBound = cursor.get(); BTreePageID pageID = cursor.get().getChildPage(); ASSERT(!pageID.empty()); + // The decode upper bound is always the next key after the child link, or the decode upper bound for this page const RedwoodRecordRef &decodeChildUpperBound = cursor.moveNext() ? cursor.get() : *decodeUpperBound; - // Skip over any next-children which do not actually link to child pages - while(cursor.valid() && !cursor.get().value.present()) { - cursor.moveNext(); + // But the decode upper bound might be a placeholder record with a null child link because + // the subtree was previously deleted but the key needed to exist to enable decoding of the + // previous child page which has not since been rewritten. + if(cursor.valid() && !cursor.get().value.present()) { + // There should only be one null child link entry, followed by a present link or the end of the page + ASSERT(!cursor.moveNext() || cursor.get().value.present()); } const RedwoodRecordRef &childUpperBound = cursor.valid() ? cursor.get() : *upperBound; From 3834d8ecec1e151a340986e77a8665732f66b529 Mon Sep 17 00:00:00 2001 From: John Leach Date: Mon, 16 Mar 2020 22:30:09 -0700 Subject: [PATCH 1492/1604] Fix #2822: ByteArrayUtil does not use the now standard Unsafe Approach for byte[] comparisons. --- ACKNOWLEDGEMENTS | 14 +- .../foundationdb/tuple/ArrayUtilTests.java | 54 ++++ .../foundationdb/tuple/ByteArrayUtil.java | 25 +- .../tuple/FastByteComparisons.java | 294 ++++++++++++++++++ 4 files changed, 366 insertions(+), 21 deletions(-) create mode 100644 bindings/java/src/main/com/apple/foundationdb/tuple/FastByteComparisons.java diff --git a/ACKNOWLEDGEMENTS b/ACKNOWLEDGEMENTS index 85c4c04d0d..c9f154657f 100644 --- a/ACKNOWLEDGEMENTS +++ b/ACKNOWLEDGEMENTS @@ -504,4 +504,16 @@ Armon Dadgar (ART) LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Copyright (C) 2009 The Guava 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. diff --git a/bindings/java/src/junit/com/apple/foundationdb/tuple/ArrayUtilTests.java b/bindings/java/src/junit/com/apple/foundationdb/tuple/ArrayUtilTests.java index 576f971c11..3cd7125a97 100644 --- a/bindings/java/src/junit/com/apple/foundationdb/tuple/ArrayUtilTests.java +++ b/bindings/java/src/junit/com/apple/foundationdb/tuple/ArrayUtilTests.java @@ -304,4 +304,58 @@ public class ArrayUtilTests { fail("Not yet implemented"); } + private static final int SAMPLE_COUNT = 1000000; + private static final int SAMPLE_MAX_SIZE = 2048; + private List unsafe; + private List java; + @Before + public void init() { + unsafe = new ArrayList(SAMPLE_COUNT); + java = new ArrayList(SAMPLE_COUNT); + Random random = new Random(); + for (int i = 0; i <= SAMPLE_COUNT; i++) { + byte[] addition = new byte[random.nextInt(SAMPLE_MAX_SIZE)]; + random.nextBytes(addition); + unsafe.add(addition); + java.add(addition); + } + } + + @Test + public void testComparatorSort() { + Collections.sort(unsafe, FastByteComparisons.lexicographicalComparerUnsafeImpl()); + Collections.sort(java, FastByteComparisons.lexicographicalComparerJavaImpl()); + Assert.assertTrue(unsafe.equals(java)); + } + + @Test + public void testUnsafeComparison() { + for (int i =0; i< SAMPLE_COUNT; i++) { + Assert.assertEquals(FastByteComparisons.lexicographicalComparerUnsafeImpl().compare(unsafe.get(i), java.get(i)), 0); + } + } + + @Test + public void testJavaComparison() { + for (int i =0; i< SAMPLE_COUNT; i++) { + Assert.assertEquals(FastByteComparisons.lexicographicalComparerJavaImpl().compare(unsafe.get(i), java.get(i)), 0); + } + } + + @Test + public void testUnsafeComparisonWithOffet() { + for (int i =0; i< SAMPLE_COUNT; i++) { + if (unsafe.get(i).length > 5) + Assert.assertEquals(FastByteComparisons.lexicographicalComparerUnsafeImpl().compareTo(unsafe.get(i), 4, unsafe.get(i).length - 4, java.get(i), 4, java.get(i).length - 4), 0); + } + } + + @Test + public void testJavaComparisonWithOffset() { + for (int i =0; i< SAMPLE_COUNT; i++) { + if (unsafe.get(i).length > 5) + Assert.assertEquals(FastByteComparisons.lexicographicalComparerJavaImpl().compareTo(unsafe.get(i), 4, unsafe.get(i).length - 4, java.get(i), 4, java.get(i).length - 4), 0); + } + } + } diff --git a/bindings/java/src/main/com/apple/foundationdb/tuple/ByteArrayUtil.java b/bindings/java/src/main/com/apple/foundationdb/tuple/ByteArrayUtil.java index 0c4e5f6e68..16011e056e 100644 --- a/bindings/java/src/main/com/apple/foundationdb/tuple/ByteArrayUtil.java +++ b/bindings/java/src/main/com/apple/foundationdb/tuple/ByteArrayUtil.java @@ -34,7 +34,7 @@ import com.apple.foundationdb.Transaction; * {@link #printable(byte[])} for debugging non-text keys and values. * */ -public class ByteArrayUtil { +public class ByteArrayUtil extends FastByteComparisons { /** * Joins a set of byte arrays into a larger array. The {@code interlude} is placed @@ -135,11 +135,7 @@ public class ByteArrayUtil { if(src.length < start + pattern.length) return false; - for(int i = 0; i < pattern.length; i++) - if(pattern[i] != src[start + i]) - return false; - - return true; + return compareTo(src, start, pattern.length, pattern, 0, pattern.length) == 0; } /** @@ -307,14 +303,7 @@ public class ByteArrayUtil { * {@code r}. */ public static int compareUnsigned(byte[] l, byte[] r) { - for(int idx = 0; idx < l.length && idx < r.length; ++idx) { - if(l[idx] != r[idx]) { - return (l[idx] & 0xFF) < (r[idx] & 0xFF) ? -1 : 1; - } - } - if(l.length == r.length) - return 0; - return l.length < r.length ? -1 : 1; + return compareTo(l, 0, l.length, r, 0, r.length); } /** @@ -328,15 +317,11 @@ public class ByteArrayUtil { * @return {@code true} if {@code array} starts with {@code prefix} */ public static boolean startsWith(byte[] array, byte[] prefix) { + // Short Circuit if(array.length < prefix.length) { return false; } - for(int i = 0; i < prefix.length; ++i) { - if(prefix[i] != array[i]) { - return false; - } - } - return true; + return compareTo(array, 0, prefix.length, prefix, 0, prefix.length) == 0; } /** diff --git a/bindings/java/src/main/com/apple/foundationdb/tuple/FastByteComparisons.java b/bindings/java/src/main/com/apple/foundationdb/tuple/FastByteComparisons.java new file mode 100644 index 0000000000..77add1db7f --- /dev/null +++ b/bindings/java/src/main/com/apple/foundationdb/tuple/FastByteComparisons.java @@ -0,0 +1,294 @@ +/* + * ByteArrayUtil.java + * + * This source file is part of the FoundationDB open source project + * + * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.apple.foundationdb.tuple; + +import java.lang.reflect.Field; +import java.nio.ByteOrder; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Comparator; + +import sun.misc.Unsafe; + + +/** + * Utility code to do optimized byte-array comparison. + * This is borrowed and slightly modified from Guava's {@link UnsignedBytes} + * class to be able to compare arrays that start at non-zero offsets. + */ +abstract class FastByteComparisons { + + private static final int UNSIGNED_MASK = 0xFF; + /** + * Lexicographically compare two byte arrays. + * + * @param buffer1 left operand, expected to not be null + * @param buffer2 right operand, expected to not be null + * @param offset1 Where to start comparing in the left buffer, expected to be >= 0 + * @param offset2 Where to start comparing in the right buffer, expected to be >= 0 + * @param length1 How much to compare from the left buffer, expected to be >= 0 + * @param length2 How much to compare from the right buffer, expected to be >= 0 + * @return 0 if equal, < 0 if left is less than right, etc. + */ + public static int compareTo(byte[] buffer1, int offset1, int length1, + byte[] buffer2, int offset2, int length2) { + return LexicographicalComparerHolder.BEST_COMPARER.compareTo( + buffer1, offset1, length1, buffer2, offset2, length2); + } + /** + * Interface for both the java and unsafe comparators + offset based comparisons. + * @param + */ + interface Comparer extends Comparator { + /** + * Lexicographically compare two byte arrays. + * + * @param buffer1 left operand + * @param buffer2 right operand + * @param offset1 Where to start comparing in the left buffer + * @param offset2 Where to start comparing in the right buffer + * @param length1 How much to compare from the left buffer + * @param length2 How much to compare from the right buffer + * @return 0 if equal, < 0 if left is less than right, etc. + */ + abstract public int compareTo(T buffer1, int offset1, int length1, + T buffer2, int offset2, int length2); + } + + /** + * Pure Java Comparer + * + * @return + */ + static Comparer lexicographicalComparerJavaImpl() { + return LexicographicalComparerHolder.PureJavaComparer.INSTANCE; + } + + /** + * Unsafe Comparer + * + * @return + */ + static Comparer lexicographicalComparerUnsafeImpl() { + return LexicographicalComparerHolder.UnsafeComparer.INSTANCE; + } + + + /** + * Provides a lexicographical comparer implementation; either a Java + * implementation or a faster implementation based on {@link Unsafe}. + * + *

Uses reflection to gracefully fall back to the Java implementation if + * {@code Unsafe} isn't available. + */ + private static class LexicographicalComparerHolder { + static final String UNSAFE_COMPARER_NAME = + LexicographicalComparerHolder.class.getName() + "$UnsafeComparer"; + + static final Comparer BEST_COMPARER = getBestComparer(); + /** + * Returns the Unsafe-using Comparer, or falls back to the pure-Java + * implementation if unable to do so. + */ + static Comparer getBestComparer() { + String arch = System.getProperty("os.arch"); + boolean unaligned = arch.equals("i386") || arch.equals("x86") + || arch.equals("amd64") || arch.equals("x86_64"); + if (!unaligned) + return lexicographicalComparerJavaImpl(); + try { + Class theClass = Class.forName(UNSAFE_COMPARER_NAME); + + // yes, UnsafeComparer does implement Comparer + @SuppressWarnings("unchecked") + Comparer comparer = + (Comparer) theClass.getEnumConstants()[0]; + return comparer; + } catch (Throwable t) { // ensure we really catch *everything* + return lexicographicalComparerJavaImpl(); + } + } + + /** + * Java Comparer doing byte by byte comparisons + * + */ + enum PureJavaComparer implements Comparer { + INSTANCE; + + /** + * + * CompareTo looking at two buffers. + * + * @param buffer1 left operand + * @param buffer2 right operand + * @param offset1 Where to start comparing in the left buffer + * @param offset2 Where to start comparing in the right buffer + * @param length1 How much to compare from the left buffer + * @param length2 How much to compare from the right buffer + * @return 0 if equal, < 0 if left is less than right, etc. + */ + @Override + public int compareTo(byte[] buffer1, int offset1, int length1, + byte[] buffer2, int offset2, int length2) { + // Short circuit equal case + if (buffer1 == buffer2 && + offset1 == offset2 && + length1 == length2) { + return 0; + } + int end1 = offset1 + length1; + int end2 = offset2 + length2; + for (int i = offset1, j = offset2; i < end1 && j < end2; i++, j++) { + int a = (buffer1[i] & UNSIGNED_MASK); + int b = (buffer2[j] & UNSIGNED_MASK); + if (a != b) { + return a - b; + } + } + return length1 - length2; + } + + /** + * Supports Comparator + * + * @param o1 + * @param o2 + * @return comparison + */ + @Override + public int compare(byte[] o1, byte[] o2) { + return compareTo(o1, 0, o1.length, o2, 0, o2.length); + } + } + + /** + * + * Takes advantage of word based comparisons + * + */ + @SuppressWarnings("unused") // used via reflection + enum UnsafeComparer implements Comparer { + INSTANCE; + + static final Unsafe theUnsafe; + + /** + * The offset to the first element in a byte array. + */ + static final int BYTE_ARRAY_BASE_OFFSET; + + @Override + public int compare(byte[] o1, byte[] o2) { + return compareTo(o1, 0, o1.length, o2, 0, o2.length); + } + + static { + theUnsafe = (Unsafe) AccessController.doPrivileged( + (PrivilegedAction) () -> { + try { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return f.get(null); + } catch (NoSuchFieldException e) { + // It doesn't matter what we throw; + // it's swallowed in getBestComparer(). + throw new Error(); + } catch (IllegalAccessException e) { + throw new Error(); + } + }); + + BYTE_ARRAY_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class); + + // sanity check - this should never fail + if (theUnsafe.arrayIndexScale(byte[].class) != 1) { + throw new AssertionError(); + } + } + + static final boolean LITTLE_ENDIAN = + ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN); + + /** + * Lexicographically compare two arrays. + * + * @param buffer1 left operand + * @param buffer2 right operand + * @param offset1 Where to start comparing in the left buffer + * @param offset2 Where to start comparing in the right buffer + * @param length1 How much to compare from the left buffer + * @param length2 How much to compare from the right buffer + * @return 0 if equal, < 0 if left is less than right, etc. + */ + @Override + public int compareTo(byte[] buffer1, int offset1, int length1, + byte[] buffer2, int offset2, int length2) { + // Short circuit equal case + if (buffer1 == buffer2 && + offset1 == offset2 && + length1 == length2) { + return 0; + } + final int stride = 8; + final int minLength = Math.min(length1, length2); + int strideLimit = minLength & ~(stride - 1); + final long offset1Adj = offset1 + BYTE_ARRAY_BASE_OFFSET; + final long offset2Adj = offset2 + BYTE_ARRAY_BASE_OFFSET; + int i; + + /* + * Compare 8 bytes at a time. Benchmarking on x86 shows a stride of 8 bytes is no slower + * than 4 bytes even on 32-bit. On the other hand, it is substantially faster on 64-bit. + */ + for (i = 0; i < strideLimit; i += stride) { + long lw = theUnsafe.getLong(buffer1, offset1Adj + i); + long rw = theUnsafe.getLong(buffer2, offset2Adj + i); + if (lw != rw) { + if(!LITTLE_ENDIAN) { + return ((lw + Long.MIN_VALUE) < (rw + Long.MIN_VALUE)) ? -1 : 1; + } + + /* + * We want to compare only the first index where left[index] != right[index]. This + * corresponds to the least significant nonzero byte in lw ^ rw, since lw and rw are + * little-endian. Long.numberOfTrailingZeros(diff) tells us the least significant + * nonzero bit, and zeroing out the first three bits of L.nTZ gives us the shift to get + * that least significant nonzero byte. This comparison logic is based on UnsignedBytes + * comparator from guava v21 + */ + int n = Long.numberOfTrailingZeros(lw ^ rw) & ~0x7; + return ((int) ((lw >>> n) & UNSIGNED_MASK)) - ((int) ((rw >>> n) & UNSIGNED_MASK)); + } + } + + // The epilogue to cover the last (minLength % stride) elements. + for (; i < minLength; i++) { + int a = (buffer1[offset1 + i] & UNSIGNED_MASK); + int b = (buffer2[offset2 + i] & UNSIGNED_MASK); + if (a != b) { + return a - b; + } + } + return length1 - length2; + } + } + } +} \ No newline at end of file From d41dca5a9404a784c353a2f3813f09b0e5244e61 Mon Sep 17 00:00:00 2001 From: John Leach Date: Thu, 12 Mar 2020 16:16:29 -0700 Subject: [PATCH 1493/1604] 2808: Add caching for jmethodID, jfieldID and jclass for JNI for Java Binding --- bindings/java/fdbJNI.cpp | 59 ++++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/bindings/java/fdbJNI.cpp b/bindings/java/fdbJNI.cpp index 232e8392fe..a127a47864 100644 --- a/bindings/java/fdbJNI.cpp +++ b/bindings/java/fdbJNI.cpp @@ -36,6 +36,11 @@ static JavaVM* g_jvm = nullptr; static thread_local JNIEnv* g_thread_jenv = nullptr; // Defined for the network thread once it is running, and for any thread that has called registerCallback static thread_local jmethodID g_IFutureCallback_call_methodID = JNI_NULL; static thread_local bool is_external = false; +static jclass range_result_summary_class; +static jclass range_result_class; +static jclass string_class; +static jmethodID range_result_init; +static jmethodID range_result_summary_init; void detachIfExternalThread(void *ignore) { if(is_external && g_thread_jenv != nullptr) { @@ -275,10 +280,9 @@ JNIEXPORT jobject JNICALL Java_com_apple_foundationdb_FutureStrings_FutureString return JNI_NULL; } - jclass str_clazz = jenv->FindClass("java/lang/String"); if( jenv->ExceptionOccurred() ) return JNI_NULL; - jobjectArray arr = jenv->NewObjectArray(count, str_clazz, JNI_NULL); + jobjectArray arr = jenv->NewObjectArray(count, string_class, JNI_NULL); if( !arr ) { if( !jenv->ExceptionOccurred() ) throwOutOfMem(jenv); @@ -306,13 +310,6 @@ JNIEXPORT jobject JNICALL Java_com_apple_foundationdb_FutureResults_FutureResult throwParamNotNull(jenv); return JNI_NULL; } - - jclass resultCls = jenv->FindClass("com/apple/foundationdb/RangeResultSummary"); - if( jenv->ExceptionOccurred() ) - return JNI_NULL; - jmethodID resultCtorId = jenv->GetMethodID(resultCls, "", "([BIZ)V"); - if( jenv->ExceptionOccurred() ) - return JNI_NULL; FDBFuture *f = (FDBFuture *)future; @@ -337,7 +334,7 @@ JNIEXPORT jobject JNICALL Java_com_apple_foundationdb_FutureResults_FutureResult jenv->SetByteArrayRegion(lastKey, 0, kvs[count - 1].key_length, (jbyte *)kvs[count - 1].key); } - jobject result = jenv->NewObject(resultCls, resultCtorId, lastKey, count, (jboolean)more); + jobject result = jenv->NewObject(range_result_summary_class, range_result_summary_init, lastKey, count, (jboolean)more); if( jenv->ExceptionOccurred() ) return JNI_NULL; @@ -350,9 +347,6 @@ JNIEXPORT jobject JNICALL Java_com_apple_foundationdb_FutureResults_FutureResult throwParamNotNull(jenv); return JNI_NULL; } - - jclass resultCls = jenv->FindClass("com/apple/foundationdb/RangeResult"); - jmethodID resultCtorId = jenv->GetMethodID(resultCls, "", "([B[IZ)V"); FDBFuture *f = (FDBFuture *)future; @@ -414,7 +408,7 @@ JNIEXPORT jobject JNICALL Java_com_apple_foundationdb_FutureResults_FutureResult jenv->ReleaseByteArrayElements(keyValueArray, (jbyte *)keyvalues_barr, 0); jenv->ReleaseIntArrayElements(lengthArray, length_barr, 0); - jobject result = jenv->NewObject(resultCls, resultCtorId, keyValueArray, lengthArray, (jboolean)more); + jobject result = jenv->NewObject(range_result_class, range_result_init, keyValueArray, lengthArray, (jboolean)more); if( jenv->ExceptionOccurred() ) return JNI_NULL; @@ -1042,8 +1036,43 @@ JNIEXPORT void JNICALL Java_com_apple_foundationdb_FDB_Network_1stop(JNIEnv *jen } jint JNI_OnLoad(JavaVM *vm, void *reserved) { + JNIEnv *env; g_jvm = vm; - return JNI_VERSION_1_1; + if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) { + return JNI_ERR; + } else { + jclass local_range_result_class = env->FindClass("com/apple/foundationdb/RangeResult"); + range_result_init = env->GetMethodID(local_range_result_class, "", "([B[IZ)V"); + range_result_class = (jclass) (env)->NewGlobalRef(local_range_result_class); + + jclass local_range_result_summary_class = env->FindClass("com/apple/foundationdb/RangeResultSummary"); + range_result_summary_init = env->GetMethodID(local_range_result_summary_class, "", "([BIZ)V"); + range_result_summary_class = (jclass) (env)->NewGlobalRef(local_range_result_summary_class); + + jclass local_string_class = env->FindClass("java/lang/String"); + string_class = (jclass) (env)->NewGlobalRef(local_string_class); + + return JNI_VERSION_1_6; + } +} + +// Is automatically called once the Classloader is destroyed +void JNI_OnUnload(JavaVM *vm, void *reserved) { + JNIEnv* env; + if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) { + return; + } else { + // delete global references so the GC can collect them + if (range_result_summary_class != NULL) { + env->DeleteGlobalRef(range_result_summary_class); + } + if (range_result_class != NULL) { + env->DeleteGlobalRef(range_result_class); + } + if (string_class != NULL) { + env->DeleteGlobalRef(string_class); + } + } } #ifdef __cplusplus From 7eaf8edb1d02fde8b3634c8d6956c2b095da706d Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Tue, 21 Apr 2020 17:38:55 +0000 Subject: [PATCH 1494/1604] Don't simulate a TTY if clangd is invoked by IDE Without this, vscode complains that clangd just crashes. Also make ~/bin/clangd executable --- build/gen_dev_docker.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/build/gen_dev_docker.sh b/build/gen_dev_docker.sh index 03b171f969..36e8264953 100755 --- a/build/gen_dev_docker.sh +++ b/build/gen_dev_docker.sh @@ -64,9 +64,14 @@ then ccache_args=\$args fi +if [ -t 1 ] ; then + TERMINAL_ARGS=-it `# Run in interactive mode and simulate a TTY` +else + TERMINAL_ARGS=-i `# Run in interactive mode` +fi sudo docker run --rm `# delete (temporary) image after return` \\ - -it `# Run in interactive mode and simulate a TTY` \\ + \${TERMINAL_ARGS} \\ --privileged=true `# Run in privileged mode ` \\ --cap-add=SYS_PTRACE \\ --security-opt seccomp=unconfined \\ @@ -87,6 +92,7 @@ then echo -e "\tThis can cause problems with some scripts (like fdb-clangd)" fi chmod +x $HOME/bin/fdb-dev +chmod +x $HOME/bin/clangd echo "To start the dev docker image run $HOME/bin/fdb-dev" echo "$HOME/bin/clangd can be used for IDE integration" echo "You can edit these files but be aware that this script will overwrite your changes if you rerun it" From 83ea7722524dbfce71b513742563a04d2abd873b Mon Sep 17 00:00:00 2001 From: Steve Atherton Date: Tue, 21 Apr 2020 10:47:15 -0700 Subject: [PATCH 1495/1604] Change simulation storage server selection back to random. --- fdbserver/SimulatedCluster.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/SimulatedCluster.actor.cpp b/fdbserver/SimulatedCluster.actor.cpp index 15ea6504d6..c11e108137 100644 --- a/fdbserver/SimulatedCluster.actor.cpp +++ b/fdbserver/SimulatedCluster.actor.cpp @@ -737,7 +737,7 @@ void SimulationConfig::generateNormalConfig(int minimumReplication, int minimumR if (deterministicRandom()->random01() < 0.25) db.desiredTLogCount = deterministicRandom()->randomInt(1,7); if (deterministicRandom()->random01() < 0.25) db.masterProxyCount = deterministicRandom()->randomInt(1,7); if (deterministicRandom()->random01() < 0.25) db.resolverCount = deterministicRandom()->randomInt(1,7); - int storage_engine_type = 3; // deterministicRandom()->randomInt(0, 4); + int storage_engine_type = deterministicRandom()->randomInt(0, 4); switch (storage_engine_type) { case 0: { TEST(true); // Simulated cluster using ssd storage engine From 71c0d52868c6589b270827848df3c88e25a99ae3 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Tue, 21 Apr 2020 10:50:33 -0700 Subject: [PATCH 1496/1604] Revert "Fix #2822: ByteArrayUtil does not use the now standard Unsafe Approach for byte[] comparisons." --- ACKNOWLEDGEMENTS | 14 +- .../foundationdb/tuple/ArrayUtilTests.java | 54 ---- .../foundationdb/tuple/ByteArrayUtil.java | 25 +- .../tuple/FastByteComparisons.java | 294 ------------------ 4 files changed, 21 insertions(+), 366 deletions(-) delete mode 100644 bindings/java/src/main/com/apple/foundationdb/tuple/FastByteComparisons.java diff --git a/ACKNOWLEDGEMENTS b/ACKNOWLEDGEMENTS index c9f154657f..85c4c04d0d 100644 --- a/ACKNOWLEDGEMENTS +++ b/ACKNOWLEDGEMENTS @@ -504,16 +504,4 @@ Armon Dadgar (ART) LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -Copyright (C) 2009 The Guava 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. + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/bindings/java/src/junit/com/apple/foundationdb/tuple/ArrayUtilTests.java b/bindings/java/src/junit/com/apple/foundationdb/tuple/ArrayUtilTests.java index 3cd7125a97..576f971c11 100644 --- a/bindings/java/src/junit/com/apple/foundationdb/tuple/ArrayUtilTests.java +++ b/bindings/java/src/junit/com/apple/foundationdb/tuple/ArrayUtilTests.java @@ -304,58 +304,4 @@ public class ArrayUtilTests { fail("Not yet implemented"); } - private static final int SAMPLE_COUNT = 1000000; - private static final int SAMPLE_MAX_SIZE = 2048; - private List unsafe; - private List java; - @Before - public void init() { - unsafe = new ArrayList(SAMPLE_COUNT); - java = new ArrayList(SAMPLE_COUNT); - Random random = new Random(); - for (int i = 0; i <= SAMPLE_COUNT; i++) { - byte[] addition = new byte[random.nextInt(SAMPLE_MAX_SIZE)]; - random.nextBytes(addition); - unsafe.add(addition); - java.add(addition); - } - } - - @Test - public void testComparatorSort() { - Collections.sort(unsafe, FastByteComparisons.lexicographicalComparerUnsafeImpl()); - Collections.sort(java, FastByteComparisons.lexicographicalComparerJavaImpl()); - Assert.assertTrue(unsafe.equals(java)); - } - - @Test - public void testUnsafeComparison() { - for (int i =0; i< SAMPLE_COUNT; i++) { - Assert.assertEquals(FastByteComparisons.lexicographicalComparerUnsafeImpl().compare(unsafe.get(i), java.get(i)), 0); - } - } - - @Test - public void testJavaComparison() { - for (int i =0; i< SAMPLE_COUNT; i++) { - Assert.assertEquals(FastByteComparisons.lexicographicalComparerJavaImpl().compare(unsafe.get(i), java.get(i)), 0); - } - } - - @Test - public void testUnsafeComparisonWithOffet() { - for (int i =0; i< SAMPLE_COUNT; i++) { - if (unsafe.get(i).length > 5) - Assert.assertEquals(FastByteComparisons.lexicographicalComparerUnsafeImpl().compareTo(unsafe.get(i), 4, unsafe.get(i).length - 4, java.get(i), 4, java.get(i).length - 4), 0); - } - } - - @Test - public void testJavaComparisonWithOffset() { - for (int i =0; i< SAMPLE_COUNT; i++) { - if (unsafe.get(i).length > 5) - Assert.assertEquals(FastByteComparisons.lexicographicalComparerJavaImpl().compareTo(unsafe.get(i), 4, unsafe.get(i).length - 4, java.get(i), 4, java.get(i).length - 4), 0); - } - } - } diff --git a/bindings/java/src/main/com/apple/foundationdb/tuple/ByteArrayUtil.java b/bindings/java/src/main/com/apple/foundationdb/tuple/ByteArrayUtil.java index 16011e056e..0c4e5f6e68 100644 --- a/bindings/java/src/main/com/apple/foundationdb/tuple/ByteArrayUtil.java +++ b/bindings/java/src/main/com/apple/foundationdb/tuple/ByteArrayUtil.java @@ -34,7 +34,7 @@ import com.apple.foundationdb.Transaction; * {@link #printable(byte[])} for debugging non-text keys and values. * */ -public class ByteArrayUtil extends FastByteComparisons { +public class ByteArrayUtil { /** * Joins a set of byte arrays into a larger array. The {@code interlude} is placed @@ -135,7 +135,11 @@ public class ByteArrayUtil extends FastByteComparisons { if(src.length < start + pattern.length) return false; - return compareTo(src, start, pattern.length, pattern, 0, pattern.length) == 0; + for(int i = 0; i < pattern.length; i++) + if(pattern[i] != src[start + i]) + return false; + + return true; } /** @@ -303,7 +307,14 @@ public class ByteArrayUtil extends FastByteComparisons { * {@code r}. */ public static int compareUnsigned(byte[] l, byte[] r) { - return compareTo(l, 0, l.length, r, 0, r.length); + for(int idx = 0; idx < l.length && idx < r.length; ++idx) { + if(l[idx] != r[idx]) { + return (l[idx] & 0xFF) < (r[idx] & 0xFF) ? -1 : 1; + } + } + if(l.length == r.length) + return 0; + return l.length < r.length ? -1 : 1; } /** @@ -317,11 +328,15 @@ public class ByteArrayUtil extends FastByteComparisons { * @return {@code true} if {@code array} starts with {@code prefix} */ public static boolean startsWith(byte[] array, byte[] prefix) { - // Short Circuit if(array.length < prefix.length) { return false; } - return compareTo(array, 0, prefix.length, prefix, 0, prefix.length) == 0; + for(int i = 0; i < prefix.length; ++i) { + if(prefix[i] != array[i]) { + return false; + } + } + return true; } /** diff --git a/bindings/java/src/main/com/apple/foundationdb/tuple/FastByteComparisons.java b/bindings/java/src/main/com/apple/foundationdb/tuple/FastByteComparisons.java deleted file mode 100644 index 77add1db7f..0000000000 --- a/bindings/java/src/main/com/apple/foundationdb/tuple/FastByteComparisons.java +++ /dev/null @@ -1,294 +0,0 @@ -/* - * ByteArrayUtil.java - * - * This source file is part of the FoundationDB open source project - * - * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.apple.foundationdb.tuple; - -import java.lang.reflect.Field; -import java.nio.ByteOrder; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.Comparator; - -import sun.misc.Unsafe; - - -/** - * Utility code to do optimized byte-array comparison. - * This is borrowed and slightly modified from Guava's {@link UnsignedBytes} - * class to be able to compare arrays that start at non-zero offsets. - */ -abstract class FastByteComparisons { - - private static final int UNSIGNED_MASK = 0xFF; - /** - * Lexicographically compare two byte arrays. - * - * @param buffer1 left operand, expected to not be null - * @param buffer2 right operand, expected to not be null - * @param offset1 Where to start comparing in the left buffer, expected to be >= 0 - * @param offset2 Where to start comparing in the right buffer, expected to be >= 0 - * @param length1 How much to compare from the left buffer, expected to be >= 0 - * @param length2 How much to compare from the right buffer, expected to be >= 0 - * @return 0 if equal, < 0 if left is less than right, etc. - */ - public static int compareTo(byte[] buffer1, int offset1, int length1, - byte[] buffer2, int offset2, int length2) { - return LexicographicalComparerHolder.BEST_COMPARER.compareTo( - buffer1, offset1, length1, buffer2, offset2, length2); - } - /** - * Interface for both the java and unsafe comparators + offset based comparisons. - * @param - */ - interface Comparer extends Comparator { - /** - * Lexicographically compare two byte arrays. - * - * @param buffer1 left operand - * @param buffer2 right operand - * @param offset1 Where to start comparing in the left buffer - * @param offset2 Where to start comparing in the right buffer - * @param length1 How much to compare from the left buffer - * @param length2 How much to compare from the right buffer - * @return 0 if equal, < 0 if left is less than right, etc. - */ - abstract public int compareTo(T buffer1, int offset1, int length1, - T buffer2, int offset2, int length2); - } - - /** - * Pure Java Comparer - * - * @return - */ - static Comparer lexicographicalComparerJavaImpl() { - return LexicographicalComparerHolder.PureJavaComparer.INSTANCE; - } - - /** - * Unsafe Comparer - * - * @return - */ - static Comparer lexicographicalComparerUnsafeImpl() { - return LexicographicalComparerHolder.UnsafeComparer.INSTANCE; - } - - - /** - * Provides a lexicographical comparer implementation; either a Java - * implementation or a faster implementation based on {@link Unsafe}. - * - *

Uses reflection to gracefully fall back to the Java implementation if - * {@code Unsafe} isn't available. - */ - private static class LexicographicalComparerHolder { - static final String UNSAFE_COMPARER_NAME = - LexicographicalComparerHolder.class.getName() + "$UnsafeComparer"; - - static final Comparer BEST_COMPARER = getBestComparer(); - /** - * Returns the Unsafe-using Comparer, or falls back to the pure-Java - * implementation if unable to do so. - */ - static Comparer getBestComparer() { - String arch = System.getProperty("os.arch"); - boolean unaligned = arch.equals("i386") || arch.equals("x86") - || arch.equals("amd64") || arch.equals("x86_64"); - if (!unaligned) - return lexicographicalComparerJavaImpl(); - try { - Class theClass = Class.forName(UNSAFE_COMPARER_NAME); - - // yes, UnsafeComparer does implement Comparer - @SuppressWarnings("unchecked") - Comparer comparer = - (Comparer) theClass.getEnumConstants()[0]; - return comparer; - } catch (Throwable t) { // ensure we really catch *everything* - return lexicographicalComparerJavaImpl(); - } - } - - /** - * Java Comparer doing byte by byte comparisons - * - */ - enum PureJavaComparer implements Comparer { - INSTANCE; - - /** - * - * CompareTo looking at two buffers. - * - * @param buffer1 left operand - * @param buffer2 right operand - * @param offset1 Where to start comparing in the left buffer - * @param offset2 Where to start comparing in the right buffer - * @param length1 How much to compare from the left buffer - * @param length2 How much to compare from the right buffer - * @return 0 if equal, < 0 if left is less than right, etc. - */ - @Override - public int compareTo(byte[] buffer1, int offset1, int length1, - byte[] buffer2, int offset2, int length2) { - // Short circuit equal case - if (buffer1 == buffer2 && - offset1 == offset2 && - length1 == length2) { - return 0; - } - int end1 = offset1 + length1; - int end2 = offset2 + length2; - for (int i = offset1, j = offset2; i < end1 && j < end2; i++, j++) { - int a = (buffer1[i] & UNSIGNED_MASK); - int b = (buffer2[j] & UNSIGNED_MASK); - if (a != b) { - return a - b; - } - } - return length1 - length2; - } - - /** - * Supports Comparator - * - * @param o1 - * @param o2 - * @return comparison - */ - @Override - public int compare(byte[] o1, byte[] o2) { - return compareTo(o1, 0, o1.length, o2, 0, o2.length); - } - } - - /** - * - * Takes advantage of word based comparisons - * - */ - @SuppressWarnings("unused") // used via reflection - enum UnsafeComparer implements Comparer { - INSTANCE; - - static final Unsafe theUnsafe; - - /** - * The offset to the first element in a byte array. - */ - static final int BYTE_ARRAY_BASE_OFFSET; - - @Override - public int compare(byte[] o1, byte[] o2) { - return compareTo(o1, 0, o1.length, o2, 0, o2.length); - } - - static { - theUnsafe = (Unsafe) AccessController.doPrivileged( - (PrivilegedAction) () -> { - try { - Field f = Unsafe.class.getDeclaredField("theUnsafe"); - f.setAccessible(true); - return f.get(null); - } catch (NoSuchFieldException e) { - // It doesn't matter what we throw; - // it's swallowed in getBestComparer(). - throw new Error(); - } catch (IllegalAccessException e) { - throw new Error(); - } - }); - - BYTE_ARRAY_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class); - - // sanity check - this should never fail - if (theUnsafe.arrayIndexScale(byte[].class) != 1) { - throw new AssertionError(); - } - } - - static final boolean LITTLE_ENDIAN = - ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN); - - /** - * Lexicographically compare two arrays. - * - * @param buffer1 left operand - * @param buffer2 right operand - * @param offset1 Where to start comparing in the left buffer - * @param offset2 Where to start comparing in the right buffer - * @param length1 How much to compare from the left buffer - * @param length2 How much to compare from the right buffer - * @return 0 if equal, < 0 if left is less than right, etc. - */ - @Override - public int compareTo(byte[] buffer1, int offset1, int length1, - byte[] buffer2, int offset2, int length2) { - // Short circuit equal case - if (buffer1 == buffer2 && - offset1 == offset2 && - length1 == length2) { - return 0; - } - final int stride = 8; - final int minLength = Math.min(length1, length2); - int strideLimit = minLength & ~(stride - 1); - final long offset1Adj = offset1 + BYTE_ARRAY_BASE_OFFSET; - final long offset2Adj = offset2 + BYTE_ARRAY_BASE_OFFSET; - int i; - - /* - * Compare 8 bytes at a time. Benchmarking on x86 shows a stride of 8 bytes is no slower - * than 4 bytes even on 32-bit. On the other hand, it is substantially faster on 64-bit. - */ - for (i = 0; i < strideLimit; i += stride) { - long lw = theUnsafe.getLong(buffer1, offset1Adj + i); - long rw = theUnsafe.getLong(buffer2, offset2Adj + i); - if (lw != rw) { - if(!LITTLE_ENDIAN) { - return ((lw + Long.MIN_VALUE) < (rw + Long.MIN_VALUE)) ? -1 : 1; - } - - /* - * We want to compare only the first index where left[index] != right[index]. This - * corresponds to the least significant nonzero byte in lw ^ rw, since lw and rw are - * little-endian. Long.numberOfTrailingZeros(diff) tells us the least significant - * nonzero bit, and zeroing out the first three bits of L.nTZ gives us the shift to get - * that least significant nonzero byte. This comparison logic is based on UnsignedBytes - * comparator from guava v21 - */ - int n = Long.numberOfTrailingZeros(lw ^ rw) & ~0x7; - return ((int) ((lw >>> n) & UNSIGNED_MASK)) - ((int) ((rw >>> n) & UNSIGNED_MASK)); - } - } - - // The epilogue to cover the last (minLength % stride) elements. - for (; i < minLength; i++) { - int a = (buffer1[offset1 + i] & UNSIGNED_MASK); - int b = (buffer2[offset2 + i] & UNSIGNED_MASK); - if (a != b) { - return a - b; - } - } - return length1 - length2; - } - } - } -} \ No newline at end of file From 7f75eab32b22b511ab1dabcee0024c62d999ec02 Mon Sep 17 00:00:00 2001 From: John Leach Date: Mon, 16 Mar 2020 22:30:09 -0700 Subject: [PATCH 1497/1604] Fix #2822: ByteArrayUtil does not use the now standard Unsafe Approach for byte[] comparisons. --- ACKNOWLEDGEMENTS | 14 +- bindings/java/CMakeLists.txt | 1 + .../foundationdb/tuple/ArrayUtilTests.java | 54 ++++ .../foundationdb/tuple/ByteArrayUtil.java | 25 +- .../tuple/FastByteComparisons.java | 294 ++++++++++++++++++ 5 files changed, 367 insertions(+), 21 deletions(-) create mode 100644 bindings/java/src/main/com/apple/foundationdb/tuple/FastByteComparisons.java diff --git a/ACKNOWLEDGEMENTS b/ACKNOWLEDGEMENTS index 85c4c04d0d..c9f154657f 100644 --- a/ACKNOWLEDGEMENTS +++ b/ACKNOWLEDGEMENTS @@ -504,4 +504,16 @@ Armon Dadgar (ART) LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Copyright (C) 2009 The Guava 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. diff --git a/bindings/java/CMakeLists.txt b/bindings/java/CMakeLists.txt index f97e12d51b..6d94d75b71 100644 --- a/bindings/java/CMakeLists.txt +++ b/bindings/java/CMakeLists.txt @@ -56,6 +56,7 @@ set(JAVA_BINDING_SRCS src/main/com/apple/foundationdb/testing/Promise.java src/main/com/apple/foundationdb/testing/PerfMetric.java src/main/com/apple/foundationdb/tuple/ByteArrayUtil.java + src/main/com/apple/foundationdb/tuple/FastByteComparisons.java src/main/com/apple/foundationdb/tuple/IterableComparator.java src/main/com/apple/foundationdb/tuple/package-info.java src/main/com/apple/foundationdb/tuple/StringUtil.java diff --git a/bindings/java/src/junit/com/apple/foundationdb/tuple/ArrayUtilTests.java b/bindings/java/src/junit/com/apple/foundationdb/tuple/ArrayUtilTests.java index 576f971c11..3cd7125a97 100644 --- a/bindings/java/src/junit/com/apple/foundationdb/tuple/ArrayUtilTests.java +++ b/bindings/java/src/junit/com/apple/foundationdb/tuple/ArrayUtilTests.java @@ -304,4 +304,58 @@ public class ArrayUtilTests { fail("Not yet implemented"); } + private static final int SAMPLE_COUNT = 1000000; + private static final int SAMPLE_MAX_SIZE = 2048; + private List unsafe; + private List java; + @Before + public void init() { + unsafe = new ArrayList(SAMPLE_COUNT); + java = new ArrayList(SAMPLE_COUNT); + Random random = new Random(); + for (int i = 0; i <= SAMPLE_COUNT; i++) { + byte[] addition = new byte[random.nextInt(SAMPLE_MAX_SIZE)]; + random.nextBytes(addition); + unsafe.add(addition); + java.add(addition); + } + } + + @Test + public void testComparatorSort() { + Collections.sort(unsafe, FastByteComparisons.lexicographicalComparerUnsafeImpl()); + Collections.sort(java, FastByteComparisons.lexicographicalComparerJavaImpl()); + Assert.assertTrue(unsafe.equals(java)); + } + + @Test + public void testUnsafeComparison() { + for (int i =0; i< SAMPLE_COUNT; i++) { + Assert.assertEquals(FastByteComparisons.lexicographicalComparerUnsafeImpl().compare(unsafe.get(i), java.get(i)), 0); + } + } + + @Test + public void testJavaComparison() { + for (int i =0; i< SAMPLE_COUNT; i++) { + Assert.assertEquals(FastByteComparisons.lexicographicalComparerJavaImpl().compare(unsafe.get(i), java.get(i)), 0); + } + } + + @Test + public void testUnsafeComparisonWithOffet() { + for (int i =0; i< SAMPLE_COUNT; i++) { + if (unsafe.get(i).length > 5) + Assert.assertEquals(FastByteComparisons.lexicographicalComparerUnsafeImpl().compareTo(unsafe.get(i), 4, unsafe.get(i).length - 4, java.get(i), 4, java.get(i).length - 4), 0); + } + } + + @Test + public void testJavaComparisonWithOffset() { + for (int i =0; i< SAMPLE_COUNT; i++) { + if (unsafe.get(i).length > 5) + Assert.assertEquals(FastByteComparisons.lexicographicalComparerJavaImpl().compareTo(unsafe.get(i), 4, unsafe.get(i).length - 4, java.get(i), 4, java.get(i).length - 4), 0); + } + } + } diff --git a/bindings/java/src/main/com/apple/foundationdb/tuple/ByteArrayUtil.java b/bindings/java/src/main/com/apple/foundationdb/tuple/ByteArrayUtil.java index 0c4e5f6e68..16011e056e 100644 --- a/bindings/java/src/main/com/apple/foundationdb/tuple/ByteArrayUtil.java +++ b/bindings/java/src/main/com/apple/foundationdb/tuple/ByteArrayUtil.java @@ -34,7 +34,7 @@ import com.apple.foundationdb.Transaction; * {@link #printable(byte[])} for debugging non-text keys and values. * */ -public class ByteArrayUtil { +public class ByteArrayUtil extends FastByteComparisons { /** * Joins a set of byte arrays into a larger array. The {@code interlude} is placed @@ -135,11 +135,7 @@ public class ByteArrayUtil { if(src.length < start + pattern.length) return false; - for(int i = 0; i < pattern.length; i++) - if(pattern[i] != src[start + i]) - return false; - - return true; + return compareTo(src, start, pattern.length, pattern, 0, pattern.length) == 0; } /** @@ -307,14 +303,7 @@ public class ByteArrayUtil { * {@code r}. */ public static int compareUnsigned(byte[] l, byte[] r) { - for(int idx = 0; idx < l.length && idx < r.length; ++idx) { - if(l[idx] != r[idx]) { - return (l[idx] & 0xFF) < (r[idx] & 0xFF) ? -1 : 1; - } - } - if(l.length == r.length) - return 0; - return l.length < r.length ? -1 : 1; + return compareTo(l, 0, l.length, r, 0, r.length); } /** @@ -328,15 +317,11 @@ public class ByteArrayUtil { * @return {@code true} if {@code array} starts with {@code prefix} */ public static boolean startsWith(byte[] array, byte[] prefix) { + // Short Circuit if(array.length < prefix.length) { return false; } - for(int i = 0; i < prefix.length; ++i) { - if(prefix[i] != array[i]) { - return false; - } - } - return true; + return compareTo(array, 0, prefix.length, prefix, 0, prefix.length) == 0; } /** diff --git a/bindings/java/src/main/com/apple/foundationdb/tuple/FastByteComparisons.java b/bindings/java/src/main/com/apple/foundationdb/tuple/FastByteComparisons.java new file mode 100644 index 0000000000..77add1db7f --- /dev/null +++ b/bindings/java/src/main/com/apple/foundationdb/tuple/FastByteComparisons.java @@ -0,0 +1,294 @@ +/* + * ByteArrayUtil.java + * + * This source file is part of the FoundationDB open source project + * + * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.apple.foundationdb.tuple; + +import java.lang.reflect.Field; +import java.nio.ByteOrder; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Comparator; + +import sun.misc.Unsafe; + + +/** + * Utility code to do optimized byte-array comparison. + * This is borrowed and slightly modified from Guava's {@link UnsignedBytes} + * class to be able to compare arrays that start at non-zero offsets. + */ +abstract class FastByteComparisons { + + private static final int UNSIGNED_MASK = 0xFF; + /** + * Lexicographically compare two byte arrays. + * + * @param buffer1 left operand, expected to not be null + * @param buffer2 right operand, expected to not be null + * @param offset1 Where to start comparing in the left buffer, expected to be >= 0 + * @param offset2 Where to start comparing in the right buffer, expected to be >= 0 + * @param length1 How much to compare from the left buffer, expected to be >= 0 + * @param length2 How much to compare from the right buffer, expected to be >= 0 + * @return 0 if equal, < 0 if left is less than right, etc. + */ + public static int compareTo(byte[] buffer1, int offset1, int length1, + byte[] buffer2, int offset2, int length2) { + return LexicographicalComparerHolder.BEST_COMPARER.compareTo( + buffer1, offset1, length1, buffer2, offset2, length2); + } + /** + * Interface for both the java and unsafe comparators + offset based comparisons. + * @param + */ + interface Comparer extends Comparator { + /** + * Lexicographically compare two byte arrays. + * + * @param buffer1 left operand + * @param buffer2 right operand + * @param offset1 Where to start comparing in the left buffer + * @param offset2 Where to start comparing in the right buffer + * @param length1 How much to compare from the left buffer + * @param length2 How much to compare from the right buffer + * @return 0 if equal, < 0 if left is less than right, etc. + */ + abstract public int compareTo(T buffer1, int offset1, int length1, + T buffer2, int offset2, int length2); + } + + /** + * Pure Java Comparer + * + * @return + */ + static Comparer lexicographicalComparerJavaImpl() { + return LexicographicalComparerHolder.PureJavaComparer.INSTANCE; + } + + /** + * Unsafe Comparer + * + * @return + */ + static Comparer lexicographicalComparerUnsafeImpl() { + return LexicographicalComparerHolder.UnsafeComparer.INSTANCE; + } + + + /** + * Provides a lexicographical comparer implementation; either a Java + * implementation or a faster implementation based on {@link Unsafe}. + * + *

Uses reflection to gracefully fall back to the Java implementation if + * {@code Unsafe} isn't available. + */ + private static class LexicographicalComparerHolder { + static final String UNSAFE_COMPARER_NAME = + LexicographicalComparerHolder.class.getName() + "$UnsafeComparer"; + + static final Comparer BEST_COMPARER = getBestComparer(); + /** + * Returns the Unsafe-using Comparer, or falls back to the pure-Java + * implementation if unable to do so. + */ + static Comparer getBestComparer() { + String arch = System.getProperty("os.arch"); + boolean unaligned = arch.equals("i386") || arch.equals("x86") + || arch.equals("amd64") || arch.equals("x86_64"); + if (!unaligned) + return lexicographicalComparerJavaImpl(); + try { + Class theClass = Class.forName(UNSAFE_COMPARER_NAME); + + // yes, UnsafeComparer does implement Comparer + @SuppressWarnings("unchecked") + Comparer comparer = + (Comparer) theClass.getEnumConstants()[0]; + return comparer; + } catch (Throwable t) { // ensure we really catch *everything* + return lexicographicalComparerJavaImpl(); + } + } + + /** + * Java Comparer doing byte by byte comparisons + * + */ + enum PureJavaComparer implements Comparer { + INSTANCE; + + /** + * + * CompareTo looking at two buffers. + * + * @param buffer1 left operand + * @param buffer2 right operand + * @param offset1 Where to start comparing in the left buffer + * @param offset2 Where to start comparing in the right buffer + * @param length1 How much to compare from the left buffer + * @param length2 How much to compare from the right buffer + * @return 0 if equal, < 0 if left is less than right, etc. + */ + @Override + public int compareTo(byte[] buffer1, int offset1, int length1, + byte[] buffer2, int offset2, int length2) { + // Short circuit equal case + if (buffer1 == buffer2 && + offset1 == offset2 && + length1 == length2) { + return 0; + } + int end1 = offset1 + length1; + int end2 = offset2 + length2; + for (int i = offset1, j = offset2; i < end1 && j < end2; i++, j++) { + int a = (buffer1[i] & UNSIGNED_MASK); + int b = (buffer2[j] & UNSIGNED_MASK); + if (a != b) { + return a - b; + } + } + return length1 - length2; + } + + /** + * Supports Comparator + * + * @param o1 + * @param o2 + * @return comparison + */ + @Override + public int compare(byte[] o1, byte[] o2) { + return compareTo(o1, 0, o1.length, o2, 0, o2.length); + } + } + + /** + * + * Takes advantage of word based comparisons + * + */ + @SuppressWarnings("unused") // used via reflection + enum UnsafeComparer implements Comparer { + INSTANCE; + + static final Unsafe theUnsafe; + + /** + * The offset to the first element in a byte array. + */ + static final int BYTE_ARRAY_BASE_OFFSET; + + @Override + public int compare(byte[] o1, byte[] o2) { + return compareTo(o1, 0, o1.length, o2, 0, o2.length); + } + + static { + theUnsafe = (Unsafe) AccessController.doPrivileged( + (PrivilegedAction) () -> { + try { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return f.get(null); + } catch (NoSuchFieldException e) { + // It doesn't matter what we throw; + // it's swallowed in getBestComparer(). + throw new Error(); + } catch (IllegalAccessException e) { + throw new Error(); + } + }); + + BYTE_ARRAY_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class); + + // sanity check - this should never fail + if (theUnsafe.arrayIndexScale(byte[].class) != 1) { + throw new AssertionError(); + } + } + + static final boolean LITTLE_ENDIAN = + ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN); + + /** + * Lexicographically compare two arrays. + * + * @param buffer1 left operand + * @param buffer2 right operand + * @param offset1 Where to start comparing in the left buffer + * @param offset2 Where to start comparing in the right buffer + * @param length1 How much to compare from the left buffer + * @param length2 How much to compare from the right buffer + * @return 0 if equal, < 0 if left is less than right, etc. + */ + @Override + public int compareTo(byte[] buffer1, int offset1, int length1, + byte[] buffer2, int offset2, int length2) { + // Short circuit equal case + if (buffer1 == buffer2 && + offset1 == offset2 && + length1 == length2) { + return 0; + } + final int stride = 8; + final int minLength = Math.min(length1, length2); + int strideLimit = minLength & ~(stride - 1); + final long offset1Adj = offset1 + BYTE_ARRAY_BASE_OFFSET; + final long offset2Adj = offset2 + BYTE_ARRAY_BASE_OFFSET; + int i; + + /* + * Compare 8 bytes at a time. Benchmarking on x86 shows a stride of 8 bytes is no slower + * than 4 bytes even on 32-bit. On the other hand, it is substantially faster on 64-bit. + */ + for (i = 0; i < strideLimit; i += stride) { + long lw = theUnsafe.getLong(buffer1, offset1Adj + i); + long rw = theUnsafe.getLong(buffer2, offset2Adj + i); + if (lw != rw) { + if(!LITTLE_ENDIAN) { + return ((lw + Long.MIN_VALUE) < (rw + Long.MIN_VALUE)) ? -1 : 1; + } + + /* + * We want to compare only the first index where left[index] != right[index]. This + * corresponds to the least significant nonzero byte in lw ^ rw, since lw and rw are + * little-endian. Long.numberOfTrailingZeros(diff) tells us the least significant + * nonzero bit, and zeroing out the first three bits of L.nTZ gives us the shift to get + * that least significant nonzero byte. This comparison logic is based on UnsignedBytes + * comparator from guava v21 + */ + int n = Long.numberOfTrailingZeros(lw ^ rw) & ~0x7; + return ((int) ((lw >>> n) & UNSIGNED_MASK)) - ((int) ((rw >>> n) & UNSIGNED_MASK)); + } + } + + // The epilogue to cover the last (minLength % stride) elements. + for (; i < minLength; i++) { + int a = (buffer1[offset1 + i] & UNSIGNED_MASK); + int b = (buffer2[offset2 + i] & UNSIGNED_MASK); + if (a != b) { + return a - b; + } + } + return length1 - length2; + } + } + } +} \ No newline at end of file From 9bfc5bbea8c5b3d2a84e1145cb214d232072ba46 Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Tue, 21 Apr 2020 11:38:45 -0700 Subject: [PATCH 1498/1604] Check RestorableFileSet's key ranges in simulation Ranges written in the manifest file should match with actual file content. --- fdbclient/BackupAgent.actor.h | 5 +++++ fdbclient/BackupContainer.actor.cpp | 34 +++++++++++++++++++++++++++++ fdbclient/BackupContainer.h | 10 +++++++++ fdbserver/RestoreMaster.actor.cpp | 16 ++------------ 4 files changed, 51 insertions(+), 14 deletions(-) diff --git a/fdbclient/BackupAgent.actor.h b/fdbclient/BackupAgent.actor.h index e308aa43a4..f728bcd488 100644 --- a/fdbclient/BackupAgent.actor.h +++ b/fdbclient/BackupAgent.actor.h @@ -934,5 +934,10 @@ struct StringRefReader { Error failure_error; }; +namespace fileBackup { +ACTOR Future>> decodeRangeFileBlock(Reference file, int64_t offset, + int len); +} + #include "flow/unactorcompiler.h" #endif diff --git a/fdbclient/BackupContainer.actor.cpp b/fdbclient/BackupContainer.actor.cpp index 1719d7837e..a5ec9223f2 100644 --- a/fdbclient/BackupContainer.actor.cpp +++ b/fdbclient/BackupContainer.actor.cpp @@ -1331,6 +1331,29 @@ public: return end; } + ACTOR static Future getSnapshotFileKeyRange_impl(Reference bc, + RangeFile file) { + state Reference inFile = wait(bc->readFile(file.fileName)); + state bool beginKeySet = false; + state Key beginKey; + state Key endKey; + state int64_t j = 0; + for (; j < file.fileSize; j += file.blockSize) { + int64_t len = std::min(file.blockSize, file.fileSize - j); + Standalone> blockData = wait(fileBackup::decodeRangeFileBlock(inFile, j, len)); + if (!beginKeySet) { + beginKey = blockData.front().key; + } + endKey = blockData.back().key; + } + return KeyRange(KeyRangeRef(beginKey, endKey)); + } + + Future getSnapshotFileKeyRange(const RangeFile& file) final { + ASSERT(g_network->isSimulated()); + return getSnapshotFileKeyRange_impl(Reference::addRef(this), file); + } + ACTOR static Future> getRestoreSet_impl(Reference bc, Version targetVersion) { // Find the most recent keyrange snapshot to end at or before targetVersion state Optional snapshot; @@ -1349,6 +1372,17 @@ public: wait(bc->readKeyspaceSnapshot(snapshot.get())); restorable.ranges = std::move(results.first); restorable.keyRanges = std::move(results.second); + if (g_network->isSimulated()) { + // Sanity check key ranges + state std::map::iterator rit; + for (rit = restorable.keyRanges.begin(); rit != restorable.keyRanges.end(); rit++) { + auto it = std::find_if(restorable.ranges.begin(), restorable.ranges.end(), + [file = rit->first](const RangeFile f) { return f.fileName == file; }); + ASSERT(it != restorable.ranges.end()); + KeyRange result = wait(bc->getSnapshotFileKeyRange(*it)); + ASSERT(rit->second.begin <= result.begin && rit->second.end >= result.end); + } + } // No logs needed if there is a complete key space snapshot at the target version. if (snapshot.get().beginVersion == snapshot.get().endVersion && diff --git a/fdbclient/BackupContainer.h b/fdbclient/BackupContainer.h index 7c6f96d38c..92c03b1985 100644 --- a/fdbclient/BackupContainer.h +++ b/fdbclient/BackupContainer.h @@ -108,6 +108,12 @@ struct RangeFile { std::string fileName; int64_t fileSize; + RangeFile() {} + RangeFile(Version v, uint32_t bSize, std::string name, int64_t size) + : version(v), blockSize(bSize), fileName(name), fileSize(size) {} + RangeFile(const RangeFile& f) + : version(f.version), blockSize(f.blockSize), fileName(f.fileName), fileSize(f.fileSize) {} + // Order by version, break ties with name bool operator< (const RangeFile &rhs) const { return version == rhs.version ? fileName < rhs.fileName : version < rhs.version; @@ -246,6 +252,10 @@ public: // Open a file for read by name virtual Future> readFile(std::string name) = 0; + // Returns the key ranges in the snapshot file. This is an expensive function + // and should only be used in simulation for sanity check. + virtual Future getSnapshotFileKeyRange(const RangeFile& file) = 0; + struct ExpireProgress { std::string step; int total; diff --git a/fdbserver/RestoreMaster.actor.cpp b/fdbserver/RestoreMaster.actor.cpp index 657862b71d..a13bf9e54b 100644 --- a/fdbserver/RestoreMaster.actor.cpp +++ b/fdbserver/RestoreMaster.actor.cpp @@ -717,22 +717,10 @@ ACTOR static Future collectBackupFiles(Reference bc, ACTOR static Future insertRangeVersion(KeyRangeMap* pRangeVersions, RestoreFileFR* file, Reference bc) { TraceEvent("FastRestoreMasterDecodeRangeVersion").detail("File", file->toString()); - state Reference inFile = wait(bc->readFile(file->fileName)); - state bool beginKeySet = false; - state Key beginKey; - state Key endKey; - state int64_t j = 0; - for (; j < file->fileSize; j += file->blockSize) { - int64_t len = std::min(file->blockSize, file->fileSize - j); - Standalone> blockData = wait(parallelFileRestore::decodeRangeFileBlock(inFile, j, len)); - if (!beginKeySet) { - beginKey = blockData.front().key; - } - endKey = blockData.back().key; - } + RangeFile rangeFile(file->version, file->blockSize, file->fileName, file->fileSize); // First and last key are the range for this file: endKey is exclusive - KeyRange fileRange = KeyRangeRef(beginKey.contents(), endKey.contents()); + KeyRange fileRange = wait(bc->getSnapshotFileKeyRange(rangeFile)); TraceEvent("FastRestoreMasterInsertRangeVersion") .detail("DecodedRangeFile", file->fileName) .detail("KeyRange", fileRange) From 6909f0b8fcac89f764b287b6acd7674a863d3b97 Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Tue, 21 Apr 2020 13:42:24 -0700 Subject: [PATCH 1499/1604] Remove decodeRangeFileBlock from parallel restore Reuse the one from fileBackup namespace. --- fdbserver/RestoreCommon.actor.cpp | 57 ------------------------------- fdbserver/RestoreCommon.actor.h | 2 -- fdbserver/RestoreLoader.actor.cpp | 18 +++++++--- 3 files changed, 13 insertions(+), 64 deletions(-) diff --git a/fdbserver/RestoreCommon.actor.cpp b/fdbserver/RestoreCommon.actor.cpp index 08de6034d4..e75c42556d 100644 --- a/fdbserver/RestoreCommon.actor.cpp +++ b/fdbserver/RestoreCommon.actor.cpp @@ -297,63 +297,6 @@ std::string RestoreConfigFR::toString() { // parallelFileRestore is copied from FileBackupAgent.actor.cpp for the same reason as RestoreConfigFR is copied namespace parallelFileRestore { -ACTOR Future>> decodeRangeFileBlock(Reference file, int64_t offset, - int len) { - state Standalone buf = makeString(len); - int rLen = wait(file->read(mutateString(buf), len, offset)); - if (rLen != len) throw restore_bad_read(); - - Standalone> results({}, buf.arena()); - state StringRefReader reader(buf, restore_corrupted_data()); - - try { - // Read header, currently only decoding version 1001 - if (reader.consume() != 1001) throw restore_unsupported_file_version(); - - // Read begin key, if this fails then block was invalid. - uint32_t kLen = reader.consumeNetworkUInt32(); - const uint8_t* k = reader.consume(kLen); - results.push_back(results.arena(), KeyValueRef(KeyRef(k, kLen), ValueRef())); - - // Read kv pairs and end key - while (1) { - // Read a key. - kLen = reader.consumeNetworkUInt32(); - k = reader.consume(kLen); - - // If eof reached or first value len byte is 0xFF then a valid block end was reached. - if (reader.eof() || *reader.rptr == 0xFF) { - results.push_back(results.arena(), KeyValueRef(KeyRef(k, kLen), ValueRef())); - break; - } - - // Read a value, which must exist or the block is invalid - uint32_t vLen = reader.consumeNetworkUInt32(); - const uint8_t* v = reader.consume(vLen); - results.push_back(results.arena(), KeyValueRef(KeyRef(k, kLen), ValueRef(v, vLen))); - - // If eof reached or first byte of next key len is 0xFF then a valid block end was reached. - if (reader.eof() || *reader.rptr == 0xFF) break; - } - - // Make sure any remaining bytes in the block are 0xFF - for (auto b : reader.remainder()) - if (b != 0xFF) throw restore_corrupted_data_padding(); - - return results; - - } catch (Error& e) { - TraceEvent(SevError, "FileRestoreCorruptRangeFileBlock") - .error(e) - .detail("Filename", file->getFilename()) - .detail("BlockOffset", offset) - .detail("BlockLen", len) - .detail("ErrorRelativeOffset", reader.rptr - buf.begin()) - .detail("ErrorAbsoluteOffset", reader.rptr - buf.begin() + offset); - throw; - } -} - ACTOR Future>> decodeLogFileBlock(Reference file, int64_t offset, int len) { state Standalone buf = makeString(len); diff --git a/fdbserver/RestoreCommon.actor.h b/fdbserver/RestoreCommon.actor.h index 268fbf26d2..41df5495fd 100644 --- a/fdbserver/RestoreCommon.actor.h +++ b/fdbserver/RestoreCommon.actor.h @@ -248,8 +248,6 @@ struct RestoreFileFR { }; namespace parallelFileRestore { -ACTOR Future>> decodeRangeFileBlock(Reference file, int64_t offset, - int len); ACTOR Future>> decodeLogFileBlock(Reference file, int64_t offset, int len); } // namespace parallelFileRestore diff --git a/fdbserver/RestoreLoader.actor.cpp b/fdbserver/RestoreLoader.actor.cpp index da741a5b5d..5bf49f3352 100644 --- a/fdbserver/RestoreLoader.actor.cpp +++ b/fdbserver/RestoreLoader.actor.cpp @@ -23,6 +23,7 @@ #include "flow/UnitTest.h" #include "fdbclient/BackupContainer.h" +#include "fdbclient/BackupAgent.actor.h" #include "fdbserver/RestoreLoader.actor.h" #include "fdbserver/RestoreRoleCommon.actor.h" @@ -816,11 +817,18 @@ ACTOR static Future _parseRangeFileToMutationsOnLoader( // The set of key value version is rangeFile.version. the key-value set in the same range file has the same version Reference inFile = wait(bc->readFile(asset.filename)); - Standalone> blockData = - wait(parallelFileRestore::decodeRangeFileBlock(inFile, asset.offset, asset.len)); - TraceEvent("FastRestore") - .detail("DecodedRangeFile", asset.filename) - .detail("DataSize", blockData.contents().size()); + state VectorRef blockData; + try { + Standalone> kvs = + wait(fileBackup::decodeRangeFileBlock(inFile, asset.offset, asset.len)); + TraceEvent("FastRestore") + .detail("DecodedRangeFile", asset.filename) + .detail("DataSize", kvs.contents().size()); + blockData = kvs; + } catch (Error& e) { + TraceEvent(SevError, "FileRestoreCorruptRangeFileBlock").error(e); + throw; + } // First and last key are the range for this file KeyRange fileRange = KeyRangeRef(blockData.front().key, blockData.back().key); From 500a265d208641188af5c36d20d465e1fd749ecc Mon Sep 17 00:00:00 2001 From: Vishesh Yadav Date: Tue, 21 Apr 2020 12:16:39 -0700 Subject: [PATCH 1500/1604] FlowTransport: Refactor and clear FailureStatus on destroy --- fdbrpc/FlowTransport.actor.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/fdbrpc/FlowTransport.actor.cpp b/fdbrpc/FlowTransport.actor.cpp index 52be77561a..219aafc1a1 100644 --- a/fdbrpc/FlowTransport.actor.cpp +++ b/fdbrpc/FlowTransport.actor.cpp @@ -439,6 +439,8 @@ ACTOR Future connectionKeeper( Reference self, state Optional firstConnFailedTime = Optional(); loop { try { + state Future delayedHealthUpdateF = Future(); + if (!conn) { // Always, except for the first loop with an incoming connection self->outgoingConnectionIdle = true; // Wait until there is something to send. @@ -461,9 +463,11 @@ ACTOR Future connectionKeeper( Reference self, TraceEvent("ConnectingTo", conn ? conn->getDebugID() : UID()) .suppressFor(1.0) .detail("PeerAddr", self->destination) - .detail("PeerReferences", self->peerReferences); + .detail("PeerReferences", self->peerReferences) + .detail("FailureStatus", IFailureMonitor::failureMonitor().getState(self->destination).isAvailable() + ? "OK" + : "FAILED"); - state Future delayedHealthUpdateF = Future(); try { choose { when(Reference _conn = @@ -513,8 +517,6 @@ ACTOR Future connectionKeeper( Reference self, delayedHealthUpdateF = delayedHealthUpdate(self->destination); wait(connectionWriter(self, conn) || reader || connectionMonitor(self)); } catch (Error& e) { - if (e.code() == error_code_connection_failed) - IFailureMonitor::failureMonitor().setStatus(self->destination, FailureStatus(true)); if (e.code() == error_code_connection_failed || e.code() == error_code_actor_cancelled || e.code() == error_code_connection_unreferenced || (g_network->isSimulated() && e.code() == error_code_checksum_failed)) @@ -564,6 +566,10 @@ ACTOR Future connectionKeeper( Reference self, .detail("PeerAddr", self->destination); } + if (e.code() == error_code_connection_failed) { + IFailureMonitor::failureMonitor().setStatus(self->destination, FailureStatus(true)); + } + if(self->destination.isPublic() && IFailureMonitor::failureMonitor().getState(self->destination).isAvailable() && !FlowTransport::transport().isClient()) @@ -587,8 +593,6 @@ ACTOR Future connectionKeeper( Reference self, conn->close(); conn = Reference(); - } else { - IFailureMonitor::failureMonitor().setStatus(self->destination, FailureStatus(true)); } // Clients might send more packets in response, which needs to go out on the next connection @@ -601,6 +605,7 @@ ACTOR Future connectionKeeper( Reference self, TraceEvent("PeerDestroy").error(e).suppressFor(1.0).detail("PeerAddr", self->destination); self->connect.cancel(); self->transport->peers.erase(self->destination); + IFailureMonitor::failureMonitor().setStatus(self->destination, FailureStatus(true)); return Void(); } } From 8b004fe8e3bab4b6370a021e3672bccc4c484814 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Tue, 21 Apr 2020 14:32:11 -0700 Subject: [PATCH 1501/1604] Move stop callbacks to be called after run() in sim2. --- fdbrpc/sim2.actor.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fdbrpc/sim2.actor.cpp b/fdbrpc/sim2.actor.cpp index 9b5697d53d..e4acc3e5df 100644 --- a/fdbrpc/sim2.actor.cpp +++ b/fdbrpc/sim2.actor.cpp @@ -872,9 +872,6 @@ public: virtual void stop() { isStopped = true; - for ( auto& fn : stopCallbacks ) { - fn(); - } } virtual void addStopCallback( std::function fn ) { stopCallbacks.emplace_back(std::move(fn)); @@ -1002,6 +999,9 @@ public: } self->currentProcess = callingMachine; self->net2->stop(); + for ( auto& fn : self->stopCallbacks ) { + fn(); + } return Void(); } From c6df20a179257f20ee41414a53e9e51fefc14810 Mon Sep 17 00:00:00 2001 From: Alex Miller <35046903+alexmiller-apple@users.noreply.github.com> Date: Tue, 21 Apr 2020 20:39:45 -0700 Subject: [PATCH 1502/1604] Use nullptr instead of NULL --- flow/Net2.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index 4dc4106e9d..e5078d46dd 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -148,7 +148,7 @@ public: if ( thread_network == this ) stopCallbacks.emplace_back(std::move(fn)); else - onMainThreadVoid( [this, fn] { this->stopCallbacks.emplace_back(std::move(fn)); }, NULL ); + onMainThreadVoid( [this, fn] { this->stopCallbacks.emplace_back(std::move(fn)); }, nullptr ); } virtual bool isSimulated() const { return false; } From d5bef6fc324f682194840f9c17707992ef9d017c Mon Sep 17 00:00:00 2001 From: Balachandar Namasivayam <36455962+bnamasivayam@users.noreply.github.com> Date: Wed, 22 Apr 2020 09:45:56 -0700 Subject: [PATCH 1503/1604] Update fdbserver/DataDistribution.actor.cpp --- fdbserver/DataDistribution.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/DataDistribution.actor.cpp b/fdbserver/DataDistribution.actor.cpp index 371cd69985..695b82f9ff 100644 --- a/fdbserver/DataDistribution.actor.cpp +++ b/fdbserver/DataDistribution.actor.cpp @@ -3810,7 +3810,7 @@ ACTOR Future storageServerTracker( } when(wait(storeTypeTracker)) {} when(wait(server->ssVersionTooFarBehind.onChange())) { } - when(wait(self->disableFailingLaggingServers.onChange())) { } + when(wait(self->disableFailingLaggingServers.onChange())) { } } if (recordTeamCollectionInfo) { From 3024162196a08fcdce125295f17358558e56e568 Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Wed, 22 Apr 2020 11:55:26 -0700 Subject: [PATCH 1504/1604] Don't create package directories in source dir This fixes one part of #2973 --- cmake/AddFdbTest.cmake | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmake/AddFdbTest.cmake b/cmake/AddFdbTest.cmake index a8fae7837b..7212bdf3db 100644 --- a/cmake/AddFdbTest.cmake +++ b/cmake/AddFdbTest.cmake @@ -160,8 +160,6 @@ function(create_test_package) string(SUBSTRING ${file} ${base_length} -1 rel_out_file) set(out_file ${CMAKE_BINARY_DIR}/packages/tests/${rel_out_file}) list(APPEND out_files ${out_file}) - get_filename_component(test_dir ${out_file} DIRECTORY) - file(MAKE_DIRECTORY packages/tests/${test_dir}) add_custom_command( OUTPUT ${out_file} DEPENDS ${file} From a4434de4e8da1cabd07d999624261c5a2d6585f2 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Wed, 22 Apr 2020 12:12:48 -0700 Subject: [PATCH 1505/1604] Depend on actorcompiler.exe Fixes #2985 This was originally done in 0443cf89eafdaaf7db813b3229f01a0debe0de37, and removed in 75f692b9317b42bc3237e940228cf23d996dea35, but I'm not sure why it was removed. I'm guessing it was a botched merge --- cmake/FlowCommands.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/FlowCommands.cmake b/cmake/FlowCommands.cmake index a9546d0bcb..53cdd7a33b 100644 --- a/cmake/FlowCommands.cmake +++ b/cmake/FlowCommands.cmake @@ -185,12 +185,12 @@ function(add_flow_target) if(WIN32) add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${generated}" COMMAND $ "${CMAKE_CURRENT_SOURCE_DIR}/${src}" "${CMAKE_CURRENT_BINARY_DIR}/${generated}" ${actor_compiler_flags} - DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${src}" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${src}" ${actor_exe} COMMENT "Compile actor: ${src}") else() add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${generated}" COMMAND ${MONO_EXECUTABLE} ${actor_exe} "${CMAKE_CURRENT_SOURCE_DIR}/${src}" "${CMAKE_CURRENT_BINARY_DIR}/${generated}" ${actor_compiler_flags} > /dev/null - DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${src}" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${src}" ${actor_exe} COMMENT "Compile actor: ${src}") endif() else() From c77650c7f3adff4cd3f2e464408385f4e1730d95 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Wed, 22 Apr 2020 13:18:39 -0700 Subject: [PATCH 1506/1604] Remove references to the late Makefile --- README.md | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/README.md b/README.md index a3964f63a8..a3e7ef5979 100755 --- a/README.md +++ b/README.md @@ -206,37 +206,3 @@ will automatically find it and build with TLS support. If you installed WIX before running `cmake` you should find the `FDBInstaller.msi` in your build directory under `packaging/msi`. -## Makefile (Deprecated - all users should transition to using cmake) - -#### MacOS - -1. Check out this repo on your Mac. -1. Install the Xcode command-line tools. -1. Download version 1.67.0 of [Boost](https://sourceforge.net/projects/boost/files/boost/1.67.0/). -1. Set the `BOOSTDIR` environment variable to the location containing this boost installation. -1. Install [Mono](http://www.mono-project.com/download/stable/). -1. Install a [JDK](http://www.oracle.com/technetwork/java/javase/downloads/index.html). FoundationDB currently builds with Java 8. -1. Navigate to the directory where you checked out the foundationdb repo. -1. Run `make`. - -#### Linux - -1. Install [Docker](https://www.docker.com/). -1. Check out the foundationdb repo. -1. Run the docker image interactively with [Docker Run](https://docs.docker.com/engine/reference/run/#general-form), and with the directory containing the foundationdb repo mounted via [Docker Mounts](https://docs.docker.com/storage/volumes/). - - ```shell - docker run -it -v '/local/dir/path/foundationdb:/docker/dir/path/foundationdb' foundationdb/foundationdb-build:latest - ``` - -1. Run `$ scl enable devtoolset-8 python27 rh-python36 rh-ruby24 -- bash` within the running container. This enables a more modern compiler, which is required to build FoundationDB. -1. Navigate to the container's mounted directory which contains the foundationdb repo. - - ```shell - cd /docker/dir/path/foundationdb - ``` - -1. Run `make`. - -This will build the fdbserver binary and the python bindings. If you want to build our other bindings, you will need to install a runtime for the language whose binding you want to build. Each binding has an `.mk` file which provides specific targets for that binding. - From d0cc2a1ee481ae916170df3bb3e0fe970d3f7c56 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 22 Apr 2020 14:24:45 -0700 Subject: [PATCH 1507/1604] added logging for parallel peeks on TLogs --- fdbserver/Knobs.cpp | 2 + fdbserver/Knobs.h | 2 + fdbserver/TLogServer.actor.cpp | 107 ++++++++++++++++++++++++++++++++- 3 files changed, 110 insertions(+), 1 deletion(-) diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index 669d041a19..4c4e3e7246 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -83,6 +83,8 @@ ServerKnobs::ServerKnobs(bool randomize, ClientKnobs* clientKnobs, bool isSimula init( TLOG_IGNORE_POP_AUTO_ENABLE_DELAY, 300.0 ); init( TXS_POPPED_MAX_DELAY, 1.0 ); if ( randomize && BUGGIFY ) TXS_POPPED_MAX_DELAY = deterministicRandom()->random01(); init( TLOG_MAX_CREATE_DURATION, 10.0 ); + init( PEEK_LOGGING_AMOUNT, 5 ); + init( PEEK_LOGGING_DELAY, 5.0 ); // disk snapshot max timeout, to be put in TLog, storage and coordinator nodes init( SNAP_CREATE_MAX_TIMEOUT, 300.0 ); diff --git a/fdbserver/Knobs.h b/fdbserver/Knobs.h index 14104c260b..f006621612 100644 --- a/fdbserver/Knobs.h +++ b/fdbserver/Knobs.h @@ -85,6 +85,8 @@ public: double TLOG_DEGRADED_DURATION; double TXS_POPPED_MAX_DELAY; double TLOG_MAX_CREATE_DURATION; + int PEEK_LOGGING_AMOUNT; + double PEEK_LOGGING_DELAY; // Data distribution queue double HEALTH_POLL_TIME; diff --git a/fdbserver/TLogServer.actor.cpp b/fdbserver/TLogServer.actor.cpp index c090a9dcae..e3f478f4c2 100644 --- a/fdbserver/TLogServer.actor.cpp +++ b/fdbserver/TLogServer.actor.cpp @@ -483,6 +483,39 @@ struct LogData : NonCopyable, public ReferenceCounted { struct PeekTrackerData { std::map>> sequence_version; double lastUpdate; + + Tag tag; + + double lastLogged; + int64_t totalPeeks; + int64_t replyBytes; + int64_t duplicatePeeks; + double queueTime; + double queueMax; + double blockTime; + double blockMax; + + int64_t unblockedPeeks; + double idleTime; + double idleMax; + + PeekTrackerData() : lastUpdate(0) { + resetMetrics(); + } + + void resetMetrics() { + lastLogged = now(); + totalPeeks = 0; + replyBytes = 0; + duplicatePeeks = 0; + queueTime = 0; + queueMax = 0; + blockTime = 0; + blockMax = 0; + unblockedPeeks = 0; + idleTime = 0; + idleMax = 0; + } }; std::map peekTracker; @@ -1335,6 +1368,7 @@ ACTOR Future tLogPeekMessages( TLogData* self, TLogPeekRequest req, Refere state BinaryWriter messages2(Unversioned()); state int sequence = -1; state UID peekId; + state double queueStart = now(); if(req.sequence.present()) { try { @@ -1345,6 +1379,7 @@ ACTOR Future tLogPeekMessages( TLogData* self, TLogPeekRequest req, Refere } auto& trackerData = logData->peekTracker[peekId]; if (sequence == 0 && trackerData.sequence_version.find(0) == trackerData.sequence_version.end()) { + trackerData.tag = req.tag; trackerData.sequence_version[0].send(std::make_pair(req.begin, req.onlySpilled)); } auto seqBegin = trackerData.sequence_version.begin(); @@ -1361,8 +1396,15 @@ ACTOR Future tLogPeekMessages( TLogData* self, TLogPeekRequest req, Refere throw timed_out(); } + Future> fPrevPeekData = trackerData.sequence_version[sequence].getFuture(); + if(fPrevPeekData.isReady()) { + trackerData.unblockedPeeks++; + double t = now() - trackerData.lastUpdate; + if(t > trackerData.idleMax) trackerData.idleMax = t; + trackData.idleTime += t; + } trackerData.lastUpdate = now(); - std::pair prevPeekData = wait(trackerData.sequence_version[sequence].getFuture()); + std::pair prevPeekData = wait(fPrevPeekData); req.begin = prevPeekData.first; req.onlySpilled = prevPeekData.second; wait(yield()); @@ -1376,6 +1418,8 @@ ACTOR Future tLogPeekMessages( TLogData* self, TLogPeekRequest req, Refere } } + state double blockStart = now(); + if( req.returnIfBlocked && logData->version.get() < req.begin ) { req.reply.sendError(end_of_stream()); if(req.sequence.present()) { @@ -1410,6 +1454,8 @@ ACTOR Future tLogPeekMessages( TLogData* self, TLogPeekRequest req, Refere wait(delay(0, TaskPriority::TLogSpilledPeekReply)); } + state double workStart = now(); + Version poppedVer = poppedVersion(logData, req.tag); if(poppedVer > req.begin) { TLogPeekReply rep; @@ -1585,6 +1631,22 @@ ACTOR Future tLogPeekMessages( TLogData* self, TLogPeekRequest req, Refere if(req.sequence.present()) { auto& trackerData = logData->peekTracker[peekId]; trackerData.lastUpdate = now(); + + double queueT = blockStart-queueStart; + double blockT = workStart-blockStart; + double workT = now()-workStart; + + trackerData.totalPeeks++; + trackerData.replyBytes += reply.messages.size(); + + if(queueT > trackerData.queueMax) trackerData.queueMax = queueT; + if(blockT > trackerData.blockMax) trackerData.blockMax = blockT; + if(workT > trackerData.workMax) trackerData.workMax = workT; + + trackerData.queueTime += queueT; + trackerData.blockTime += blockT; + trackerData.workTime += workT; + auto& sequenceData = trackerData.sequence_version[sequence+1]; if(trackerData.sequence_version.size() && sequence+1 < trackerData.sequence_version.begin()->first) { req.reply.sendError(timed_out()); @@ -1593,6 +1655,7 @@ ACTOR Future tLogPeekMessages( TLogData* self, TLogPeekRequest req, Refere return Void(); } if(sequenceData.isSet()) { + trackerData.duplicatePeeks++; if(sequenceData.getFuture().get().first != reply.end) { TEST(true); //tlog peek second attempt ended at a different version req.reply.sendError(timed_out()); @@ -1929,6 +1992,47 @@ ACTOR Future cleanupPeekTrackers( LogData* logData ) { } } +ACTOR Future logPeekTrackers( LogData* logData ) { + loop { + int64_t logThreshold = 1; + if(logData->peekTracker.size() > SERVER_KNOBS->PEEK_LOGGING_AMOUNT) { + std::vector peekCounts; + peekCounts.reserve(logData->peekTracker.size()); + for( auto& it : logData->peekTracker ) { + peekCounts.push_back(it.totalPeeks); + } + size_t pivot = peekCounts.size()-SERVER_KNOBS->PEEK_LOGGING_AMOUNT; + std::nth_element(peekCounts.begin(), peekCounts.begin()+pivot, peekCounts.end()); + logThreshold = std::max(1,peekCounts[pivot]); + } + int logCount = 0; + for( auto& it : logData->peekTracker ) { + if(it.second.totalPeeks >= logThreshold) { + logCount++; + TraceEvent("PeekMetrics", logData->logId) + .detail("Tag", it.second.tag.toString()) + .detail("Elapsed", now() - it.second.lastLogged) + .detail("MeanReplyBytes", it.second.replyBytes/it.second.totalPeeks) + .detail("TotalPeeks", it.second.totalPeeks) + .detail("UnblockedPeeks", it.second.unblockedPeeks) + .detail("DuplicatePeeks", it.second.duplicatePeeks) + .detail("Sequence", it.second.sequence_version.size() ? it.second.sequence_version.begin()->first : -1) + .detail("IdleSeconds", it.second.idleTime) + .detail("IdleMax", it.second.idleMax) + .detail("QueueSeconds", it.second.queueTime) + .detail("QueueMax", it.second.queueMax) + .detail("BlockSeconds", it.second.blockTime) + .detail("BlockMax", it.second.blockMax) + .detail("WorkSeconds", it.second.workTime) + .detail("WorkMax", it.second.workMax) + it.second.resetMetrics(); + } + } + + wait( delay(SERVER_KNOBS->PEEK_LOGGING_DELAY * std::max(1,logCount)) ); + } +} + void getQueuingMetrics( TLogData* self, Reference logData, TLogQueuingMetricsRequest const& req ) { TLogQueuingMetricsReply reply; reply.localTime = now(); @@ -2278,6 +2382,7 @@ ACTOR Future tLogCore( TLogData* self, Reference logData, TLogInt logData->addActor.send( traceCounters("TLogMetrics", logData->logId, SERVER_KNOBS->STORAGE_LOGGING_DELAY, &logData->cc, logData->logId.toString() + "/TLogMetrics")); logData->addActor.send( serveTLogInterface(self, tli, logData, warningCollectorInput) ); logData->addActor.send( cleanupPeekTrackers(logData.getPtr()) ); + logData->addActor.send( logPeekTrackers(logData.getPtr()) ); if(!logData->isPrimary) { std::vector tags; From dfb0593ae67fd1e772ae8440e11315c34f584945 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 22 Apr 2020 14:24:59 -0700 Subject: [PATCH 1508/1604] increases priority of status requests --- fdbserver/WorkerInterface.actor.h | 1 + 1 file changed, 1 insertion(+) diff --git a/fdbserver/WorkerInterface.actor.h b/fdbserver/WorkerInterface.actor.h index a2eb851db2..7d9e00bfae 100644 --- a/fdbserver/WorkerInterface.actor.h +++ b/fdbserver/WorkerInterface.actor.h @@ -80,6 +80,7 @@ struct WorkerInterface { logRouter.getEndpoint( TaskPriority::Worker ); debugPing.getEndpoint( TaskPriority::Worker ); coordinationPing.getEndpoint( TaskPriority::Worker ); + eventLogRequest.getEndpoint( TaskPriority::Worker ); } template From 74d8a2358d8e6bb260c9a75255f24ce5890ede3b Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Wed, 22 Apr 2020 14:20:06 -0700 Subject: [PATCH 1509/1604] Add design doc for the new backup system The new backup system introduces partitioned logs for storing mutations. --- design/backup_v2_partitioned_logs.md | 336 +++++++++++++++++++++++++++ 1 file changed, 336 insertions(+) create mode 100644 design/backup_v2_partitioned_logs.md diff --git a/design/backup_v2_partitioned_logs.md b/design/backup_v2_partitioned_logs.md new file mode 100644 index 0000000000..ef5a118a2a --- /dev/null +++ b/design/backup_v2_partitioned_logs.md @@ -0,0 +1,336 @@ +# The New FDB Backup System: Requirements & Design + +Github tracking issue: https://github.com/apple/foundationdb/issues/1003 + +## Purpose and Audience + +The purpose of this document is to capture functional requirements as well as propose a high level design for implementation of the new backup system in FoundationDB. The intended audience for this document includes: + +* **FDB users** - Users can understand what are the changes in the new backup system, especially how to start a backup using the new backup system. The restore for new backup is handled by the [Performant Restore System](https://github.com/apple/foundationdb/issues/1049). +* **SRE's and Support** - can understand the high level architecture and know the requirements, including the metrics, tooling, and documentation to ensure that the new FDB backup can be supported. +* **Developers** - can know why this feature is needed, what it does, and how it is to be implemented. The hope is that this document becomes the starting point for any developer wishing to understand or be involved in the related aspects of FDB. + +## Functional Requirements + +As an essential component of a database system, backup and restore is commonly used technique for disaster recovery, reliability, audit and compliance purposes. The current FDB backup system consumes about half of the cluster’s write bandwidth, causes write skew among storage servers, increases storage space usage, and results in data balancing. The new backup system aims to double cluster’s write bandwidth for *HA clusters* (old DR clusters still need old style backup system). + +## Background + +FDB backup system continuously scan the database’s key-value space, save key-value pairs and mutations at versions into range files and log files in blob storage. Specifically, mutation logs are generated at Proxy, and are written to transaction logs along with regular mutations. In production clusters like CK clusters, backup system is always on, which means each mutation is written twice to transaction logs, consuming about half of write bandwidth and about 40% of Proxy CPU time. + +The design of old backup system is [here](https://github.com/apple/foundationdb/blob/master/design/backup.md), and the data format of range files and mutations files is [here](https://github.com/apple/foundationdb/blob/master/design/backup-dataFormat.md). The technical overview of FDB is [here](https://github.com/apple/foundationdb/wiki/Technical-Overview-of-the-Database). The FDB recovery is described in this [doc](https://github.com/apple/foundationdb/blob/master/design/recovery-internals.md). + + +## Terminology + +* **Blob storage**: blob storage is an object storage for unstructed data. Backup files are encoded in binary format and saved in blob storage, e.g., Amazon S3. +* **Version**: FDB continuously generate increasing number as version and use version to decide mutation ordering. Version number typically advance one million per second. To restore a FDB cluster to a specified date and time, the restore system first convert the date and time to the corresponding version number and restore the cluster to the version number. +* **Epoch**: A generation of FDB’s transaction system. After a component of the transaction system failed, FDB automatically initiates a recovery and restores the system in a new healthy generation, which is called an epoch. +* **Backup worker**: is a new role added to the FDB cluster that is responsible for pulling mutations from transaction logs and saving them to blob storage. +* **Tag**: A tag is a short address for a mutation’s destination, which includes a locality (`int8_t`, representing the data center ID and a negative number denotes special system locality) and an ID (`int16_t`). The idea is that the tag is a small data structure that consumes less bytes than using IP addresses or storage server’s UIDs (16 bytes each), since tags are associated with each mutation and are stored both in memory and on disk. +* **Tag partitioned log system**: FDB’s write-ahead log is a tag partitioned log system, where each mutation is assigned a number of tags. +* **Log router tag**: is a special system tag, e.g., `-2:0` where locality `-2` means log router tag and `0` means ID. If attached to a mutation, originally this tag means the mutation should be sent to a remote log router. In the new backup system, we reuse this tag for backup workers to receive all mutations in a number of partitioned streams. +* **Restorable version:** The version that a backup can be restored to. A version `v` is a restorable version if the entire key-space and mutations in version `[v1, v)` are recorded in backup files. +* **Node**: A node is a machine or a process in a cluster. + +## Detailed Feature Requirements + +Feature priorities: Feature 1, 2, 3, 4, 5 are must-have; Feature 6 is better to have. + +1. **Write bandwidth reduction by half**: removes the requirement to generate backup mutations at the Proxy, thus reduce TLog write bandwidth usage by half and significantly improve Proxy CPU usage; +2. **Correctness**: The restored database must be consistent: each *restored* state (i.e., key-value pair) at a version `v` must match the original state at version `v`. +3. **Performance**: The backup system should be performant, mostly measured as a small CPU overhead on transaction logs and backup workers. The version lag on backup workers is an indicator of performance. +4. **Fault-tolerant**: The backup system should be fault-tolerant to node failures in the FDB cluster. +5. **Restore ready**: The new backup system should be restored by the Performant Restore System. As a fallback for new performant restore system, we can convert new backup logs into the format of old backup logs, thus enabling restore of the new backup with existing old restore system. +6. **Backward compatibility**: The new backup system should allow both old style backup and DR (FDB 6.2 and below) to be performed, as well as support new backup in FDB 6.3 and above. + +## Security and Privacy Requirements + +**Security**: The backup system’s components are assumed to be trusted components, because they are running on the nodes in a FDB cluster. The transmission from cluster to blob store is through SSL connections. Blob credentials are passed in from “fdbserver” command line. + +**Privacy**: Backup data are stored in blob store with appropriate access control. Data retention policy can be set with “fdbbackup” tool to delete older backup data. + +## Operational and Maintainability Requirements + +This section discusses changes that may need to be identified or accounted for on the back-end in order to support the feature from a monitoring or management perspective. + +### Tooling / Front-End + +Workflow is needed for DBA to start, pause, resume, abort the new type of backups. The difference from the old type of backups should be only a flag change for starting the backup. The FDB cluster then generates backups as specified by the flag. + +A command line tool `fdbconvert` has been written to convert new backup logs into the format of old backup logs. Thus, if the new restore system has issues, we can still restore the new backup with existing old restore system. + +**Deployment instructions for tooling development** + +* A new stateless role “`Backup Worker`” (or “`BW`” for abbreviation) is introduced in a FDB cluster. The number of BW processes is based on the number of log routers (usually they are the same). If there is no log routers, the number of transaction logs is used. Note that occasionally the cluster may recruit more backup workers for version ranges in the old epoch. Since these version ranges are small, the resource requirements for these short-lived backup workers are very small. +* As in the old backup system, backup agents need to be started for saving snapshot files to blob storage. In contrast, backup workers in the new backup system running in the primary DC are responsible for saving mutation logs to blob storage. +* Backup worker’s memory should be large enough to hold 10s of seconds worth of mutation data from TLogs. The memory requirement can be calculated as: `WriteThroughput * BufferPeriod / partitions + SafetyMargin`, where `WriteThroughput` is the aggregated TLog write bandwidth, `partitions` is the number of log router tags. +* A new process class “backup” is defined for backup workers. +* How to start a new type backup: e.g., + + ``` + fdbbackup start -C fdb.cluster **-****p** -d blob_url + ``` + +### KPI's and Health + +The solution must provide at least the following KPIs: + +* How fast (MB/s) does the transaction logs commit writes (already existed); +* How much backup data has been processed; +* An estimation of backup delay; + +### Customer Care + +The feature does not require any specific customer care awareness or interaction. + +### Roll-out + +The feature must follow the usual roll-out process. It needs to coexist with the existing backup system and periodically restore clusters to test its correctness. Only after we gain enough confidence will we deprecate the existing backup system. + +Note the new backup system is designed for HA clusters. Existing DR clusters still uses the old backup system. Thus, rolling out of the new backup system is only for HA clusters. + +### Quota + +This feature requires a blob storage for saving all log files. The blob storage must have enough: + +* disk capacity for all backup data; +* write bandwidth for uploading backup data; +* file count for backup data: the new backup system stored partitioned mutation logs, thus expecting several time increases of the file count. + +## Success Criteria + +* Write bandwidth reduction meets the expectation: TLog write bandwidth is reduced by half; +* New backup workflow is available to SREs; +* Continuous backup and restore should be performed to validate the restore. + +# Design + +**One sentence summary**: the new backup system introduces a new role, backup worker, to pull mutations from transaction logs and save them, thus removing the burden of saving mutation logs into the database. + +The old backup system writes the mutation log to the database itself, thus doubling the write bandwidth usage. Backup agents later fetch mutation logs from the database, upload them to blob storage, and then remove the mutation logs from the database. + +This project saves the mutation log to blob storage directly from the FDB cluster, which should almost double the database's write bandwidth when backup is enabled. In FDB, every mutation already has exactly one log router tag, so the idea of the new system is to backup data for each log router tag individually (i.e., saving mutation logs into multiple partitioned logs). During restore time, these partitioned mutation logs are combined together to form a continuous mutation log stream. + +## Design choices + +**Design question 1**: Should backup workers be recruited as part of log system or not? +There are two design alternatives: + +1. Backup worker is external to the log system. In other words, backup workers survive master recovery. Thus, backup workers are recruited and monitored by the cluster controller. + 1. The advantage is that the failure of backup workers does not cause master recovery. + 2. The disadvantage is that backup workers need to monitor master recovery, especially configuration changes. Because the number of log routers can change after a recovery, we might need to recruit more backup workers for an increase and need to pause/shutdown backup workers for a decrease, which complicates the recruitment logic; or we might need to changing the mapping of tags to backup workers, which is also complex. A further complication is that backup workers need to constantly monitor master recovery and be very careful about the version boundary between two consecutive epochs, because the number of tags may change. +2. Backup worker is recruited during master recovery as part of log system. The Master recruits a fixed number of backup workers, i.e., the same number as LogRouters. + 1. The advantage is that recruiting and mapping from backup worker to LogRouter tags are simple, i.e., one tag per worker. + 2. The disadvantages is that backup workers are tied with master recovery -- a failure of a backup worker results in a master recovery, and a master recovery stops old backup workers and starts new ones. + +**Decision**: We choose the second approach for the simplicity of the recruiting process and handling of mapping of LogRouter tags to backup workers. + +**Design question 2**: Place of backup workers on the primary or remote Data Center (DC)? +Placing backup workers on the primary side has the advantage of supporting any deployment configurations (single DC, multi DC). + +Placing on the remote is desirable to reduce the workload on the primary DC’s transaction logs. Since log routers on the remote side is already pulling mutations from primary DC, backup workers can simply pull from these log routers. + +**Decision**: We choose to recruit backup workers on the primary DC, because not all clusters are configured with multiple DCs and the backup system needs to support all types of deployment. + +## Design Assumptions + +The design proposed below is based upon the following assumptions: + +* Blob system has enough write bandwidth and storage space for backup workers to save log files. +* FDB cluster has enough stateless processes to run as backup workers and these processes have memory capacity to buffer 10s of seconds of commit data. + +## Design Challenges + +The requirement of the new backup system raises several design challenges: + +1. Correctness of the new backup files. Backup files must be complete and accurate to capture all data, otherwise we end up with corrupted data in the backup. The challenge here is to make sure no mutation is missing, even when the FDB cluster experiences failures and has to perform recovery. +2. Testing of the new backup system. How can we test the new backup system when there is no restore system available? We need to verify backup files are correct without performing a full restore. + +## System components + +**Backup Worker**: This is a new role introduced in the new backup system. A backup worker is a `fdbserver` process running inside a FDB cluster, responsible for pulling mutations from transaction logs and saving the mutations to blob storage. + +**Master**: The master is responsible for coordinating the transition of the FDB transaction sub-system from one generation to the next. In particular, the master recruits backup workers during the recovery. + +**Transaction Logs (TLogs)**: The transaction logs make mutations durable to disk for fast commit latencies. The logs receive commits from the proxy in version order, and only respond to the proxy once the data has been written and fsync'ed to an append only mutation log on disk. Storage servers retrieve mutations from TLogs. Once the storage servers have persisted mutations, storage servers then pop the mutations from the TLogs. + +**Proxy**: The proxies are responsible for providing read versions, committing transactions, and tracking the storage servers responsible for each range of keys. In the old backup system, Proxies are responsible to group mutations into backup mutations and write them to the database. + +## System overview + +From an end-to-end perspective, the new backup system works in the following steps: + +1. Operators issue a new backup request via `fdbbackup` command line tool; +2. FDB cluster receives the request and registers the request in the database (internal `TaskBucket` and system keys); +3. Backup workers monitor changes to system keys, register the request in its own internal queue, and starts logging mutations for the request key range; at the same time, backup agents (scheduled by `TaskBucket`) starts taking snapshots of key ranges in the database; +4. Periodically, backup workers upload mutations to the requested blob storage, and save the progress into the database; +5. The backup is restorable when backup workers have saved versions that are larger than the complete snapshot’s end version, and the backup is stopped if a stop on restorable flag is set in the request. + +The new backup has four major components: 1) backup workers; 2) recruitment of backup workers; 3) extension of tag partitioned log system to support pseudo tags; 4) integration with existing `TaskBucket` based backup command interface; and 5) integration with the Performant Restore System. + +### Backup workers + +Backup worker is a new role introduced in the new backup system. A backup worker is responsible for pulling mutations from transaction logs and saving the mutations to blob storage. Internally, a backup worker maintains a message buffer, which keeps mutations pulled from transaction logs, but have not been saved to blob storage yet. Periodically, the backup worker parses mutations in the message buffer, extracts those mutations that are within user specified key ranges, and then uploads mutation data to blob storage. After data is saved, the backup worker removes these messages from its internal buffer and saves its progress in the database, so that after a failure, a new backup worker starts from the previously saved version. + +Backup worker has two modes of operation: *no-op* mode, and *working* mode. When there is no active backup in the cluster, backup worker operates in the no-op mode, which simply obtains the recently committed version from Proxies and then pops mutations from transaction logs. After operators submit a new backup request to the cluster, backup workers transition into the working mode that starts pulling mutations from transaction logs and saving the mutation data to blob storage. + +In the working mode, the popping of backup workers need to follow a strictly increasing version order. For the same tag, there could be multiple backup workers, each is responsible for a different epoch. These backup workers must coordinating their popping order, otherwise the backup can miss some mutation data. This coordination among backup workers is achieved by deferring popping of a later epoch and only allowing the oldest epoch to pop first. After the oldest epoch has finished, these corresponding backup workers notifies the master, which will then advances the oldest backup epoch so that the next epoch can proceed the popping. + +A subtle issue for a displaced backup worker (i.e., being displaced because a new epoch begins), is that the last pop of the backup worker can cause missing version ranges in mutation logs. This is because the transaction for saving the progress may be delayed during recovery. As a result, the master could already recruited a new backup worker for the old epoch starting at the previously saved progress version. Then the saving transaction succeeds, and the worker pops mutations that the new backup worker is supposed to save, resulting in missing data for new backup worker’s log. The solution to this problem can be: 1) the old backup worker aborts immediately after knowing itself is displaced, thus not trying to save its progress; or 2) the old backup worker skip its last pop, since the next epoch will pop versions larger than its progress. Because the second approach avoids doing duplicated work in the new epoch, we choose to the second approach. + +Finally, multiple concurrent backups are supported. Each backup worker keeps track of current backup jobs and saves mutations to corresponding backup containers for the same batch of mutations. + +### Recruitment of Backup workers + +Backup workers are recruited during master recovery as part of log system. The Master recruits a fixed number of backup workers, one for each log router tag. During the recruiting process, the master sends backup worker initialization request as: + +``` +struct InitializeBackupRequest { + UID reqId; + LogEpoch epoch; // epoch this worker is recruited + LogEpoch backupEpoch; // epoch that this worker actually works on + Tag routerTag; + Version startVersion; + Optional endVersion; // Only present for unfinished old epoch + ReplyPromise reply; + … // additional methods elided +}; + +``` + +Note we need two epochs here: one for the recruited epoch and one for backing up epoch. The recruited epoch is the epoch of the log system, which is used by a backup worker to find out if it works for the current epoch. If so, the worker should save its progress and immediately exit. The `backupEpoch` is used for saving progress. The `backupEpoch` is usually the same as the epoch that the worker is recruited. However, it can be some earlier epoch than the recruiting epoch, signifying that the worker is responsible for data in that earlier epoch. In this case, when the worker is done and exits, the master should not flag its departure as a trigger of recovery. This is solved by the following protocol: + +1. The backup worker finishes its work, including saving progress to the key value store and uploading to cloud storage, and then sends a `BackupWorkerDoneRequest` to the master; +2. The master receives the request, removes the worker from its log system, and updates the oldest backing up epoch `oldestBackupEpoch`; +3. The master sends backup a reply message to the backup worker and registers the new log system with cluster controller; +4. The backup worker exits after receiving the reply. Other backup workers in the system get the new log system from the cluster controller. If a backup worker’s `backupEpoch` is equal to `oldestBackupEpoch`, then the worker may start popping from TLogs. + +Note `oldestBackupEpoch` is introduced to prevent a backup worker for a newer epoch from popping when there are backup workers for older epochs. Otherwise, these older backup workers may lose data. + +### Extension of tag partitioned log system to support pseudo tags + +The tag partitioned log system is modeled like a FIFO queue, where Proxies push mutations to the queue and Storage Servers or Log Routers pop mutations from the queue. Specifically, consumers of the tag partitioned log system use two operations, `peek` and `pop`, to read mutations for a given tag and to pop mutations from the queue. Because Proxies assign each mutation a unique log router tag, the backup system reuses this tag to obtain the whole mutation stream. As a result, each log router tag now has two consumers, a log router and a backup worker. + +To support multiple consumers of the log router tag, the peek and pop has been extended to support pseudo tags. In other words, each log router tag can be mapped to multiple pseudo tags. Log routers and Backup workers still `peek` mutations with the log router tag, but `pop` with different pseudo tags. Only after both pseudo tags are popped, TLogs can pop the mutations from its internal queue. + +Note the introduction of pseudo tags opens the possibility for more usage scenarios. For instance, a change stream can be implemented with a pseudo tag, where the new consumer can look at each mutation and emit mutations on specified key ranges. + +### Integration with existing taskbucket based backup command interface + +We strive to keep the operational interface the same as the old backup system. That is, the new backup is initiated by the client as before with an additional flag. FDB cluster receives the backup request, sees the flag being set, and uses the new system for generating mutation logs. + +By default, backup workers are not enabled in the system. When operators submit a new backup request for the first time, the database performs a configuration change (`backup_worker_enabled:=1`) that enables backup workers. + +The operator’s backup request can indicate if an old backup or a new backup is used. This is a command line option (i.e., `-p` or `--partitioned_log`) in the `fdbbackup` command. A backup request of the new type is started in the following steps: + +1. Operators use `fdbbackup` tool to write the backup range to a system key, i.e., `\xff\x02/backupStarted`. +2. All backup workers monitor the key `\xff\x02/backupStarted`, see the change, and start logging mutations. +3. After all backup workers have started, the `fdbbackup` tool initiates the backup of all or specified key ranges by issuing a transaction `Ts`. + +Compared to the old backup system, the above step 1 and 2 are new and is only triggered if client requests for a new type of backup. The purpose is to allow backup workers to function as no-op if there are no ongoing backups. However, the backup workers should still continuously pop their corresponding tags, otherwise mutations will be kept in the TLog. In order to know the version to pop, backup workers can obtain the read version from any proxy. Because the read version must be a committed version, so popping to this version is safe. + +**Backup Submission Protocol** +Protocol for `submitBackup()` to ensure that all backup workers of the current epoch have started logging mutations: + +1. After the `submitBackup()` call, the task bucket (i.e., `StartFullBackupTaskFunc`) starts by creating a `BackupConfig` object in the system key space. +2. Each backup worker monitors the `\xff\x02/backupStarted` key and notices the new backup job. Then the backup worker inserts the new job into its internal queue, and writes to `startedBackupWorkers` key in the `BackupConfig` object if the worker’s `backupEpoch` is the current epoch. Among these workers, the worker with Log Router Tag `-2:0` monitors the `startedBackupWorkers` key, and sets `allWorkerStarted` key after all workers have updated the `startedBackupWorkers` key. +3. The task bucket watches change to the `startedBackupWorkers` key and declares the job submission successful. + +This protocol was implemented after another abandoned protocol: the `startedBackupWorkers` key is set after all backup workers have saved logs with versions larger than the version of `submitBackup()` call. This protocol fails if there is already a backup job and there is a backup worker that doesn’t notice the change to the `\xff\x02/backupStarted` key. As a result, the worker is saving versions larger than the new job’s start version, but in the old backup container. Thus the new container misses some mutations. + +**Protocol for Determining A Backup is Restorable** + +1. Each backup worker independently logs mutations to a backup container and updates its progress in the system key space. +2. The worker with Log Router Tag `-2:0` of current epoch monitors all workers’ progress. If the oldest backup epoch is the current epoch (i.e, there are no backup workers for any old epochs, thus no version ranges missing before this epoch), this worker updates `latestBackupWorkerSavedVersion` key in the `BackupConfig` object with the minimum saved version among workers. +3. The client calls `describeBackup()`, which eventually calls `getLatestRestorableVersion` to read the value from the `latestBackupWorkerSavedVersion` key. If this version is larger than the first snapshot’s end version, then the backup is restorable. + +**Pause and Resume Backups** +The command line for pause or resume backups remains the same, but the implementation for the new backup system is different from the old one. This is because in the old backup system, both mutation logs and range logs are handled by `TaskBucket`, an asynchronous task scheduling framework that stores states in the FDB database. Thus, the old backup system simply pauses or resumes the `TaskBucket`. In the new backup system, mutation logs are generated by backup workers, thus the pause or resume command needs to tell all backup workers to pause or resume pulling mutations from TLogs. Specifically, + +1. The operator issues a pause or resume request that upates both the `TaskBucket` and `\xff\x02/backupPaused` key. +2. Each backup worker monitors the `\xff\x02/backupPaused` key and notices the change. Then the backup worker pauses or resumes pulling from TLogs. + +**Backup Container Changes** + +* Partitioned mutation logs are stored in `plogs/XXXX/XXXX` directory and their names are in the format of `log,[startVersion],[endVersion],[UID],[N-of-M],[blockSize]`, where `M` is total partition number, `N` can be any number from `0` to `M - 1`. In contrast, old mutation logs are stored in `logs/XXXX/XXXX` directory and are named differently. +* To restore a version range, all partitioned logs for the range needs to be available. The restore process should read all partitioned logs, and combine mutations from different logs into one mutation stream, ordered by `(commit_version, subsequence)` pair. It is guaranteed that all mutations form a total order. Note in the old backup files, there is no subsequence number, as each version’s mutations are serialized in order in one file. + +### Integration with the [Performant Restore System](https://github.com/apple/foundationdb/issues/1049) + +As discussed above, the new backup system split mutation logs into multiple partitions. Thus, the restore process must verify the backup files are continuous for all partitions with the restore’s version range. This is possible because each log file name has the information about its partition number and the total number of partitions. + +Once the restore system verifies the version range is continuous, the restore system needs to filter out duplicated version range among different log files (both log continuity analysis and dedup logic are implemented in `BackupContainer` abstraction). A given version range may be stored in **multiple** mutation log files. This can happen because a recruited backup worker can upload mutation files successfully, but doesn’t save the progress before another recovery happens. As a result, the new epoch tries to backup this version range again, producing the same version ranges (though the file names are different). + +Finally, the restore system loads the same version’s mutations from all partitions, and then merges these mutations in the order of their subsequence number before they are applied on the restore cluster. Note the mutations in the old backup system lack subsequence numbers. As a result, restoring old backups needs to assign subsequence number to mutations. + +## Ordered and Complete Guarantee of Mutation Logs + +The backup system must generate log files that the restore system can apply all the mutations on the backup cluster in the same order exactly once. + +**Ordering guarantee**. To maintain the ordering of mutations, each mutation is stored with its commit version and a subsequence number, both are assigned by Proxies during commit. The restore system can load all mutations and derive a total order among all the mutations. + +**Completeness guarantee**. All mutations should be saved in log files. We cannot allow any mutations missing from the backup. This is guaranteed by the fault tolerance discussed below. Essentially all backup workers checkpoint their progress in the database. After the recovery, the new master reads previous checkpoints and recruit new backup workers for any missing version ranges. + +## Backup File Format + +The old backup file format is documented [here](https://github.com/apple/foundationdb/blob/release-6.2/design/backup-dataFormat.md). We can’t use this file format, because our backup files are created for log router tags. When there are more than one log routers (almost always the case), the mutations in one transaction can be given different log router tags. As a result, for the same version, mutations are distributed in many files. Another subtle issue is that, there can be two mutations, (e.g., `a = 1` and `a = 2` in a transaction), which are given two different tags. We have to preserve the order of these two mutations in the restore process. Even though the order is saved in the sub-sequence number of a version, we still need to merge mutations from multiple files and apply them in the correct order. + +In the new backup system, mutation log file is named as `log,[startVersion],[endVersion],[UID],[N-of-M],[blockSize]`, where `startVersion` is inclusive and `endVersion` is *not* inclusive, e.g., `log,332850851,332938927,7be23c0a3e80df8ab1530fa76fa66980,1-of-4,1048576`. With the information from all file names, the restore process can find all files for a version range, i.e., versions intersect with the range and all log router tags. “`M`” is the total number of tags, and “`N`” is from `0` to `m - 1`.Note `tagId` is not required in the old backup filename, since all mutations for a version are included in one file. + +Each file content is a list of fixed size blocks. Each block contains a sequence of mutations, where each mutation consists of a serialized `Version`, `int32_t`, `int32_t`, (all these three numbers are in big endian) and `Mutation`, where `Mutation` is of format `type|kLen|vLen|Key|Value`, where `type` is the mutation type (e.g., `Set` or `Clear`), `kLen` and `vLen` respectively are the lengths of the key and value in the mutation. `Key` and `Value` are the serialized value of the Key and Value in the mutation. The paddings at the end of the block are bytes of `0xFF`. + +``` +`` +`` +`` +`…` +` +` +``` + +Note the big Endianness for version is required, as `0xFF` is used as the padding to indicate block end. A little endian number can easily be mistaken as the end. In contrast, big endian for version almost guarantee the first byte is not `0xFF` (should always be `0x00`). + +## Performance optimization + +### Future Optimizations + +Add a metadata file describe the backup file: + +* The number of mutations; +* The number of atomic operations; +* key range and version range of mutations in each backup file; + +The information can be used to optimize the restore process. For instance, the number of mutations can be used to make better load balancing decisions; if there is no atomic operations, the restore can apply mutation in a backward fashion -- skipping mutations with earlier versions. + +## Fault Tolerance + +Failures of a backup worker will trigger a master recovery. After the recovery, the new master recruits a new set of backup workers. Among them, a new backup worker shall continue the work of the failed backup worker from the previous epoch. + +The interesting part is the handling of old epochs, since the backup workers for the old epoch are in the “displaced” state and should exit. So the basic idea is that we need a set of backup workers for the data left in the old epochs. To figure out the set of data not backed up yet, the master first loads saved backup progress data ` `from the database, and then computes for each epoch, what version ranges have not been backed up. For each of the version range and tag, master recruit a worker to resume the backup for that version range and tag. Note that this worker has a different worker UID from the worker in the original epoch. As a result, for a given epoch and a tag, there might be multiple progress status, as these workers are recruited at different epochs. + +## KPI's and Metrics + +The backup system emits the following metrics: + +* How much backup data has been processed: the backup command line tool `fdbbackup` can show the status of backup, including the size of mutation logs (`LogBytes written`) and snapshots (`RangeBytes written`). By taking two consecutive backup status, the backup speed can be estimated as (`2nd_LogBytes - 1st_LogBytes) / interval`. +* An estimation of backup delay: Each backup worker emits `BackupWorkerMetrics` trace events every 5 seconds, which includes `SavedVersion`, `MinKnownCommittedVersion`, and `MsgQ`. The backup delay can be estimated as (`MinKnownCommittedVersion - SavedVersion) / 1,000,000` seconds, which is the difference between a worker’s saved version and current committed version, divided by 1M version per second. `MsgQ` is the queue size of memory buffer of the backup worker. + +## Controlling Properties + +System operator can control the following backup properties: + +* **Backup key ranges**: The non-overlapped key ranges that will be backed up to the blob storage. +* **Blob url**: The root path in blob that host all backup files. +* **Performance knobs**: The knobs that control the performance + * The backup interval (knob `BACKUP_UPLOAD_DELAY`) for saving mutation logs to blob storage; + +## Testing + +The feature will be tested both in simulation and in real clusters: + +* New test cases are added into the test folder in FDB. The nightly correctness (i.e., simulation) tests will test the correctness of both backup and restore. +* Tests will be added to constantly backup a cluster with the new backup system and restore the backup to ensure the restore works on real clusters. During the time period of active backup, the cluster should have better write performance than using old backup system. +* Tests should also be conducted with production data. This ensures backup data is restorable and catches potential bugs in backup and restore. This test is preferably conducted regularly, e.g., weekly per cluster. + +Before the restore system is available, the testing strategy for backup files is to keep old backup system running. Thus, both new backup files and old backup files are generated. Then both types of log files are decoded and compared against. The new backup file is considered correct if its content matches the content of old log files. From 68906bf3c3c203135fa8432407986c81730e2685 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 22 Apr 2020 14:36:41 -0700 Subject: [PATCH 1510/1604] fix compile errors --- fdbserver/TLogServer.actor.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fdbserver/TLogServer.actor.cpp b/fdbserver/TLogServer.actor.cpp index e3f478f4c2..eca47510d3 100644 --- a/fdbserver/TLogServer.actor.cpp +++ b/fdbserver/TLogServer.actor.cpp @@ -494,6 +494,8 @@ struct LogData : NonCopyable, public ReferenceCounted { double queueMax; double blockTime; double blockMax; + double workTime; + double workMax; int64_t unblockedPeeks; double idleTime; @@ -512,6 +514,8 @@ struct LogData : NonCopyable, public ReferenceCounted { queueMax = 0; blockTime = 0; blockMax = 0; + workTime = 0; + workMax = 0; unblockedPeeks = 0; idleTime = 0; idleMax = 0; @@ -1401,7 +1405,7 @@ ACTOR Future tLogPeekMessages( TLogData* self, TLogPeekRequest req, Refere trackerData.unblockedPeeks++; double t = now() - trackerData.lastUpdate; if(t > trackerData.idleMax) trackerData.idleMax = t; - trackData.idleTime += t; + trackerData.idleTime += t; } trackerData.lastUpdate = now(); std::pair prevPeekData = wait(fPrevPeekData); From 9606a9f11417d1f3c37d7c876de8b65f29369ff4 Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Wed, 22 Apr 2020 14:40:23 -0700 Subject: [PATCH 1511/1604] Minor format fix --- design/backup_v2_partitioned_logs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/design/backup_v2_partitioned_logs.md b/design/backup_v2_partitioned_logs.md index ef5a118a2a..2fb6528baf 100644 --- a/design/backup_v2_partitioned_logs.md +++ b/design/backup_v2_partitioned_logs.md @@ -69,7 +69,7 @@ A command line tool `fdbconvert` has been written to convert new backup logs int * How to start a new type backup: e.g., ``` - fdbbackup start -C fdb.cluster **-****p** -d blob_url + fdbbackup start -C fdb.cluster -p -d blob_url ``` ### KPI's and Health From 0a1b2a572f039d5c03713a305aa8189eb5061574 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 22 Apr 2020 14:41:17 -0700 Subject: [PATCH 1512/1604] more compile fixes --- fdbserver/TLogServer.actor.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fdbserver/TLogServer.actor.cpp b/fdbserver/TLogServer.actor.cpp index eca47510d3..e731699eca 100644 --- a/fdbserver/TLogServer.actor.cpp +++ b/fdbserver/TLogServer.actor.cpp @@ -2003,11 +2003,11 @@ ACTOR Future logPeekTrackers( LogData* logData ) { std::vector peekCounts; peekCounts.reserve(logData->peekTracker.size()); for( auto& it : logData->peekTracker ) { - peekCounts.push_back(it.totalPeeks); + peekCounts.push_back(it.second.totalPeeks); } size_t pivot = peekCounts.size()-SERVER_KNOBS->PEEK_LOGGING_AMOUNT; std::nth_element(peekCounts.begin(), peekCounts.begin()+pivot, peekCounts.end()); - logThreshold = std::max(1,peekCounts[pivot]); + logThreshold = std::max(1,peekCounts[pivot]); } int logCount = 0; for( auto& it : logData->peekTracker ) { @@ -2028,7 +2028,7 @@ ACTOR Future logPeekTrackers( LogData* logData ) { .detail("BlockSeconds", it.second.blockTime) .detail("BlockMax", it.second.blockMax) .detail("WorkSeconds", it.second.workTime) - .detail("WorkMax", it.second.workMax) + .detail("WorkMax", it.second.workMax); it.second.resetMetrics(); } } From 46ec766cab71db3bad249b511f381ff5a974493d Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Wed, 22 Apr 2020 16:11:46 -0700 Subject: [PATCH 1513/1604] FastRestore:Disable debug trace --- fdbserver/RestoreUtil.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fdbserver/RestoreUtil.h b/fdbserver/RestoreUtil.h index 733083d7b8..8879f1e2df 100644 --- a/fdbserver/RestoreUtil.h +++ b/fdbserver/RestoreUtil.h @@ -35,8 +35,8 @@ #include #include -//#define SevFRMutationInfo SevVerbose -#define SevFRMutationInfo SevInfo +#define SevFRMutationInfo SevVerbose +//#define SevFRMutationInfo SevInfo using MutationsVec = Standalone>; using LogMessageVersionVec = Standalone>; From 3a5315d10cdae98154b47f5c2f892839eefd3953 Mon Sep 17 00:00:00 2001 From: Vishesh Yadav Date: Wed, 22 Apr 2020 19:38:01 -0700 Subject: [PATCH 1514/1604] FlowTransport: Don't immediately mark connections failed In connectionKeeper(), when a connection is failed for FAILURE_DETECTION_DELAY, then only mark connection as failed. This is much closer to the original centralized behaviour, and also adds more confidence on whether the connection is actually failed. --- fdbrpc/FailureMonitor.actor.cpp | 3 --- fdbrpc/FlowTransport.actor.cpp | 43 +++++++++++++++++++++++---------- fdbrpc/FlowTransport.h | 5 +--- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/fdbrpc/FailureMonitor.actor.cpp b/fdbrpc/FailureMonitor.actor.cpp index 7d985fe854..fcea9ec014 100644 --- a/fdbrpc/FailureMonitor.actor.cpp +++ b/fdbrpc/FailureMonitor.actor.cpp @@ -33,9 +33,6 @@ ACTOR Future waitForContinuousFailure(IFailureMonitor* monitor, Endpoint e double sustainedFailureDuration, double slope) { state double startT = now(); - // Since, FailureMonitoring is now localized we should add some slack for `connectionKeeper` - // to try reconnecting. - sustainedFailureDuration += FLOW_KNOBS->FAILURE_DETECTION_DELAY; loop { wait(monitor->onFailed(endpoint)); if (monitor->permanentlyFailed(endpoint)) return Void(); diff --git a/fdbrpc/FlowTransport.actor.cpp b/fdbrpc/FlowTransport.actor.cpp index 219aafc1a1..915f744bef 100644 --- a/fdbrpc/FlowTransport.actor.cpp +++ b/fdbrpc/FlowTransport.actor.cpp @@ -437,6 +437,8 @@ ACTOR Future connectionKeeper( Reference self, ASSERT_WE_THINK(FlowTransport::transport().getLocalAddress() != self->destination); state Optional firstConnFailedTime = Optional(); + state int retryConnect = false; + loop { try { state Future delayedHealthUpdateF = Future(); @@ -445,12 +447,13 @@ ACTOR Future connectionKeeper( Reference self, self->outgoingConnectionIdle = true; // Wait until there is something to send. while (self->unsent.empty()) { - if (self->destination.isPublic() && - IFailureMonitor::failureMonitor().getState(self->destination).isFailed()) { - break; - } + // Override waiting, if we are in failed state to update failure monitoring status. + Future retryConnectF = retryConnect ? delay(FLOW_KNOBS->SERVER_REQUEST_INTERVAL) : Never(); - wait (self->dataToSend.onTrigger()); + choose { + when(wait(self->dataToSend.onTrigger())) {} + when(wait(retryConnectF)) { break; } + } } ASSERT(self->destination.isPublic()); @@ -480,6 +483,7 @@ ACTOR Future connectionKeeper( Reference self, when(wait(delayedHealthUpdateF)) { conn->close(); conn = Reference(); + retryConnect = false; continue; } when(wait(self->dataToSend.onTrigger())) {} @@ -546,6 +550,18 @@ ACTOR Future connectionKeeper( Reference self, firstConnFailedTime = now(); } + // Don't immediately mark connection as failed. To stay closed to earlier behaviour of centralized + // failure monitoring, wait until connection stays failed for FLOW_KNOBS->FAILURE_DETECTION_DELAY timeout. + retryConnect = self->destination.isPublic() && e.code() == error_code_connection_failed; + if (e.code() == error_code_connection_failed) { + if (!self->destination.isPublic()) { + // Can't connect back to non-public addresses. + IFailureMonitor::failureMonitor().setStatus(self->destination, FailureStatus(true)); + } else if (now() - firstConnFailedTime.get() > FLOW_KNOBS->FAILURE_DETECTION_DELAY) { + IFailureMonitor::failureMonitor().setStatus(self->destination, FailureStatus(true)); + } + } + self->discardUnreliablePackets(); reader = Future(); bool ok = e.code() == error_code_connection_failed || e.code() == error_code_actor_cancelled || @@ -566,10 +582,6 @@ ACTOR Future connectionKeeper( Reference self, .detail("PeerAddr", self->destination); } - if (e.code() == error_code_connection_failed) { - IFailureMonitor::failureMonitor().setStatus(self->destination, FailureStatus(true)); - } - if(self->destination.isPublic() && IFailureMonitor::failureMonitor().getState(self->destination).isAvailable() && !FlowTransport::transport().isClient()) @@ -605,13 +617,20 @@ ACTOR Future connectionKeeper( Reference self, TraceEvent("PeerDestroy").error(e).suppressFor(1.0).detail("PeerAddr", self->destination); self->connect.cancel(); self->transport->peers.erase(self->destination); - IFailureMonitor::failureMonitor().setStatus(self->destination, FailureStatus(true)); return Void(); } } } } +Peer::Peer(TransportData* transport, NetworkAddress const& destination) + : transport(transport), destination(destination), outgoingConnectionIdle(true), lastConnectTime(0.0), + reconnectionDelay(FLOW_KNOBS->INITIAL_RECONNECTION_TIME), compatible(true), outstandingReplies(0), + incompatibleProtocolVersionNewer(false), peerReferences(-1), bytesReceived(0), lastDataPacketSentTime(now()) { + + IFailureMonitor::failureMonitor().setStatus(destination, FailureStatus(false)); +} + void Peer::send(PacketBuffer* pb, ReliablePacket* rp, bool firstUnsent) { unsent.setWriteBuffer(pb); if (rp) reliable.insert(rp); @@ -1163,9 +1182,7 @@ void FlowTransport::addPeerReference(const Endpoint& endpoint, bool isStream) { return; Reference peer = self->getOrOpenPeer(endpoint.getPrimaryAddress()); - - if(peer->peerReferences == -1) { - IFailureMonitor::failureMonitor().setStatus(endpoint.getPrimaryAddress(), FailureStatus(false)); + if (peer->peerReferences == -1) { peer->peerReferences = 1; } else { peer->peerReferences++; diff --git a/fdbrpc/FlowTransport.h b/fdbrpc/FlowTransport.h index 3fd39cadeb..597fcab626 100644 --- a/fdbrpc/FlowTransport.h +++ b/fdbrpc/FlowTransport.h @@ -124,10 +124,7 @@ struct Peer : public ReferenceCounted { double lastDataPacketSentTime; int outstandingReplies; - explicit Peer(TransportData* transport, NetworkAddress const& destination) - : transport(transport), destination(destination), outgoingConnectionIdle(true), lastConnectTime(0.0), - reconnectionDelay(FLOW_KNOBS->INITIAL_RECONNECTION_TIME), compatible(true), outstandingReplies(0), - incompatibleProtocolVersionNewer(false), peerReferences(-1), bytesReceived(0), lastDataPacketSentTime(now()) {} + explicit Peer(TransportData* transport, NetworkAddress const& destination); void send(PacketBuffer* pb, ReliablePacket* rp, bool firstUnsent); From b1f525583a8c056491aa1c8d21b2cf44c2c07da9 Mon Sep 17 00:00:00 2001 From: tclinken Date: Wed, 22 Apr 2020 21:53:42 -0700 Subject: [PATCH 1515/1604] Added -Wclass-memaccess compiler option and fixed warnings --- cmake/ConfigureCompiler.cmake | 3 ++- fdbclient/FDBTypes.h | 5 +++++ fdbserver/DiskQueue.actor.cpp | 2 +- fdbserver/VFSAsync.cpp | 2 +- flow/Arena.h | 30 +++++++++++++++++++----------- 5 files changed, 28 insertions(+), 14 deletions(-) diff --git a/cmake/ConfigureCompiler.cmake b/cmake/ConfigureCompiler.cmake index 5f263788b2..d291d1076d 100644 --- a/cmake/ConfigureCompiler.cmake +++ b/cmake/ConfigureCompiler.cmake @@ -243,7 +243,8 @@ else() -Wno-deprecated -fvisibility=hidden -Wreturn-type - -fPIC) + -fPIC + -Wclass-memaccess) if (GPERFTOOLS_FOUND AND GCC) add_compile_options( -fno-builtin-malloc diff --git a/fdbclient/FDBTypes.h b/fdbclient/FDBTypes.h index 7d765392fd..a638644139 100644 --- a/fdbclient/FDBTypes.h +++ b/fdbclient/FDBTypes.h @@ -26,6 +26,7 @@ #include #include +#include "flow/Arena.h" #include "flow/flow.h" #include "fdbclient/Knobs.h" @@ -77,6 +78,10 @@ struct Tag { serializer(ar, locality, id); } }; + +template <> +struct non_flow_ref : std::integral_constant {}; + #pragma pack(pop) template void load( Ar& ar, Tag& tag ) { tag.serialize_unversioned(ar); } diff --git a/fdbserver/DiskQueue.actor.cpp b/fdbserver/DiskQueue.actor.cpp index 1f38dfb8ee..9ec422ad7c 100644 --- a/fdbserver/DiskQueue.actor.cpp +++ b/fdbserver/DiskQueue.actor.cpp @@ -1013,7 +1013,7 @@ private: ASSERT( nextPageSeq%sizeof(Page)==0 ); auto& p = backPage(); - memset(&p, 0, sizeof(Page)); // FIXME: unnecessary? + memset(static_cast(&p), 0, sizeof(Page)); // FIXME: unnecessary? p.magic = 0xFDB; switch (diskQueueVersion) { case DiskQueueVersion::V0: diff --git a/fdbserver/VFSAsync.cpp b/fdbserver/VFSAsync.cpp index 3d53aaccfb..0a1feff976 100644 --- a/fdbserver/VFSAsync.cpp +++ b/fdbserver/VFSAsync.cpp @@ -531,7 +531,7 @@ static int asyncOpen( if (flags & SQLITE_OPEN_WAL) oflags |= IAsyncFile::OPEN_LARGE_PAGES; oflags |= IAsyncFile::OPEN_LOCK; - memset(p, 0, sizeof(VFSAsyncFile)); + memset(static_cast(p), 0, sizeof(VFSAsyncFile)); new (p) VFSAsyncFile(zName, flags); try { // Note that SQLiteDB::open also opens the db file, so its flags and modes are important, too diff --git a/flow/Arena.h b/flow/Arena.h index 74a29c8b82..c61d9d9c85 100644 --- a/flow/Arena.h +++ b/flow/Arena.h @@ -697,11 +697,19 @@ inline bool operator >= ( const StringRef& lhs, const StringRef& rhs ) { return // FIXME: VectorRef really should use std::is_trivially_copyable for this BUT that is not implemented // in gcc c++0x so instead we will use this custom trait which defaults to std::is_trivial, which // handles most situations but others will have to be specialized. + +// This trait is used by VectorRef to determine if deep copy constructor should recursively +// call deep copies of each element. +// TODO: There should be an easier way to identify the difference between +// flow_ref and non-flow_ref types. template -struct memcpy_able : std::is_trivial {}; +struct non_flow_ref : std::is_fundamental {}; template <> -struct memcpy_able : std::integral_constant {}; +struct non_flow_ref : std::integral_constant {}; + +template +struct non_flow_ref> : std::integral_constant {}; template struct string_serialized_traits : std::false_type { @@ -792,19 +800,19 @@ public: return *this; } - // Arena constructor for non-Ref types, identified by memcpy_able + // Arena constructor for non-Ref types, identified by non_flow_ref template - VectorRef(Arena& p, const VectorRef& toCopy, typename std::enable_if::value, int>::type = 0) + VectorRef(Arena& p, const VectorRef& toCopy, typename std::enable_if::value, int>::type = 0) : VPS(toCopy), data((T*)new (p) uint8_t[sizeof(T) * toCopy.size()]), m_size(toCopy.size()), m_capacity(toCopy.size()) { if (m_size > 0) { - memcpy(data, toCopy.data, m_size * sizeof(T)); + std::copy(toCopy.data, toCopy.data + m_size, data); } } // Arena constructor for Ref types, which must have an Arena constructor template - VectorRef(Arena& p, const VectorRef& toCopy, typename std::enable_if::value, int>::type = 0) + VectorRef(Arena& p, const VectorRef& toCopy, typename std::enable_if::value, int>::type = 0) : VPS(), data((T*)new (p) uint8_t[sizeof(T) * toCopy.size()]), m_size(toCopy.size()), m_capacity(toCopy.size()) { for (int i = 0; i < m_size; i++) { auto ptr = new (&data[i]) T(p, toCopy[i]); @@ -894,7 +902,7 @@ public: if (m_size + count > m_capacity) reallocate(p, m_size + count); VPS::invalidate(); if (count > 0) { - memcpy(data + m_size, begin, sizeof(T) * count); + std::copy(begin, begin + count, data + m_size); } m_size += count; } @@ -934,15 +942,15 @@ public: if (size > m_capacity) reallocate(p, size); } - // expectedSize() for non-Ref types, identified by memcpy_able + // expectedSize() for non-Ref types, identified by non_flow_ref template - typename std::enable_if::value, size_t>::type expectedSize() const { + typename std::enable_if::value, size_t>::type expectedSize() const { return sizeof(T) * m_size; } // expectedSize() for Ref types, which must in turn have expectedSize() implemented. template - typename std::enable_if::value, size_t>::type expectedSize() const { + typename std::enable_if::value, size_t>::type expectedSize() const { size_t t = sizeof(T) * m_size; for (int i = 0; i < m_size; i++) t += data[i].expectedSize(); return t; @@ -961,7 +969,7 @@ private: // SOMEDAY: Maybe we are right at the end of the arena and can expand cheaply T* newData = (T*)new (p) uint8_t[requiredCapacity * sizeof(T)]; if (m_size > 0) { - memcpy(newData, data, m_size * sizeof(T)); + std::copy(data, data + m_size, newData); } data = newData; m_capacity = requiredCapacity; From 91fba9106dcdf5b42cb8b953a11584a5d58e4dec Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 22 Apr 2020 23:35:48 -0700 Subject: [PATCH 1516/1604] ported peek metrics to old tlog 6.0 --- fdbserver/OldTLogServer_6_0.actor.cpp | 112 +++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-) diff --git a/fdbserver/OldTLogServer_6_0.actor.cpp b/fdbserver/OldTLogServer_6_0.actor.cpp index 564bb96370..c071f03033 100644 --- a/fdbserver/OldTLogServer_6_0.actor.cpp +++ b/fdbserver/OldTLogServer_6_0.actor.cpp @@ -384,6 +384,43 @@ struct LogData : NonCopyable, public ReferenceCounted { struct PeekTrackerData { std::map>> sequence_version; double lastUpdate; + + Tag tag; + + double lastLogged; + int64_t totalPeeks; + int64_t replyBytes; + int64_t duplicatePeeks; + double queueTime; + double queueMax; + double blockTime; + double blockMax; + double workTime; + double workMax; + + int64_t unblockedPeeks; + double idleTime; + double idleMax; + + PeekTrackerData() : lastUpdate(0) { + resetMetrics(); + } + + void resetMetrics() { + lastLogged = now(); + totalPeeks = 0; + replyBytes = 0; + duplicatePeeks = 0; + queueTime = 0; + queueMax = 0; + blockTime = 0; + blockMax = 0; + workTime = 0; + workMax = 0; + unblockedPeeks = 0; + idleTime = 0; + idleMax = 0; + } }; std::map peekTracker; @@ -1032,6 +1069,7 @@ ACTOR Future tLogPeekMessages( TLogData* self, TLogPeekRequest req, Refere state BinaryWriter messages2(Unversioned()); state int sequence = -1; state UID peekId; + state double queueStart = now(); if(req.sequence.present()) { try { @@ -1042,6 +1080,7 @@ ACTOR Future tLogPeekMessages( TLogData* self, TLogPeekRequest req, Refere } auto& trackerData = logData->peekTracker[peekId]; if (sequence == 0 && trackerData.sequence_version.find(0) == trackerData.sequence_version.end()) { + trackerData.tag = req.tag; trackerData.sequence_version[0].send(std::make_pair(req.begin, req.onlySpilled)); } auto seqBegin = trackerData.sequence_version.begin(); @@ -1057,8 +1096,16 @@ ACTOR Future tLogPeekMessages( TLogData* self, TLogPeekRequest req, Refere throw timed_out(); } + Future> fPrevPeekData = trackerData.sequence_version[sequence].getFuture(); + if(fPrevPeekData.isReady()) { + trackerData.unblockedPeeks++; + double t = now() - trackerData.lastUpdate; + if(t > trackerData.idleMax) trackerData.idleMax = t; + trackerData.idleTime += t; + } trackerData.lastUpdate = now(); - std::pair prevPeekData = wait(trackerData.sequence_version[sequence].getFuture()); + std::pair prevPeekData = wait(fPrevPeekData); + req.begin = prevPeekData.first; req.onlySpilled = prevPeekData.second; wait(yield()); @@ -1072,6 +1119,8 @@ ACTOR Future tLogPeekMessages( TLogData* self, TLogPeekRequest req, Refere } } + state double blockStart = now(); + if( req.returnIfBlocked && logData->version.get() < req.begin ) { req.reply.sendError(end_of_stream()); if(req.sequence.present()) { @@ -1106,6 +1155,8 @@ ACTOR Future tLogPeekMessages( TLogData* self, TLogPeekRequest req, Refere wait(delay(0, TaskPriority::TLogSpilledPeekReply)); } + state double workStart = now(); + Version poppedVer = poppedVersion(logData, req.tag); if(poppedVer > req.begin) { TLogPeekReply rep; @@ -1194,6 +1245,22 @@ ACTOR Future tLogPeekMessages( TLogData* self, TLogPeekRequest req, Refere if(req.sequence.present()) { auto& trackerData = logData->peekTracker[peekId]; trackerData.lastUpdate = now(); + + double queueT = blockStart-queueStart; + double blockT = workStart-blockStart; + double workT = now()-workStart; + + trackerData.totalPeeks++; + trackerData.replyBytes += reply.messages.size(); + + if(queueT > trackerData.queueMax) trackerData.queueMax = queueT; + if(blockT > trackerData.blockMax) trackerData.blockMax = blockT; + if(workT > trackerData.workMax) trackerData.workMax = workT; + + trackerData.queueTime += queueT; + trackerData.blockTime += blockT; + trackerData.workTime += workT; + auto& sequenceData = trackerData.sequence_version[sequence+1]; if(trackerData.sequence_version.size() && sequence+1 < trackerData.sequence_version.begin()->first) { req.reply.sendError(timed_out()); @@ -1202,6 +1269,7 @@ ACTOR Future tLogPeekMessages( TLogData* self, TLogPeekRequest req, Refere return Void(); } if(sequenceData.isSet()) { + trackerData.duplicatePeeks++; if(sequenceData.getFuture().get().first != reply.end) { TEST(true); //tlog peek second attempt ended at a different version req.reply.sendError(timed_out()); @@ -1537,6 +1605,47 @@ ACTOR Future cleanupPeekTrackers( LogData* logData ) { } } +ACTOR Future logPeekTrackers( LogData* logData ) { + loop { + int64_t logThreshold = 1; + if(logData->peekTracker.size() > SERVER_KNOBS->PEEK_LOGGING_AMOUNT) { + std::vector peekCounts; + peekCounts.reserve(logData->peekTracker.size()); + for( auto& it : logData->peekTracker ) { + peekCounts.push_back(it.second.totalPeeks); + } + size_t pivot = peekCounts.size()-SERVER_KNOBS->PEEK_LOGGING_AMOUNT; + std::nth_element(peekCounts.begin(), peekCounts.begin()+pivot, peekCounts.end()); + logThreshold = std::max(1,peekCounts[pivot]); + } + int logCount = 0; + for( auto& it : logData->peekTracker ) { + if(it.second.totalPeeks >= logThreshold) { + logCount++; + TraceEvent("PeekMetrics", logData->logId) + .detail("Tag", it.second.tag.toString()) + .detail("Elapsed", now() - it.second.lastLogged) + .detail("MeanReplyBytes", it.second.replyBytes/it.second.totalPeeks) + .detail("TotalPeeks", it.second.totalPeeks) + .detail("UnblockedPeeks", it.second.unblockedPeeks) + .detail("DuplicatePeeks", it.second.duplicatePeeks) + .detail("Sequence", it.second.sequence_version.size() ? it.second.sequence_version.begin()->first : -1) + .detail("IdleSeconds", it.second.idleTime) + .detail("IdleMax", it.second.idleMax) + .detail("QueueSeconds", it.second.queueTime) + .detail("QueueMax", it.second.queueMax) + .detail("BlockSeconds", it.second.blockTime) + .detail("BlockMax", it.second.blockMax) + .detail("WorkSeconds", it.second.workTime) + .detail("WorkMax", it.second.workMax); + it.second.resetMetrics(); + } + } + + wait( delay(SERVER_KNOBS->PEEK_LOGGING_DELAY * std::max(1,logCount)) ); + } +} + void getQueuingMetrics( TLogData* self, Reference logData, TLogQueuingMetricsRequest const& req ) { TLogQueuingMetricsReply reply; reply.localTime = now(); @@ -1876,6 +1985,7 @@ ACTOR Future tLogCore( TLogData* self, Reference logData, TLogInt logData->addActor.send( traceCounters("TLogMetrics", logData->logId, SERVER_KNOBS->STORAGE_LOGGING_DELAY, &logData->cc, logData->logId.toString() + "/TLogMetrics")); logData->addActor.send( serveTLogInterface(self, tli, logData, warningCollectorInput) ); logData->addActor.send( cleanupPeekTrackers(logData.getPtr()) ); + logData->addActor.send( logPeekTrackers(logData.getPtr()) ); if(!logData->isPrimary) { std::vector tags; From 810bba2067b613d775c9598f00cce7f6164b0bc3 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 22 Apr 2020 23:36:40 -0700 Subject: [PATCH 1517/1604] cleanup calls to FlowTransport::isClient() --- fdbclient/FailureMonitorClient.actor.cpp | 4 ++-- fdbrpc/FlowTransport.actor.cpp | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/fdbclient/FailureMonitorClient.actor.cpp b/fdbclient/FailureMonitorClient.actor.cpp index 7cb1a3144e..16952a755a 100644 --- a/fdbclient/FailureMonitorClient.actor.cpp +++ b/fdbclient/FailureMonitorClient.actor.cpp @@ -167,8 +167,8 @@ ACTOR Future failureMonitorClientLoop( } ACTOR Future failureMonitorClient( Reference>> ci, bool trackMyStatus ) { - TraceEvent("FailureMonitorStart").detail("IsClient", FlowTransport::transport().isClient()); - if (FlowTransport::transport().isClient()) { + TraceEvent("FailureMonitorStart").detail("IsClient", FlowTransport::isClient()); + if (FlowTransport::isClient()) { wait(Never()); } diff --git a/fdbrpc/FlowTransport.actor.cpp b/fdbrpc/FlowTransport.actor.cpp index 5041489d67..81b7104acd 100644 --- a/fdbrpc/FlowTransport.actor.cpp +++ b/fdbrpc/FlowTransport.actor.cpp @@ -295,7 +295,7 @@ static ReliablePacket* sendPacket( TransportData* self, Reference peer, IS ACTOR Future connectionMonitor( Reference peer ) { state Endpoint remotePingEndpoint({ peer->destination }, WLTOKEN_PING_PACKET); loop { - if (!FlowTransport::transport().isClient() && !peer->destination.isPublic() && peer->compatible) { + if (!FlowTransport::isClient() && !peer->destination.isPublic() && peer->compatible) { // Don't send ping messages to clients unless necessary. Instead monitor incoming client pings. // We ignore this block for incompatible clients because pings from server would trigger the // peer->resetPing and prevent 'connection_failed' due to ping timeout. @@ -324,7 +324,7 @@ ACTOR Future connectionMonitor( Reference peer ) { (peer->lastDataPacketSentTime < now() - FLOW_KNOBS->CONNECTION_MONITOR_UNREFERENCED_CLOSE_DELAY)) { // TODO: What about when peerReference == -1? throw connection_unreferenced(); - } else if (FlowTransport::transport().isClient() && peer->compatible && peer->destination.isPublic() && + } else if (FlowTransport::isClient() && peer->compatible && peer->destination.isPublic() && (peer->lastConnectTime < now() - FLOW_KNOBS->CONNECTION_MONITOR_IDLE_TIMEOUT) && (peer->lastDataPacketSentTime < now() - FLOW_KNOBS->CONNECTION_MONITOR_IDLE_TIMEOUT)) { // First condition is necessary because we may get here if we are server. @@ -413,7 +413,7 @@ ACTOR Future connectionKeeper( Reference self, self->outgoingConnectionIdle = true; // Wait until there is something to send. while (self->unsent.empty()) { - if (FlowTransport::transport().isClient() && self->destination.isPublic() && + if (FlowTransport::isClient() && self->destination.isPublic() && clientReconnectDelay) { break; } @@ -434,7 +434,7 @@ ACTOR Future connectionKeeper( Reference self, when( Reference _conn = wait( INetworkConnections::net()->connect(self->destination) ) ) { conn = _conn; wait(conn->connectHandshake()); - if (FlowTransport::transport().isClient()) { + if (FlowTransport::isClient()) { IFailureMonitor::failureMonitor().setStatus(self->destination, FailureStatus(false)); } if (self->unsent.empty()) { @@ -459,7 +459,7 @@ ACTOR Future connectionKeeper( Reference self, throw; } TraceEvent("ConnectionTimedOut", conn ? conn->getDebugID() : UID()).suppressFor(1.0).detail("PeerAddr", self->destination); - if (FlowTransport::transport().isClient()) { + if (FlowTransport::isClient()) { IFailureMonitor::failureMonitor().setStatus(self->destination, FailureStatus(true)); clientReconnectDelay = true; } @@ -511,7 +511,7 @@ ACTOR Future connectionKeeper( Reference self, if(self->destination.isPublic() && IFailureMonitor::failureMonitor().getState(self->destination).isAvailable() - && !FlowTransport::transport().isClient()) + && !FlowTransport::isClient()) { auto& it = self->transport->closedPeers[self->destination]; if(now() - it.second > FLOW_KNOBS->TOO_MANY_CONNECTIONS_CLOSED_RESET_DELAY) { @@ -526,7 +526,7 @@ ACTOR Future connectionKeeper( Reference self, } if (conn) { - clientReconnectDelay = FlowTransport::transport().isClient() && e.code() != error_code_connection_idle; + clientReconnectDelay = FlowTransport::isClient() && e.code() != error_code_connection_idle; conn->close(); conn = Reference(); } @@ -1107,7 +1107,7 @@ void FlowTransport::addPeerReference(const Endpoint& endpoint, bool isStream) { Reference peer = self->getOrOpenPeer(endpoint.getPrimaryAddress()); if(peer->peerReferences == -1) { - if (FlowTransport::transport().isClient()) { + if (FlowTransport::isClient()) { IFailureMonitor::failureMonitor().setStatus(endpoint.getPrimaryAddress(), FailureStatus(false)); } peer->peerReferences = 1; From 37f9456010b66f2946fdbf17047ceee14f670a2a Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 22 Apr 2020 23:37:29 -0700 Subject: [PATCH 1518/1604] added logging when encountering an inverted range --- fdbclient/FDBTypes.h | 1 + 1 file changed, 1 insertion(+) diff --git a/fdbclient/FDBTypes.h b/fdbclient/FDBTypes.h index a3678bd95c..765d5a69ce 100644 --- a/fdbclient/FDBTypes.h +++ b/fdbclient/FDBTypes.h @@ -242,6 +242,7 @@ struct KeyRangeRef { force_inline void serialize(Ar& ar) { serializer(ar, const_cast(begin), const_cast(end)); if( begin > end ) { + TraceEvent("InvertedRange").detail("Begin", begin).detail("End", end); throw inverted_range(); }; } From a8351236809c3c2c7410384f0f898605feeb4a5f Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 22 Apr 2020 23:38:46 -0700 Subject: [PATCH 1519/1604] exit fdbserver after a ReceiverError, because a packet which should be delivered will never be received --- fdbrpc/FlowTransport.actor.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fdbrpc/FlowTransport.actor.cpp b/fdbrpc/FlowTransport.actor.cpp index 81b7104acd..3f7ba5bc63 100644 --- a/fdbrpc/FlowTransport.actor.cpp +++ b/fdbrpc/FlowTransport.actor.cpp @@ -659,6 +659,9 @@ ACTOR static void deliver(TransportData* self, Endpoint destination, ArenaReader } catch (Error& e) { g_currentDeliveryPeerAddress = {NetworkAddress()}; TraceEvent(SevError, "ReceiverError").error(e).detail("Token", destination.token.toString()).detail("Peer", destination.getPrimaryAddress()); + if(!FlowTransport::isClient()) { + flushAndExit(FDB_EXIT_ERROR); + } throw; } } else if (destination.token.first() & TOKEN_STREAM_FLAG) { From ab46405bf43fe0cf80d0533b9be13fed0b87f986 Mon Sep 17 00:00:00 2001 From: Steve Atherton Date: Thu, 23 Apr 2020 02:33:43 -0700 Subject: [PATCH 1520/1604] Improvement to an edge case where writePages() would unexpectedly return only 1 page but not attempt to reuse the existing BTreePageID for it. --- fdbserver/VersionedBTree.actor.cpp | 40 +++++++++++++----------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index 2713ad44d6..99c0bf30ed 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -3416,15 +3416,6 @@ private: while(i < entries.size() && (i - start < minimumEntries || compressedBytes < pageFillTarget) ) { const RedwoodRecordRef &entry = entries[i]; - // If this is an internal page, first entry, and it has a null value, skip it. - // It only exists to serve as an upper boundary for a child page that has not been rewritten in the - // current commit, and that purpose will now be served by the parent page's upper boundary - if(i == start && height != 1 && !entry.value.present()) { - ++i; - ++start; - continue; - } - // Get delta from previous record or page lower boundary if this is the first item in a page const RedwoodRecordRef &base = (i == start) ? pageLowerBound : entries[i - 1]; @@ -3478,18 +3469,20 @@ private: ++i; } - // If there are no records to write to this page, it is because it would have been an internal page - // with exactly one record which would be childless and so it was skipped above. - if(start == i) { - ASSERT(height != 1); - break; + // Flush the accumulated records to a page + state int nextStart = i; + // If we are building internal pages and there is a record after this page (index nextStart) but it has an empty childPage value then skip it. + // It only exists to serve as an upper boundary for a child page that has not been rewritten in the current commit, and that + // purpose will now be served by the upper bound of the page we are now building. + if(height != 1 && nextStart < entries.size() && !entries[nextStart].value.present()) { + ++nextStart; } - // Flush the accumulated records to a page - state bool isLastPage = (i == entries.size()); - pageUpperBound = isLastPage ? upperBound->withoutValue() : entries[i].withoutValue(); + // Use the next entry as the upper bound, or upperBound if there are no more entries beyond this page + pageUpperBound = (i == entries.size()) ? upperBound->withoutValue() : entries[i].withoutValue(); // If this is a leaf page, and not the last one to be written, shorten the upper boundary + state bool isLastPage = (nextStart == entries.size()); if(!isLastPage && height == 1) { int commonPrefix = pageUpperBound.getCommonPrefixLen(entries[i - 1], 0); pageUpperBound.truncate(commonPrefix + 1); @@ -3541,7 +3534,7 @@ private: state int p; state BTreePageID childPageID; - // If we are only writing 1 page and it has the same BTreePageID size as the original they try to reuse the + // If we are only writing 1 page and it has the same BTreePageID size as the original then try to reuse the // LogicalPageIDs in previousID and try to update them atomically. bool isOnlyPage = isLastPage && (start == 0); if(isOnlyPage && previousID.size() == pages.size()) { @@ -3592,16 +3585,17 @@ private: break; } - start = i; + start = nextStart; kvBytes = 0; compressedBytes = BTreePage::BinaryTree::emptyTreeSize(); pageLowerBound = pageUpperBound; } - // If we're writing internal pages, if pageUpperBound is not upperBound then we ended on an empty page because it contained only one childless internal record - // So, we have to add a childless internal record for that upper bound for the output set so that the parent of these new pages includes it so the child - // page to its left is still decoded correctly. - if(height != 1 && !pageUpperBound.sameExceptValue(*upperBound)) { + // If we're writing internal pages, if the last entry was the start of a new page and had an empty child link then it would not be written to a page. + // This means that the upper boundary for the the page set being built is not the upper bound of the final page in that set, so it must be added + // to the output set to preserve the decodability of the subtree to its left. + // Fortunately, this is easy to detect because the loop above would exit before i has reached the item count. + if(height != 1 && i != entries.size()) { debug_printf("Adding dummy record to avoid writing useless page: %s\n", pageUpperBound.toString(false).c_str()); records.push_back_deep(records.arena(), pageUpperBound); } From d4509090d4c270575e29b12c018a77732a31c652 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Thu, 23 Apr 2020 07:55:44 -0700 Subject: [PATCH 1521/1604] Generate fastrestore_agent symlink in CMake --- fdbbackup/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fdbbackup/CMakeLists.txt b/fdbbackup/CMakeLists.txt index 3c6fd0ef58..b9259935f3 100644 --- a/fdbbackup/CMakeLists.txt +++ b/fdbbackup/CMakeLists.txt @@ -45,11 +45,11 @@ if(NOT OPEN_FOR_IDE) symlink_files( LOCATION packages/bin SOURCE fdbbackup - TARGETS fdbdr dr_agent backup_agent fdbrestore) + TARGETS fdbdr dr_agent backup_agent fdbrestore fastrestore_agent) symlink_files( LOCATION bin SOURCE fdbbackup - TARGETS fdbdr dr_agent backup_agent fdbrestore) + TARGETS fdbdr dr_agent backup_agent fdbrestore fastrestore_agent) endif() if (GPERFTOOLS_FOUND) From c30010f5b8f41628910b877db32ec3fc509f56fe Mon Sep 17 00:00:00 2001 From: Vishesh Yadav Date: Thu, 23 Apr 2020 09:44:30 -0700 Subject: [PATCH 1522/1604] FlowTransport: Increase delay for connecting to failed connections --- fdbrpc/FlowTransport.actor.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fdbrpc/FlowTransport.actor.cpp b/fdbrpc/FlowTransport.actor.cpp index 915f744bef..102db758bf 100644 --- a/fdbrpc/FlowTransport.actor.cpp +++ b/fdbrpc/FlowTransport.actor.cpp @@ -448,7 +448,12 @@ ACTOR Future connectionKeeper( Reference self, // Wait until there is something to send. while (self->unsent.empty()) { // Override waiting, if we are in failed state to update failure monitoring status. - Future retryConnectF = retryConnect ? delay(FLOW_KNOBS->SERVER_REQUEST_INTERVAL) : Never(); + Future retryConnectF = Never(); + if (retryConnect) { + retryConnectF = IFailureMonitor::failureMonitor().getState(self->destination).isAvailable() + ? delay(FLOW_KNOBS->FAILURE_DETECTION_DELAY) + : delay(FLOW_KNOBS->SERVER_REQUEST_INTERVAL); + } choose { when(wait(self->dataToSend.onTrigger())) {} From c551258018a3094ea107a6240f8553bb8f1fb3c6 Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Thu, 23 Apr 2020 10:16:45 -0700 Subject: [PATCH 1523/1604] pass working directory to `fdb-dev` This allows a user to run commands without using the docker shell (for example `fdb-dev ninja`) --- build/gen_dev_docker.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/build/gen_dev_docker.sh b/build/gen_dev_docker.sh index 36e8264953..4ef4ebd2d0 100755 --- a/build/gen_dev_docker.sh +++ b/build/gen_dev_docker.sh @@ -76,6 +76,7 @@ sudo docker run --rm `# delete (temporary) image after return` \\ --cap-add=SYS_PTRACE \\ --security-opt seccomp=unconfined \\ -v "${HOME}:${HOME}" `# Mount home directory` \\ + -w="\$(pwd)" \\ \${ccache_args} \\ ${image} "\$@" EOF From f8ad7ffd916124571b808406e8f7b000d1684850 Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Thu, 23 Apr 2020 10:17:53 -0700 Subject: [PATCH 1524/1604] Use foundationdb-dev docker image --- build/gen_dev_docker.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/gen_dev_docker.sh b/build/gen_dev_docker.sh index 4ef4ebd2d0..89129d5a86 100755 --- a/build/gen_dev_docker.sh +++ b/build/gen_dev_docker.sh @@ -20,7 +20,7 @@ cd ${tmpdir} echo cat <> Dockerfile -FROM foundationdb/foundationdb-build:latest +FROM foundationdb/foundationdb-dev:0.11.1 RUN yum install -y sudo RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers RUN groupadd -g 1100 sudo From 010592a41511f563c69ea8beabe106bb6469843e Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Thu, 23 Apr 2020 10:59:28 -0700 Subject: [PATCH 1525/1604] updated documentation for 6.2.20 --- documentation/sphinx/source/downloads.rst | 24 +++++++++---------- documentation/sphinx/source/release-notes.rst | 10 ++++++++ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/documentation/sphinx/source/downloads.rst b/documentation/sphinx/source/downloads.rst index 9802afd4e5..eaa4e370ff 100644 --- a/documentation/sphinx/source/downloads.rst +++ b/documentation/sphinx/source/downloads.rst @@ -10,38 +10,38 @@ macOS The macOS installation package is supported on macOS 10.7+. It includes the client and (optionally) the server. -* `FoundationDB-6.2.19.pkg `_ +* `FoundationDB-6.2.20.pkg `_ Ubuntu ------ The Ubuntu packages are supported on 64-bit Ubuntu 12.04+, but beware of the Linux kernel bug in Ubuntu 12.x. -* `foundationdb-clients-6.2.19-1_amd64.deb `_ -* `foundationdb-server-6.2.19-1_amd64.deb `_ (depends on the clients package) +* `foundationdb-clients-6.2.20-1_amd64.deb `_ +* `foundationdb-server-6.2.20-1_amd64.deb `_ (depends on the clients package) RHEL/CentOS EL6 --------------- The RHEL/CentOS EL6 packages are supported on 64-bit RHEL/CentOS 6.x. -* `foundationdb-clients-6.2.19-1.el6.x86_64.rpm `_ -* `foundationdb-server-6.2.19-1.el6.x86_64.rpm `_ (depends on the clients package) +* `foundationdb-clients-6.2.20-1.el6.x86_64.rpm `_ +* `foundationdb-server-6.2.20-1.el6.x86_64.rpm `_ (depends on the clients package) RHEL/CentOS EL7 --------------- The RHEL/CentOS EL7 packages are supported on 64-bit RHEL/CentOS 7.x. -* `foundationdb-clients-6.2.19-1.el7.x86_64.rpm `_ -* `foundationdb-server-6.2.19-1.el7.x86_64.rpm `_ (depends on the clients package) +* `foundationdb-clients-6.2.20-1.el7.x86_64.rpm `_ +* `foundationdb-server-6.2.20-1.el7.x86_64.rpm `_ (depends on the clients package) Windows ------- The Windows installer is supported on 64-bit Windows XP and later. It includes the client and (optionally) the server. -* `foundationdb-6.2.19-x64.msi `_ +* `foundationdb-6.2.20-x64.msi `_ API Language Bindings ===================== @@ -58,18 +58,18 @@ On macOS and Windows, the FoundationDB Python API bindings are installed as part If you need to use the FoundationDB Python API from other Python installations or paths, download the Python package: -* `foundationdb-6.2.19.tar.gz `_ +* `foundationdb-6.2.20.tar.gz `_ Ruby 1.9.3/2.0.0+ ----------------- -* `fdb-6.2.19.gem `_ +* `fdb-6.2.20.gem `_ Java 8+ ------- -* `fdb-java-6.2.19.jar `_ -* `fdb-java-6.2.19-javadoc.jar `_ +* `fdb-java-6.2.20.jar `_ +* `fdb-java-6.2.20-javadoc.jar `_ Go 1.11+ -------- diff --git a/documentation/sphinx/source/release-notes.rst b/documentation/sphinx/source/release-notes.rst index c6077721a5..40acdef787 100644 --- a/documentation/sphinx/source/release-notes.rst +++ b/documentation/sphinx/source/release-notes.rst @@ -2,6 +2,16 @@ Release Notes ############# +6.2.20 +====== + +Fixes +----- + +* In rare scenarios, clients could send corrupted data to the server. `(PR #2976) `_ +* Internal tools like ``fdbbackup`` are no longer tracked as clients in status (introduced in 6.2.18) `(PR #2849) `_ +* Changed TLS error handling to match the behavior of 6.2.15. `(PR #2993) `_ `(PR #2977) `_ + 6.2.19 ====== From 159b97517bfbdea79389fe092671ba68d2d9457b Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Thu, 23 Apr 2020 12:24:12 -0700 Subject: [PATCH 1526/1604] Chose sensible compiler defaults --- cmake/CompilerChecks.cmake | 49 +++++++++++++++++++++++++++++++++++ cmake/ConfigureCompiler.cmake | 35 ++++++++++++------------- 2 files changed, 65 insertions(+), 19 deletions(-) create mode 100644 cmake/CompilerChecks.cmake diff --git a/cmake/CompilerChecks.cmake b/cmake/CompilerChecks.cmake new file mode 100644 index 0000000000..6239e60afc --- /dev/null +++ b/cmake/CompilerChecks.cmake @@ -0,0 +1,49 @@ +include(CheckCXXCompilerFlag) + +function(env_set var_name default_value type docstring) + set(val ${default_value}) + if(DEFINED ENV{${var_name}}) + set(val $ENV{${var_name}}) + endif() + set(${var_name} ${val} CACHE ${type} "${docstring}") +endfunction() + +function(default_linker var_name) + if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + find_program(lld_path ld.lld "Path to LLD - is only used to determine default linker") + if(lld_path) + set("${var_name}" "LLD" PARENT_SCOPE) + else() + set("${var_name}" "DEFAULT" PARENT_SCOPE) + endif() + else() + set("${var_name}" "DEFAULT" PARENT_SCOPE) + endif() +endfunction() + +function(use_libcxx out) + if(APPLE OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set("${out}" ON PARENT_SCOPE) + else() + set("${out}" OFF PARENT_SCOPE) + endif() +endfunction() + +function(static_link_libcxx out) + if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + default_linker(linker) + if(NOT linker STREQUAL "LLD") + set("${out}" OFF PARENT_SCOPE) + return() + endif() + find_library(libcxx_a libc++.a) + find_library(libcxx_abi libc++abi.a) + if(libcxx_a AND libcxx_abi) + set("${out}" ON PARENT_SCOPE) + else() + set("${out}" OFF PARENT_SCOPE) + endif() + else() + set("${out}" ON PARENT_SCOPE) + endif() +endfunction() diff --git a/cmake/ConfigureCompiler.cmake b/cmake/ConfigureCompiler.cmake index 5f263788b2..fc99241d0a 100644 --- a/cmake/ConfigureCompiler.cmake +++ b/cmake/ConfigureCompiler.cmake @@ -1,25 +1,22 @@ -function(env_set var_name default_value type docstring) - set(val ${default_value}) - if(DEFINED ENV{${var_name}}) - set(val $ENV{${var_name}}) - endif() - set(${var_name} ${val} CACHE ${type} "${docstring}") -endfunction() +include(CompilerChecks) -set(USE_GPERFTOOLS OFF CACHE BOOL "Use gperfools for profiling") +env_set(USE_GPERFTOOLS OFF BOOL "Use gperfools for profiling") env_set(USE_VALGRIND OFF BOOL "Compile for valgrind usage") -set(USE_VALGRIND_FOR_CTEST ${USE_VALGRIND} CACHE BOOL "Use valgrind for ctest") -set(ALLOC_INSTRUMENTATION OFF CACHE BOOL "Instrument alloc") -set(WITH_UNDODB OFF CACHE BOOL "Use rr or undodb") -set(USE_ASAN OFF CACHE BOOL "Compile with address sanitizer") -set(USE_UBSAN OFF CACHE BOOL "Compile with undefined behavior sanitizer") -set(FDB_RELEASE OFF CACHE BOOL "This is a building of a final release") -env_set(USE_LD "DEFAULT" STRING "The linker to use for building: can be LD (system default, default choice), BFD, GOLD, or LLD") -env_set(USE_LIBCXX OFF BOOL "Use libc++") +env_set(USE_VALGRIND_FOR_CTEST ${USE_VALGRIND} BOOL "Use valgrind for ctest") +env_set(ALLOC_INSTRUMENTATION OFF BOOL "Instrument alloc") +env_set(WITH_UNDODB OFF BOOL "Use rr or undodb") +env_set(USE_ASAN OFF BOOL "Compile with address sanitizer") +env_set(USE_UBSAN OFF BOOL "Compile with undefined behavior sanitizer") +env_set(FDB_RELEASE OFF BOOL "This is a building of a final release") env_set(USE_CCACHE OFF BOOL "Use ccache for compilation if available") -set(RELATIVE_DEBUG_PATHS OFF CACHE BOOL "Use relative file paths in debug info") -set(STATIC_LINK_LIBCXX ON CACHE BOOL "Statically link libstdcpp/libc++") -set(USE_WERROR OFF CACHE BOOL "Compile with -Werror. Recommended for local development and CI.") +env_set(RELATIVE_DEBUG_PATHS OFF BOOL "Use relative file paths in debug info") +env_set(USE_WERROR OFF BOOL "Compile with -Werror. Recommended for local development and CI.") +default_linker(_use_ld) +env_set(USE_LD "${_use_ld}" STRING "The linker to use for building: can be LD (system default, default choice), BFD, GOLD, or LLD") +use_libcxx(_use_libcxx) +env_set(USE_LIBCXX "${_use_libcxx}" BOOL "Use libc++") +static_link_libcxx(_static_link_libcxx) +env_set(STATIC_LINK_LIBCXX "${_static_link_libcxx}" BOOL "Statically link libstdcpp/libc++") if(USE_LIBCXX AND STATIC_LINK_LIBCXX AND NOT USE_LD STREQUAL "LLD") message(FATAL_ERROR "Unsupported configuration: STATIC_LINK_LIBCXX with libc+++ only works if USE_LD=LLD") From 430cf4dbaa6d416ea92f5e0eb53bce6118084d76 Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Thu, 23 Apr 2020 12:29:36 -0700 Subject: [PATCH 1527/1604] don't explicitely set `LLD` on MacOS --- cmake/CompilerChecks.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/CompilerChecks.cmake b/cmake/CompilerChecks.cmake index 6239e60afc..11c85856c7 100644 --- a/cmake/CompilerChecks.cmake +++ b/cmake/CompilerChecks.cmake @@ -9,7 +9,9 @@ function(env_set var_name default_value type docstring) endfunction() function(default_linker var_name) - if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + if(APPLE OR WIN32) + set("${var_name}" "DEFAULT" PARENT_SCOPE) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") find_program(lld_path ld.lld "Path to LLD - is only used to determine default linker") if(lld_path) set("${var_name}" "LLD" PARENT_SCOPE) From b58b0358718fc46fd37bc21f56d93a6097fb02ed Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Thu, 23 Apr 2020 12:33:18 -0700 Subject: [PATCH 1528/1604] next attempt for fixing macos build --- cmake/CompilerChecks.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/CompilerChecks.cmake b/cmake/CompilerChecks.cmake index 11c85856c7..b0614eb0e0 100644 --- a/cmake/CompilerChecks.cmake +++ b/cmake/CompilerChecks.cmake @@ -9,7 +9,7 @@ function(env_set var_name default_value type docstring) endfunction() function(default_linker var_name) - if(APPLE OR WIN32) + if(APPLE) set("${var_name}" "DEFAULT" PARENT_SCOPE) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") find_program(lld_path ld.lld "Path to LLD - is only used to determine default linker") From 2049b3802a540faa796b118a231c3d56e989b64f Mon Sep 17 00:00:00 2001 From: tclinken Date: Thu, 23 Apr 2020 12:42:26 -0700 Subject: [PATCH 1529/1604] Only use -Wclass-memaccess when compiling C++ files --- cmake/ConfigureCompiler.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/ConfigureCompiler.cmake b/cmake/ConfigureCompiler.cmake index d291d1076d..ac28b37ed5 100644 --- a/cmake/ConfigureCompiler.cmake +++ b/cmake/ConfigureCompiler.cmake @@ -243,8 +243,8 @@ else() -Wno-deprecated -fvisibility=hidden -Wreturn-type - -fPIC - -Wclass-memaccess) + -fPIC) + add_compile_options($<$:-Wclass-memaccess>) if (GPERFTOOLS_FOUND AND GCC) add_compile_options( -fno-builtin-malloc From b313e63a77c7c9d383f0b59d090748a93783dfc0 Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Thu, 23 Apr 2020 13:11:09 -0700 Subject: [PATCH 1530/1604] another MacOS fix --- cmake/CompilerChecks.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/CompilerChecks.cmake b/cmake/CompilerChecks.cmake index b0614eb0e0..027be35796 100644 --- a/cmake/CompilerChecks.cmake +++ b/cmake/CompilerChecks.cmake @@ -32,7 +32,9 @@ function(use_libcxx out) endfunction() function(static_link_libcxx out) - if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + if(APPLE) + set("${out}" OFF PARENT_SCOPE) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") default_linker(linker) if(NOT linker STREQUAL "LLD") set("${out}" OFF PARENT_SCOPE) From 8eef766546edf37269c802205c23304a1f9070e7 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Thu, 23 Apr 2020 13:16:46 -0700 Subject: [PATCH 1531/1604] update version to 6.2.21 --- CMakeLists.txt | 2 +- bindings/python/LICENSE | 207 ++++++++++++++++++++++++++++++++++++++++ versions.target | 2 +- 3 files changed, 209 insertions(+), 2 deletions(-) create mode 100644 bindings/python/LICENSE diff --git a/CMakeLists.txt b/CMakeLists.txt index fe9c560dfb..12cb52e6a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ # limitations under the License. cmake_minimum_required(VERSION 3.12) project(foundationdb - VERSION 6.2.20 + VERSION 6.2.21 DESCRIPTION "FoundationDB is a scalable, fault-tolerant, ordered key-value store with full ACID transactions." HOMEPAGE_URL "http://www.foundationdb.org/" LANGUAGES C CXX ASM) diff --git a/bindings/python/LICENSE b/bindings/python/LICENSE new file mode 100644 index 0000000000..19586598a8 --- /dev/null +++ b/bindings/python/LICENSE @@ -0,0 +1,207 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + 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. + +------------------------------------------------------------------------------- +SOFTWARE DISTRIBUTED WITH FOUNDATIONDB: + +The FoundationDB software includes a number of subcomponents with separate +copyright notices and license terms - please see the file ACKNOWLEDGEMENTS. +------------------------------------------------------------------------------- diff --git a/versions.target b/versions.target index 43ddf5c1f7..cb3caaf5b0 100644 --- a/versions.target +++ b/versions.target @@ -1,7 +1,7 @@ - 6.2.20 + 6.2.21 6.2 From 44cf59ca81ff7855ef81c9e212f064241525f479 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Thu, 23 Apr 2020 13:16:46 -0700 Subject: [PATCH 1532/1604] update installer WIX GUID following release --- packaging/msi/FDBInstaller.wxs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/msi/FDBInstaller.wxs b/packaging/msi/FDBInstaller.wxs index a9bfc62418..dad6bb058e 100644 --- a/packaging/msi/FDBInstaller.wxs +++ b/packaging/msi/FDBInstaller.wxs @@ -32,7 +32,7 @@ Date: Thu, 23 Apr 2020 14:08:44 -0700 Subject: [PATCH 1533/1604] Remove brittle ASSERT The vtables are sorted by address, so ASLR makes this test non-deterministic --- flow/flat_buffers.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/flow/flat_buffers.cpp b/flow/flat_buffers.cpp index 89fb058f98..1cb4b1099d 100644 --- a/flow/flat_buffers.cpp +++ b/flow/flat_buffers.cpp @@ -488,10 +488,6 @@ TEST_CASE("/flow/FlatBuffers/Standalone") { // Meant to be run with valgrind or asan, to catch heap buffer overflows TEST_CASE("/flow/FlatBuffers/Void") { Standalone msg = ObjectWriter::toValue(Void(), Unversioned()); - // Manually verified to be a valid flatbuffers message. This is technically brittle since there are other valid - // encodings of this message, but our implementation is unlikely to change. - ASSERT(msg == LiteralStringRef("\x14\x00\x00\x00J\xad\x1e\x00\x00\x00\x04\x00\x04\x00\x06\x00\x08\x00\x04\x00\x06" - "\x00\x00\x00\x04\x00\x00\x00\x12\x00\x00\x00")); auto buffer = std::make_unique(msg.size()); // Make a heap allocation of precisely the right size, so // that asan or valgrind will catch any overflows memcpy(buffer.get(), msg.begin(), msg.size()); From e870925b803d1409d54b819407a59053300bfe16 Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Thu, 23 Apr 2020 14:37:20 -0700 Subject: [PATCH 1534/1604] fixed documentation for USE_LD --- cmake/ConfigureCompiler.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/ConfigureCompiler.cmake b/cmake/ConfigureCompiler.cmake index fc99241d0a..33b749c0ee 100644 --- a/cmake/ConfigureCompiler.cmake +++ b/cmake/ConfigureCompiler.cmake @@ -12,7 +12,8 @@ env_set(USE_CCACHE OFF BOOL "Use ccache for compilation if available") env_set(RELATIVE_DEBUG_PATHS OFF BOOL "Use relative file paths in debug info") env_set(USE_WERROR OFF BOOL "Compile with -Werror. Recommended for local development and CI.") default_linker(_use_ld) -env_set(USE_LD "${_use_ld}" STRING "The linker to use for building: can be LD (system default, default choice), BFD, GOLD, or LLD") +env_set(USE_LD "${_use_ld}" STRING + "The linker to use for building: can be LD (system default and same as DEFAULT), BFD, GOLD, or LLD - will be LLD for Clang if available, DEFAULT otherwise") use_libcxx(_use_libcxx) env_set(USE_LIBCXX "${_use_libcxx}" BOOL "Use libc++") static_link_libcxx(_static_link_libcxx) From 59217ddf1e9474a5c478b5907a55c9f243e7728b Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Thu, 23 Apr 2020 14:59:55 -0700 Subject: [PATCH 1535/1604] Remove sanity check on metadata The sanity check parses each range file to get the key range of each range file. The parsing incurs restore_unsupported_file_version error. We need to include this sanity check before 6.3 release. --- fdbclient/BackupContainer.actor.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fdbclient/BackupContainer.actor.cpp b/fdbclient/BackupContainer.actor.cpp index a5ec9223f2..2fd21ebb73 100644 --- a/fdbclient/BackupContainer.actor.cpp +++ b/fdbclient/BackupContainer.actor.cpp @@ -1343,6 +1343,7 @@ public: Standalone> blockData = wait(fileBackup::decodeRangeFileBlock(inFile, j, len)); if (!beginKeySet) { beginKey = blockData.front().key; + beginKeySet = true; } endKey = blockData.back().key; } @@ -1372,7 +1373,7 @@ public: wait(bc->readKeyspaceSnapshot(snapshot.get())); restorable.ranges = std::move(results.first); restorable.keyRanges = std::move(results.second); - if (g_network->isSimulated()) { + if (false && g_network->isSimulated()) { // TODO: Reenable sanity check // Sanity check key ranges state std::map::iterator rit; for (rit = restorable.keyRanges.begin(); rit != restorable.keyRanges.end(); rit++) { From 0efce542c890ff879ef5b1bc5d13ca6e1a12296b Mon Sep 17 00:00:00 2001 From: tclinken Date: Thu, 23 Apr 2020 15:27:11 -0700 Subject: [PATCH 1536/1604] Removed outdated comments --- flow/Arena.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/flow/Arena.h b/flow/Arena.h index c61d9d9c85..0344929b0e 100644 --- a/flow/Arena.h +++ b/flow/Arena.h @@ -693,11 +693,6 @@ inline bool operator != (const StringRef& lhs, const StringRef& rhs ) { return ! inline bool operator <= ( const StringRef& lhs, const StringRef& rhs ) { return !(lhs>rhs); } inline bool operator >= ( const StringRef& lhs, const StringRef& rhs ) { return !(lhs::value); - // T must be trivially destructible (and copyable)! + // T must be trivially destructible! VectorRef() : data(0), m_size(0), m_capacity(0) {} template From 2daf228e10ee65000af37e78ab1952e2256459e3 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Fri, 24 Apr 2020 12:21:35 -0700 Subject: [PATCH 1537/1604] Avoid some unnecessary copies --- flow/genericactors.actor.cpp | 5 ++++- flow/genericactors.actor.h | 25 +++++++++++++++---------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/flow/genericactors.actor.cpp b/flow/genericactors.actor.cpp index 64a5d60940..9a70c659d3 100644 --- a/flow/genericactors.actor.cpp +++ b/flow/genericactors.actor.cpp @@ -69,6 +69,8 @@ ACTOR Future timeoutWarningCollector( FutureStream input, double log ACTOR Future quorumEqualsTrue( std::vector> futures, int required ) { state std::vector< Future > true_futures; state std::vector< Future > false_futures; + true_futures.reserve(futures.size()); + false_futures.reserve(futures.size()); for(int i=0; i quorumEqualsTrue( std::vector> futures, int requ ACTOR Future shortCircuitAny( std::vector> f ) { std::vector> sc; + sc.reserve(f.size()); for(Future fut : f) { sc.push_back(returnIfTrue(fut)); } @@ -96,7 +99,7 @@ ACTOR Future shortCircuitAny( std::vector> f ) // Handle a possible race condition? If the _last_ term to // be evaluated triggers the waitForAll before bubbling // out of the returnIfTrue quorum - for ( auto fut : f ) { + for (const auto& fut : f) { if ( fut.get() ) { return true; } diff --git a/flow/genericactors.actor.h b/flow/genericactors.actor.h index 0150d17855..728e877dc2 100644 --- a/flow/genericactors.actor.h +++ b/flow/genericactors.actor.h @@ -312,8 +312,8 @@ template std::vector>> mapAsync(std::vector> const& what, F const& actorFunc) { std::vector> ret; - for(auto f : what) - ret.push_back(mapAsync( f, actorFunc )); + ret.reserve(what.size()); + for (const auto& f : what) ret.push_back(mapAsync(f, actorFunc)); return ret; } @@ -371,8 +371,8 @@ template std::vector>> map(std::vector> const& what, F const& func) { std::vector>> ret; - for(auto f : what) - ret.push_back(map( f, func )); + ret.reserve(what.size()); + for (const auto& f : what) ret.push_back(map(f, func)); return ret; } @@ -585,6 +585,7 @@ public: } std::vector getKeys() { std::vector keys; + keys.reserve(items.size()); for(auto i = items.begin(); i != items.end(); ++i) keys.push_back( i->first ); return keys; @@ -887,6 +888,7 @@ Future streamHelper( PromiseStream output, PromiseStream errors, template Future makeStream( const std::vector>& futures, PromiseStream& stream, PromiseStream& errors ) { std::vector> forwarders; + forwarders.reserve(futures.size()); for(int f=0; f> getAll( std::vector> input ) { wait( quorum( input, input.size() ) ); std::vector output; + output.reserve(input.size()); for(int i=0; i> appendAll( std::vector>> input ) { wait( quorum( input, input.size() ) ); std::vector output; + size_t sz = 0; + for (const auto& f : input) { + sz += f.get().size(); + } + output.reserve(sz); + for(int i=0; i operator &&( Future const& lhs, Future const& rh else return lhs; } - std::vector> v; - v.push_back( lhs ); - v.push_back( rhs ); - return waitForAll(v); + return waitForAll(std::vector>{ lhs, rhs }); } // error || unset -> error @@ -1626,8 +1632,7 @@ public: return futures[0]; Future f = waitForAll(futures); - futures = std::vector>(); - futures.push_back(f); + futures = std::vector>{ f }; return f; } From ee5051792a55ae4e611f07ffe51fbacf07ad2b4e Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Fri, 24 Apr 2020 12:54:28 -0700 Subject: [PATCH 1538/1604] Add BUGGIFY back to DESIRED_TEAMS_PER_SERVER knob Kudos to A.J. who found this bug. --- fdbserver/Knobs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index 512fdf0140..dc63d21a48 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -203,7 +203,7 @@ void ServerKnobs::initialize(bool randomize, ClientKnobs* clientKnobs, bool isSi init( STORAGE_METRICS_POLLING_DELAY, 2.0 ); if( randomize && BUGGIFY ) STORAGE_METRICS_POLLING_DELAY = 15.0; init( STORAGE_METRICS_RANDOM_DELAY, 0.2 ); init( AVAILABLE_SPACE_RATIO_CUTOFF, 0.05 ); - init( DESIRED_TEAMS_PER_SERVER, 5 ); DESIRED_TEAMS_PER_SERVER = deterministicRandom()->randomInt(1, 10); + init( DESIRED_TEAMS_PER_SERVER, 5 ); if( randomize && BUGGIFY ) DESIRED_TEAMS_PER_SERVER = deterministicRandom()->randomInt(1, 10); init( MAX_TEAMS_PER_SERVER, 5*DESIRED_TEAMS_PER_SERVER ); init( DD_SHARD_SIZE_GRANULARITY, 5000000 ); init( DD_SHARD_SIZE_GRANULARITY_SIM, 500000 ); if( randomize && BUGGIFY ) DD_SHARD_SIZE_GRANULARITY_SIM = 0; From dda0993d16b0b3a9792637343fd71bcaba53badc Mon Sep 17 00:00:00 2001 From: Steve Atherton Date: Fri, 24 Apr 2020 14:12:40 -0700 Subject: [PATCH 1539/1604] Apply clang-format to Redwood source. --- fdbserver/DeltaTree.h | 563 ++--- fdbserver/IPager.h | 40 +- fdbserver/IVersionedStore.h | 31 +- fdbserver/VersionedBTree.actor.cpp | 3566 +++++++++++++--------------- 4 files changed, 1993 insertions(+), 2207 deletions(-) diff --git a/fdbserver/DeltaTree.h b/fdbserver/DeltaTree.h index 639daa98fe..6e821cb2b4 100644 --- a/fdbserver/DeltaTree.h +++ b/fdbserver/DeltaTree.h @@ -32,10 +32,10 @@ static inline int commonPrefixLength(uint8_t const* ap, uint8_t const* bp, int c int i = 0; const int wordEnd = cl - sizeof(Word) + 1; - for(; i < wordEnd; i += sizeof(Word)) { - Word a = *(Word *)ap; - Word b = *(Word *)bp; - if(a != b) { + for (; i < wordEnd; i += sizeof(Word)) { + Word a = *(Word*)ap; + Word b = *(Word*)bp; + if (a != b) { return i + ctzll(a ^ b) / 8; } ap += sizeof(Word); @@ -59,31 +59,32 @@ static int commonPrefixLength(StringRef a, StringRef b) { // This appears to be the fastest version static int lessOrEqualPowerOfTwo(int n) { int p; - for (p = 1; p+p <= n; p+=p); + for (p = 1; p + p <= n; p += p) + ; return p; } /* static int _lessOrEqualPowerOfTwo(uint32_t n) { - if(n == 0) - return n; - int trailing = __builtin_ctz(n); - int leading = __builtin_clz(n); - if(trailing + leading == ((sizeof(n) * 8) - 1)) - return n; - return 1 << ( (sizeof(n) * 8) - leading - 1); + if(n == 0) + return n; + int trailing = __builtin_ctz(n); + int leading = __builtin_clz(n); + if(trailing + leading == ((sizeof(n) * 8) - 1)) + return n; + return 1 << ( (sizeof(n) * 8) - leading - 1); } static int __lessOrEqualPowerOfTwo(unsigned int n) { - int p = 1; - for(; p <= n; p <<= 1); - return p >> 1; + int p = 1; + for(; p <= n; p <<= 1); + return p >> 1; } */ static int perfectSubtreeSplitPoint(int subtree_size) { // return the inorder index of the root node in a subtree of the given size - // consistent with the resulting binary search tree being "perfect" (having minimal height + // consistent with the resulting binary search tree being "perfect" (having minimal height // and all missing nodes as far right as possible). // There has to be a simpler way to do this. int s = lessOrEqualPowerOfTwo((subtree_size - 1) / 2 + 1) - 1; @@ -91,16 +92,14 @@ static int perfectSubtreeSplitPoint(int subtree_size) { } static int perfectSubtreeSplitPointCached(int subtree_size) { - static uint16_t *points = nullptr; + static uint16_t* points = nullptr; static const int max = 500; - if(points == nullptr) { + if (points == nullptr) { points = new uint16_t[max]; - for(int i = 0; i < max; ++i) - points[i] = perfectSubtreeSplitPoint(i); + for (int i = 0; i < max; ++i) points[i] = perfectSubtreeSplitPoint(i); } - if(subtree_size < max) - return points[subtree_size]; + if (subtree_size < max) return points[subtree_size]; return perfectSubtreeSplitPoint(subtree_size); } @@ -129,7 +128,7 @@ static int perfectSubtreeSplitPointCached(int subtree_size) { // int getCommonPrefixLen(const T &base, int skip) const; // // // Returns the size of the delta object needed to make *this from base -// // TODO: Explain contract required for deltaSize to be used to predict final +// // TODO: Explain contract required for deltaSize to be used to predict final // // balanced tree size incrementally while adding sorted items to a build set // int deltaSize(const T &base) const; // @@ -147,7 +146,7 @@ static int perfectSubtreeSplitPointCached(int subtree_size) { // // Retrieves the previously stored boolean // bool getPrefixSource() const; // -#pragma pack(push,1) +#pragma pack(push, 1) template struct DeltaTree { struct Node { @@ -161,45 +160,35 @@ struct DeltaTree { uint16_t right; } smallOffsets; }; - - static int headerSize(bool large) { - return large ? sizeof(largeOffsets) : sizeof(smallOffsets); - } - - inline DeltaT & delta(bool large) { - return large ? *(DeltaT *)(&largeOffsets + 1) : *(DeltaT *)(&smallOffsets + 1); + + static int headerSize(bool large) { return large ? sizeof(largeOffsets) : sizeof(smallOffsets); } + + inline DeltaT& delta(bool large) { + return large ? *(DeltaT*)(&largeOffsets + 1) : *(DeltaT*)(&smallOffsets + 1); }; - inline const DeltaT & delta(bool large) const { - return large ? *(const DeltaT *)(&largeOffsets + 1) : *(const DeltaT *)(&smallOffsets + 1); + inline const DeltaT& delta(bool large) const { + return large ? *(const DeltaT*)(&largeOffsets + 1) : *(const DeltaT*)(&smallOffsets + 1); }; - Node * resolvePointer(int offset) const { - return offset == 0 ? nullptr : (Node *)((uint8_t *)this + offset); - } + Node* resolvePointer(int offset) const { return offset == 0 ? nullptr : (Node*)((uint8_t*)this + offset); } - Node * rightChild(bool large) const { - return resolvePointer(large ? largeOffsets.right : smallOffsets.right); - } + Node* rightChild(bool large) const { return resolvePointer(large ? largeOffsets.right : smallOffsets.right); } - Node * leftChild(bool large) const { - return resolvePointer(large ? largeOffsets.left : smallOffsets.left); - } + Node* leftChild(bool large) const { return resolvePointer(large ? largeOffsets.left : smallOffsets.left); } void setRightChildOffset(bool large, int offset) { - if(large) { + if (large) { largeOffsets.right = offset; - } - else { + } else { smallOffsets.right = offset; } } void setLeftChildOffset(bool large, int offset) { - if(large) { + if (large) { largeOffsets.left = offset; - } - else { + } else { smallOffsets.left = offset; } } @@ -213,90 +202,69 @@ struct DeltaTree { static constexpr int LargeTreePerNodeExtraOverhead = sizeof(Node::largeOffsets) - sizeof(Node::smallOffsets); struct { - uint16_t numItems; // Number of items in the tree. - uint32_t nodeBytesUsed; // Bytes used by nodes (everything after the tree header) - uint32_t nodeBytesFree; // Bytes left at end of tree to expand into - uint32_t nodeBytesDeleted; // Delta bytes deleted from tree. Note that some of these bytes could be borrowed by descendents. - uint8_t initialHeight; // Height of tree as originally built - uint8_t maxHeight; // Maximum height of tree after any insertion. Value of 0 means no insertions done. - bool largeNodes; // Node size, can be calculated as capacity > SmallSizeLimit but it will be used a lot + uint16_t numItems; // Number of items in the tree. + uint32_t nodeBytesUsed; // Bytes used by nodes (everything after the tree header) + uint32_t nodeBytesFree; // Bytes left at end of tree to expand into + uint32_t nodeBytesDeleted; // Delta bytes deleted from tree. Note that some of these bytes could be borrowed by + // descendents. + uint8_t initialHeight; // Height of tree as originally built + uint8_t maxHeight; // Maximum height of tree after any insertion. Value of 0 means no insertions done. + bool largeNodes; // Node size, can be calculated as capacity > SmallSizeLimit but it will be used a lot }; #pragma pack(pop) - inline Node & root() { - return *(Node *)(this + 1); - } + inline Node& root() { return *(Node*)(this + 1); } - inline const Node & root() const { - return *(const Node *)(this + 1); - } + inline const Node& root() const { return *(const Node*)(this + 1); } - int size() const { - return sizeof(DeltaTree) + nodeBytesUsed; - } + int size() const { return sizeof(DeltaTree) + nodeBytesUsed; } - int capacity() const { - return size() + nodeBytesFree; - } + int capacity() const { return size() + nodeBytesFree; } - inline Node & newNode() { - return *(Node *)((uint8_t *)this + size()); - } + inline Node& newNode() { return *(Node*)((uint8_t*)this + size()); } public: // Get count of total overhead bytes (everything but the user-formatted Delta) for a tree given size n - static int emptyTreeSize() { - return sizeof(DeltaTree); - } + static int emptyTreeSize() { return sizeof(DeltaTree); } struct DecodedNode { DecodedNode() {} // construct root node - DecodedNode(Node *raw, const T *prev, const T *next, Arena &arena, bool large) - : raw(raw), parent(nullptr), otherAncestor(nullptr), leftChild(nullptr), rightChild(nullptr), prev(prev), next(next), - item(raw->delta(large).apply(raw->delta(large).getPrefixSource() ? *prev : *next, arena)), - large(large) - { - //printf("DecodedNode1 raw=%p delta=%s\n", raw, raw->delta(large).toString().c_str()); + DecodedNode(Node* raw, const T* prev, const T* next, Arena& arena, bool large) + : raw(raw), parent(nullptr), otherAncestor(nullptr), leftChild(nullptr), rightChild(nullptr), prev(prev), + next(next), item(raw->delta(large).apply(raw->delta(large).getPrefixSource() ? *prev : *next, arena)), + large(large) { + // printf("DecodedNode1 raw=%p delta=%s\n", raw, raw->delta(large).toString().c_str()); } - + // Construct non-root node - // wentLeft indicates that we've gone left to get to the raw node. - DecodedNode(Node *raw, DecodedNode *parent, bool wentLeft, Arena &arena) - : parent(parent), large(parent->large), otherAncestor(wentLeft ? parent->getPrevAncestor() : parent->getNextAncestor()), - prev(wentLeft ? parent->prev : &parent->item), - next(wentLeft ? &parent->item : parent->next), - leftChild(nullptr), rightChild(nullptr), - raw(raw), item(raw->delta(large).apply(raw->delta(large).getPrefixSource() ? *prev : *next, arena)) - { - //printf("DecodedNode2 raw=%p delta=%s\n", raw, raw->delta(large).toString().c_str()); + // wentLeft indicates that we've gone left to get to the raw node. + DecodedNode(Node* raw, DecodedNode* parent, bool wentLeft, Arena& arena) + : parent(parent), large(parent->large), + otherAncestor(wentLeft ? parent->getPrevAncestor() : parent->getNextAncestor()), + prev(wentLeft ? parent->prev : &parent->item), next(wentLeft ? &parent->item : parent->next), + leftChild(nullptr), rightChild(nullptr), raw(raw), + item(raw->delta(large).apply(raw->delta(large).getPrefixSource() ? *prev : *next, arena)) { + // printf("DecodedNode2 raw=%p delta=%s\n", raw, raw->delta(large).toString().c_str()); } // Returns true if otherAncestor is the previous ("greatest lesser") ancestor - bool otherAncestorPrev() const { - return parent && parent->leftChild == this; - } + bool otherAncestorPrev() const { return parent && parent->leftChild == this; } // Returns true if otherAncestor is the next ("least greator") ancestor - bool otherAncestorNext() const { - return parent && parent->rightChild == this; - } + bool otherAncestorNext() const { return parent && parent->rightChild == this; } - DecodedNode * getPrevAncestor() const { - return otherAncestorPrev() ? otherAncestor : parent; - } + DecodedNode* getPrevAncestor() const { return otherAncestorPrev() ? otherAncestor : parent; } - DecodedNode * getNextAncestor() const { - return otherAncestorNext() ? otherAncestor : parent; - } + DecodedNode* getNextAncestor() const { return otherAncestorNext() ? otherAncestor : parent; } - DecodedNode * jumpUpNext(DecodedNode *root, bool &othersChild) const { - if(parent != nullptr) { - if(parent->rightChild == this) { + DecodedNode* jumpUpNext(DecodedNode* root, bool& othersChild) const { + if (parent != nullptr) { + if (parent->rightChild == this) { return otherAncestor; } - if(otherAncestor != nullptr) { + if (otherAncestor != nullptr) { othersChild = true; return otherAncestor->rightChild; } @@ -304,12 +272,12 @@ public: return parent; } - DecodedNode * jumpUpPrev(DecodedNode *root, bool &othersChild) const { - if(parent != nullptr) { - if(parent->leftChild == this) { + DecodedNode* jumpUpPrev(DecodedNode* root, bool& othersChild) const { + if (parent != nullptr) { + if (parent->leftChild == this) { return otherAncestor; } - if(otherAncestor != nullptr) { + if (otherAncestor != nullptr) { othersChild = true; return otherAncestor->leftChild; } @@ -317,62 +285,56 @@ public: return parent; } - DecodedNode * jumpNext(DecodedNode *root) const { - if(otherAncestorNext()) { + DecodedNode* jumpNext(DecodedNode* root) const { + if (otherAncestorNext()) { return (otherAncestor != nullptr) ? otherAncestor : rightChild; - } - else { - if(this == root) { + } else { + if (this == root) { return rightChild; } return (otherAncestor != nullptr) ? otherAncestor->rightChild : root; } } - DecodedNode * jumpPrev(DecodedNode *root) const { - if(otherAncestorPrev()) { + DecodedNode* jumpPrev(DecodedNode* root) const { + if (otherAncestorPrev()) { return (otherAncestor != nullptr) ? otherAncestor : leftChild; - } - else { - if(this == root) { + } else { + if (this == root) { return leftChild; } return (otherAncestor != nullptr) ? otherAncestor->leftChild : root; } } - void setDeleted(bool deleted) { - raw->delta(large).setDeleted(deleted); - } + void setDeleted(bool deleted) { raw->delta(large).setDeleted(deleted); } - bool isDeleted() const { - return raw->delta(large).getDeleted(); - } + bool isDeleted() const { return raw->delta(large).getDeleted(); } - bool large; // Node size - Node *raw; - DecodedNode *parent; - DecodedNode *otherAncestor; - DecodedNode *leftChild; - DecodedNode *rightChild; - const T *prev; // greatest ancestor to the left, or tree lower bound - const T *next; // least ancestor to the right, or tree upper bound + bool large; // Node size + Node* raw; + DecodedNode* parent; + DecodedNode* otherAncestor; + DecodedNode* leftChild; + DecodedNode* rightChild; + const T* prev; // greatest ancestor to the left, or tree lower bound + const T* next; // least ancestor to the right, or tree upper bound T item; - DecodedNode *getRightChild(Arena &arena) { - if(rightChild == nullptr) { - Node *n = raw->rightChild(large); - if(n != nullptr) { + DecodedNode* getRightChild(Arena& arena) { + if (rightChild == nullptr) { + Node* n = raw->rightChild(large); + if (n != nullptr) { rightChild = new (arena) DecodedNode(n, this, false, arena); } } return rightChild; } - DecodedNode *getLeftChild(Arena &arena) { - if(leftChild == nullptr) { - Node *n = raw->leftChild(large); - if(n != nullptr) { + DecodedNode* getLeftChild(Arena& arena) { + if (leftChild == nullptr) { + Node* n = raw->leftChild(large); + if (n != nullptr) { leftChild = new (arena) DecodedNode(n, this, true, arena); } } @@ -389,75 +351,69 @@ public: struct Mirror : FastAllocated { friend class Cursor; - Mirror(const void *treePtr = nullptr, const T *lowerBound = nullptr, const T *upperBound = nullptr) - : tree((DeltaTree *)treePtr), lower(lowerBound), upper(upperBound) - { - // TODO: Remove these copies into arena and require users of Mirror to keep prev and next alive during its lifetime - lower = new(arena) T(arena, *lower); - upper = new(arena) T(arena, *upper); + Mirror(const void* treePtr = nullptr, const T* lowerBound = nullptr, const T* upperBound = nullptr) + : tree((DeltaTree*)treePtr), lower(lowerBound), upper(upperBound) { + // TODO: Remove these copies into arena and require users of Mirror to keep prev and next alive during its + // lifetime + lower = new (arena) T(arena, *lower); + upper = new (arena) T(arena, *upper); - root = (tree->nodeBytesUsed == 0) ? nullptr : new (arena) DecodedNode(&tree->root(), lower, upper, arena, tree->largeNodes); + root = (tree->nodeBytesUsed == 0) ? nullptr + : new (arena) + DecodedNode(&tree->root(), lower, upper, arena, tree->largeNodes); } - const T *lowerBound() const { - return lower; - } + const T* lowerBound() const { return lower; } - const T *upperBound() const { - return upper; - } + const T* upperBound() const { return upper; } -private: + private: Arena arena; - DeltaTree *tree; - DecodedNode *root; - const T *lower; - const T *upper; -public: + DeltaTree* tree; + DecodedNode* root; + const T* lower; + const T* upper; - Cursor getCursor() { - return Cursor(this); - } + public: + Cursor getCursor() { return Cursor(this); } // Try to insert k into the DeltaTree, updating byte counts and initialHeight if they // have changed (they won't if k already exists in the tree but was deleted). // Returns true if successful, false if k does not fit in the space available // or if k is already in the tree (and was not already deleted). - bool insert(const T &k, int skipLen = 0, int maxHeightAllowed = std::numeric_limits::max()) { + bool insert(const T& k, int skipLen = 0, int maxHeightAllowed = std::numeric_limits::max()) { int height = 1; - DecodedNode *n = root; + DecodedNode* n = root; bool addLeftChild = false; - while(n != nullptr) { + while (n != nullptr) { int cmp = k.compare(n->item, skipLen); - if(cmp >= 0) { + if (cmp >= 0) { // If we found an item identical to k then if it is deleted, undeleted it, // otherwise fail - if(cmp == 0) { - auto &d = n->raw->delta(tree->largeNodes); - if(d.getDeleted()) { + if (cmp == 0) { + auto& d = n->raw->delta(tree->largeNodes); + if (d.getDeleted()) { d.setDeleted(false); ++tree->numItems; return true; - } - else { + } else { return false; } } - DecodedNode *right = n->getRightChild(arena); + DecodedNode* right = n->getRightChild(arena); - if(right == nullptr) { + if (right == nullptr) { break; } n = right; - } - else { - DecodedNode *left = n->getLeftChild(arena); + } else { + DecodedNode* left = n->getLeftChild(arena); - if(left == nullptr) { + if (left == nullptr) { addLeftChild = true; break; } @@ -467,14 +423,14 @@ public: ++height; } - if(height > maxHeightAllowed) { + if (height > maxHeightAllowed) { return false; } // Insert k as the left or right child of n, depending on the value of addLeftChild // First, see if it will fit. - const T *prev = addLeftChild ? n->prev : &n->item; - const T *next = addLeftChild ? &n->item : n->next; + const T* prev = addLeftChild ? n->prev : &n->item; + const T* next = addLeftChild ? &n->item : n->next; int common = prev->getCommonPrefixLen(*next, skipLen); int commonWithPrev = k.getCommonPrefixLen(*prev, common); @@ -482,26 +438,25 @@ public: bool basePrev = commonWithPrev >= commonWithNext; int commonPrefix = basePrev ? commonWithPrev : commonWithNext; - const T *base = basePrev ? prev : next; + const T* base = basePrev ? prev : next; int deltaSize = k.deltaSize(*base, commonPrefix, false); int nodeSpace = deltaSize + Node::headerSize(tree->largeNodes); - if(nodeSpace > tree->nodeBytesFree) { + if (nodeSpace > tree->nodeBytesFree) { return false; } - DecodedNode *newNode = new (arena) DecodedNode(); - Node *raw = &tree->newNode(); + DecodedNode* newNode = new (arena) DecodedNode(); + Node* raw = &tree->newNode(); raw->setLeftChildOffset(tree->largeNodes, 0); raw->setRightChildOffset(tree->largeNodes, 0); - int newOffset = (uint8_t *)raw - (uint8_t *)n->raw; - //printf("Inserting %s at offset %d\n", k.toString().c_str(), newOffset); + int newOffset = (uint8_t*)raw - (uint8_t*)n->raw; + // printf("Inserting %s at offset %d\n", k.toString().c_str(), newOffset); - if(addLeftChild) { + if (addLeftChild) { n->leftChild = newNode; n->raw->setLeftChildOffset(tree->largeNodes, newOffset); - } - else { + } else { n->rightChild = newNode; n->raw->setRightChildOffset(tree->largeNodes, newOffset); } @@ -518,7 +473,8 @@ public: ASSERT(deltaSize == k.writeDelta(raw->delta(tree->largeNodes), *base, commonPrefix)); raw->delta(tree->largeNodes).setPrefixSource(basePrev); - // Initialize node's item from the delta (instead of copying into arena) to avoid unnecessary arena space usage + // Initialize node's item from the delta (instead of copying into arena) to avoid unnecessary arena space + // usage newNode->item = raw->delta(tree->largeNodes).apply(*base, arena); tree->nodeBytesUsed += nodeSpace; @@ -526,7 +482,7 @@ public: ++tree->numItems; // Update max height of the tree if necessary - if(height > tree->maxHeight) { + if (height > tree->maxHeight) { tree->maxHeight = height; } @@ -534,11 +490,11 @@ public: } // Erase k by setting its deleted flag to true. Returns true only if k existed - bool erase(const T &k, int skipLen = 0) { + bool erase(const T& k, int skipLen = 0) { Cursor c = getCursor(); int cmp = c.seek(k); // If exactly k is found - if(cmp == 0 && !c.node->isDeleted()) { + if (cmp == 0 && !c.node->isDeleted()) { c.erase(); return true; } @@ -549,34 +505,22 @@ public: // Cursor provides a way to seek into a DeltaTree and iterate over its contents // All Cursors from a Mirror share the same decoded node 'cache' (tree of DecodedNodes) struct Cursor { - Cursor() : mirror(nullptr), node(nullptr) { - } + Cursor() : mirror(nullptr), node(nullptr) {} - Cursor(Mirror *r) : mirror(r), node(mirror->root) { - } + Cursor(Mirror* r) : mirror(r), node(mirror->root) {} - Mirror *mirror; - DecodedNode *node; + Mirror* mirror; + DecodedNode* node; - bool valid() const { - return node != nullptr; - } + bool valid() const { return node != nullptr; } - const T & get() const { - return node->item; - } + const T& get() const { return node->item; } - const T & getOrUpperBound() const { - return valid() ? node->item : *mirror->upperBound(); - } + const T& getOrUpperBound() const { return valid() ? node->item : *mirror->upperBound(); } - bool operator==(const Cursor &rhs) const { - return node == rhs.node; - } + bool operator==(const Cursor& rhs) const { return node == rhs.node; } - bool operator!=(const Cursor &rhs) const { - return node != rhs.node; - } + bool operator!=(const Cursor& rhs) const { return node != rhs.node; } void erase() { node->setDeleted(true); @@ -584,72 +528,69 @@ public: moveNext(); } - // TODO: Make hint-based seek() use the hint logic in this, which is better and actually improves seek times, then remove this function. - bool seekLessThanOrEqualOld(const T &s, int skipLen, const Cursor *pHint, int initialCmp) { - DecodedNode *n; + // TODO: Make hint-based seek() use the hint logic in this, which is better and actually improves seek times, + // then remove this function. + bool seekLessThanOrEqualOld(const T& s, int skipLen, const Cursor* pHint, int initialCmp) { + DecodedNode* n; // If there's a hint position, use it // At the end of using the hint, if n is valid it should point to a node which has not yet been compared to. - if(pHint != nullptr && pHint->node != nullptr) { + if (pHint != nullptr && pHint->node != nullptr) { n = pHint->node; - if(initialCmp == 0) { + if (initialCmp == 0) { node = n; return _hideDeletedBackward(); } - if(initialCmp > 0) { + if (initialCmp > 0) { node = n; - while(n != nullptr) { + while (n != nullptr) { n = n->jumpNext(mirror->root); - if(n == nullptr) { + if (n == nullptr) { break; } int cmp = s.compare(n->item, skipLen); - if(cmp > 0) { + if (cmp > 0) { node = n; continue; } - if(cmp == 0) { + if (cmp == 0) { node = n; n = nullptr; - } - else { + } else { n = n->leftChild; } break; } - } - else { - while(n != nullptr) { + } else { + while (n != nullptr) { n = n->jumpPrev(mirror->root); - if(n == nullptr) { + if (n == nullptr) { break; } int cmp = s.compare(n->item, skipLen); - if(cmp >= 0) { + if (cmp >= 0) { node = n; n = (cmp == 0) ? nullptr : n->rightChild; break; } } } - } - else { + } else { // Start at root, clear current position n = mirror->root; node = nullptr; } - while(n != nullptr) { + while (n != nullptr) { int cmp = s.compare(n->item, skipLen); - if(cmp < 0) { + if (cmp < 0) { n = n->getLeftChild(mirror->arena); - } - else { + } else { // n <= s so store it in node as a potential result node = n; - if(cmp == 0) { + if (cmp == 0) { break; } @@ -665,54 +606,54 @@ public: // Then will not "see" erased records. // If successful, they return true, and if not then false a while making the cursor invalid. // These methods forward arguments to the seek() overloads, see those for argument descriptions. - template + template bool seekLessThan(Args... args) { int cmp = seek(args...); - if(cmp < 0 || (cmp == 0 && node != nullptr)) { + if (cmp < 0 || (cmp == 0 && node != nullptr)) { movePrev(); } return _hideDeletedBackward(); } - template + template bool seekLessThanOrEqual(Args... args) { int cmp = seek(args...); - if(cmp < 0) { + if (cmp < 0) { movePrev(); } return _hideDeletedBackward(); } - template + template bool seekGreaterThan(Args... args) { int cmp = seek(args...); - if(cmp > 0 || (cmp == 0 && node != nullptr)) { + if (cmp > 0 || (cmp == 0 && node != nullptr)) { moveNext(); } return _hideDeletedForward(); } - template + template bool seekGreaterThanOrEqual(Args... args) { int cmp = seek(args...); - if(cmp > 0) { + if (cmp > 0) { moveNext(); } return _hideDeletedForward(); } - // seek() moves the cursor to a node containing s or the node that would be the parent of s if s were to be added to the tree. - // If the tree was empty, the cursor will be invalid and the return value will be 0. + // seek() moves the cursor to a node containing s or the node that would be the parent of s if s were to be + // added to the tree. If the tree was empty, the cursor will be invalid and the return value will be 0. // Otherwise, returns the result of s.compare(item at cursor position) // Does not skip/avoid deleted nodes. - int seek(const T &s, int skipLen = 0) { - DecodedNode *n = mirror->root; + int seek(const T& s, int skipLen = 0) { + DecodedNode* n = mirror->root; node = nullptr; int cmp = 0; - while(n != nullptr) { + while (n != nullptr) { node = n; cmp = s.compare(n->item, skipLen); - if(cmp == 0) { + if (cmp == 0) { break; } @@ -724,34 +665,36 @@ public: // Same usage as seek() but with a hint of a cursor, which can't be null, whose starting position // should be close to s in the tree to improve seek time. - // initialCmp should be logically equivalent to s.compare(pHint->get()) or 0, in which + // initialCmp should be logically equivalent to s.compare(pHint->get()) or 0, in which // case the comparison will be done in this method. - // TODO: This is broken, it's not faster than not using a hint. See Make thisUnfortunately in a microbenchmark attempting to approximate a common use case, this version - // of using a cursor hint is actually slower than not using a hint. - int seek(const T &s, int skipLen, const Cursor *pHint, int initialCmp = 0) { - DecodedNode *n = mirror->root; + // TODO: This is broken, it's not faster than not using a hint. See Make thisUnfortunately in a microbenchmark + // attempting to approximate a common use case, this version of using a cursor hint is actually slower than not + // using a hint. + int seek(const T& s, int skipLen, const Cursor* pHint, int initialCmp = 0) { + DecodedNode* n = mirror->root; node = nullptr; int cmp; // If there's a hint position, use it // At the end of using the hint, if n is valid it should point to a node which has not yet been compared to. - if(pHint->node != nullptr) { + if (pHint->node != nullptr) { n = pHint->node; - if(initialCmp == 0) { + if (initialCmp == 0) { initialCmp = s.compare(pHint->get()); } cmp = initialCmp; - while(true) { + while (true) { node = n; - if(cmp == 0) { + if (cmp == 0) { return cmp; } // Attempt to jump up and past s bool othersChild = false; - n = (initialCmp > 0) ? n->jumpUpNext(mirror->root, othersChild) : n->jumpUpPrev(mirror->root, othersChild); - if(n == nullptr) { + n = (initialCmp > 0) ? n->jumpUpNext(mirror->root, othersChild) + : n->jumpUpPrev(mirror->root, othersChild); + if (n == nullptr) { n = (cmp > 0) ? node->rightChild : node->leftChild; break; } @@ -760,15 +703,14 @@ public: cmp = s.compare(n->item, skipLen); // n is on the oposite side of s than node is, then n is too far. - if(cmp != 0 && ((initialCmp ^ cmp) < 0)) { - if(!othersChild) { + if (cmp != 0 && ((initialCmp ^ cmp) < 0)) { + if (!othersChild) { n = (cmp < 0) ? node->rightChild : node->leftChild; } break; } } - } - else { + } else { // Start at root, clear current position n = mirror->root; node = nullptr; @@ -776,10 +718,10 @@ public: } // Search starting from n, which is either the root or the result of applying the hint - while(n != nullptr) { + while (n != nullptr) { node = n; cmp = s.compare(n->item, skipLen); - if(cmp == 0) { + if (cmp == 0) { break; } @@ -790,23 +732,21 @@ public: } bool moveFirst() { - DecodedNode *n = mirror->root; + DecodedNode* n = mirror->root; node = n; - while(n != nullptr) { + while (n != nullptr) { n = n->getLeftChild(mirror->arena); - if(n != nullptr) - node = n; + if (n != nullptr) node = n; } return _hideDeletedForward(); } bool moveLast() { - DecodedNode *n = mirror->root; + DecodedNode* n = mirror->root; node = n; - while(n != nullptr) { + while (n != nullptr) { n = n->getRightChild(mirror->arena); - if(n != nullptr) - node = n; + if (n != nullptr) node = n; } return _hideDeletedBackward(); } @@ -814,15 +754,14 @@ public: // Try to move to next node, sees deleted nodes. void _moveNext() { // Try to go right - DecodedNode *n = node->getRightChild(mirror->arena); + DecodedNode* n = node->getRightChild(mirror->arena); // If we couldn't go right, then the answer is our next ancestor - if(n == nullptr) { + if (n == nullptr) { node = node->getNextAncestor(); - } - else { + } else { // Go left as far as possible - while(n != nullptr) { + while (n != nullptr) { node = n; n = n->getLeftChild(mirror->arena); } @@ -832,15 +771,14 @@ public: // Try to move to previous node, sees deleted nodes. void _movePrev() { // Try to go left - DecodedNode *n = node->getLeftChild(mirror->arena); + DecodedNode* n = node->getLeftChild(mirror->arena); // If we couldn't go left, then the answer is our prev ancestor - if(n == nullptr) { + if (n == nullptr) { node = node->getPrevAncestor(); - } - else { + } else { // Go right as far as possible - while(n != nullptr) { + while (n != nullptr) { node = n; n = n->getRightChild(mirror->arena); } @@ -859,14 +797,14 @@ public: private: bool _hideDeletedBackward() { - while(node != nullptr && node->isDeleted()) { + while (node != nullptr && node->isDeleted()) { _movePrev(); } return node != nullptr; } bool _hideDeletedForward() { - while(node != nullptr && node->isDeleted()) { + while (node != nullptr && node->isDeleted()) { _moveNext(); } return node != nullptr; @@ -874,7 +812,7 @@ public: }; // Returns number of bytes written - int build(int spaceAvailable, const T *begin, const T *end, const T *prev, const T *next) { + int build(int spaceAvailable, const T* begin, const T* end, const T* prev, const T* next) { largeNodes = spaceAvailable > SmallSizeLimit; int count = end - begin; numItems = count; @@ -883,10 +821,9 @@ public: maxHeight = 0; // The boundary leading to the new page acts as the last time we branched right - if(begin != end) { + if (begin != end) { nodeBytesUsed = buildSubtree(root(), begin, end, prev, next, prev->getCommonPrefixLen(*next, 0)); - } - else { + } else { nodeBytesUsed = 0; } nodeBytesFree = spaceAvailable - size(); @@ -894,28 +831,28 @@ public: } private: - int buildSubtree(Node &node, const T *begin, const T *end, const T *prev, const T *next, int subtreeCommon) { - //printf("build: %s to %s\n", begin->toString().c_str(), (end - 1)->toString().c_str()); - //printf("build: root at %p Node::headerSize %d delta at %p \n", &root, Node::headerSize(largeNodes), &node.delta(largeNodes)); + int buildSubtree(Node& node, const T* begin, const T* end, const T* prev, const T* next, int subtreeCommon) { + // printf("build: %s to %s\n", begin->toString().c_str(), (end - 1)->toString().c_str()); + // printf("build: root at %p Node::headerSize %d delta at %p \n", &root, Node::headerSize(largeNodes), + // &node.delta(largeNodes)); ASSERT(end != begin); int count = end - begin; // Find key to be stored in root int mid = perfectSubtreeSplitPointCached(count); - const T &item = begin[mid]; + const T& item = begin[mid]; int commonWithPrev = item.getCommonPrefixLen(*prev, subtreeCommon); int commonWithNext = item.getCommonPrefixLen(*next, subtreeCommon); bool prefixSourcePrev; int commonPrefix; - const T *base; - if(commonWithPrev >= commonWithNext) { + const T* base; + if (commonWithPrev >= commonWithNext) { prefixSourcePrev = true; commonPrefix = commonWithPrev; base = prev; - } - else { + } else { prefixSourcePrev = false; commonPrefix = commonWithNext; base = next; @@ -923,29 +860,27 @@ private: int deltaSize = item.writeDelta(node.delta(largeNodes), *base, commonPrefix); node.delta(largeNodes).setPrefixSource(prefixSourcePrev); - //printf("Serialized %s to %p\n", item.toString().c_str(), &root.delta(largeNodes)); + // printf("Serialized %s to %p\n", item.toString().c_str(), &root.delta(largeNodes)); // Continue writing after the serialized Delta. - uint8_t *wptr = (uint8_t *)&node.delta(largeNodes) + deltaSize; + uint8_t* wptr = (uint8_t*)&node.delta(largeNodes) + deltaSize; // Serialize left child - if(count > 1) { - wptr += buildSubtree(*(Node *)wptr, begin, begin + mid, prev, &item, commonWithPrev); + if (count > 1) { + wptr += buildSubtree(*(Node*)wptr, begin, begin + mid, prev, &item, commonWithPrev); node.setLeftChildOffset(largeNodes, Node::headerSize(largeNodes) + deltaSize); - } - else { + } else { node.setLeftChildOffset(largeNodes, 0); } // Serialize right child - if(count > 2) { - node.setRightChildOffset(largeNodes, wptr - (uint8_t *)&node); - wptr += buildSubtree(*(Node *)wptr, begin + mid + 1, end, &item, next, commonWithNext); - } - else { + if (count > 2) { + node.setRightChildOffset(largeNodes, wptr - (uint8_t*)&node); + wptr += buildSubtree(*(Node*)wptr, begin + mid + 1, end, &item, next, commonWithNext); + } else { node.setRightChildOffset(largeNodes, 0); } - return wptr - (uint8_t *)&node; + return wptr - (uint8_t*)&node; } }; diff --git a/fdbserver/IPager.h b/fdbserver/IPager.h index 5043d315fa..b3991a025c 100644 --- a/fdbserver/IPager.h +++ b/fdbserver/IPager.h @@ -4,13 +4,13 @@ * This source file is part of the FoundationDB open source project * * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -30,23 +30,29 @@ #define REDWOOD_DEBUG 0 #define debug_printf_stream stdout -#define debug_printf_always(...) { fprintf(debug_printf_stream, "%s %f %04d ", g_network->getLocalAddress().toString().c_str(), now(), __LINE__); fprintf(debug_printf_stream, __VA_ARGS__); fflush(debug_printf_stream); } +#define debug_printf_always(...) \ + { \ + fprintf(debug_printf_stream, "%s %f %04d ", g_network->getLocalAddress().toString().c_str(), now(), __LINE__); \ + fprintf(debug_printf_stream, __VA_ARGS__); \ + fflush(debug_printf_stream); \ + } #define debug_printf_noop(...) #if defined(NO_INTELLISENSE) - #if REDWOOD_DEBUG - #define debug_printf debug_printf_always - #else - #define debug_printf debug_printf_noop - #endif +#if REDWOOD_DEBUG +#define debug_printf debug_printf_always #else - // To get error-checking on debug_printf statements in IDE - #define debug_printf printf +#define debug_printf debug_printf_noop +#endif +#else +// To get error-checking on debug_printf statements in IDE +#define debug_printf printf #endif #define BEACON debug_printf_always("HERE\n") -#define TRACE debug_printf_always("%s: %s line %d %s\n", __FUNCTION__, __FILE__, __LINE__, platform::get_backtrace().c_str()); +#define TRACE \ + debug_printf_always("%s: %s line %d %s\n", __FUNCTION__, __FILE__, __LINE__, platform::get_backtrace().c_str()); #ifndef VALGRIND #define VALGRIND_MAKE_MEM_UNDEFINED(x, y) @@ -67,12 +73,10 @@ public: // Must return the same size for all pages created by the same pager instance virtual int size() const = 0; - StringRef asStringRef() const { - return StringRef(begin(), size()); - } + StringRef asStringRef() const { return StringRef(begin(), size()); } virtual ~IPage() { - if(userData != nullptr && userDataDestructor != nullptr) { + if (userData != nullptr && userDataDestructor != nullptr) { userDataDestructor(userData); } } @@ -82,8 +86,8 @@ public: virtual void addref() const = 0; virtual void delref() const = 0; - mutable void *userData; - mutable void (*userDataDestructor)(void *); + mutable void* userData; + mutable void (*userDataDestructor)(void*); }; class IPagerSnapshot { diff --git a/fdbserver/IVersionedStore.h b/fdbserver/IVersionedStore.h index 9baf5c4469..b1feb8063c 100644 --- a/fdbserver/IVersionedStore.h +++ b/fdbserver/IVersionedStore.h @@ -4,13 +4,13 @@ * This source file is part of the FoundationDB open source project * * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -46,28 +46,33 @@ public: class IVersionedStore : public IClosable { public: virtual KeyValueStoreType getType() = 0; - virtual bool supportsMutation(int op) = 0; // If this returns true, then mutate(op, ...) may be called + virtual bool supportsMutation(int op) = 0; // If this returns true, then mutate(op, ...) may be called virtual StorageBytes getStorageBytes() = 0; // Writes are provided in an ordered stream. - // A write is considered part of (a change leading to) the version determined by the previous call to setWriteVersion() - // A write shall not become durable until the following call to commit() begins, and shall be durable once the following call to commit() returns + // A write is considered part of (a change leading to) the version determined by the previous call to + // setWriteVersion() A write shall not become durable until the following call to commit() begins, and shall be + // durable once the following call to commit() returns virtual void set(KeyValueRef keyValue) = 0; virtual void clear(KeyRangeRef range) = 0; virtual void mutate(int op, StringRef param1, StringRef param2) = 0; - virtual void setWriteVersion(Version) = 0; // The write version must be nondecreasing - virtual void setOldestVersion(Version v) = 0; // Set oldest readable version to be used in next commit - virtual Version getOldestVersion() = 0; // Get oldest readable version + virtual void setWriteVersion(Version) = 0; // The write version must be nondecreasing + virtual void setOldestVersion(Version v) = 0; // Set oldest readable version to be used in next commit + virtual Version getOldestVersion() = 0; // Get oldest readable version virtual Future commit() = 0; virtual Future init() = 0; virtual Version getLatestVersion() = 0; - // readAtVersion() may only be called on a version which has previously been passed to setWriteVersion() and never previously passed - // to forgetVersion. The returned results when violating this precondition are unspecified; the store is not required to be able to detect violations. - // The returned read cursor provides a consistent snapshot of the versioned store, corresponding to all the writes done with write versions less + // readAtVersion() may only be called on a version which has previously been passed to setWriteVersion() and never + // previously passed + // to forgetVersion. The returned results when violating this precondition are unspecified; the store is not + // required to be able to detect violations. + // The returned read cursor provides a consistent snapshot of the versioned store, corresponding to all the writes + // done with write versions less // than or equal to the given version. - // If readAtVersion() is called on the *current* write version, the given read cursor MAY reflect subsequent writes at the same + // If readAtVersion() is called on the *current* write version, the given read cursor MAY reflect subsequent writes + // at the same // write version, OR it may represent a snapshot as of the call to readAtVersion(). virtual Reference readAtVersion(Version) = 0; }; diff --git a/fdbserver/VersionedBTree.actor.cpp b/fdbserver/VersionedBTree.actor.cpp index 99c0bf30ed..084fead508 100644 --- a/fdbserver/VersionedBTree.actor.cpp +++ b/fdbserver/VersionedBTree.actor.cpp @@ -4,13 +4,13 @@ * This source file is part of the FoundationDB open source project * * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -42,8 +42,8 @@ // Some convenience functions for debugging to stringify various structures // Classes can add compatibility by either specializing toString or implementing // std::string toString() const; -template -std::string toString(const T &o) { +template +std::string toString(const T& o) { return o.toString(); } @@ -52,27 +52,26 @@ std::string toString(StringRef s) { } std::string toString(LogicalPageID id) { - if(id == invalidLogicalPageID) { + if (id == invalidLogicalPageID) { return "LogicalPageID{invalid}"; } return format("LogicalPageID{%" PRId64 "}", id); } -template -std::string toString(const Standalone &s) { +template +std::string toString(const Standalone& s) { return toString((T)s); } -template -std::string toString(const T *begin, const T *end) { +template +std::string toString(const T* begin, const T* end) { std::string r = "{"; bool comma = false; - while(begin != end) { - if(comma) { + while (begin != end) { + if (comma) { r += ", "; - } - else { + } else { comma = true; } r += toString(*begin++); @@ -82,25 +81,25 @@ std::string toString(const T *begin, const T *end) { return r; } -template -std::string toString(const std::vector &v) { +template +std::string toString(const std::vector& v) { return toString(&v.front(), &v.back() + 1); } -template -std::string toString(const VectorRef &v) { +template +std::string toString(const VectorRef& v) { return toString(v.begin(), v.end()); } -template -std::string toString(const Optional &o) { - if(o.present()) { +template +std::string toString(const Optional& o) { + if (o.present()) { return toString(o.get()); } return ""; } -// A FIFO queue of T stored as a linked list of pages. +// A FIFO queue of T stored as a linked list of pages. // Main operations are pop(), pushBack(), pushFront(), and flush(). // // flush() will ensure all queue pages are written to the pager and move the unflushed @@ -133,64 +132,54 @@ std::string toString(const Optional &o) { // // Serialize *this to dst, return number of bytes written to dst // int writeToBytes(uint8_t *dst) const; // - must be supported by toString(object) (see above) -template +template struct FIFOQueueCodec { - static T readFromBytes(const uint8_t *src, int &bytesRead) { + static T readFromBytes(const uint8_t* src, int& bytesRead) { T x; bytesRead = x.readFromBytes(src); return x; } - static int bytesNeeded(const T &x) { - return x.bytesNeeded(); - } - static int writeToBytes(uint8_t *dst, const T &x) { - return x.writeToBytes(dst); - } + static int bytesNeeded(const T& x) { return x.bytesNeeded(); } + static int writeToBytes(uint8_t* dst, const T& x) { return x.writeToBytes(dst); } }; -template +template struct FIFOQueueCodec::value>::type> { static_assert(std::is_trivially_copyable::value); - static T readFromBytes(const uint8_t *src, int &bytesRead) { + static T readFromBytes(const uint8_t* src, int& bytesRead) { bytesRead = sizeof(T); - return *(T *)src; + return *(T*)src; } - static int bytesNeeded(const T &x) { - return sizeof(T); - } - static int writeToBytes(uint8_t *dst, const T &x) { - *(T *)dst = x; + static int bytesNeeded(const T& x) { return sizeof(T); } + static int writeToBytes(uint8_t* dst, const T& x) { + *(T*)dst = x; return sizeof(T); } }; -template> +template > class FIFOQueue { public: #pragma pack(push, 1) struct QueueState { - bool operator==(const QueueState &rhs) const { - return memcmp(this, &rhs, sizeof(QueueState)) == 0; - } + bool operator==(const QueueState& rhs) const { return memcmp(this, &rhs, sizeof(QueueState)) == 0; } LogicalPageID headPageID = invalidLogicalPageID; LogicalPageID tailPageID = invalidLogicalPageID; uint16_t headOffset; - // Note that there is no tail index because the tail page is always never-before-written and its index will start at 0 + // Note that there is no tail index because the tail page is always never-before-written and its index will + // start at 0 int64_t numPages; int64_t numEntries; std::string toString() const { - return format("{head: %s:%d tail: %s numPages: %" PRId64 " numEntries: %" PRId64 "}", ::toString(headPageID).c_str(), (int)headOffset, ::toString(tailPageID).c_str(), numPages, numEntries); + return format("{head: %s:%d tail: %s numPages: %" PRId64 " numEntries: %" PRId64 "}", + ::toString(headPageID).c_str(), (int)headOffset, ::toString(tailPageID).c_str(), numPages, + numEntries); } }; #pragma pack(pop) struct Cursor { - enum Mode { - NONE, - POP, - READONLY, - WRITE - }; + enum Mode { NONE, POP, READONLY, WRITE }; // The current page being read or written to LogicalPageID pageID; @@ -198,23 +187,23 @@ public: // The first page ID to be written to the pager, if this cursor has written anything LogicalPageID firstPageIDWritten; - // Offset after RawPage header to next read from or write to + // Offset after RawPage header to next read from or write to int offset; // A read cursor will not read this page (or beyond) LogicalPageID endPageID; Reference page; - FIFOQueue *queue; + FIFOQueue* queue; Future operation; Mode mode; - Cursor() : mode(NONE) { - } + Cursor() : mode(NONE) {} - // Initialize a cursor. - void init(FIFOQueue *q = nullptr, Mode m = NONE, LogicalPageID initialPageID = invalidLogicalPageID, int readOffset = 0, LogicalPageID endPage = invalidLogicalPageID) { - if(operation.isValid()) { + // Initialize a cursor. + void init(FIFOQueue* q = nullptr, Mode m = NONE, LogicalPageID initialPageID = invalidLogicalPageID, + int readOffset = 0, LogicalPageID endPage = invalidLogicalPageID) { + if (operation.isValid()) { operation.cancel(); } queue = q; @@ -224,44 +213,45 @@ public: endPageID = endPage; page.clear(); - if(mode == POP || mode == READONLY) { + if (mode == POP || mode == READONLY) { // If cursor is not pointed at the end page then start loading it. // The end page will not have been written to disk yet. pageID = initialPageID; operation = (pageID == endPageID) ? Void() : loadPage(); - } - else { + } else { pageID = invalidLogicalPageID; - ASSERT(mode == WRITE || (initialPageID == invalidLogicalPageID && readOffset == 0 && endPage == invalidLogicalPageID)); + ASSERT(mode == WRITE || + (initialPageID == invalidLogicalPageID && readOffset == 0 && endPage == invalidLogicalPageID)); operation = Void(); } debug_printf("FIFOQueue::Cursor(%s) initialized\n", toString().c_str()); - if(mode == WRITE && initialPageID != invalidLogicalPageID) { + if (mode == WRITE && initialPageID != invalidLogicalPageID) { addNewPage(initialPageID, 0, true); } } // Since cursors can have async operations pending which modify their state they can't be copied cleanly - Cursor(const Cursor &other) = delete; + Cursor(const Cursor& other) = delete; // A read cursor can be initialized from a pop cursor - void initReadOnly(const Cursor &c) { + void initReadOnly(const Cursor& c) { ASSERT(c.mode == READONLY || c.mode == POP); init(c.queue, READONLY, c.pageID, c.offset, c.endPageID); } - ~Cursor() { - operation.cancel(); - } + ~Cursor() { operation.cancel(); } std::string toString() const { - if(mode == WRITE) { - return format("{WriteCursor %s:%p pos=%s:%d endOffset=%d}", queue->name.c_str(), this, ::toString(pageID).c_str(), offset, page ? raw()->endOffset : -1); + if (mode == WRITE) { + return format("{WriteCursor %s:%p pos=%s:%d endOffset=%d}", queue->name.c_str(), this, + ::toString(pageID).c_str(), offset, page ? raw()->endOffset : -1); } - if(mode == POP || mode == READONLY) { - return format("{ReadCursor %s:%p pos=%s:%d endOffset=%d endPage=%s}", queue->name.c_str(), this, ::toString(pageID).c_str(), offset, page ? raw()->endOffset : -1, ::toString(endPageID).c_str()); + if (mode == POP || mode == READONLY) { + return format("{ReadCursor %s:%p pos=%s:%d endOffset=%d endPage=%s}", queue->name.c_str(), this, + ::toString(pageID).c_str(), offset, page ? raw()->endOffset : -1, + ::toString(endPageID).c_str()); } ASSERT(mode == NONE); return format("{NullCursor=%p}", this); @@ -272,28 +262,20 @@ public: LogicalPageID nextPageID; uint16_t nextOffset; uint16_t endOffset; - uint8_t * begin() { - return (uint8_t *)(this + 1); - } + uint8_t* begin() { return (uint8_t*)(this + 1); } }; #pragma pack(pop) - Future notBusy() { - return operation; - } + Future notBusy() { return operation; } // Returns true if any items have been written to the last page - bool pendingWrites() const { - return mode == WRITE && offset != 0; - } + bool pendingWrites() const { return mode == WRITE && offset != 0; } - RawPage * raw() const { - return ((RawPage *)(page->begin())); - } + RawPage* raw() const { return ((RawPage*)(page->begin())); } void setNext(LogicalPageID pageID, int offset) { ASSERT(mode == WRITE); - RawPage *p = raw(); + RawPage* p = raw(); p->nextPageID = pageID; p->nextOffset = offset; } @@ -314,21 +296,22 @@ public: VALGRIND_MAKE_MEM_DEFINED(raw()->begin(), offset); VALGRIND_MAKE_MEM_DEFINED(raw()->begin() + offset, queue->dataBytesPerPage - raw()->endOffset); queue->pager->updatePage(pageID, page); - if(firstPageIDWritten == invalidLogicalPageID) { + if (firstPageIDWritten == invalidLogicalPageID) { firstPageIDWritten = pageID; } } // Link the current page to newPageID:newOffset and then write it to the pager. - // If initializeNewPage is true a page buffer will be allocated for the new page and it will be initialized + // If initializeNewPage is true a page buffer will be allocated for the new page and it will be initialized // as a new tail page. void addNewPage(LogicalPageID newPageID, int newOffset, bool initializeNewPage) { ASSERT(mode == WRITE); ASSERT(newPageID != invalidLogicalPageID); - debug_printf("FIFOQueue::Cursor(%s) Adding page %s init=%d\n", toString().c_str(), ::toString(newPageID).c_str(), initializeNewPage); + debug_printf("FIFOQueue::Cursor(%s) Adding page %s init=%d\n", toString().c_str(), + ::toString(newPageID).c_str(), initializeNewPage); // Update existing page and write, if it exists - if(page) { + if (page) { setNext(newPageID, newOffset); debug_printf("FIFOQueue::Cursor(%s) Linked new page\n", toString().c_str()); writePage(); @@ -337,21 +320,20 @@ public: pageID = newPageID; offset = newOffset; - if(initializeNewPage) { + if (initializeNewPage) { debug_printf("FIFOQueue::Cursor(%s) Initializing new page\n", toString().c_str()); page = queue->pager->newPageBuffer(); setNext(0, 0); auto p = raw(); ASSERT(newOffset == 0); p->endOffset = 0; - } - else { + } else { page.clear(); } } // Write item to the next position in the current page or, if it won't fit, add a new page and write it there. - ACTOR static Future write_impl(Cursor *self, T item, Future start) { + ACTOR static Future write_impl(Cursor* self, T item, Future start) { ASSERT(self->mode == WRITE); // Wait for the previous operation to finish @@ -360,14 +342,16 @@ public: wait(previous); state int bytesNeeded = Codec::bytesNeeded(item); - if(self->pageID == invalidLogicalPageID || self->offset + bytesNeeded > self->queue->dataBytesPerPage) { - debug_printf("FIFOQueue::Cursor(%s) write(%s) page is full, adding new page\n", self->toString().c_str(), ::toString(item).c_str()); + if (self->pageID == invalidLogicalPageID || self->offset + bytesNeeded > self->queue->dataBytesPerPage) { + debug_printf("FIFOQueue::Cursor(%s) write(%s) page is full, adding new page\n", + self->toString().c_str(), ::toString(item).c_str()); LogicalPageID newPageID = wait(self->queue->pager->newPageID()); self->addNewPage(newPageID, 0, true); ++self->queue->numPages; wait(yield()); } - debug_printf("FIFOQueue::Cursor(%s) before write(%s)\n", self->toString().c_str(), ::toString(item).c_str()); + debug_printf("FIFOQueue::Cursor(%s) before write(%s)\n", self->toString().c_str(), + ::toString(item).c_str()); auto p = self->raw(); Codec::writeToBytes(p->begin() + self->offset, item); self->offset += bytesNeeded; @@ -376,14 +360,15 @@ public: return Void(); } - void write(const T &item) { + void write(const T& item) { Promise p; operation = write_impl(this, item, p.getFuture()); p.send(Void()); } - // Read the next item at the cursor (if <= upperBound), moving to a new page first if the current page is exhausted - ACTOR static Future> readNext_impl(Cursor *self, Optional upperBound, Future start) { + // Read the next item at the cursor (if <= upperBound), moving to a new page first if the current page is + // exhausted + ACTOR static Future> readNext_impl(Cursor* self, Optional upperBound, Future start) { ASSERT(self->mode == POP || self->mode == READONLY); // Wait for the previous operation to finish @@ -392,13 +377,13 @@ public: wait(previous); debug_printf("FIFOQueue::Cursor(%s) readNext begin\n", self->toString().c_str()); - if(self->pageID == invalidLogicalPageID || self->pageID == self->endPageID) { + if (self->pageID == invalidLogicalPageID || self->pageID == self->endPageID) { debug_printf("FIFOQueue::Cursor(%s) readNext returning nothing\n", self->toString().c_str()); return Optional(); } // We now know we are pointing to PageID and it should be read and used, but it may not be loaded yet. - if(!self->page) { + if (!self->page) { wait(self->loadPage()); wait(yield()); } @@ -409,46 +394,50 @@ public: int bytesRead; T result = Codec::readFromBytes(p->begin() + self->offset, bytesRead); - if(upperBound.present() && upperBound.get() < result) { - debug_printf("FIFOQueue::Cursor(%s) not popping %s, exceeds upper bound %s\n", - self->toString().c_str(), ::toString(result).c_str(), ::toString(upperBound.get()).c_str()); + if (upperBound.present() && upperBound.get() < result) { + debug_printf("FIFOQueue::Cursor(%s) not popping %s, exceeds upper bound %s\n", self->toString().c_str(), + ::toString(result).c_str(), ::toString(upperBound.get()).c_str()); return Optional(); } self->offset += bytesRead; - if(self->mode == POP) { + if (self->mode == POP) { --self->queue->numEntries; } - debug_printf("FIFOQueue::Cursor(%s) after read of %s\n", self->toString().c_str(), ::toString(result).c_str()); + debug_printf("FIFOQueue::Cursor(%s) after read of %s\n", self->toString().c_str(), + ::toString(result).c_str()); ASSERT(self->offset <= p->endOffset); - if(self->offset == p->endOffset) { + if (self->offset == p->endOffset) { debug_printf("FIFOQueue::Cursor(%s) Page exhausted\n", self->toString().c_str()); LogicalPageID oldPageID = self->pageID; self->pageID = p->nextPageID; self->offset = p->nextOffset; - if(self->mode == POP) { + if (self->mode == POP) { --self->queue->numPages; } self->page.clear(); - debug_printf("FIFOQueue::Cursor(%s) readNext page exhausted, moved to new page\n", self->toString().c_str()); + debug_printf("FIFOQueue::Cursor(%s) readNext page exhausted, moved to new page\n", + self->toString().c_str()); - if(self->mode == POP) { - // Freeing the old page must happen after advancing the cursor and clearing the page reference because - // freePage() could cause a push onto a queue that causes a newPageID() call which could pop() from this - // very same queue. - // Queue pages are freed at page 0 because they can be reused after the next commit. + if (self->mode == POP) { + // Freeing the old page must happen after advancing the cursor and clearing the page reference + // because freePage() could cause a push onto a queue that causes a newPageID() call which could + // pop() from this very same queue. Queue pages are freed at page 0 because they can be reused after + // the next commit. self->queue->pager->freePage(oldPageID, 0); } } - debug_printf("FIFOQueue(%s) %s(upperBound=%s) -> %s\n", self->queue->name.c_str(), (self->mode == POP ? "pop" : "peek"), ::toString(upperBound).c_str(), ::toString(result).c_str()); + debug_printf("FIFOQueue(%s) %s(upperBound=%s) -> %s\n", self->queue->name.c_str(), + (self->mode == POP ? "pop" : "peek"), ::toString(upperBound).c_str(), + ::toString(result).c_str()); return result; } // Read and move past the next item if is <= upperBound or if upperBound is not present - Future> readNext(const Optional &upperBound = {}) { - if(mode == NONE) { + Future> readNext(const Optional& upperBound = {}) { + if (mode == NONE) { return Optional(); } Promise p; @@ -460,18 +449,15 @@ public: }; public: - FIFOQueue() : pager(nullptr) { - } + FIFOQueue() : pager(nullptr) {} - ~FIFOQueue() { - newTailPage.cancel(); - } + ~FIFOQueue() { newTailPage.cancel(); } - FIFOQueue(const FIFOQueue &other) = delete; - void operator=(const FIFOQueue &rhs) = delete; + FIFOQueue(const FIFOQueue& other) = delete; + void operator=(const FIFOQueue& rhs) = delete; // Create a new queue at newPageID - void create(IPager2 *p, LogicalPageID newPageID, std::string queueName) { + void create(IPager2* p, LogicalPageID newPageID, std::string queueName) { debug_printf("FIFOQueue(%s) create from page %s\n", queueName.c_str(), toString(newPageID).c_str()); pager = p; name = queueName; @@ -486,7 +472,7 @@ public: } // Load an existing queue from its queue state - void recover(IPager2 *p, const QueueState &qs, std::string queueName) { + void recover(IPager2* p, const QueueState& qs, std::string queueName) { debug_printf("FIFOQueue(%s) recover from queue state %s\n", queueName.c_str(), qs.toString().c_str()); pager = p; name = queueName; @@ -500,7 +486,7 @@ public: debug_printf("FIFOQueue(%s) recovered\n", queueName.c_str()); } - ACTOR static Future>> peekAll_impl(FIFOQueue *self) { + ACTOR static Future>> peekAll_impl(FIFOQueue* self) { state Standalone> results; state Cursor c; c.initReadOnly(self->headReader); @@ -508,7 +494,7 @@ public: loop { Optional x = wait(c.readNext()); - if(!x.present()) { + if (!x.present()) { break; } results.push_back(results.arena(), x.get()); @@ -517,14 +503,10 @@ public: return results; } - Future>> peekAll() { - return peekAll_impl(this); - } + Future>> peekAll() { return peekAll_impl(this); } // Pop the next item on front of queue if it is <= upperBound or if upperBound is not present - Future> pop(Optional upperBound = {}) { - return headReader.readNext(upperBound); - } + Future> pop(Optional upperBound = {}) { return headReader.readNext(upperBound); } QueueState getState() const { QueueState s; @@ -538,12 +520,12 @@ public: return s; } - void pushBack(const T &item) { + void pushBack(const T& item) { debug_printf("FIFOQueue(%s) pushBack(%s)\n", name.c_str(), toString(item).c_str()); tailWriter.write(item); } - void pushFront(const T &item) { + void pushFront(const T& item) { debug_printf("FIFOQueue(%s) pushFront(%s)\n", name.c_str(), toString(item).c_str()); headWriter.write(item); } @@ -555,7 +537,8 @@ public: // Returns true if any most recently started operations on any cursors are not ready bool busy() { - return !headWriter.notBusy().isReady() || !headReader.notBusy().isReady() || !tailWriter.notBusy().isReady() || !newTailPage.isReady(); + return !headWriter.notBusy().isReady() || !headReader.notBusy().isReady() || !tailWriter.notBusy().isReady() || + !newTailPage.isReady(); } // preFlush() prepares this queue to be flushed to disk, but doesn't actually do it so the queue can still @@ -571,7 +554,7 @@ public: // - queue push() can call pager->newPageID() which can call pop() on the same or another queue // This creates a circular dependency with 1 or more queues when those queues are used by the pager // to manage free page IDs. - ACTOR static Future preFlush_impl(FIFOQueue *self) { + ACTOR static Future preFlush_impl(FIFOQueue* self) { debug_printf("FIFOQueue(%s) preFlush begin\n", self->name.c_str()); wait(self->notBusy()); @@ -579,14 +562,15 @@ public: // so see if any work is pending now. bool workPending = self->busy(); - if(!workPending) { + if (!workPending) { // A newly created or flushed queue starts out in a state where its tail page to be written to is empty. - // After pushBack() is called, this is no longer the case and never will be again until the queue is flushed. - // Before the non-empty tail page is written it must be linked to a new empty page for use after the next - // flush. (This is explained more at the top of FIFOQueue but it is because queue pages can only be written - // once because once they contain durable data a second write to link to a new page could corrupt the existing - // data if the subsequent commit never succeeds.) - if(self->newTailPage.isReady() && self->newTailPage.get() == invalidLogicalPageID && self->tailWriter.pendingWrites()) { + // After pushBack() is called, this is no longer the case and never will be again until the queue is + // flushed. Before the non-empty tail page is written it must be linked to a new empty page for use after + // the next flush. (This is explained more at the top of FIFOQueue but it is because queue pages can only + // be written once because once they contain durable data a second write to link to a new page could corrupt + // the existing data if the subsequent commit never succeeds.) + if (self->newTailPage.isReady() && self->newTailPage.get() == invalidLogicalPageID && + self->tailWriter.pendingWrites()) { self->newTailPage = self->pager->newPageID(); workPending = true; } @@ -596,16 +580,14 @@ public: return workPending; } - Future preFlush() { - return preFlush_impl(this); - } + Future preFlush() { return preFlush_impl(this); } void finishFlush() { debug_printf("FIFOQueue(%s) finishFlush start\n", name.c_str()); ASSERT(!busy()); // If a new tail page was allocated, link the last page of the tail writer to it. - if(newTailPage.get() != invalidLogicalPageID) { + if (newTailPage.get() != invalidLogicalPageID) { tailWriter.addNewPage(newTailPage.get(), 0, false); // The flush sequence allocated a page and added it to the queue so increment numPages ++numPages; @@ -618,7 +600,7 @@ public: // If the headWriter wrote anything, link its tail page to the headReader position and point the headReader // to the start of the headWriter - if(headWriter.pendingWrites()) { + if (headWriter.pendingWrites()) { headWriter.addNewPage(headReader.pageID, headReader.offset, false); headReader.pageID = headWriter.firstPageIDWritten; headReader.offset = 0; @@ -635,10 +617,10 @@ public: debug_printf("FIFOQueue(%s) finishFlush end\n", name.c_str()); } - ACTOR static Future flush_impl(FIFOQueue *self) { + ACTOR static Future flush_impl(FIFOQueue* self) { loop { bool notDone = wait(self->preFlush()); - if(!notDone) { + if (!notDone) { break; } } @@ -646,15 +628,13 @@ public: return Void(); } - Future flush() { - return flush_impl(this); - } + Future flush() { return flush_impl(this); } - IPager2 *pager; + IPager2* pager; int64_t numPages; int64_t numEntries; int dataBytesPerPage; - + Cursor headReader; Cursor tailWriter; Cursor headWriter; @@ -673,63 +653,44 @@ class FastAllocatedPage : public IPage, public FastAllocated, public: // Create a fast-allocated page with size total bytes INCLUDING checksum FastAllocatedPage(int size, int bufferSize) : logicalSize(size), bufferSize(bufferSize) { - buffer = (uint8_t *)allocateFast(bufferSize); + buffer = (uint8_t*)allocateFast(bufferSize); // Mark any unused page portion defined VALGRIND_MAKE_MEM_DEFINED(buffer + logicalSize, bufferSize - logicalSize); }; - virtual ~FastAllocatedPage() { - freeFast(bufferSize, buffer); - } + virtual ~FastAllocatedPage() { freeFast(bufferSize, buffer); } virtual Reference clone() const { - FastAllocatedPage *p = new FastAllocatedPage(logicalSize, bufferSize); + FastAllocatedPage* p = new FastAllocatedPage(logicalSize, bufferSize); memcpy(p->buffer, buffer, logicalSize); return Reference(p); } // Usable size, without checksum - int size() const { - return logicalSize - sizeof(Checksum); - } + int size() const { return logicalSize - sizeof(Checksum); } - uint8_t const* begin() const { - return buffer; - } + uint8_t const* begin() const { return buffer; } - uint8_t* mutate() { - return buffer; - } + uint8_t* mutate() { return buffer; } - void addref() const { - ReferenceCounted::addref(); - } + void addref() const { ReferenceCounted::addref(); } + + void delref() const { ReferenceCounted::delref(); } - void delref() const { - ReferenceCounted::delref(); - } - typedef uint32_t Checksum; - Checksum & getChecksum() { - return *(Checksum *)(buffer + size()); - } + Checksum& getChecksum() { return *(Checksum*)(buffer + size()); } - Checksum calculateChecksum(LogicalPageID pageID) { - return crc32c_append(pageID, buffer, size()); - } + Checksum calculateChecksum(LogicalPageID pageID) { return crc32c_append(pageID, buffer, size()); } - void updateChecksum(LogicalPageID pageID) { - getChecksum() = calculateChecksum(pageID); - } + void updateChecksum(LogicalPageID pageID) { getChecksum() = calculateChecksum(pageID); } + + bool verifyChecksum(LogicalPageID pageID) { return getChecksum() == calculateChecksum(pageID); } - bool verifyChecksum(LogicalPageID pageID) { - return getChecksum() == calculateChecksum(pageID); - } private: int logicalSize; int bufferSize; - uint8_t *buffer; + uint8_t* buffer; }; // Holds an index of recently used objects. @@ -737,12 +698,11 @@ private: // bool evictable() const; // return true if the entry can be evicted // Future onEvictable() const; // ready when entry can be evicted // indicating if it is safe to evict. -template +template class ObjectCache : NonCopyable { struct Entry : public boost::intrusive::list_base_hook<> { - Entry() : hits(0) { - } + Entry() : hits(0) {} IndexType index; ObjectType item; int hits; @@ -752,8 +712,8 @@ class ObjectCache : NonCopyable { typedef boost::intrusive::list EvictionOrderT; public: - ObjectCache(int sizeLimit = 1) : sizeLimit(sizeLimit), cacheHits(0), cacheMisses(0), noHitEvictions(0), failedEvictions(0) { - } + ObjectCache(int sizeLimit = 1) + : sizeLimit(sizeLimit), cacheHits(0), cacheMisses(0), noHitEvictions(0), failedEvictions(0) {} void setSizeLimit(int n) { ASSERT(n > 0); @@ -762,9 +722,9 @@ public: // Get the object for i if it exists, else return nullptr. // If the object exists, its eviction order will NOT change as this is not a cache hit. - ObjectType * getIfExists(const IndexType &index) { + ObjectType* getIfExists(const IndexType& index) { auto i = cache.find(index); - if(i != cache.end()) { + if (i != cache.end()) { ++i->second.hits; return &i->second.item; } @@ -773,20 +733,19 @@ public: // Get the object for i or create a new one. // After a get(), the object for i is the last in evictionOrder. - ObjectType & get(const IndexType &index, bool noHit = false) { - Entry &entry = cache[index]; + ObjectType& get(const IndexType& index, bool noHit = false) { + Entry& entry = cache[index]; // If entry is linked into evictionOrder then move it to the back of the order - if(entry.is_linked()) { - if(!noHit) { + if (entry.is_linked()) { + if (!noHit) { ++entry.hits; ++cacheHits; } // Move the entry to the back of the eviction order evictionOrder.erase(evictionOrder.iterator_to(entry)); evictionOrder.push_back(entry); - } - else { + } else { ++cacheMisses; // Finish initializing entry entry.index = index; @@ -795,25 +754,27 @@ public: evictionOrder.push_back(entry); // While the cache is too big, evict the oldest entry until the oldest entry can't be evicted. - while(cache.size() > sizeLimit) { - Entry &toEvict = evictionOrder.front(); - debug_printf("Trying to evict %s to make room for %s\n", toString(toEvict.index).c_str(), toString(index).c_str()); + while (cache.size() > sizeLimit) { + Entry& toEvict = evictionOrder.front(); + debug_printf("Trying to evict %s to make room for %s\n", toString(toEvict.index).c_str(), + toString(index).c_str()); - // It's critical that we do not evict the item we just added (or the reference we return would be invalid) but - // since sizeLimit must be > 0, entry was just added to the end of the evictionOrder, and this loop will end - // if we move anything to the end of the eviction order, we can be guaraunted that entry != toEvict, so we - // do not need to check. - // If the item is not evictable then move it to the back of the eviction order and stop. - if(!toEvict.item.evictable()) { + // It's critical that we do not evict the item we just added (or the reference we return would be + // invalid) but since sizeLimit must be > 0, entry was just added to the end of the evictionOrder, and + // this loop will end if we move anything to the end of the eviction order, we can be guaraunted that + // entry != toEvict, so we do not need to check. If the item is not evictable then move it to the back + // of the eviction order and stop. + if (!toEvict.item.evictable()) { evictionOrder.erase(evictionOrder.iterator_to(toEvict)); evictionOrder.push_back(toEvict); ++failedEvictions; break; } else { - if(toEvict.hits == 0) { + if (toEvict.hits == 0) { ++noHitEvictions; } - debug_printf("Evicting %s to make room for %s\n", toString(toEvict.index).c_str(), toString(index).c_str()); + debug_printf("Evicting %s to make room for %s\n", toString(toEvict.index).c_str(), + toString(index).c_str()); evictionOrder.pop_front(); cache.erase(toEvict.index); } @@ -825,12 +786,12 @@ public: // Clears the cache, saving the entries, and then waits for eachWaits for each item to be evictable and evicts it. // The cache should not be Evicts all evictable entries - ACTOR static Future clear_impl(ObjectCache *self) { + ACTOR static Future clear_impl(ObjectCache* self) { state ObjectCache::CacheT cache; state EvictionOrderT evictionOrder; // Swap cache contents to local state vars - // After this, no more entries will be added to or read from these + // After this, no more entries will be added to or read from these // structures so we know for sure that no page will become unevictable // after it is either evictable or onEvictable() is ready. cache.swap(self->cache); @@ -839,8 +800,8 @@ public: state typename EvictionOrderT::iterator i = evictionOrder.begin(); state typename EvictionOrderT::iterator iEnd = evictionOrder.begin(); - while(i != iEnd) { - if(!i->item.evictable()) { + while (i != iEnd) { + if (!i->item.evictable()) { wait(i->item.onEvictable()); } ++i; @@ -852,9 +813,7 @@ public: return Void(); } - Future clear() { - return clear_impl(this); - } + Future clear() { return clear_impl(this); } int count() const { ASSERT(evictionOrder.size() == cache.size()); @@ -872,13 +831,13 @@ private: EvictionOrderT evictionOrder; }; -ACTOR template Future forwardError(Future f, Promise target) { +ACTOR template +Future forwardError(Future f, Promise target) { try { T x = wait(f); return x; - } - catch(Error &e) { - if(e.code() != error_code_actor_cancelled && target.canBeSet()) { + } catch (Error& e) { + if (e.code() != error_code_actor_cancelled && target.canBeSet()) { target.sendError(e); } @@ -892,7 +851,7 @@ class DWALPagerSnapshot; // It does this internally mapping the original page ID to alternate page IDs by write version. // The page id remaps are kept in memory and also logged to a "remap queue" which must be reloaded on cold start. // To prevent the set of remaps from growing unboundedly, once a remap is old enough to be at or before the -// oldest pager version being maintained the remap can be "undone" by popping it from the remap queue, +// oldest pager version being maintained the remap can be "undone" by popping it from the remap queue, // copying the alternate page ID's data over top of the original page ID's data, and deleting the remap from memory. // This process basically describes a "Delayed" Write-Ahead-Log (DWAL) because the remap queue and the newly allocated // alternate pages it references basically serve as a write ahead log for pages that will eventially be copied @@ -907,9 +866,7 @@ public: Version version; LogicalPageID pageID; - bool operator<(const DelayedFreePage &rhs) const { - return version < rhs.version; - } + bool operator<(const DelayedFreePage& rhs) const { return version < rhs.version; } std::string toString() const { return format("DelayedFreePage{%s @%" PRId64 "}", ::toString(pageID).c_str(), version); @@ -921,12 +878,11 @@ public: LogicalPageID originalPageID; LogicalPageID newPageID; - bool operator<(const RemappedPage &rhs) { - return version < rhs.version; - } + bool operator<(const RemappedPage& rhs) { return version < rhs.version; } std::string toString() const { - return format("RemappedPage(%s -> %s @%" PRId64 "}", ::toString(originalPageID).c_str(), ::toString(newPageID).c_str(), version); + return format("RemappedPage(%s -> %s @%" PRId64 "}", ::toString(originalPageID).c_str(), + ::toString(newPageID).c_str(), version); } }; @@ -938,10 +894,11 @@ public: // If the file already exists, pageSize might be different than desiredPageSize // Use pageCacheSizeBytes == 0 for default DWALPager(int desiredPageSize, std::string filename, int64_t pageCacheSizeBytes) - : desiredPageSize(desiredPageSize), filename(filename), pHeader(nullptr), pageCacheBytes(pageCacheSizeBytes) - { - if(pageCacheBytes == 0) { - pageCacheBytes = g_network->isSimulated() ? (BUGGIFY ? FLOW_KNOBS->BUGGIFY_SIM_PAGE_CACHE_4K : FLOW_KNOBS->SIM_PAGE_CACHE_4K) : FLOW_KNOBS->PAGE_CACHE_4K; + : desiredPageSize(desiredPageSize), filename(filename), pHeader(nullptr), pageCacheBytes(pageCacheSizeBytes) { + if (pageCacheBytes == 0) { + pageCacheBytes = g_network->isSimulated() + ? (BUGGIFY ? FLOW_KNOBS->BUGGIFY_SIM_PAGE_CACHE_4K : FLOW_KNOBS->SIM_PAGE_CACHE_4K) + : FLOW_KNOBS->PAGE_CACHE_4K; } commitFuture = Void(); recoverFuture = forwardError(recover(this), errorPromise); @@ -950,10 +907,10 @@ public: void setPageSize(int size) { logicalPageSize = size; physicalPageSize = smallestPhysicalBlock; - while(logicalPageSize > physicalPageSize) { + while (logicalPageSize > physicalPageSize) { physicalPageSize += smallestPhysicalBlock; } - if(pHeader != nullptr) { + if (pHeader != nullptr) { pHeader->pageSize = logicalPageSize; } pageCache.setSizeLimit(pageCacheBytes / physicalPageSize); @@ -963,14 +920,15 @@ public: memcpy(lastCommittedHeaderPage->mutate(), headerPage->begin(), smallestPhysicalBlock); } - ACTOR static Future recover(DWALPager *self) { + ACTOR static Future recover(DWALPager* self) { ASSERT(!self->recoverFuture.isValid()); self->remapUndoFuture = Void(); - int64_t flags = IAsyncFile::OPEN_UNCACHED | IAsyncFile::OPEN_UNBUFFERED | IAsyncFile::OPEN_READWRITE | IAsyncFile::OPEN_LOCK; + int64_t flags = IAsyncFile::OPEN_UNCACHED | IAsyncFile::OPEN_UNBUFFERED | IAsyncFile::OPEN_READWRITE | + IAsyncFile::OPEN_LOCK; state bool exists = fileExists(self->filename); - if(!exists) { + if (!exists) { flags |= IAsyncFile::OPEN_ATOMIC_WRITE_AND_CREATE | IAsyncFile::OPEN_CREATE; } @@ -979,19 +937,20 @@ public: // Header page is always treated as having a page size of smallestPhysicalBlock self->setPageSize(smallestPhysicalBlock); self->lastCommittedHeaderPage = self->newPageBuffer(); - self->pLastCommittedHeader = (Header *)self->lastCommittedHeaderPage->begin(); + self->pLastCommittedHeader = (Header*)self->lastCommittedHeaderPage->begin(); state int64_t fileSize = 0; - if(exists) { + if (exists) { wait(store(fileSize, self->pageFile->size())); } - debug_printf("DWALPager(%s) recover exists=%d fileSize=%" PRId64 "\n", self->filename.c_str(), exists, fileSize); + debug_printf("DWALPager(%s) recover exists=%d fileSize=%" PRId64 "\n", self->filename.c_str(), exists, + fileSize); // TODO: If the file exists but appears to never have been successfully committed is this an error or // should recovery proceed with a new pager instance? // If there are at least 2 pages then try to recover the existing file - if(exists && fileSize >= (self->smallestPhysicalBlock * 2)) { + if (exists && fileSize >= (self->smallestPhysicalBlock * 2)) { debug_printf("DWALPager(%s) recovering using existing file\n"); state bool recoveredHeader = false; @@ -1000,44 +959,42 @@ public: wait(store(self->headerPage, self->readHeaderPage(self, 0))); // If the checksum fails for the header page, try to recover committed header backup from page 1 - if(!self->headerPage.castTo()->verifyChecksum(0)) { + if (!self->headerPage.castTo()->verifyChecksum(0)) { TraceEvent(SevWarn, "DWALPagerRecoveringHeader").detail("Filename", self->filename); - + wait(store(self->headerPage, self->readHeaderPage(self, 1))); - if(!self->headerPage.castTo()->verifyChecksum(1)) { - if(g_network->isSimulated()) { + if (!self->headerPage.castTo()->verifyChecksum(1)) { + if (g_network->isSimulated()) { // TODO: Detect if process is being restarted and only throw injected if so? throw io_error().asInjectedFault(); } Error e = checksum_failed(); - TraceEvent(SevError, "DWALPagerRecoveryFailed") - .detail("Filename", self->filename) - .error(e); + TraceEvent(SevError, "DWALPagerRecoveryFailed").detail("Filename", self->filename).error(e); throw e; } recoveredHeader = true; } - self->pHeader = (Header *)self->headerPage->begin(); + self->pHeader = (Header*)self->headerPage->begin(); - if(self->pHeader->formatVersion != Header::FORMAT_VERSION) { - Error e = internal_error(); // TODO: Something better? + if (self->pHeader->formatVersion != Header::FORMAT_VERSION) { + Error e = internal_error(); // TODO: Something better? TraceEvent(SevError, "DWALPagerRecoveryFailedWrongVersion") - .detail("Filename", self->filename) - .detail("Version", self->pHeader->formatVersion) - .detail("ExpectedVersion", Header::FORMAT_VERSION) - .error(e); + .detail("Filename", self->filename) + .detail("Version", self->pHeader->formatVersion) + .detail("ExpectedVersion", Header::FORMAT_VERSION) + .error(e); throw e; } self->setPageSize(self->pHeader->pageSize); - if(self->logicalPageSize != self->desiredPageSize) { + if (self->logicalPageSize != self->desiredPageSize) { TraceEvent(SevWarn, "DWALPagerPageSizeNotDesired") - .detail("Filename", self->filename) - .detail("ExistingPageSize", self->logicalPageSize) - .detail("DesiredPageSize", self->desiredPageSize); + .detail("Filename", self->filename) + .detail("ExistingPageSize", self->logicalPageSize) + .detail("DesiredPageSize", self->desiredPageSize); } self->freeList.recover(self, self->pHeader->freeList, "FreeListRecovered"); @@ -1045,15 +1002,15 @@ public: self->remapQueue.recover(self, self->pHeader->remapQueue, "RemapQueueRecovered"); Standalone> remaps = wait(self->remapQueue.peekAll()); - for(auto &r : remaps) { - if(r.newPageID != invalidLogicalPageID) { + for (auto& r : remaps) { + if (r.newPageID != invalidLogicalPageID) { self->remappedPages[r.originalPageID][r.version] = r.newPageID; } } // If the header was recovered from the backup at Page 1 then write and sync it to Page 0 before continuing. // If this fails, the backup header is still in tact for the next recovery attempt. - if(recoveredHeader) { + if (recoveredHeader) { // Write the header to page 0 wait(self->writeHeaderPage(0, self->headerPage)); @@ -1065,19 +1022,19 @@ public: debug_printf("DWALPager(%s) Header recovery complete.\n", self->filename.c_str()); } - // Update the last committed header with the one that was recovered (which is the last known committed header) + // Update the last committed header with the one that was recovered (which is the last known committed + // header) self->updateCommittedHeader(); self->addLatestSnapshot(); - } - else { - // Note: If the file contains less than 2 pages but more than 0 bytes then the pager was never successfully committed. - // A new pager will be created in its place. + } else { + // Note: If the file contains less than 2 pages but more than 0 bytes then the pager was never successfully + // committed. A new pager will be created in its place. // TODO: Is the right behavior? debug_printf("DWALPager(%s) creating new pager\n"); self->headerPage = self->newPageBuffer(); - self->pHeader = (Header *)self->headerPage->begin(); + self->pHeader = (Header*)self->headerPage->begin(); // Now that the header page has been allocated, set page size to desired self->setPageSize(self->desiredPageSize); @@ -1107,7 +1064,8 @@ public: self->pHeader->remapQueue = self->remapQueue.getState(); // Set remaining header bytes to \xff - memset(self->headerPage->mutate() + self->pHeader->size(), 0xff, self->headerPage->size() - self->pHeader->size()); + memset(self->headerPage->mutate() + self->pHeader->size(), 0xff, + self->headerPage->size() - self->pHeader->size()); // Since there is no previously committed header use the initial header for the initial commit. self->updateCommittedHeader(); @@ -1115,7 +1073,9 @@ public: wait(self->commit()); } - debug_printf("DWALPager(%s) recovered. committedVersion=%" PRId64 " logicalPageSize=%d physicalPageSize=%d\n", self->filename.c_str(), self->pHeader->committedVersion, self->logicalPageSize, self->physicalPageSize); + debug_printf("DWALPager(%s) recovered. committedVersion=%" PRId64 " logicalPageSize=%d physicalPageSize=%d\n", + self->filename.c_str(), self->pHeader->committedVersion, self->logicalPageSize, + self->physicalPageSize); return Void(); } @@ -1125,31 +1085,34 @@ public: // Returns the usable size of pages returned by the pager (i.e. the size of the page that isn't pager overhead). // For a given pager instance, separate calls to this function must return the same value. - int getUsablePageSize() override { - return logicalPageSize - sizeof(FastAllocatedPage::Checksum); - } + int getUsablePageSize() override { return logicalPageSize - sizeof(FastAllocatedPage::Checksum); } // Get a new, previously available page ID. The page will be considered in-use after the next commit // regardless of whether or not it was written to, until it is returned to the pager via freePage() - ACTOR static Future newPageID_impl(DWALPager *self) { + ACTOR static Future newPageID_impl(DWALPager* self) { // First try the free list Optional freePageID = wait(self->freeList.pop()); - if(freePageID.present()) { - debug_printf("DWALPager(%s) newPageID() returning %s from free list\n", self->filename.c_str(), toString(freePageID.get()).c_str()); + if (freePageID.present()) { + debug_printf("DWALPager(%s) newPageID() returning %s from free list\n", self->filename.c_str(), + toString(freePageID.get()).c_str()); return freePageID.get(); } - // Try to reuse pages up to the earlier of the oldest version set by the user or the oldest snapshot still in the snapshots list + // Try to reuse pages up to the earlier of the oldest version set by the user or the oldest snapshot still in + // the snapshots list ASSERT(!self->snapshots.empty()); - Optional delayedFreePageID = wait(self->delayedFreeList.pop(DelayedFreePage{self->effectiveOldestVersion(), 0})); - if(delayedFreePageID.present()) { - debug_printf("DWALPager(%s) newPageID() returning %s from delayed free list\n", self->filename.c_str(), toString(delayedFreePageID.get()).c_str()); + Optional delayedFreePageID = + wait(self->delayedFreeList.pop(DelayedFreePage{ self->effectiveOldestVersion(), 0 })); + if (delayedFreePageID.present()) { + debug_printf("DWALPager(%s) newPageID() returning %s from delayed free list\n", self->filename.c_str(), + toString(delayedFreePageID.get()).c_str()); return delayedFreePageID.get().pageID; } // Lastly, add a new page to the pager LogicalPageID id = self->newLastPageID(); - debug_printf("DWALPager(%s) newPageID() returning %s at end of file\n", self->filename.c_str(), toString(id).c_str()); + debug_printf("DWALPager(%s) newPageID() returning %s at end of file\n", self->filename.c_str(), + toString(id).c_str()); return id; }; @@ -1160,22 +1123,24 @@ public: return id; } - Future newPageID() override { - return newPageID_impl(this); - } + Future newPageID() override { return newPageID_impl(this); } Future writePhysicalPage(PhysicalPageID pageID, Reference page, bool header = false) { - debug_printf("DWALPager(%s) op=%s %s ptr=%p\n", filename.c_str(), (header ? "writePhysicalHeader" : "writePhysical"), toString(pageID).c_str(), page->begin()); + debug_printf("DWALPager(%s) op=%s %s ptr=%p\n", filename.c_str(), + (header ? "writePhysicalHeader" : "writePhysical"), toString(pageID).c_str(), page->begin()); VALGRIND_MAKE_MEM_DEFINED(page->begin(), page->size()); - ((Page *)page.getPtr())->updateChecksum(pageID); + ((Page*)page.getPtr())->updateChecksum(pageID); // Note: Not using forwardError here so a write error won't be discovered until commit time. int blockSize = header ? smallestPhysicalBlock : physicalPageSize; - Future f = holdWhile(page, map(pageFile->write(page->begin(), blockSize, (int64_t)pageID * blockSize), [=](Void) { - debug_printf("DWALPager(%s) op=%s %s ptr=%p\n", filename.c_str(), (header ? "writePhysicalHeaderComplete" : "writePhysicalComplete"), toString(pageID).c_str(), page->begin()); - return Void(); - })); + Future f = + holdWhile(page, map(pageFile->write(page->begin(), blockSize, (int64_t)pageID * blockSize), [=](Void) { + debug_printf("DWALPager(%s) op=%s %s ptr=%p\n", filename.c_str(), + (header ? "writePhysicalHeaderComplete" : "writePhysicalComplete"), + toString(pageID).c_str(), page->begin()); + return Void(); + })); operations.add(f); return f; } @@ -1186,8 +1151,11 @@ public: void updatePage(LogicalPageID pageID, Reference data) override { // Get the cache entry for this page, without counting it as a cache hit as we're replacing its contents now - PageCacheEntry &cacheEntry = pageCache.get(pageID, true); - debug_printf("DWALPager(%s) op=write %s cached=%d reading=%d writing=%d\n", filename.c_str(), toString(pageID).c_str(), cacheEntry.initialized(), cacheEntry.initialized() && cacheEntry.reading(), cacheEntry.initialized() && cacheEntry.writing()); + PageCacheEntry& cacheEntry = pageCache.get(pageID, true); + debug_printf("DWALPager(%s) op=write %s cached=%d reading=%d writing=%d\n", filename.c_str(), + toString(pageID).c_str(), cacheEntry.initialized(), + cacheEntry.initialized() && cacheEntry.reading(), + cacheEntry.initialized() && cacheEntry.writing()); // If the page is still being read then it's not also being written because a write places // the new content into readFuture when the write is launched, not when it is completed. @@ -1195,25 +1163,23 @@ public: // is necessary for remap erasure to work correctly since the oldest version of a page, located // at the original page ID, could have a pending read when that version is expired and the write // of the next newest version over top of the original page begins. - if(!cacheEntry.initialized()) { + if (!cacheEntry.initialized()) { cacheEntry.writeFuture = writePhysicalPage(pageID, data); - } - else if(cacheEntry.reading()) { + } else if (cacheEntry.reading()) { // Wait for the read to finish, then start the write. cacheEntry.writeFuture = map(success(cacheEntry.readFuture), [=](Void) { writePhysicalPage(pageID, data); return Void(); }); - } + } // If the page is being written, wait for this write before issuing the new write to ensure the // writes happen in the correct order - else if(cacheEntry.writing()) { + else if (cacheEntry.writing()) { cacheEntry.writeFuture = map(cacheEntry.writeFuture, [=](Void) { writePhysicalPage(pageID, data); return Void(); }); - } - else { + } else { cacheEntry.writeFuture = writePhysicalPage(pageID, data); } @@ -1227,7 +1193,7 @@ public: Future f = map(newPageID(), [=](LogicalPageID newPageID) { updatePage(newPageID, data); // TODO: Possibly limit size of remap queue since it must be recovered on cold start - RemappedPage r{v, pageID, newPageID}; + RemappedPage r{ v, pageID, newPageID }; remapQueue.pushBack(r); remappedPages[pageID][v] = newPageID; debug_printf("DWALPager(%s) pushed %s\n", filename.c_str(), RemappedPage(r).toString().c_str()); @@ -1239,62 +1205,71 @@ public: } void freePage(LogicalPageID pageID, Version v) override { - // If pageID has been remapped, then it can't be freed until all existing remaps for that page have been undone, so queue it for later deletion - if(remappedPages.find(pageID) != remappedPages.end()) { - debug_printf("DWALPager(%s) op=freeRemapped %s @%" PRId64 " oldestVersion=%" PRId64 "\n", filename.c_str(), toString(pageID).c_str(), v, pLastCommittedHeader->oldestVersion); - remapQueue.pushBack(RemappedPage{v, pageID, invalidLogicalPageID}); + // If pageID has been remapped, then it can't be freed until all existing remaps for that page have been undone, + // so queue it for later deletion + if (remappedPages.find(pageID) != remappedPages.end()) { + debug_printf("DWALPager(%s) op=freeRemapped %s @%" PRId64 " oldestVersion=%" PRId64 "\n", filename.c_str(), + toString(pageID).c_str(), v, pLastCommittedHeader->oldestVersion); + remapQueue.pushBack(RemappedPage{ v, pageID, invalidLogicalPageID }); return; } // If v is older than the oldest version still readable then mark pageID as free as of the next commit - if(v < effectiveOldestVersion()) { - debug_printf("DWALPager(%s) op=freeNow %s @%" PRId64 " oldestVersion=%" PRId64 "\n", filename.c_str(), toString(pageID).c_str(), v, pLastCommittedHeader->oldestVersion); + if (v < effectiveOldestVersion()) { + debug_printf("DWALPager(%s) op=freeNow %s @%" PRId64 " oldestVersion=%" PRId64 "\n", filename.c_str(), + toString(pageID).c_str(), v, pLastCommittedHeader->oldestVersion); freeList.pushBack(pageID); - } - else { + } else { // Otherwise add it to the delayed free list - debug_printf("DWALPager(%s) op=freeLater %s @%" PRId64 " oldestVersion=%" PRId64 "\n", filename.c_str(), toString(pageID).c_str(), v, pLastCommittedHeader->oldestVersion); - delayedFreeList.pushBack({v, pageID}); + debug_printf("DWALPager(%s) op=freeLater %s @%" PRId64 " oldestVersion=%" PRId64 "\n", filename.c_str(), + toString(pageID).c_str(), v, pLastCommittedHeader->oldestVersion); + delayedFreeList.pushBack({ v, pageID }); } }; // Read a physical page from the page file. Note that header pages use a page size of smallestPhysicalBlock // If the user chosen physical page size is larger, then there will be a gap of unused space after the header pages // and before the user-chosen sized pages. - ACTOR static Future> readPhysicalPage(DWALPager *self, PhysicalPageID pageID, bool header = false) { - if(g_network->getCurrentTask() > TaskPriority::DiskRead) { + ACTOR static Future> readPhysicalPage(DWALPager* self, PhysicalPageID pageID, + bool header = false) { + if (g_network->getCurrentTask() > TaskPriority::DiskRead) { wait(delay(0, TaskPriority::DiskRead)); } - state Reference page = header ? Reference(new FastAllocatedPage(smallestPhysicalBlock, smallestPhysicalBlock)) : self->newPageBuffer(); - debug_printf("DWALPager(%s) op=readPhysicalStart %s ptr=%p\n", self->filename.c_str(), toString(pageID).c_str(), page->begin()); + state Reference page = + header ? Reference(new FastAllocatedPage(smallestPhysicalBlock, smallestPhysicalBlock)) + : self->newPageBuffer(); + debug_printf("DWALPager(%s) op=readPhysicalStart %s ptr=%p\n", self->filename.c_str(), toString(pageID).c_str(), + page->begin()); int blockSize = header ? smallestPhysicalBlock : self->physicalPageSize; // TODO: Could a dispatched read try to write to page after it has been destroyed if this actor is cancelled? int readBytes = wait(self->pageFile->read(page->mutate(), blockSize, (int64_t)pageID * blockSize)); - debug_printf("DWALPager(%s) op=readPhysicalComplete %s ptr=%p bytes=%d\n", self->filename.c_str(), toString(pageID).c_str(), page->begin(), readBytes); + debug_printf("DWALPager(%s) op=readPhysicalComplete %s ptr=%p bytes=%d\n", self->filename.c_str(), + toString(pageID).c_str(), page->begin(), readBytes); // Header reads are checked explicitly during recovery - if(!header) { - Page *p = (Page *)page.getPtr(); - if(!p->verifyChecksum(pageID)) { - debug_printf("DWALPager(%s) checksum failed for %s\n", self->filename.c_str(), toString(pageID).c_str()); + if (!header) { + Page* p = (Page*)page.getPtr(); + if (!p->verifyChecksum(pageID)) { + debug_printf("DWALPager(%s) checksum failed for %s\n", self->filename.c_str(), + toString(pageID).c_str()); Error e = checksum_failed(); TraceEvent(SevError, "DWALPagerChecksumFailed") - .detail("Filename", self->filename.c_str()) - .detail("PageID", pageID) - .detail("PageSize", self->physicalPageSize) - .detail("Offset", pageID * self->physicalPageSize) - .detail("CalculatedChecksum", p->calculateChecksum(pageID)) - .detail("ChecksumInPage", p->getChecksum()) - .error(e); + .detail("Filename", self->filename.c_str()) + .detail("PageID", pageID) + .detail("PageSize", self->physicalPageSize) + .detail("Offset", pageID * self->physicalPageSize) + .detail("CalculatedChecksum", p->calculateChecksum(pageID)) + .detail("ChecksumInPage", p->getChecksum()) + .error(e); throw e; } } return page; } - static Future> readHeaderPage(DWALPager *self, PhysicalPageID pageID) { + static Future> readHeaderPage(DWALPager* self, PhysicalPageID pageID) { return readPhysicalPage(self, pageID, true); } @@ -1302,10 +1277,10 @@ public: Future> readPage(LogicalPageID pageID, bool cacheable, bool noHit = false) override { // Use cached page if present, without triggering a cache hit. // Otherwise, read the page and return it but don't add it to the cache - if(!cacheable) { + if (!cacheable) { debug_printf("DWALPager(%s) op=readUncached %s\n", filename.c_str(), toString(pageID).c_str()); - PageCacheEntry *pCacheEntry = pageCache.getIfExists(pageID); - if(pCacheEntry != nullptr) { + PageCacheEntry* pCacheEntry = pageCache.getIfExists(pageID); + if (pCacheEntry != nullptr) { debug_printf("DWALPager(%s) op=readUncachedHit %s\n", filename.c_str(), toString(pageID).c_str()); return pCacheEntry->readFuture; } @@ -1314,10 +1289,13 @@ public: return forwardError(readPhysicalPage(this, (PhysicalPageID)pageID), errorPromise); } - PageCacheEntry &cacheEntry = pageCache.get(pageID, noHit); - debug_printf("DWALPager(%s) op=read %s cached=%d reading=%d writing=%d noHit=%d\n", filename.c_str(), toString(pageID).c_str(), cacheEntry.initialized(), cacheEntry.initialized() && cacheEntry.reading(), cacheEntry.initialized() && cacheEntry.writing(), noHit); + PageCacheEntry& cacheEntry = pageCache.get(pageID, noHit); + debug_printf("DWALPager(%s) op=read %s cached=%d reading=%d writing=%d noHit=%d\n", filename.c_str(), + toString(pageID).c_str(), cacheEntry.initialized(), + cacheEntry.initialized() && cacheEntry.reading(), cacheEntry.initialized() && cacheEntry.writing(), + noHit); - if(!cacheEntry.initialized()) { + if (!cacheEntry.initialized()) { debug_printf("DWALPager(%s) issuing actual read of %s\n", filename.c_str(), toString(pageID).c_str()); cacheEntry.readFuture = readPhysicalPage(this, (PhysicalPageID)pageID); cacheEntry.writeFuture = Void(); @@ -1330,16 +1308,17 @@ public: Future> readPageAtVersion(LogicalPageID pageID, Version v, bool cacheable, bool noHit) { auto i = remappedPages.find(pageID); - if(i != remappedPages.end()) { + if (i != remappedPages.end()) { auto j = i->second.upper_bound(v); - if(j != i->second.begin()) { + if (j != i->second.begin()) { --j; - debug_printf("DWALPager(%s) read %s @%" PRId64 " -> %s\n", filename.c_str(), toString(pageID).c_str(), v, toString(j->second).c_str()); + debug_printf("DWALPager(%s) read %s @%" PRId64 " -> %s\n", filename.c_str(), toString(pageID).c_str(), + v, toString(j->second).c_str()); pageID = j->second; } - } - else { - debug_printf("DWALPager(%s) read %s @%" PRId64 " (not remapped)\n", filename.c_str(), toString(pageID).c_str(), v); + } else { + debug_printf("DWALPager(%s) read %s @%" PRId64 " (not remapped)\n", filename.c_str(), + toString(pageID).c_str(), v); } return readPage(pageID, cacheable, noHit); @@ -1359,9 +1338,7 @@ public: // Get the oldest *readable* version, which is not the same as the oldest retained version as the version // returned could have been set as the oldest version in the pending commit - Version getOldestVersion() override { - return pHeader->oldestVersion; - }; + Version getOldestVersion() override { return pHeader->oldestVersion; }; // Calculate the *effective* oldest version, which can be older than the one set in the last commit since we // are allowing active snapshots to temporarily delay page reuse. @@ -1369,27 +1346,28 @@ public: return std::min(pLastCommittedHeader->oldestVersion, snapshots.front().version); } - ACTOR static Future undoRemaps(DWALPager *self) { + ACTOR static Future undoRemaps(DWALPager* self) { state RemappedPage cutoff; cutoff.version = self->effectiveOldestVersion(); // TODO: Use parallel reads - // TODO: One run of this actor might write to the same original page more than once, in which case just unmap the latest + // TODO: One run of this actor might write to the same original page more than once, in which case just unmap + // the latest loop { - if(self->remapUndoStop) { + if (self->remapUndoStop) { break; } state Optional p = wait(self->remapQueue.pop(cutoff)); - if(!p.present()) { + if (!p.present()) { break; } debug_printf("DWALPager(%s) undoRemaps popped %s\n", self->filename.c_str(), p.get().toString().c_str()); - if(p.get().newPageID == invalidLogicalPageID) { - debug_printf("DWALPager(%s) undoRemaps freeing %s\n", self->filename.c_str(), p.get().toString().c_str()); + if (p.get().newPageID == invalidLogicalPageID) { + debug_printf("DWALPager(%s) undoRemaps freeing %s\n", self->filename.c_str(), + p.get().toString().c_str()); self->freePage(p.get().originalPageID, p.get().version); - } - else { + } else { // Read the data from the page that the original was mapped to Reference data = wait(self->readPage(p.get().newPageID, false)); @@ -1398,24 +1376,25 @@ public: // Remove the remap from this page, deleting the entry for the pageID if its map becomes empty auto i = self->remappedPages.find(p.get().originalPageID); - if(i->second.size() == 1) { + if (i->second.size() == 1) { self->remappedPages.erase(i); - } - else { + } else { i->second.erase(p.get().version); } - // Now that the remap has been undone nothing will read this page so it can be freed as of the next commit. + // Now that the remap has been undone nothing will read this page so it can be freed as of the next + // commit. self->freePage(p.get().newPageID, 0); } } - debug_printf("DWALPager(%s) undoRemaps stopped, remapQueue size is %d\n", self->filename.c_str(), self->remapQueue.numEntries); + debug_printf("DWALPager(%s) undoRemaps stopped, remapQueue size is %d\n", self->filename.c_str(), + self->remapQueue.numEntries); return Void(); } // Flush all queues so they have no operations pending. - ACTOR static Future flushQueues(DWALPager *self) { + ACTOR static Future flushQueues(DWALPager* self) { ASSERT(self->remapUndoFuture.isReady()); // Flush remap queue separately, it's not involved in free page management @@ -1429,7 +1408,7 @@ public: // Once preFlush() returns false for both queues then there are no more operations pending // on either queue. If preFlush() returns true for either queue in one loop execution then // it could have generated new work for itself or the other queue. - if(!freeBusy && !delayedFreeBusy) { + if (!freeBusy && !delayedFreeBusy) { break; } } @@ -1439,7 +1418,7 @@ public: return Void(); } - ACTOR static Future commit_impl(DWALPager *self) { + ACTOR static Future commit_impl(DWALPager* self) { debug_printf("DWALPager(%s) commit begin\n", self->filename.c_str()); // Write old committed header to Page 1 @@ -1461,19 +1440,21 @@ public: debug_printf("DWALPager(%s) Syncing\n", self->filename.c_str()); // Sync everything except the header - if(g_network->getCurrentTask() > TaskPriority::DiskWrite) { + if (g_network->getCurrentTask() > TaskPriority::DiskWrite) { wait(delay(0, TaskPriority::DiskWrite)); } wait(self->pageFile->sync()); - debug_printf("DWALPager(%s) commit version %" PRId64 " sync 1\n", self->filename.c_str(), self->pHeader->committedVersion); + debug_printf("DWALPager(%s) commit version %" PRId64 " sync 1\n", self->filename.c_str(), + self->pHeader->committedVersion); // Update header on disk and sync again. wait(self->writeHeaderPage(0, self->headerPage)); - if(g_network->getCurrentTask() > TaskPriority::DiskWrite) { + if (g_network->getCurrentTask() > TaskPriority::DiskWrite) { wait(delay(0, TaskPriority::DiskWrite)); } wait(self->pageFile->sync()); - debug_printf("DWALPager(%s) commit version %" PRId64 " sync 2\n", self->filename.c_str(), self->pHeader->committedVersion); + debug_printf("DWALPager(%s) commit version %" PRId64 " sync 2\n", self->filename.c_str(), + self->pHeader->committedVersion); // Update the last committed header for use in the next commit. self->updateCommittedHeader(); @@ -1497,19 +1478,13 @@ public: return commitFuture; } - Key getMetaKey() const override { - return pHeader->getMetaKey(); - } + Key getMetaKey() const override { return pHeader->getMetaKey(); } - void setCommitVersion(Version v) override { - pHeader->committedVersion = v; - } + void setCommitVersion(Version v) override { pHeader->committedVersion = v; } - void setMetaKey(KeyRef metaKey) override { - pHeader->setMetaKey(metaKey); - } - - ACTOR void shutdown(DWALPager *self, bool dispose) { + void setMetaKey(KeyRef metaKey) override { pHeader->setMetaKey(metaKey); } + + ACTOR void shutdown(DWALPager* self, bool dispose) { debug_printf("DWALPager(%s) shutdown cancel recovery\n", self->filename.c_str()); self->recoverFuture.cancel(); debug_printf("DWALPager(%s) shutdown cancel commit\n", self->filename.c_str()); @@ -1517,9 +1492,9 @@ public: debug_printf("DWALPager(%s) shutdown cancel remap\n", self->filename.c_str()); self->remapUndoFuture.cancel(); - if(self->errorPromise.canBeSet()) { + if (self->errorPromise.canBeSet()) { debug_printf("DWALPager(%s) shutdown sending error\n", self->filename.c_str()); - self->errorPromise.sendError(actor_cancelled()); // Ideally this should be shutdown_in_progress + self->errorPromise.sendError(actor_cancelled()); // Ideally this should be shutdown_in_progress } // Must wait for pending operations to complete, canceling them can cause a crash because the underlying @@ -1532,7 +1507,7 @@ public: // Unreference the file and clear self->pageFile.clear(); - if(dispose) { + if (dispose) { debug_printf("DWALPager(%s) shutdown deleting file\n", self->filename.c_str()); wait(IAsyncFileSystem::filesystem()->incrementalDeleteFile(self->filename, true)); } @@ -1541,21 +1516,13 @@ public: delete self; } - void dispose() override { - shutdown(this, true); - } + void dispose() override { shutdown(this, true); } - void close() override { - shutdown(this, false); - } + void close() override { shutdown(this, false); } - Future getError() override { - return errorPromise.getFuture(); - } - - Future onClosed() override { - return closedPromise.getFuture(); - } + Future getError() override { return errorPromise.getFuture(); } + + Future onClosed() override { return closedPromise.getFuture(); } StorageBytes getStorageBytes() override { ASSERT(recoverFuture.isReady()); @@ -1564,41 +1531,42 @@ public: g_network->getDiskBytes(parentDirectory(filename), free, total); int64_t pagerSize = pHeader->pageCount * physicalPageSize; - // It is not exactly known how many pages on the delayed free list are usable as of right now. It could be known, - // if each commit delayed entries that were freeable were shuffled from the delayed free queue to the free queue, - // but this doesn't seem necessary. + // It is not exactly known how many pages on the delayed free list are usable as of right now. It could be + // known, if each commit delayed entries that were freeable were shuffled from the delayed free queue to the + // free queue, but this doesn't seem necessary. int64_t reusable = (freeList.numEntries + delayedFreeList.numEntries) * physicalPageSize; return StorageBytes(free, total, pagerSize - reusable, free + reusable); } - ACTOR static Future getUserPageCount_cleanup(DWALPager *self) { + ACTOR static Future getUserPageCount_cleanup(DWALPager* self) { // Wait for the remap eraser to finish all of its work (not triggering stop) wait(self->remapUndoFuture); // Flush queues so there are no pending freelist operations wait(flushQueues(self)); - + return Void(); } // Get the number of pages in use by the pager's user Future getUserPageCount() override { return map(getUserPageCount_cleanup(this), [=](Void) { - int64_t userPages = pHeader->pageCount - 2 - freeList.numPages - freeList.numEntries - delayedFreeList.numPages - delayedFreeList.numEntries - remapQueue.numPages; - debug_printf("DWALPager(%s) userPages=%" PRId64 " totalPageCount=%" PRId64 " freeQueuePages=%" PRId64 " freeQueueCount=%" PRId64 " delayedFreeQueuePages=%" PRId64 " delayedFreeQueueCount=%" PRId64 " remapQueuePages=%" PRId64 " remapQueueCount=%" PRId64 "\n", - filename.c_str(), userPages, pHeader->pageCount, freeList.numPages, freeList.numEntries, delayedFreeList.numPages, delayedFreeList.numEntries, remapQueue.numPages, remapQueue.numEntries); + int64_t userPages = pHeader->pageCount - 2 - freeList.numPages - freeList.numEntries - + delayedFreeList.numPages - delayedFreeList.numEntries - remapQueue.numPages; + debug_printf("DWALPager(%s) userPages=%" PRId64 " totalPageCount=%" PRId64 " freeQueuePages=%" PRId64 + " freeQueueCount=%" PRId64 " delayedFreeQueuePages=%" PRId64 " delayedFreeQueueCount=%" PRId64 + " remapQueuePages=%" PRId64 " remapQueueCount=%" PRId64 "\n", + filename.c_str(), userPages, pHeader->pageCount, freeList.numPages, freeList.numEntries, + delayedFreeList.numPages, delayedFreeList.numEntries, remapQueue.numPages, + remapQueue.numEntries); return userPages; }); } - Future init() override { - return recoverFuture; - } + Future init() override { return recoverFuture; } - Version getLatestVersion() override { - return pLastCommittedHeader->committedVersion; - } + Version getLatestVersion() override { return pLastCommittedHeader->committedVersion; } private: ~DWALPager() {} @@ -1617,12 +1585,10 @@ private: FIFOQueue::QueueState delayedFreeList; FIFOQueue::QueueState remapQueue; Version committedVersion; - Version oldestVersion; + Version oldestVersion; int32_t metaKeySize; - KeyRef getMetaKey() const { - return KeyRef((const uint8_t *)(this + 1), metaKeySize); - } + KeyRef getMetaKey() const { return KeyRef((const uint8_t*)(this + 1), metaKeySize); } void setMetaKey(StringRef key) { ASSERT(key.size() < (smallestPhysicalBlock - sizeof(Header))); @@ -1632,9 +1598,7 @@ private: } } - int size() const { - return sizeof(Header) + metaKeySize; - } + int size() const { return sizeof(Header) + metaKeySize; } private: Header(); @@ -1645,26 +1609,18 @@ private: Future> readFuture; Future writeFuture; - bool initialized() const { - return readFuture.isValid(); - } + bool initialized() const { return readFuture.isValid(); } - bool reading() const { - return !readFuture.isReady(); - } + bool reading() const { return !readFuture.isReady(); } - bool writing() const { - return !writeFuture.isReady(); - } + bool writing() const { return !writeFuture.isReady(); } bool evictable() const { // Don't evict if a page is still being read or written return !reading() && !writing(); } - Future onEvictable() const { - return ready(readFuture) && writeFuture; - } + Future onEvictable() const { return ready(readFuture) && writeFuture; } }; // Physical page sizes will always be a multiple of 4k because AsyncFileNonDurable requires @@ -1672,18 +1628,18 @@ private: // Allowing a smaller 'logical' page size is very useful for testing. static constexpr int smallestPhysicalBlock = 4096; int physicalPageSize; - int logicalPageSize; // In simulation testing it can be useful to use a small logical page size + int logicalPageSize; // In simulation testing it can be useful to use a small logical page size int64_t pageCacheBytes; // The header will be written to / read from disk as a smallestPhysicalBlock sized chunk. Reference headerPage; - Header *pHeader; + Header* pHeader; int desiredPageSize; Reference lastCommittedHeaderPage; - Header *pLastCommittedHeader; + Header* pLastCommittedHeader; std::string filename; @@ -1691,7 +1647,7 @@ private: PageCacheT pageCache; Promise closedPromise; - Promise errorPromise; + Promise errorPromise; Future commitFuture; SignalableActorCollection operations; Future recoverFuture; @@ -1715,13 +1671,9 @@ private: }; struct SnapshotEntryLessThanVersion { - bool operator() (Version v, const SnapshotEntry &snapshot) { - return v < snapshot.version; - } + bool operator()(Version v, const SnapshotEntry& snapshot) { return v < snapshot.version; } - bool operator() (const SnapshotEntry &snapshot, Version v) { - return snapshot.version < v; - } + bool operator()(const SnapshotEntry& snapshot, Version v) { return snapshot.version < v; } }; // TODO: Better data structure @@ -1733,46 +1685,38 @@ private: // Prevents pager from reusing freed pages from version until the snapshot is destroyed class DWALPagerSnapshot : public IPagerSnapshot, public ReferenceCounted { public: - DWALPagerSnapshot(DWALPager *pager, Key meta, Version version, Future expiredFuture) : pager(pager), metaKey(meta), version(version), expired(expiredFuture) { - } - virtual ~DWALPagerSnapshot() { - } + DWALPagerSnapshot(DWALPager* pager, Key meta, Version version, Future expiredFuture) + : pager(pager), metaKey(meta), version(version), expired(expiredFuture) {} + virtual ~DWALPagerSnapshot() {} Future> getPhysicalPage(LogicalPageID pageID, bool cacheable, bool noHit) override { - if(expired.isError()) { + if (expired.isError()) { throw expired.getError(); } - return map(pager->readPageAtVersion(pageID, version, cacheable, noHit), [=](Reference p) { - return Reference(p); - }); + return map(pager->readPageAtVersion(pageID, version, cacheable, noHit), + [=](Reference p) { return Reference(p); }); } - Key getMetaKey() const override { - return metaKey; - } + Key getMetaKey() const override { return metaKey; } - Version getVersion() const override { - return version; - } + Version getVersion() const override { return version; } - void addref() override { - ReferenceCounted::addref(); - } + void addref() override { ReferenceCounted::addref(); } - void delref() override { - ReferenceCounted::delref(); - } + void delref() override { ReferenceCounted::delref(); } - DWALPager *pager; + DWALPager* pager; Future expired; Version version; Key metaKey; }; void DWALPager::expireSnapshots(Version v) { - debug_printf("DWALPager(%s) expiring snapshots through %" PRId64 " snapshot count %d\n", filename.c_str(), v, (int)snapshots.size()); - while(snapshots.size() > 1 && snapshots.front().version < v && snapshots.front().snapshot->isSoleOwner()) { - debug_printf("DWALPager(%s) expiring snapshot for %" PRId64 " soleOwner=%d\n", filename.c_str(), snapshots.front().version, snapshots.front().snapshot->isSoleOwner()); + debug_printf("DWALPager(%s) expiring snapshots through %" PRId64 " snapshot count %d\n", filename.c_str(), v, + (int)snapshots.size()); + while (snapshots.size() > 1 && snapshots.front().version < v && snapshots.front().snapshot->isSoleOwner()) { + debug_printf("DWALPager(%s) expiring snapshot for %" PRId64 " soleOwner=%d\n", filename.c_str(), + snapshots.front().version, snapshots.front().snapshot->isSoleOwner()); // The snapshot contract could be made such that the expired promise isn't need anymore. In practice it // probably is already not needed but it will gracefully handle the case where a user begins a page read // with a snapshot reference, keeps the page read future, and drops the snapshot reference. @@ -1785,7 +1729,7 @@ Reference DWALPager::getReadSnapshot(Version v) { ASSERT(!snapshots.empty()); auto i = std::upper_bound(snapshots.begin(), snapshots.end(), v, SnapshotEntryLessThanVersion()); - if(i == snapshots.begin()) { + if (i == snapshots.begin()) { throw version_invalid(); } --i; @@ -1794,35 +1738,30 @@ Reference DWALPager::getReadSnapshot(Version v) { void DWALPager::addLatestSnapshot() { Promise expired; - snapshots.push_back({ - pLastCommittedHeader->committedVersion, - expired, - Reference(new DWALPagerSnapshot(this, pLastCommittedHeader->getMetaKey(), pLastCommittedHeader->committedVersion, expired.getFuture())) - }); + snapshots.push_back({ pLastCommittedHeader->committedVersion, expired, + Reference(new DWALPagerSnapshot(this, pLastCommittedHeader->getMetaKey(), + pLastCommittedHeader->committedVersion, + expired.getFuture())) }); } - // TODO: Move this to a flow header once it is mature. struct SplitStringRef { StringRef a; StringRef b; - SplitStringRef(StringRef a = StringRef(), StringRef b = StringRef()) : a(a), b(b) { - } + SplitStringRef(StringRef a = StringRef(), StringRef b = StringRef()) : a(a), b(b) {} - SplitStringRef(Arena &arena, const SplitStringRef &toCopy) - : a(toStringRef(arena)), b() { - } + SplitStringRef(Arena& arena, const SplitStringRef& toCopy) : a(toStringRef(arena)), b() {} SplitStringRef prefix(int len) const { - if(len <= a.size()) { + if (len <= a.size()) { return SplitStringRef(a.substr(0, len)); } len -= a.size(); return SplitStringRef(a, b.substr(0, len)); } - StringRef toStringRef(Arena &arena) const { + StringRef toStringRef(Arena& arena) const { StringRef c = makeString(size(), arena); memcpy(mutateString(c), a.begin(), a.size()); memcpy(mutateString(c) + a.size(), b.begin(), b.size()); @@ -1834,82 +1773,66 @@ struct SplitStringRef { return Standalone(toStringRef(a), a); } - int size() const { - return a.size() + b.size(); - } + int size() const { return a.size() + b.size(); } - int expectedSize() const { - return size(); - } + int expectedSize() const { return size(); } - std::string toString() const { - return format("%s%s", a.toString().c_str(), b.toString().c_str()); - } + std::string toString() const { return format("%s%s", a.toString().c_str(), b.toString().c_str()); } - std::string toHexString() const { - return format("%s%s", a.toHexString().c_str(), b.toHexString().c_str()); - } + std::string toHexString() const { return format("%s%s", a.toHexString().c_str(), b.toHexString().c_str()); } struct const_iterator { - const uint8_t *ptr; - const uint8_t *end; - const uint8_t *next; + const uint8_t* ptr; + const uint8_t* end; + const uint8_t* next; - inline bool operator==(const const_iterator &rhs) const { - return ptr == rhs.ptr; - } + inline bool operator==(const const_iterator& rhs) const { return ptr == rhs.ptr; } - inline const_iterator & operator++() { + inline const_iterator& operator++() { ++ptr; - if(ptr == end) { + if (ptr == end) { ptr = next; } return *this; } - inline const_iterator & operator+(int n) { + inline const_iterator& operator+(int n) { ptr += n; - if(ptr >= end) { + if (ptr >= end) { ptr = next + (ptr - end); } return *this; } - inline uint8_t operator *() const { - return *ptr; - } + inline uint8_t operator*() const { return *ptr; } }; - inline const_iterator begin() const { - return {a.begin(), a.end(), b.begin()}; - } + inline const_iterator begin() const { return { a.begin(), a.end(), b.begin() }; } - inline const_iterator end() const { - return {b.end()}; - } + inline const_iterator end() const { return { b.end() }; } - template - int compare(const StringT &rhs) const { + template + int compare(const StringT& rhs) const { auto j = begin(); auto k = rhs.begin(); auto jEnd = end(); auto kEnd = rhs.end(); - while(j != jEnd && k != kEnd) { + while (j != jEnd && k != kEnd) { int cmp = *j - *k; - if(cmp != 0) { + if (cmp != 0) { return cmp; } } - // If we've reached the end of *this, then values are equal if rhs is also exhausted, otherwise *this is less than rhs - if(j == jEnd) { + // If we've reached the end of *this, then values are equal if rhs is also exhausted, otherwise *this is less + // than rhs + if (j == jEnd) { return k == kEnd ? 0 : -1; } return 1; } - }; // A BTree "page id" is actually a list of LogicalPageID's whose contents should be concatenated together. @@ -1925,45 +1848,37 @@ struct RedwoodRecordRef { typedef uint8_t byte; RedwoodRecordRef(KeyRef key = KeyRef(), Version ver = 0, Optional value = {}) - : key(key), version(ver), value(value) - { - } + : key(key), version(ver), value(value) {} - RedwoodRecordRef(Arena &arena, const RedwoodRecordRef &toCopy) - : key(arena, toCopy.key), version(toCopy.version) - { - if(toCopy.value.present()) { + RedwoodRecordRef(Arena& arena, const RedwoodRecordRef& toCopy) : key(arena, toCopy.key), version(toCopy.version) { + if (toCopy.value.present()) { value = ValueRef(arena, toCopy.value.get()); } } - KeyValueRef toKeyValueRef() const { - return KeyValueRef(key, value.get()); - } + KeyValueRef toKeyValueRef() const { return KeyValueRef(key, value.get()); } // RedwoodRecordRefs are used for both internal and leaf pages of the BTree. // Boundary records in internal pages are made from leaf records. // These functions make creating and working with internal page records more convenient. inline BTreePageID getChildPage() const { ASSERT(value.present()); - return BTreePageID((LogicalPageID *)value.get().begin(), value.get().size() / sizeof(LogicalPageID)); + return BTreePageID((LogicalPageID*)value.get().begin(), value.get().size() / sizeof(LogicalPageID)); } inline void setChildPage(BTreePageID id) { - value = ValueRef((const uint8_t *)id.begin(), id.size() * sizeof(LogicalPageID)); + value = ValueRef((const uint8_t*)id.begin(), id.size() * sizeof(LogicalPageID)); } - inline void setChildPage(Arena &arena, BTreePageID id) { - value = ValueRef(arena, (const uint8_t *)id.begin(), id.size() * sizeof(LogicalPageID)); + inline void setChildPage(Arena& arena, BTreePageID id) { + value = ValueRef(arena, (const uint8_t*)id.begin(), id.size() * sizeof(LogicalPageID)); } inline RedwoodRecordRef withPageID(BTreePageID id) const { - return RedwoodRecordRef(key, version, ValueRef((const uint8_t *)id.begin(), id.size() * sizeof(LogicalPageID))); + return RedwoodRecordRef(key, version, ValueRef((const uint8_t*)id.begin(), id.size() * sizeof(LogicalPageID))); } - inline RedwoodRecordRef withoutValue() const { - return RedwoodRecordRef(key, version); - } + inline RedwoodRecordRef withoutValue() const { return RedwoodRecordRef(key, version); } // Truncate (key, version, part) tuple to len bytes. void truncate(int len) { @@ -1973,32 +1888,34 @@ struct RedwoodRecordRef { } // Find the common key prefix between two records, assuming that the first skipLen bytes are the same - inline int getCommonPrefixLen(const RedwoodRecordRef &other, int skipLen = 0) const { + inline int getCommonPrefixLen(const RedwoodRecordRef& other, int skipLen = 0) const { int skipStart = std::min(skipLen, key.size()); - return skipStart + commonPrefixLength(key.begin() + skipStart, other.key.begin() + skipStart, std::min(other.key.size(), key.size()) - skipStart); + return skipStart + commonPrefixLength(key.begin() + skipStart, other.key.begin() + skipStart, + std::min(other.key.size(), key.size()) - skipStart); } // Compares and orders by key, version, chunk.total, chunk.start, value // This is the same order that delta compression uses for prefix borrowing - int compare(const RedwoodRecordRef &rhs, int skip = 0) const { + int compare(const RedwoodRecordRef& rhs, int skip = 0) const { int keySkip = std::min(skip, key.size()); int cmp = key.substr(keySkip).compare(rhs.key.substr(keySkip)); - if(cmp == 0) { + if (cmp == 0) { cmp = version - rhs.version; - if(cmp == 0) { + if (cmp == 0) { cmp = value.compare(rhs.value); } } return cmp; } - bool sameUserKey(const StringRef &k, int skipLen) const { - // Keys are the same if the sizes are the same and either the skipLen is longer or the non-skipped suffixes are the same. + bool sameUserKey(const StringRef& k, int skipLen) const { + // Keys are the same if the sizes are the same and either the skipLen is longer or the non-skipped suffixes are + // the same. return (key.size() == k.size()) && (key.substr(skipLen) == k.substr(skipLen)); } - bool sameExceptValue(const RedwoodRecordRef &rhs, int skipLen = 0) const { + bool sameExceptValue(const RedwoodRecordRef& rhs, int skipLen = 0) const { return sameUserKey(rhs.key, skipLen) && version == rhs.version; } @@ -2007,15 +1924,13 @@ struct RedwoodRecordRef { Optional value; Version version; - int expectedSize() const { - return key.expectedSize() + value.expectedSize(); - } + int expectedSize() const { return key.expectedSize() + value.expectedSize(); } class Reader { public: - Reader(const void *ptr) : rptr((const byte *)ptr) {} + Reader(const void* ptr) : rptr((const byte*)ptr) {} - const byte *rptr; + const byte* rptr; StringRef readString(int len) { StringRef s(rptr, len); @@ -2024,7 +1939,7 @@ struct RedwoodRecordRef { } }; -#pragma pack(push,1) +#pragma pack(push, 1) struct Delta { uint8_t flags; @@ -2062,8 +1977,9 @@ struct RedwoodRecordRef { int16_t low; }; - static constexpr int LengthFormatSizes[] = {sizeof(LengthFormat0), sizeof(LengthFormat1), sizeof(LengthFormat2), sizeof(LengthFormat3)}; - static constexpr int VersionDeltaSizes[] = {0, sizeof(int32_t), sizeof(int48_t), sizeof(int64_t)}; + static constexpr int LengthFormatSizes[] = { sizeof(LengthFormat0), sizeof(LengthFormat1), + sizeof(LengthFormat2), sizeof(LengthFormat3) }; + static constexpr int VersionDeltaSizes[] = { 0, sizeof(int32_t), sizeof(int48_t), sizeof(int64_t) }; // Serialized Format // @@ -2077,7 +1993,7 @@ struct RedwoodRecordRef { // // Length fields using 3 to 8 bytes total depending on length fields format // - // Byte strings + // Byte strings // Key suffix bytes // Value bytes // Version delta bytes @@ -2094,72 +2010,79 @@ struct RedwoodRecordRef { static inline int determineLengthFormat(int prefixLength, int suffixLength, int valueLength) { // Large prefix or suffix length, which should be rare, is format 3 - if(prefixLength > 0xFF || suffixLength > 0xFF) { + if (prefixLength > 0xFF || suffixLength > 0xFF) { return 3; - } - else if(valueLength < 0x100) { + } else if (valueLength < 0x100) { return 0; - } - else if(valueLength < 0x10000) { + } else if (valueLength < 0x10000) { return 1; - } - else { + } else { return 2; } } // Large prefix or suffix length, which should be rare, is format 3 - byte * data() const { - switch(flags & LENGTHS_FORMAT) { - case 0: return (byte *)(&LengthFormat0 + 1); - case 1: return (byte *)(&LengthFormat1 + 1); - case 2: return (byte *)(&LengthFormat2 + 1); - case 3: - default: return (byte *)(&LengthFormat3 + 1); + byte* data() const { + switch (flags & LENGTHS_FORMAT) { + case 0: + return (byte*)(&LengthFormat0 + 1); + case 1: + return (byte*)(&LengthFormat1 + 1); + case 2: + return (byte*)(&LengthFormat2 + 1); + case 3: + default: + return (byte*)(&LengthFormat3 + 1); } } int getKeyPrefixLength() const { - switch(flags & LENGTHS_FORMAT) { - case 0: return LengthFormat0.prefixLength; - case 1: return LengthFormat1.prefixLength; - case 2: return LengthFormat2.prefixLength; - case 3: - default: return LengthFormat3.prefixLength; + switch (flags & LENGTHS_FORMAT) { + case 0: + return LengthFormat0.prefixLength; + case 1: + return LengthFormat1.prefixLength; + case 2: + return LengthFormat2.prefixLength; + case 3: + default: + return LengthFormat3.prefixLength; } } int getKeySuffixLength() const { - switch(flags & LENGTHS_FORMAT) { - case 0: return LengthFormat0.suffixLength; - case 1: return LengthFormat1.suffixLength; - case 2: return LengthFormat2.suffixLength; - case 3: - default: return LengthFormat3.suffixLength; + switch (flags & LENGTHS_FORMAT) { + case 0: + return LengthFormat0.suffixLength; + case 1: + return LengthFormat1.suffixLength; + case 2: + return LengthFormat2.suffixLength; + case 3: + default: + return LengthFormat3.suffixLength; } } int getValueLength() const { - switch(flags & LENGTHS_FORMAT) { - case 0: return LengthFormat0.valueLength; - case 1: return LengthFormat1.valueLength; - case 2: return LengthFormat2.valueLength; - case 3: - default: return LengthFormat3.valueLength; + switch (flags & LENGTHS_FORMAT) { + case 0: + return LengthFormat0.valueLength; + case 1: + return LengthFormat1.valueLength; + case 2: + return LengthFormat2.valueLength; + case 3: + default: + return LengthFormat3.valueLength; } } - StringRef getKeySuffix() const { - return StringRef(data(), getKeySuffixLength()); - } + StringRef getKeySuffix() const { return StringRef(data(), getKeySuffixLength()); } - StringRef getValue() const { - return StringRef(data() + getKeySuffixLength(), getValueLength()); - } + StringRef getValue() const { return StringRef(data() + getKeySuffixLength(), getValueLength()); } - bool hasVersion() const { - return flags & HAS_VERSION; - } + bool hasVersion() const { return flags & HAS_VERSION; } int getVersionDeltaSizeBytes() const { int code = (flags & VERSION_DELTA_SIZE) >> 2; @@ -2167,84 +2090,75 @@ struct RedwoodRecordRef { } static int getVersionDeltaSizeBytes(Version d) { - if(d == 0) { + if (d == 0) { return 0; - } - else if(d == (int32_t)d) { + } else if (d == (int32_t)d) { return sizeof(int32_t); - } - else if(d == (d & int48_t::MASK)) { + } else if (d == (d & int48_t::MASK)) { return sizeof(int48_t); } return sizeof(int64_t); } - int getVersionDelta(const uint8_t *r) const { + int getVersionDelta(const uint8_t* r) const { int code = (flags & VERSION_DELTA_SIZE) >> 2; - switch(code) { - case 0: return 0; - case 1: return *(int32_t *)r; - case 2: return (((int64_t)((int48_t *)r)->high) << 16) | (((int48_t *)r)->low & 0xFFFF); - case 3: - default: return *(int64_t *)r; + switch (code) { + case 0: + return 0; + case 1: + return *(int32_t*)r; + case 2: + return (((int64_t)((int48_t*)r)->high) << 16) | (((int48_t*)r)->low & 0xFFFF); + case 3: + default: + return *(int64_t*)r; } } // Version delta size should be 0 before calling - int setVersionDelta(Version d, uint8_t *w) { + int setVersionDelta(Version d, uint8_t* w) { flags |= HAS_VERSION; - if(d == 0) { + if (d == 0) { return 0; - } - else if(d == (int32_t)d) { + } else if (d == (int32_t)d) { flags |= 1 << 2; - *(uint32_t *)w = d; + *(uint32_t*)w = d; return sizeof(uint32_t); - } - else if(d == (d & int48_t::MASK)) { + } else if (d == (d & int48_t::MASK)) { flags |= 2 << 2; - ((int48_t *)w)->high = d >> 16; - ((int48_t *)w)->low = d; + ((int48_t*)w)->high = d >> 16; + ((int48_t*)w)->low = d; return sizeof(int48_t); - } - else { + } else { flags |= 3 << 2; - *(int64_t *)w = d; + *(int64_t*)w = d; return sizeof(int64_t); } } - bool hasValue() const { - return flags & HAS_VALUE; - } + bool hasValue() const { return flags & HAS_VALUE; } void setPrefixSource(bool val) { - if(val) { + if (val) { flags |= PREFIX_SOURCE_PREV; - } - else { + } else { flags &= ~PREFIX_SOURCE_PREV; } } - bool getPrefixSource() const { - return flags & PREFIX_SOURCE_PREV; - } + bool getPrefixSource() const { return flags & PREFIX_SOURCE_PREV; } void setDeleted(bool val) { - if(val) { + if (val) { flags |= IS_DELETED; - } - else { + } else { flags &= ~IS_DELETED; } } - bool getDeleted() const { - return flags & IS_DELETED; - } + bool getDeleted() const { return flags & IS_DELETED; } - RedwoodRecordRef apply(const RedwoodRecordRef &base, Arena &arena) const { + RedwoodRecordRef apply(const RedwoodRecordRef& base, Arena& arena) const { int keyPrefixLen = getKeyPrefixLength(); int keySuffixLen = getKeySuffixLength(); int valueLen = hasValue() ? getValueLength() : 0; @@ -2253,24 +2167,23 @@ struct RedwoodRecordRef { Reader r(data()); // If there is a key suffix, reconstitute the complete key into a contiguous string - if(keySuffixLen > 0) { + if (keySuffixLen > 0) { StringRef keySuffix = r.readString(keySuffixLen); k = makeString(keyPrefixLen + keySuffixLen, arena); memcpy(mutateString(k), base.key.begin(), keyPrefixLen); memcpy(mutateString(k) + keyPrefixLen, keySuffix.begin(), keySuffixLen); - } - else { + } else { // Otherwise just reference the base key's memory k = base.key.substr(0, keyPrefixLen); } Optional value; - if(hasValue()) { + if (hasValue()) { value = r.readString(valueLen); } Version v = 0; - if(hasVersion()) { + if (hasVersion()) { v = base.version + getVersionDelta(r.rptr); } @@ -2279,27 +2192,31 @@ struct RedwoodRecordRef { int size() const { int size = 1 + getVersionDeltaSizeBytes(); - switch(flags & LENGTHS_FORMAT) { - case 0: return size + sizeof(LengthFormat0) + LengthFormat0.suffixLength + LengthFormat0.valueLength; - case 1: return size + sizeof(LengthFormat1) + LengthFormat1.suffixLength + LengthFormat1.valueLength; - case 2: return size + sizeof(LengthFormat2) + LengthFormat2.suffixLength + LengthFormat2.valueLength; - case 3: - default: return size + sizeof(LengthFormat3) + LengthFormat3.suffixLength + LengthFormat3.valueLength; + switch (flags & LENGTHS_FORMAT) { + case 0: + return size + sizeof(LengthFormat0) + LengthFormat0.suffixLength + LengthFormat0.valueLength; + case 1: + return size + sizeof(LengthFormat1) + LengthFormat1.suffixLength + LengthFormat1.valueLength; + case 2: + return size + sizeof(LengthFormat2) + LengthFormat2.suffixLength + LengthFormat2.valueLength; + case 3: + default: + return size + sizeof(LengthFormat3) + LengthFormat3.suffixLength + LengthFormat3.valueLength; } } std::string toString() const { std::string flagString = " "; - if(flags & PREFIX_SOURCE_PREV) { + if (flags & PREFIX_SOURCE_PREV) { flagString += "PrefixSource|"; } - if(flags & IS_DELETED) { + if (flags & IS_DELETED) { flagString += "IsDeleted|"; } - if(hasValue()) { + if (hasValue()) { flagString += "HasValue|"; } - if(hasVersion()) { + if (hasVersion()) { flagString += "HasVersion|"; } int lengthFormat = flags & LENGTHS_FORMAT; @@ -2309,18 +2226,20 @@ struct RedwoodRecordRef { int keySuffixLen = getKeySuffixLength(); int valueLen = getValueLength(); - return format("lengthFormat: %d totalDeltaSize: %d flags: %s prefixLen: %d keySuffixLen: %d versionDeltaSizeBytes: %d valueLen %d raw: %s", - lengthFormat, size(), flagString.c_str(), prefixLen, keySuffixLen, getVersionDeltaSizeBytes(), valueLen, StringRef((const uint8_t *)this, size()).toHexString().c_str()); + return format("lengthFormat: %d totalDeltaSize: %d flags: %s prefixLen: %d keySuffixLen: %d " + "versionDeltaSizeBytes: %d valueLen %d raw: %s", + lengthFormat, size(), flagString.c_str(), prefixLen, keySuffixLen, getVersionDeltaSizeBytes(), + valueLen, StringRef((const uint8_t*)this, size()).toHexString().c_str()); } }; // Using this class as an alternative for Delta enables reading a DeltaTree while only decoding // its values, so the Reader does not require the original prev/next ancestors. struct DeltaValueOnly : Delta { - RedwoodRecordRef apply(const RedwoodRecordRef &base, Arena &arena) const { + RedwoodRecordRef apply(const RedwoodRecordRef& base, Arena& arena) const { Optional value; - if(hasValue()) { + if (hasValue()) { value = getValue(); } @@ -2329,43 +2248,30 @@ struct RedwoodRecordRef { }; #pragma pack(pop) - bool operator==(const RedwoodRecordRef &rhs) const { - return compare(rhs) == 0; - } + bool operator==(const RedwoodRecordRef& rhs) const { return compare(rhs) == 0; } - bool operator!=(const RedwoodRecordRef &rhs) const { - return compare(rhs) != 0; - } + bool operator!=(const RedwoodRecordRef& rhs) const { return compare(rhs) != 0; } - bool operator<(const RedwoodRecordRef &rhs) const { - return compare(rhs) < 0; - } + bool operator<(const RedwoodRecordRef& rhs) const { return compare(rhs) < 0; } - bool operator>(const RedwoodRecordRef &rhs) const { - return compare(rhs) > 0; - } + bool operator>(const RedwoodRecordRef& rhs) const { return compare(rhs) > 0; } - bool operator<=(const RedwoodRecordRef &rhs) const { - return compare(rhs) <= 0; - } + bool operator<=(const RedwoodRecordRef& rhs) const { return compare(rhs) <= 0; } - bool operator>=(const RedwoodRecordRef &rhs) const { - return compare(rhs) >= 0; - } + bool operator>=(const RedwoodRecordRef& rhs) const { return compare(rhs) >= 0; } // Worst case overhead means to assu - int deltaSize(const RedwoodRecordRef &base, int skipLen, bool worstCaseOverhead) const { + int deltaSize(const RedwoodRecordRef& base, int skipLen, bool worstCaseOverhead) const { int prefixLen = getCommonPrefixLen(base, skipLen); int keySuffixLen = key.size() - prefixLen; int valueLen = value.present() ? value.get().size() : 0; int formatType; int versionBytes; - if(worstCaseOverhead) { + if (worstCaseOverhead) { formatType = Delta::determineLengthFormat(key.size(), key.size(), valueLen); versionBytes = version == 0 ? 0 : Delta::getVersionDeltaSizeBytes(version << 1); - } - else { + } else { formatType = Delta::determineLengthFormat(prefixLen, keySuffixLen, valueLen); versionBytes = version == 0 ? 0 : Delta::getVersionDeltaSizeBytes(version - base.version); } @@ -2374,10 +2280,10 @@ struct RedwoodRecordRef { } // commonPrefix between *this and base can be passed if known - int writeDelta(Delta &d, const RedwoodRecordRef &base, int keyPrefixLen = -1) const { + int writeDelta(Delta& d, const RedwoodRecordRef& base, int keyPrefixLen = -1) const { d.flags = value.present() ? Delta::HAS_VALUE : 0; - if(keyPrefixLen < 0) { + if (keyPrefixLen < 0) { keyPrefixLen = getCommonPrefixLen(base, 0); } @@ -2387,35 +2293,51 @@ struct RedwoodRecordRef { int formatType = Delta::determineLengthFormat(keyPrefixLen, keySuffix.size(), valueLen); d.flags |= formatType; - switch(formatType) { - case 0: d.LengthFormat0.prefixLength = keyPrefixLen; d.LengthFormat0.suffixLength = keySuffix.size(); d.LengthFormat0.valueLength = valueLen; break; - case 1: d.LengthFormat1.prefixLength = keyPrefixLen; d.LengthFormat1.suffixLength = keySuffix.size(); d.LengthFormat1.valueLength = valueLen; break; - case 2: d.LengthFormat2.prefixLength = keyPrefixLen; d.LengthFormat2.suffixLength = keySuffix.size(); d.LengthFormat2.valueLength = valueLen; break; - case 3: - default: d.LengthFormat3.prefixLength = keyPrefixLen; d.LengthFormat3.suffixLength = keySuffix.size(); d.LengthFormat3.valueLength = valueLen; break; + switch (formatType) { + case 0: + d.LengthFormat0.prefixLength = keyPrefixLen; + d.LengthFormat0.suffixLength = keySuffix.size(); + d.LengthFormat0.valueLength = valueLen; + break; + case 1: + d.LengthFormat1.prefixLength = keyPrefixLen; + d.LengthFormat1.suffixLength = keySuffix.size(); + d.LengthFormat1.valueLength = valueLen; + break; + case 2: + d.LengthFormat2.prefixLength = keyPrefixLen; + d.LengthFormat2.suffixLength = keySuffix.size(); + d.LengthFormat2.valueLength = valueLen; + break; + case 3: + default: + d.LengthFormat3.prefixLength = keyPrefixLen; + d.LengthFormat3.suffixLength = keySuffix.size(); + d.LengthFormat3.valueLength = valueLen; + break; } - uint8_t *wptr = d.data(); + uint8_t* wptr = d.data(); // Write key suffix string wptr = keySuffix.copyTo(wptr); // Write value bytes - if(value.present()) { + if (value.present()) { wptr = value.get().copyTo(wptr); } - if(version != 0) { + if (version != 0) { wptr += d.setVersionDelta(version - base.version, wptr); } - return wptr - (uint8_t *)&d; + return wptr - (uint8_t*)&d; } static std::string kvformat(StringRef s, int hexLimit = -1) { bool hex = false; - for(auto c : s) { - if(!isprint(c)) { + for (auto c : s) { + if (!isprint(c)) { hex = true; break; } @@ -2427,15 +2349,13 @@ struct RedwoodRecordRef { std::string toString(bool leaf = true) const { std::string r; r += format("'%s'@%" PRId64 " => ", kvformat(key).c_str(), version); - if(value.present()) { - if(leaf) { + if (value.present()) { + if (leaf) { r += format("'%s'", kvformat(value.get()).c_str()); - } - else { + } else { r += format("[%s]", ::toString(getChildPage()).c_str()); } - } - else { + } else { r += "(absent)"; } return r; @@ -2446,7 +2366,7 @@ struct BTreePage { typedef DeltaTree BinaryTree; typedef DeltaTree ValueTree; -#pragma pack(push,1) +#pragma pack(push, 1) struct { uint8_t height; uint32_t kvBytes; @@ -2454,33 +2374,27 @@ struct BTreePage { #pragma pack(pop) int size() const { - const BinaryTree *t = &tree(); - return (uint8_t *)t - (uint8_t *)this + t->size(); + const BinaryTree* t = &tree(); + return (uint8_t*)t - (uint8_t*)this + t->size(); } - bool isLeaf() const { - return height == 1; - } + bool isLeaf() const { return height == 1; } - BinaryTree & tree() { - return *(BinaryTree *)(this + 1); - } + BinaryTree& tree() { return *(BinaryTree*)(this + 1); } - const BinaryTree & tree() const { - return *(const BinaryTree *)(this + 1); - } + const BinaryTree& tree() const { return *(const BinaryTree*)(this + 1); } - const ValueTree & valueTree() const { - return *(const ValueTree *)(this + 1); - } + const ValueTree& valueTree() const { return *(const ValueTree*)(this + 1); } - std::string toString(bool write, BTreePageID id, Version ver, const RedwoodRecordRef *lowerBound, const RedwoodRecordRef *upperBound) const { + std::string toString(bool write, BTreePageID id, Version ver, const RedwoodRecordRef* lowerBound, + const RedwoodRecordRef* upperBound) const { std::string r; - r += format("BTreePage op=%s %s @%" PRId64 " ptr=%p height=%d count=%d kvBytes=%d\n lowerBound: %s\n upperBound: %s\n", - write ? "write" : "read", ::toString(id).c_str(), ver, this, height, (int)tree().numItems, (int)kvBytes, - lowerBound->toString(false).c_str(), upperBound->toString(false).c_str()); + r += format("BTreePage op=%s %s @%" PRId64 + " ptr=%p height=%d count=%d kvBytes=%d\n lowerBound: %s\n upperBound: %s\n", + write ? "write" : "read", ::toString(id).c_str(), ver, this, height, (int)tree().numItems, + (int)kvBytes, lowerBound->toString(false).c_str(), upperBound->toString(false).c_str()); try { - if(tree().numItems > 0) { + if (tree().numItems > 0) { // This doesn't use the cached reader for the page but it is only for debugging purposes BinaryTree::Mirror reader(&tree(), lowerBound, upperBound); BinaryTree::Cursor c = reader.getCursor(); @@ -2495,18 +2409,18 @@ struct BTreePage { bool tooLow = c.get().withoutValue() < lowerBound->withoutValue(); bool tooHigh = c.get().withoutValue() >= upperBound->withoutValue(); - if(tooLow || tooHigh) { + if (tooLow || tooHigh) { anyOutOfRange = true; - if(tooLow) { + if (tooLow) { r += " (too low)"; } - if(tooHigh) { + if (tooHigh) { r += " (too high)"; } } r += "\n"; - } while(c.moveNext()); + } while (c.moveNext()); ASSERT(!anyOutOfRange); } } catch (Error& e) { @@ -2520,14 +2434,14 @@ struct BTreePage { }; static void makeEmptyRoot(Reference page) { - BTreePage *btpage = (BTreePage *)page->begin(); + BTreePage* btpage = (BTreePage*)page->begin(); btpage->height = 1; btpage->kvBytes = 0; btpage->tree().build(page->size(), nullptr, nullptr, nullptr, nullptr); } -BTreePage::BinaryTree::Cursor getCursor(const Reference &page) { - return ((BTreePage::BinaryTree::Mirror *)page->userData)->getCursor(); +BTreePage::BinaryTree::Cursor getCursor(const Reference& page) { + return ((BTreePage::BinaryTree::Mirror*)page->userData)->getCursor(); } struct BoundaryRefAndPage { @@ -2540,32 +2454,23 @@ struct BoundaryRefAndPage { } }; -#define NOT_IMPLEMENTED { UNSTOPPABLE_ASSERT(false); } +#define NOT_IMPLEMENTED \ + { UNSTOPPABLE_ASSERT(false); } #pragma pack(push, 1) -template +template struct InPlaceArray { SizeT count; - const T * begin() const { - return (T *)(this + 1); - } - - T * begin() { - return (T *)(this + 1); - } + const T* begin() const { return (T*)(this + 1); } - const T * end() const { - return begin() + count; - } - - T * end() { - return begin() + count; - } + T* begin() { return (T*)(this + 1); } - VectorRef get() { - return VectorRef(begin(), count); - } + const T* end() const { return begin() + count; } + + T* end() { return begin() + count; } + + VectorRef get() { return VectorRef(begin(), count); } void set(VectorRef v, int availableSpace) { ASSERT(sizeof(T) * v.size() <= availableSpace); @@ -2573,9 +2478,7 @@ struct InPlaceArray { memcpy(begin(), v.begin(), sizeof(T) * v.size()); } - int extraSize() const { - return count * sizeof(T); - } + int extraSize() const { return count * sizeof(T); } }; #pragma pack(pop) @@ -2590,33 +2493,27 @@ public: Version version; Standalone pageID; - bool operator< (const LazyDeleteQueueEntry &rhs) const { - return version < rhs.version; - } + bool operator<(const LazyDeleteQueueEntry& rhs) const { return version < rhs.version; } - int readFromBytes(const uint8_t *src) { - version = *(Version *)src; + int readFromBytes(const uint8_t* src) { + version = *(Version*)src; src += sizeof(Version); int count = *src++; - pageID = BTreePageID((LogicalPageID *)src, count); + pageID = BTreePageID((LogicalPageID*)src, count); return bytesNeeded(); } - int bytesNeeded() const { - return sizeof(Version) + 1 + (pageID.size() * sizeof(LogicalPageID)); - } + int bytesNeeded() const { return sizeof(Version) + 1 + (pageID.size() * sizeof(LogicalPageID)); } - int writeToBytes(uint8_t *dst) const { - *(Version *)dst = version; + int writeToBytes(uint8_t* dst) const { + *(Version*)dst = version; dst += sizeof(Version); *dst++ = pageID.size(); memcpy(dst, pageID.begin(), pageID.size() * sizeof(LogicalPageID)); return bytesNeeded(); } - std::string toString() const { - return format("{%s @%" PRId64 "}", ::toString(pageID).c_str(), version); - } + std::string toString() const { return format("{%s @%" PRId64 "}", ::toString(pageID).c_str(), version); } }; typedef FIFOQueue LazyDeleteQueueT; @@ -2630,9 +2527,7 @@ public: LazyDeleteQueueT::QueueState lazyDeleteQueue; InPlaceArray root; - KeyRef asKeyRef() const { - return KeyRef((uint8_t *)this, sizeof(MetaKey) + root.extraSize()); - } + KeyRef asKeyRef() const { return KeyRef((uint8_t*)this, sizeof(MetaKey) + root.extraSize()); } void fromKeyRef(KeyRef k) { memcpy(this, k.begin(), k.size()); @@ -2640,9 +2535,9 @@ public: } std::string toString() { - return format("{height=%d formatVersion=%d root=%s lazyDeleteQueue=%s}", (int)height, (int)formatVersion, ::toString(root.get()).c_str(), lazyDeleteQueue.toString().c_str()); + return format("{height=%d formatVersion=%d root=%s lazyDeleteQueue=%s}", (int)height, (int)formatVersion, + ::toString(root.get()).c_str(), lazyDeleteQueue.toString().c_str()); } - }; #pragma pack(pop) @@ -2652,9 +2547,7 @@ public: startTime = g_network ? now() : 0; } - void clear() { - *this = Counts(); - } + void clear() { *this = Counts(); } int64_t pageReads; int64_t extPageReads; @@ -2675,16 +2568,23 @@ public: double startTime; std::string toString(bool clearAfter = false) { - const char *labels[] = {"set", "clear", "clearSingleKey", "get", "getRange", "commit", "pageReads", "extPageRead", "pagePreloads", "extPagePreloads", "pageWrite", "extPageWrite", "commitPage", "commitPageStart", "pageUpdates"}; - const int64_t values[] = {sets, clears, clearSingleKey, gets, getRanges, commits, pageReads, extPageReads, pagePreloads, extPagePreloads, pageWrites, extPageWrites, commitToPage, commitToPageStart, pageUpdates}; + const char* labels[] = { "set", "clear", "clearSingleKey", "get", + "getRange", "commit", "pageReads", "extPageRead", + "pagePreloads", "extPagePreloads", "pageWrite", "extPageWrite", + "commitPage", "commitPageStart", "pageUpdates" }; + const int64_t values[] = { + sets, clears, clearSingleKey, gets, getRanges, commits, pageReads, + extPageReads, pagePreloads, extPagePreloads, pageWrites, extPageWrites, commitToPage, commitToPageStart, + pageUpdates + }; double elapsed = now() - startTime; std::string s; - for(int i = 0; i < sizeof(values) / sizeof(int64_t); ++i) { + for (int i = 0; i < sizeof(values) / sizeof(int64_t); ++i) { s += format("%s=%" PRId64 " (%d/s) ", labels[i], values[i], int(values[i] / elapsed)); } - if(clearAfter) { + if (clearAfter) { clear(); } @@ -2697,40 +2597,32 @@ public: // All async opts on the btree are based on pager reads, writes, and commits, so // we can mostly forward these next few functions to the pager - Future getError() { - return m_pager->getError(); - } + Future getError() { return m_pager->getError(); } - Future onClosed() { - return m_pager->onClosed(); - } + Future onClosed() { return m_pager->onClosed(); } void close_impl(bool dispose) { - auto *pager = m_pager; + auto* pager = m_pager; delete this; - if(dispose) + if (dispose) pager->dispose(); else pager->close(); } - void dispose() { - return close_impl(true); - } + void dispose() { return close_impl(true); } - void close() { - return close_impl(false); - } + void close() { return close_impl(false); } - KeyValueStoreType getType() NOT_IMPLEMENTED - bool supportsMutation(int op) NOT_IMPLEMENTED - StorageBytes getStorageBytes() { + KeyValueStoreType getType() NOT_IMPLEMENTED bool supportsMutation(int op) NOT_IMPLEMENTED StorageBytes + getStorageBytes() { return m_pager->getStorageBytes(); } // Writes are provided in an ordered stream. - // A write is considered part of (a change leading to) the version determined by the previous call to setWriteVersion() - // A write shall not become durable until the following call to commit() begins, and shall be durable once the following call to commit() returns + // A write is considered part of (a change leading to) the version determined by the previous call to + // setWriteVersion() A write shall not become durable until the following call to commit() begins, and shall be + // durable once the following call to commit() returns void set(KeyValueRef keyValue) { ++counts.sets; m_pBuffer->insert(keyValue.key).mutation().setBoundaryValue(m_pBuffer->copyToArena(keyValue.value)); @@ -2738,10 +2630,8 @@ public: void clear(KeyRangeRef clearedRange) { // Optimization for single key clears to create just one mutation boundary instead of two - if(clearedRange.begin.size() == clearedRange.end.size() - 1 - && clearedRange.end[clearedRange.end.size() - 1] == 0 - && clearedRange.end.startsWith(clearedRange.begin) - ) { + if (clearedRange.begin.size() == clearedRange.end.size() - 1 && + clearedRange.end[clearedRange.end.size() - 1] == 0 && clearedRange.end.startsWith(clearedRange.begin)) { ++counts.clears; ++counts.clearSingleKey; m_pBuffer->insert(clearedRange.begin).mutation().clearBoundary(); @@ -2759,40 +2649,31 @@ public: void mutate(int op, StringRef param1, StringRef param2) NOT_IMPLEMENTED - void setOldestVersion(Version v) { + void setOldestVersion(Version v) { m_newOldestVersion = v; } - Version getOldestVersion() { - return m_pager->getOldestVersion(); - } + Version getOldestVersion() { return m_pager->getOldestVersion(); } Version getLatestVersion() { - if(m_writeVersion != invalidVersion) - return m_writeVersion; + if (m_writeVersion != invalidVersion) return m_writeVersion; return m_pager->getLatestVersion(); } - Version getWriteVersion() { - return m_writeVersion; - } + Version getWriteVersion() { return m_writeVersion; } - Version getLastCommittedVersion() { - return m_lastCommittedVersion; - } + Version getLastCommittedVersion() { return m_lastCommittedVersion; } - VersionedBTree(IPager2 *pager, std::string name) - : m_pager(pager), - m_writeVersion(invalidVersion), - m_lastCommittedVersion(invalidVersion), - m_pBuffer(nullptr), - m_name(name) - { + VersionedBTree(IPager2* pager, std::string name) + : m_pager(pager), m_writeVersion(invalidVersion), m_lastCommittedVersion(invalidVersion), m_pBuffer(nullptr), + m_name(name) { m_init = init_impl(this); m_latestCommit = m_init; } - ACTOR static Future incrementalSubtreeClear(VersionedBTree *self, bool *pStop = nullptr, int batchSize = 10, unsigned int minPages = 0, int maxPages = std::numeric_limits::max()) { + ACTOR static Future incrementalSubtreeClear(VersionedBTree* self, bool* pStop = nullptr, int batchSize = 10, + unsigned int minPages = 0, + int maxPages = std::numeric_limits::max()) { // TODO: Is it contractually okay to always to read at the latest version? state Reference snapshot = self->m_pager->getReadSnapshot(self->m_pager->getLatestVersion()); state int freedPages = 0; @@ -2801,52 +2682,52 @@ public: state std::vector>>> entries; // Take up to batchSize pages from front of queue - while(entries.size() < batchSize) { + while (entries.size() < batchSize) { Optional q = wait(self->m_lazyDeleteQueue.pop()); debug_printf("LazyDelete: popped %s\n", toString(q).c_str()); - if(!q.present()) { + if (!q.present()) { break; } // Start reading the page, without caching - entries.push_back(std::make_pair(q.get(), self->readPage(snapshot, q.get().pageID, nullptr, nullptr, true))); + entries.push_back( + std::make_pair(q.get(), self->readPage(snapshot, q.get().pageID, nullptr, nullptr, true))); } - if(entries.empty()) { + if (entries.empty()) { break; } state int i; - for(i = 0; i < entries.size(); ++i) { + for (i = 0; i < entries.size(); ++i) { Reference p = wait(entries[i].second); - const LazyDeleteQueueEntry &entry = entries[i].first; - const BTreePage &btPage = *(BTreePage *)p->begin(); + const LazyDeleteQueueEntry& entry = entries[i].first; + const BTreePage& btPage = *(BTreePage*)p->begin(); debug_printf("LazyDelete: processing %s\n", toString(entry).c_str()); // Level 1 (leaf) nodes should never be in the lazy delete queue ASSERT(btPage.height > 1); - + // Iterate over page entries, skipping key decoding using BTreePage::ValueTree which uses // RedwoodRecordRef::DeltaValueOnly as the delta type type to skip key decoding BTreePage::ValueTree::Mirror reader(&btPage.valueTree(), &dbBegin, &dbEnd); auto c = reader.getCursor(); ASSERT(c.moveFirst()); Version v = entry.version; - while(1) { - if(c.get().value.present()) { + while (1) { + if (c.get().value.present()) { BTreePageID btChildPageID = c.get().getChildPage(); // If this page is height 2, then the children are leaves so free - if(btPage.height == 2) { + if (btPage.height == 2) { debug_printf("LazyDelete: freeing child %s\n", toString(btChildPageID).c_str()); self->freeBtreePage(btChildPageID, v); freedPages += btChildPageID.size(); - } - else { + } else { // Otherwise, queue them for lazy delete. debug_printf("LazyDelete: queuing child %s\n", toString(btChildPageID).c_str()); - self->m_lazyDeleteQueue.pushFront(LazyDeleteQueueEntry{v, btChildPageID}); + self->m_lazyDeleteQueue.pushFront(LazyDeleteQueueEntry{ v, btChildPageID }); } } - if(!c.moveNext()) { + if (!c.moveNext()) { break; } } @@ -2858,28 +2739,30 @@ public: } // If stop is set and we've freed the minimum number of pages required, or the maximum is exceeded, return. - if((freedPages >= minPages && pStop != nullptr && *pStop) || freedPages >= maxPages) { + if ((freedPages >= minPages && pStop != nullptr && *pStop) || freedPages >= maxPages) { break; } } - debug_printf("LazyDelete: freed %d pages, %s has %" PRId64 " entries\n", freedPages, self->m_lazyDeleteQueue.name.c_str(), self->m_lazyDeleteQueue.numEntries); + debug_printf("LazyDelete: freed %d pages, %s has %" PRId64 " entries\n", freedPages, + self->m_lazyDeleteQueue.name.c_str(), self->m_lazyDeleteQueue.numEntries); return freedPages; } - ACTOR static Future init_impl(VersionedBTree *self) { + ACTOR static Future init_impl(VersionedBTree* self) { wait(self->m_pager->init()); state Version latest = self->m_pager->getLatestVersion(); self->m_newOldestVersion = self->m_pager->getOldestVersion(); - debug_printf("Recovered pager to version %" PRId64 ", oldest version is %" PRId64 "\n", self->m_newOldestVersion); + debug_printf("Recovered pager to version %" PRId64 ", oldest version is %" PRId64 "\n", + self->m_newOldestVersion); state Key meta = self->m_pager->getMetaKey(); - if(meta.size() == 0) { + if (meta.size() == 0) { self->m_header.formatVersion = MetaKey::FORMAT_VERSION; LogicalPageID id = wait(self->m_pager->newPageID()); - BTreePageID newRoot((LogicalPageID *)&id, 1); + BTreePageID newRoot((LogicalPageID*)&id, 1); debug_printf("new root %s\n", toString(newRoot).c_str()); self->m_header.root.set(newRoot, sizeof(headerSpace) - sizeof(m_header)); self->m_header.height = 1; @@ -2895,8 +2778,7 @@ public: self->m_pager->setMetaKey(self->m_header.asKeyRef()); wait(self->m_pager->commit()); debug_printf("Committed initial commit.\n"); - } - else { + } else { self->m_header.fromKeyRef(meta); self->m_lazyDeleteQueue.recover(self->m_pager, self->m_header.lazyDeleteQueue, "LazyDeleteQueueRecovered"); } @@ -2907,13 +2789,11 @@ public: return Void(); } - Future init() override { - return m_init; - } + Future init() override { return m_init; } virtual ~VersionedBTree() { // This probably shouldn't be called directly (meaning deleting an instance directly) but it should be safe, - // it will cancel init and commit and leave the pager alive but with potentially an incomplete set of + // it will cancel init and commit and leave the pager alive but with potentially an incomplete set of // uncommitted writes so it should not be committed. m_init.cancel(); m_latestCommit.cancel(); @@ -2928,19 +2808,18 @@ public: KeyRef m = snapshot->getMetaKey(); // Currently all internal records generated in the write path are at version 0 - return Reference(new Cursor(snapshot, ((MetaKey *)m.begin())->root.get(), (Version)0)); + return Reference(new Cursor(snapshot, ((MetaKey*)m.begin())->root.get(), (Version)0)); } // Must be nondecreasing void setWriteVersion(Version v) { ASSERT(v > m_lastCommittedVersion); // If there was no current mutation buffer, create one in the buffer map and update m_pBuffer - if(m_pBuffer == nullptr) { + if (m_pBuffer == nullptr) { // When starting a new mutation buffer its start version must be greater than the last write version ASSERT(v > m_writeVersion); m_pBuffer = &m_mutationBuffers[v]; - } - else { + } else { // It's OK to set the write version to the same version repeatedly so long as m_pBuffer is not null ASSERT(v >= m_writeVersion); } @@ -2948,12 +2827,11 @@ public: } Future commit() { - if(m_pBuffer == nullptr) - return m_latestCommit; + if (m_pBuffer == nullptr) return m_latestCommit; return commit_impl(this); } - ACTOR static Future destroyAndCheckSanity_impl(VersionedBTree *self) { + ACTOR static Future destroyAndCheckSanity_impl(VersionedBTree* self) { ASSERT(g_network->isSimulated()); debug_printf("Clearing tree.\n"); @@ -2964,7 +2842,7 @@ public: state int freedPages = wait(self->incrementalSubtreeClear(self)); wait(self->commit()); // Keep looping until the last commit doesn't do anything at all - if(self->m_lazyDeleteQueue.numEntries == 0 && freedPages == 0) { + if (self->m_lazyDeleteQueue.numEntries == 0 && freedPages == 0) { break; } self->setWriteVersion(self->getLatestVersion() + 1); @@ -2994,29 +2872,22 @@ public: return Void(); } - Future destroyAndCheckSanity() { - return destroyAndCheckSanity_impl(this); - } + Future destroyAndCheckSanity() { return destroyAndCheckSanity_impl(this); } private: struct ChildLinksRef { ChildLinksRef() = default; ChildLinksRef(VectorRef children, RedwoodRecordRef upperBound) - : children(children), upperBound(upperBound) { - } + : children(children), upperBound(upperBound) {} - ChildLinksRef(const RedwoodRecordRef *child, const RedwoodRecordRef *upperBound) - : children((RedwoodRecordRef *)child, 1), upperBound(*upperBound) { - } + ChildLinksRef(const RedwoodRecordRef* child, const RedwoodRecordRef* upperBound) + : children((RedwoodRecordRef*)child, 1), upperBound(*upperBound) {} - ChildLinksRef(Arena &arena, const ChildLinksRef &toCopy) - : children(arena, toCopy.children), upperBound(arena, toCopy.upperBound) { - } + ChildLinksRef(Arena& arena, const ChildLinksRef& toCopy) + : children(arena, toCopy.children), upperBound(arena, toCopy.upperBound) {} - int expectedSize() const { - return children.expectedSize() + upperBound.expectedSize(); - } + int expectedSize() const { return children.expectedSize() + upperBound.expectedSize(); } std::string toString() const { return format("{children=%s upperbound=%s}", ::toString(children).c_str(), upperBound.toString().c_str()); @@ -3033,38 +2904,36 @@ private: // boundaries of consecutive entries. struct InternalPageBuilder { // Cursor must be at first entry in page - InternalPageBuilder(const BTreePage::BinaryTree::Cursor &c) - : cursor(c), modified(false), childPageCount(0) - { - } + InternalPageBuilder(const BTreePage::BinaryTree::Cursor& c) : cursor(c), modified(false), childPageCount(0) {} private: // This must be called internally, on records whose arena has already been added to the entries arena - inline void addEntry(const RedwoodRecordRef &rec) { - if(rec.value.present()) { + inline void addEntry(const RedwoodRecordRef& rec) { + if (rec.value.present()) { ++childPageCount; } // If no modification detected yet then check that this record is identical to the next // record from the original page which is at the current cursor position. - if(!modified) { - if(cursor.valid()) { - if(rec != cursor.get()) { - debug_printf("InternalPageBuilder: Found internal page difference. new: %s old: %s\n", rec.toString().c_str(), cursor.get().toString().c_str()); + if (!modified) { + if (cursor.valid()) { + if (rec != cursor.get()) { + debug_printf("InternalPageBuilder: Found internal page difference. new: %s old: %s\n", + rec.toString().c_str(), cursor.get().toString().c_str()); modified = true; - } - else { + } else { cursor.moveNext(); } - } - else { - debug_printf("InternalPageBuilder: Found internal page difference. new: %s old: \n", rec.toString().c_str()); + } else { + debug_printf("InternalPageBuilder: Found internal page difference. new: %s old: \n", + rec.toString().c_str()); modified = true; } } entries.push_back(entries.arena(), rec); } + public: // Add the child entries from newSet into entries void addEntries(ChildLinksRef newSet) { @@ -3072,14 +2941,14 @@ private: // as the first lowerBound in newSet (or newSet is empty, as the next newSet is necessarily greater) // then add the upper bound of the previous set as a value-less record so that on future reads // the previous child page can be decoded correctly. - if(!entries.empty() && entries.back().value.present() - && (newSet.children.empty() || !newSet.children.front().sameExceptValue(lastUpperBound))) - { - debug_printf("InternalPageBuilder: Added placeholder %s\n", lastUpperBound.withoutValue().toString().c_str()); + if (!entries.empty() && entries.back().value.present() && + (newSet.children.empty() || !newSet.children.front().sameExceptValue(lastUpperBound))) { + debug_printf("InternalPageBuilder: Added placeholder %s\n", + lastUpperBound.withoutValue().toString().c_str()); addEntry(lastUpperBound.withoutValue()); } - for(auto &child : newSet.children) { + for (auto& child : newSet.children) { debug_printf("InternalPageBuilder: Adding child entry %s\n", child.toString().c_str()); addEntry(child); } @@ -3096,32 +2965,40 @@ private: // This is only done if modified is set to avoid rewriting this page for this purpose only. // // After this call, lastUpperBound is internal page's upper bound. - void finalize(const RedwoodRecordRef &upperBound, const RedwoodRecordRef &decodeUpperBound) { - debug_printf("InternalPageBuilder::end modified=%d upperBound=%s decodeUpperBound=%s lastUpperBound=%s\n", modified, upperBound.toString().c_str(), decodeUpperBound.toString().c_str(), lastUpperBound.toString().c_str()); + void finalize(const RedwoodRecordRef& upperBound, const RedwoodRecordRef& decodeUpperBound) { + debug_printf( + "InternalPageBuilder::end modified=%d upperBound=%s decodeUpperBound=%s lastUpperBound=%s\n", + modified, upperBound.toString().c_str(), decodeUpperBound.toString().c_str(), + lastUpperBound.toString().c_str()); modified = modified || cursor.valid(); debug_printf("InternalPageBuilder::end modified=%d after cursor check\n", modified); - // If there are boundary key entries and the last one has a child page then the + // If there are boundary key entries and the last one has a child page then the // upper bound for this internal page must match the required upper bound for // the last child entry. - if(!entries.empty() && entries.back().value.present()) { + if (!entries.empty() && entries.back().value.present()) { debug_printf("InternalPageBuilder::end last entry is not null\n"); // If the page contents were not modified so far and the upper bound required // for the last child page (lastUpperBound) does not match what the page // was encoded with then the page must be modified. - if(!modified && !lastUpperBound.sameExceptValue(decodeUpperBound)) { - debug_printf("InternalPageBuilder::end modified set true because lastUpperBound does not match decodeUpperBound\n"); + if (!modified && !lastUpperBound.sameExceptValue(decodeUpperBound)) { + debug_printf("InternalPageBuilder::end modified set true because lastUpperBound does not match " + "decodeUpperBound\n"); modified = true; } - if(modified && !lastUpperBound.sameExceptValue(upperBound)) { - debug_printf("InternalPageBuilder::end Modified is true but lastUpperBound does not match upperBound so adding placeholder\n"); + if (modified && !lastUpperBound.sameExceptValue(upperBound)) { + debug_printf("InternalPageBuilder::end Modified is true but lastUpperBound does not match " + "upperBound so adding placeholder\n"); addEntry(lastUpperBound.withoutValue()); lastUpperBound = upperBound; } } - debug_printf("InternalPageBuilder::end exit. modified=%d upperBound=%s decodeUpperBound=%s lastUpperBound=%s\n", modified, upperBound.toString().c_str(), decodeUpperBound.toString().c_str(), lastUpperBound.toString().c_str()); + debug_printf( + "InternalPageBuilder::end exit. modified=%d upperBound=%s decodeUpperBound=%s lastUpperBound=%s\n", + modified, upperBound.toString().c_str(), decodeUpperBound.toString().c_str(), + lastUpperBound.toString().c_str()); } BTreePage::BinaryTree::Cursor cursor; @@ -3153,33 +3030,25 @@ private: // No point in serializing an atomic op, it needs to be coalesced to a real value. ASSERT(!isAtomicOp()); - if(isClear()) - return RedwoodRecordRef(userKey, version); + if (isClear()) return RedwoodRecordRef(userKey, version); return RedwoodRecordRef(userKey, version, value); } - std::string toString() const { - return format("op=%d val='%s'", op, printable(value).c_str()); - } + std::string toString() const { return format("op=%d val='%s'", op, printable(value).c_str()); } }; struct RangeMutation { - RangeMutation() : boundaryChanged(false), clearAfterBoundary(false) { - } + RangeMutation() : boundaryChanged(false), clearAfterBoundary(false) {} bool boundaryChanged; - Optional boundaryValue; // Not present means cleared + Optional boundaryValue; // Not present means cleared bool clearAfterBoundary; - bool boundaryCleared() const { - return boundaryChanged && !boundaryValue.present(); - } + bool boundaryCleared() const { return boundaryChanged && !boundaryValue.present(); } // Returns true if this RangeMutation doesn't actually mutate anything - bool noChanges() const { - return !boundaryChanged && !clearAfterBoundary; - } + bool noChanges() const { return !boundaryChanged && !clearAfterBoundary; } void clearBoundary() { boundaryChanged = true; @@ -3190,24 +3059,21 @@ private: clearBoundary(); clearAfterBoundary = true; } - + void setBoundaryValue(ValueRef v) { boundaryChanged = true; boundaryValue = v; } - bool boundarySet() const { - return boundaryChanged && boundaryValue.present(); - } + bool boundarySet() const { return boundaryChanged && boundaryValue.present(); } std::string toString() const { - return format("boundaryChanged=%d clearAfterBoundary=%d boundaryValue=%s", boundaryChanged, clearAfterBoundary, ::toString(boundaryValue).c_str()); + return format("boundaryChanged=%d clearAfterBoundary=%d boundaryValue=%s", boundaryChanged, + clearAfterBoundary, ::toString(boundaryValue).c_str()); } }; public: - - #include "ArtMutationBuffer.h" struct MutationBufferStdMap { MutationBufferStdMap() { @@ -3228,52 +3094,36 @@ public: struct iterator : public MutationsT::iterator { typedef MutationsT::iterator Base; iterator() = default; - iterator(const MutationsT::iterator &i) : Base(i) { - } + iterator(const MutationsT::iterator& i) : Base(i) {} - const KeyRef & key() { - return (*this)->first; - } + const KeyRef& key() { return (*this)->first; } - RangeMutation & mutation() { - return (*this)->second; - } + RangeMutation& mutation() { return (*this)->second; } }; struct const_iterator : public MutationsT::const_iterator { typedef MutationsT::const_iterator Base; const_iterator() = default; - const_iterator(const MutationsT::const_iterator &i) : Base(i) { - } - const_iterator(const MutationsT::iterator &i) : Base(i) { - } + const_iterator(const MutationsT::const_iterator& i) : Base(i) {} + const_iterator(const MutationsT::iterator& i) : Base(i) {} - const KeyRef & key() { - return (*this)->first; - } + const KeyRef& key() { return (*this)->first; } - const RangeMutation & mutation() { - return (*this)->second; - } + const RangeMutation& mutation() { return (*this)->second; } }; // Return a T constructed in arena - template T copyToArena(const T &object) { + template + T copyToArena(const T& object) { return T(arena, object); } - const_iterator upper_bound(const KeyRef &k) const { - return mutations.upper_bound(k); - } + const_iterator upper_bound(const KeyRef& k) const { return mutations.upper_bound(k); } - const_iterator lower_bound(const KeyRef &k) const { - return mutations.lower_bound(k); - } + const_iterator lower_bound(const KeyRef& k) const { return mutations.lower_bound(k); } // erase [begin, end) from the mutation map - void erase(const const_iterator &begin, const const_iterator &end) { - mutations.erase(begin, end); - } + void erase(const const_iterator& begin, const const_iterator& end) { mutations.erase(begin, end); } // Find or create a mutation buffer boundary for bound and return an iterator to it iterator insert(KeyRef boundary) { @@ -3284,34 +3134,34 @@ public: iterator ib = mutations.lower_bound(boundary); // If we found the boundary we are looking for, return its iterator - if(ib.key() == boundary) { + if (ib.key() == boundary) { return ib; } // ib is our insert hint. Copy boundary into arena and insert boundary into buffer boundary = KeyRef(arena, boundary); - ib = mutations.insert(ib, {boundary, RangeMutation()}); + ib = mutations.insert(ib, { boundary, RangeMutation() }); // ib is certainly > begin() because it is guaranteed that the empty string // boundary exists and the only way to have found that is to look explicitly // for it in which case we would have returned above. iterator iPrevious = ib; --iPrevious; - // If the range we just divided was being cleared, then the dividing boundary key and range after it must also be cleared - if(iPrevious.mutation().clearAfterBoundary) { + // If the range we just divided was being cleared, then the dividing boundary key and range after it must + // also be cleared + if (iPrevious.mutation().clearAfterBoundary) { ib.mutation().clearAll(); } return ib; } - }; #define USE_ART_MUTATION_BUFFER 1 #ifdef USE_ART_MUTATION_BUFFER - typedef struct MutationBufferART MutationBuffer; + typedef struct MutationBufferART MutationBuffer; #else - typedef struct MutationBufferStdMap MutationBuffer; + typedef struct MutationBufferStdMap MutationBuffer; #endif private: @@ -3320,10 +3170,10 @@ private: * This structure's organization is meant to put pending updates for the btree in an order * that makes it efficient to query all pending mutations across all pending versions which are * relevant to a particular subtree of the btree. - * + * * At the top level, it is a map of the start of a range being modified to a RangeMutation. * The end of the range is map key (which is the next range start in the map). - * + * * - The buffer starts out with keys '' and endKVV.key already populated. * * - When a new key is inserted into the buffer map, it is by definition @@ -3364,8 +3214,8 @@ private: * to be sorted later just before being merged into the existing leaf page. */ - IPager2 *m_pager; - MutationBuffer *m_pBuffer; + IPager2* m_pager; + MutationBuffer* m_pBuffer; std::map m_mutationBuffers; Version m_writeVersion; @@ -3384,14 +3234,16 @@ private: LazyDeleteQueueT m_lazyDeleteQueue; // Writes entries to 1 or more pages and return a vector of boundary keys with their IPage(s) - ACTOR static Future>> writePages(VersionedBTree *self, const RedwoodRecordRef *lowerBound, const RedwoodRecordRef *upperBound, VectorRef entries, int height, Version v, BTreePageID previousID) { + ACTOR static Future>> writePages( + VersionedBTree* self, const RedwoodRecordRef* lowerBound, const RedwoodRecordRef* upperBound, + VectorRef entries, int height, Version v, BTreePageID previousID) { ASSERT(entries.size() > 0); state Standalone> records; // This is how much space for the binary tree exists in the page, after the header state int blockSize = self->m_pager->getUsablePageSize(); state int pageSize = blockSize - sizeof(BTreePage); - state float fillFactor = 0.66; // TODO: Make this a knob + state float fillFactor = 0.66; // TODO: Make this a knob state int pageFillTarget = pageSize * fillFactor; state int blockCount = 1; @@ -3406,20 +3258,21 @@ private: // Leaves can have just one record if it's large, but internal pages should have at least 4 state int minimumEntries = (height == 1 ? 1 : 4); - + // Lower bound of the page being added to state RedwoodRecordRef pageLowerBound = lowerBound->withoutValue(); state RedwoodRecordRef pageUpperBound; - while(1) { + while (1) { // While there are still entries to add and the page isn't full enough, add an entry - while(i < entries.size() && (i - start < minimumEntries || compressedBytes < pageFillTarget) ) { - const RedwoodRecordRef &entry = entries[i]; + while (i < entries.size() && (i - start < minimumEntries || compressedBytes < pageFillTarget)) { + const RedwoodRecordRef& entry = entries[i]; // Get delta from previous record or page lower boundary if this is the first item in a page - const RedwoodRecordRef &base = (i == start) ? pageLowerBound : entries[i - 1]; + const RedwoodRecordRef& base = (i == start) ? pageLowerBound : entries[i - 1]; - // All record pairs in entries have skipLen bytes in common with each other, but for i == 0 the base is lowerBound + // All record pairs in entries have skipLen bytes in common with each other, but for i == 0 the base is + // lowerBound int skip = i == 0 ? 0 : skipLen; // In a delta tree, all common prefix bytes that can be borrowed, will be, but not necessarily @@ -3432,27 +3285,29 @@ private: int valueSize = entry.value.present() ? entry.value.get().size() : 0; int nodeSize = BTreePage::BinaryTree::Node::headerSize(largeTree) + deltaSize; - debug_printf("Adding %3d of %3lu (i=%3d) klen %4d vlen %5d nodeSize %5d deltaSize %5d page usage: %d/%d (%.2f%%) record=%s\n", - i + 1, entries.size(), i, keySize, valueSize, nodeSize, deltaSize, compressedBytes, pageSize, (float)compressedBytes / pageSize * 100, entry.toString(height == 1).c_str()); + debug_printf("Adding %3d of %3lu (i=%3d) klen %4d vlen %5d nodeSize %5d deltaSize %5d page usage: " + "%d/%d (%.2f%%) record=%s\n", + i + 1, entries.size(), i, keySize, valueSize, nodeSize, deltaSize, compressedBytes, + pageSize, (float)compressedBytes / pageSize * 100, entry.toString(height == 1).c_str()); // While the node doesn't fit, expand the page. // This is a loop because if the page size moves into "large" range for DeltaTree // then the overhead will increase, which could require another page expansion. int spaceAvailable = pageSize - compressedBytes; - if(nodeSize > spaceAvailable) { + if (nodeSize > spaceAvailable) { // Figure out how many additional whole or partial blocks are needed // newBlocks = ceil ( additional space needed / block size) int newBlocks = 1 + (nodeSize - spaceAvailable - 1) / blockSize; int newPageSize = pageSize + (newBlocks * blockSize); // If we've moved into "large" page range for the delta tree then add additional overhead required - if(!largeTree && newPageSize > BTreePage::BinaryTree::SmallSizeLimit) { + if (!largeTree && newPageSize > BTreePage::BinaryTree::SmallSizeLimit) { largeTree = true; // Add increased overhead for the current node to nodeSize nodeSize += BTreePage::BinaryTree::LargeTreePerNodeExtraOverhead; // Add increased overhead for all previously added nodes compressedBytes += (i - start) * BTreePage::BinaryTree::LargeTreePerNodeExtraOverhead; - + // Update calculations above made with previous overhead sizes spaceAvailable = pageSize - compressedBytes; newBlocks = 1 + (nodeSize - spaceAvailable - 1) / blockSize; @@ -3471,10 +3326,11 @@ private: // Flush the accumulated records to a page state int nextStart = i; - // If we are building internal pages and there is a record after this page (index nextStart) but it has an empty childPage value then skip it. - // It only exists to serve as an upper boundary for a child page that has not been rewritten in the current commit, and that - // purpose will now be served by the upper bound of the page we are now building. - if(height != 1 && nextStart < entries.size() && !entries[nextStart].value.present()) { + // If we are building internal pages and there is a record after this page (index nextStart) but it has an + // empty childPage value then skip it. It only exists to serve as an upper boundary for a child page that + // has not been rewritten in the current commit, and that purpose will now be served by the upper bound of + // the page we are now building. + if (height != 1 && nextStart < entries.size() && !entries[nextStart].value.present()) { ++nextStart; } @@ -3483,51 +3339,56 @@ private: // If this is a leaf page, and not the last one to be written, shorten the upper boundary state bool isLastPage = (nextStart == entries.size()); - if(!isLastPage && height == 1) { + if (!isLastPage && height == 1) { int commonPrefix = pageUpperBound.getCommonPrefixLen(entries[i - 1], 0); pageUpperBound.truncate(commonPrefix + 1); } state std::vector> pages; - BTreePage *btPage; + BTreePage* btPage; - if(blockCount == 1) { + if (blockCount == 1) { Reference page = self->m_pager->newPageBuffer(); - btPage = (BTreePage *)page->mutate(); + btPage = (BTreePage*)page->mutate(); pages.push_back(std::move(page)); - } - else { + } else { ASSERT(blockCount > 1); int size = blockSize * blockCount; - btPage = (BTreePage *)new uint8_t[size]; + btPage = (BTreePage*)new uint8_t[size]; } btPage->height = height; btPage->kvBytes = kvBytes; - debug_printf("Building tree. start=%d i=%d count=%d page usage: %d/%d (%.2f%%) bytes\nlower: %s\nupper: %s\n", start, i, i - start, - compressedBytes, pageSize, (float)compressedBytes / pageSize * 100, pageLowerBound.toString(false).c_str(), pageUpperBound.toString(false).c_str()); + debug_printf( + "Building tree. start=%d i=%d count=%d page usage: %d/%d (%.2f%%) bytes\nlower: %s\nupper: %s\n", + start, i, i - start, compressedBytes, pageSize, (float)compressedBytes / pageSize * 100, + pageLowerBound.toString(false).c_str(), pageUpperBound.toString(false).c_str()); - int written = btPage->tree().build(pageSize, &entries[start], &entries[i], &pageLowerBound, &pageUpperBound); - if(written > pageSize) { - debug_printf("ERROR: Wrote %d bytes to %d byte page (%d blocks). recs %d kvBytes %d compressed %d\n", written, pageSize, blockCount, i - start, kvBytes, compressedBytes); - fprintf(stderr, "ERROR: Wrote %d bytes to %d byte page (%d blocks). recs %d kvBytes %d compressed %d\n", written, pageSize, blockCount, i - start, kvBytes, compressedBytes); + int written = + btPage->tree().build(pageSize, &entries[start], &entries[i], &pageLowerBound, &pageUpperBound); + if (written > pageSize) { + debug_printf("ERROR: Wrote %d bytes to %d byte page (%d blocks). recs %d kvBytes %d compressed %d\n", + written, pageSize, blockCount, i - start, kvBytes, compressedBytes); + fprintf(stderr, + "ERROR: Wrote %d bytes to %d byte page (%d blocks). recs %d kvBytes %d compressed %d\n", + written, pageSize, blockCount, i - start, kvBytes, compressedBytes); ASSERT(false); } // Create chunked pages // TODO: Avoid copying page bytes, but this is not trivial due to how pager checksums are currently handled. - if(blockCount != 1) { + if (blockCount != 1) { // Mark the slack in the page buffer as defined - VALGRIND_MAKE_MEM_DEFINED(((uint8_t *)btPage) + written, (blockCount * blockSize) - written); - const uint8_t *rptr = (const uint8_t *)btPage; - for(int b = 0; b < blockCount; ++b) { + VALGRIND_MAKE_MEM_DEFINED(((uint8_t*)btPage) + written, (blockCount * blockSize) - written); + const uint8_t* rptr = (const uint8_t*)btPage; + for (int b = 0; b < blockCount; ++b) { Reference page = self->m_pager->newPageBuffer(); memcpy(page->mutate(), rptr, blockSize); rptr += blockSize; pages.push_back(std::move(page)); } - delete [] (uint8_t *)btPage; + delete[](uint8_t*) btPage; } // Write this btree page, which is made of 1 or more pager pages. @@ -3537,21 +3398,20 @@ private: // If we are only writing 1 page and it has the same BTreePageID size as the original then try to reuse the // LogicalPageIDs in previousID and try to update them atomically. bool isOnlyPage = isLastPage && (start == 0); - if(isOnlyPage && previousID.size() == pages.size()) { - for(p = 0; p < pages.size(); ++p) { + if (isOnlyPage && previousID.size() == pages.size()) { + for (p = 0; p < pages.size(); ++p) { LogicalPageID id = wait(self->m_pager->atomicUpdatePage(previousID[p], pages[p], v)); childPageID.push_back(records.arena(), id); } - } - else { + } else { // Either the original page is being split, or it's not but it has changed BTreePageID size. // Either way, there is no point in reusing any of the original page IDs because the parent // must be rewritten anyway to count for the change in child count or child links. // Free the old IDs, but only once (before the first output record is added). - if(records.empty()) { + if (records.empty()) { self->freeBtreePage(previousID, v); } - for(p = 0; p < pages.size(); ++p) { + for (p = 0; p < pages.size(); ++p) { LogicalPageID id = wait(self->m_pager->newPageID()); self->m_pager->updatePage(id, pages[p]); childPageID.push_back(records.arena(), id); @@ -3562,15 +3422,18 @@ private: // Update activity counts ++counts.pageWrites; - if(pages.size() > 1) { + if (pages.size() > 1) { counts.extPageWrites += pages.size() - 1; } - debug_printf("Flushing %s lastPage=%d original=%s start=%d i=%d count=%d page usage: %d/%d (%.2f%%) bytes\nlower: %s\nupper: %s\n", toString(childPageID).c_str(), isLastPage, toString(previousID).c_str(), start, i, i - start, - compressedBytes, pageSize, (float)compressedBytes / pageSize * 100, pageLowerBound.toString(false).c_str(), pageUpperBound.toString(false).c_str()); + debug_printf("Flushing %s lastPage=%d original=%s start=%d i=%d count=%d page usage: %d/%d (%.2f%%) " + "bytes\nlower: %s\nupper: %s\n", + toString(childPageID).c_str(), isLastPage, toString(previousID).c_str(), start, i, i - start, + compressedBytes, pageSize, (float)compressedBytes / pageSize * 100, + pageLowerBound.toString(false).c_str(), pageUpperBound.toString(false).c_str()); - if(REDWOOD_DEBUG) { - for(int j = start; j < i; ++j) { + if (REDWOOD_DEBUG) { + for (int j = start; j < i; ++j) { debug_printf(" %3d: %s\n", j, entries[j].toString(height == 1).c_str()); } ASSERT(pageLowerBound.key <= pageUpperBound.key); @@ -3578,10 +3441,11 @@ private: // Push a new record onto the results set, without the child page, copying it into the records arena records.push_back_deep(records.arena(), pageLowerBound.withoutValue()); - // Set the child page value of the inserted record to childPageID, which has already been allocated in records.arena() above + // Set the child page value of the inserted record to childPageID, which has already been allocated in + // records.arena() above records.back().setChildPage(childPageID); - if(isLastPage) { + if (isLastPage) { break; } @@ -3591,85 +3455,82 @@ private: pageLowerBound = pageUpperBound; } - // If we're writing internal pages, if the last entry was the start of a new page and had an empty child link then it would not be written to a page. - // This means that the upper boundary for the the page set being built is not the upper bound of the final page in that set, so it must be added - // to the output set to preserve the decodability of the subtree to its left. - // Fortunately, this is easy to detect because the loop above would exit before i has reached the item count. - if(height != 1 && i != entries.size()) { - debug_printf("Adding dummy record to avoid writing useless page: %s\n", pageUpperBound.toString(false).c_str()); + // If we're writing internal pages, if the last entry was the start of a new page and had an empty child link + // then it would not be written to a page. This means that the upper boundary for the the page set being built + // is not the upper bound of the final page in that set, so it must be added to the output set to preserve the + // decodability of the subtree to its left. Fortunately, this is easy to detect because the loop above would + // exit before i has reached the item count. + if (height != 1 && i != entries.size()) { + debug_printf("Adding dummy record to avoid writing useless page: %s\n", + pageUpperBound.toString(false).c_str()); records.push_back_deep(records.arena(), pageUpperBound); } return records; } - ACTOR static Future>> buildNewRoot(VersionedBTree *self, Version version, Standalone> records, int height) { + ACTOR static Future>> buildNewRoot( + VersionedBTree* self, Version version, Standalone> records, int height) { debug_printf("buildNewRoot start version %" PRId64 ", %lu records\n", version, records.size()); // While there are multiple child pages for this version we must write new tree levels. - while(records.size() > 1) { + while (records.size() > 1) { self->m_header.height = ++height; - Standalone> newRecords = wait(writePages(self, &dbBegin, &dbEnd, records, height, version, BTreePageID())); - debug_printf("Wrote a new root level at version %" PRId64 " height %d size %lu pages\n", version, height, newRecords.size()); + Standalone> newRecords = + wait(writePages(self, &dbBegin, &dbEnd, records, height, version, BTreePageID())); + debug_printf("Wrote a new root level at version %" PRId64 " height %d size %lu pages\n", version, height, + newRecords.size()); records = newRecords; } return records; } - class SuperPage : public IPage, ReferenceCounted, public FastAllocated{ + class SuperPage : public IPage, ReferenceCounted, public FastAllocated { public: SuperPage(std::vector> pages) { int blockSize = pages.front()->size(); m_size = blockSize * pages.size(); m_data = new uint8_t[m_size]; - uint8_t *wptr = m_data; - for(auto &p : pages) { + uint8_t* wptr = m_data; + for (auto& p : pages) { ASSERT(p->size() == blockSize); memcpy(wptr, p->begin(), blockSize); wptr += blockSize; } } - virtual ~SuperPage() { - delete [] m_data; - } + virtual ~SuperPage() { delete[] m_data; } virtual Reference clone() const { - return Reference(new SuperPage({Reference::addRef(this)})); + return Reference(new SuperPage({ Reference::addRef(this) })); } - void addref() const { - ReferenceCounted::addref(); - } + void addref() const { ReferenceCounted::addref(); } - void delref() const { - ReferenceCounted::delref(); - } + void delref() const { ReferenceCounted::delref(); } - int size() const { - return m_size; - } + int size() const { return m_size; } - uint8_t const* begin() const { - return m_data; - } + uint8_t const* begin() const { return m_data; } - uint8_t* mutate() { - return m_data; - } + uint8_t* mutate() { return m_data; } private: - uint8_t *m_data; + uint8_t* m_data; int m_size; }; - ACTOR static Future> readPage(Reference snapshot, BTreePageID id, const RedwoodRecordRef *lowerBound, const RedwoodRecordRef *upperBound, bool forLazyDelete = false) { - if(!forLazyDelete) { - debug_printf("readPage() op=read %s @%" PRId64 " lower=%s upper=%s\n", toString(id).c_str(), snapshot->getVersion(), lowerBound->toString().c_str(), upperBound->toString().c_str()); - } - else { - debug_printf("readPage() op=readForDeferredClear %s @%" PRId64 " \n", toString(id).c_str(), snapshot->getVersion()); + ACTOR static Future> readPage(Reference snapshot, BTreePageID id, + const RedwoodRecordRef* lowerBound, + const RedwoodRecordRef* upperBound, + bool forLazyDelete = false) { + if (!forLazyDelete) { + debug_printf("readPage() op=read %s @%" PRId64 " lower=%s upper=%s\n", toString(id).c_str(), + snapshot->getVersion(), lowerBound->toString().c_str(), upperBound->toString().c_str()); + } else { + debug_printf("readPage() op=readForDeferredClear %s @%" PRId64 " \n", toString(id).c_str(), + snapshot->getVersion()); } wait(yield()); @@ -3677,15 +3538,14 @@ private: state Reference page; ++counts.pageReads; - if(id.size() == 1) { + if (id.size() == 1) { Reference p = wait(snapshot->getPhysicalPage(id.front(), !forLazyDelete, false)); page = p; - } - else { + } else { ASSERT(!id.empty()); counts.extPageReads += (id.size() - 1); std::vector>> reads; - for(auto &pageID : id) { + for (auto& pageID : id) { reads.push_back(snapshot->getPhysicalPage(pageID, !forLazyDelete, false)); } std::vector> pages = wait(getAll(reads)); @@ -3694,52 +3554,54 @@ private: } debug_printf("readPage() op=readComplete %s @%" PRId64 " \n", toString(id).c_str(), snapshot->getVersion()); - const BTreePage *pTreePage = (const BTreePage *)page->begin(); + const BTreePage* pTreePage = (const BTreePage*)page->begin(); - if(!forLazyDelete && page->userData == nullptr) { - debug_printf("readPage() Creating Reader for %s @%" PRId64 " lower=%s upper=%s\n", toString(id).c_str(), snapshot->getVersion(), lowerBound->toString().c_str(), upperBound->toString().c_str()); + if (!forLazyDelete && page->userData == nullptr) { + debug_printf("readPage() Creating Reader for %s @%" PRId64 " lower=%s upper=%s\n", toString(id).c_str(), + snapshot->getVersion(), lowerBound->toString().c_str(), upperBound->toString().c_str()); page->userData = new BTreePage::BinaryTree::Mirror(&pTreePage->tree(), lowerBound, upperBound); - page->userDataDestructor = [](void *ptr) { delete (BTreePage::BinaryTree::Mirror *)ptr; }; + page->userDataDestructor = [](void* ptr) { delete (BTreePage::BinaryTree::Mirror*)ptr; }; } - if(!forLazyDelete) { - debug_printf("readPage() %s\n", pTreePage->toString(false, id, snapshot->getVersion(), lowerBound, upperBound).c_str()); + if (!forLazyDelete) { + debug_printf("readPage() %s\n", + pTreePage->toString(false, id, snapshot->getVersion(), lowerBound, upperBound).c_str()); } return page; } - static void preLoadPage(IPagerSnapshot *snapshot, BTreePageID id) { + static void preLoadPage(IPagerSnapshot* snapshot, BTreePageID id) { ++counts.pagePreloads; counts.extPagePreloads += (id.size() - 1); - - for(auto pageID : id) { + + for (auto pageID : id) { snapshot->getPhysicalPage(pageID, true, true); } } void freeBtreePage(BTreePageID btPageID, Version v) { // Free individual pages at v - for(LogicalPageID id : btPageID) { + for (LogicalPageID id : btPageID) { m_pager->freePage(id, v); } } // Write new version of pageID at version v using page as its data. // Attempts to reuse original id(s) in btPageID, returns BTreePageID. - ACTOR static Future updateBtreePage(VersionedBTree *self, BTreePageID oldID, Arena *arena, Reference page, Version writeVersion) { + ACTOR static Future updateBtreePage(VersionedBTree* self, BTreePageID oldID, Arena* arena, + Reference page, Version writeVersion) { state BTreePageID newID; newID.resize(*arena, oldID.size()); - if(oldID.size() == 1) { + if (oldID.size() == 1) { LogicalPageID id = wait(self->m_pager->atomicUpdatePage(oldID.front(), page, writeVersion)); newID.front() = id; - } - else { + } else { state std::vector> pages; - const uint8_t *rptr = page->begin(); + const uint8_t* rptr = page->begin(); int bytesLeft = page->size(); - while(bytesLeft > 0) { + while (bytesLeft > 0) { Reference p = self->m_pager->newPageBuffer(); int blockSize = p->size(); memcpy(p->mutate(), rptr, blockSize); @@ -3751,7 +3613,7 @@ private: // Write pages, trying to reuse original page IDs state int i = 0; - for(; i < pages.size(); ++i) { + for (; i < pages.size(); ++i) { LogicalPageID id = wait(self->m_pager->atomicUpdatePage(oldID[i], pages[i], writeVersion)); newID[i] = id; } @@ -3759,7 +3621,7 @@ private: // Update activity counts ++counts.pageWrites; - if(newID.size() > 1) { + if (newID.size() > 1) { counts.extPageWrites += newID.size() - 1; } @@ -3770,11 +3632,12 @@ private: Reference cloneForUpdate(Reference page) { Reference newPage = page->clone(); - auto oldMirror = (const BTreePage::BinaryTree::Mirror *)page->userData; - auto newBTPage = (BTreePage *)newPage->mutate(); + auto oldMirror = (const BTreePage::BinaryTree::Mirror*)page->userData; + auto newBTPage = (BTreePage*)newPage->mutate(); - newPage->userData = new BTreePage::BinaryTree::Mirror(&newBTPage->tree(), oldMirror->lowerBound(), oldMirror->upperBound()); - newPage->userDataDestructor = [](void *ptr) { delete (BTreePage::BinaryTree::Mirror *)ptr; }; + newPage->userData = + new BTreePage::BinaryTree::Mirror(&newBTPage->tree(), oldMirror->lowerBound(), oldMirror->upperBound()); + newPage->userDataDestructor = [](void* ptr) { delete (BTreePage::BinaryTree::Mirror*)ptr; }; return newPage; } @@ -3782,30 +3645,26 @@ private: // iMutationBoundary is greatest boundary <= lowerBound->key // iMutationBoundaryEnd is least boundary >= upperBound->key ACTOR static Future> commitSubtree( - VersionedBTree *self, - MutationBuffer *mutationBuffer, - //MutationBuffer::const_iterator iMutationBoundary, // = mutationBuffer->upper_bound(lowerBound->key); --iMutationBoundary; - //MutationBuffer::const_iterator iMutationBoundaryEnd, // = mutationBuffer->lower_bound(upperBound->key); - Reference snapshot, - BTreePageID rootID, - bool isLeaf, - const RedwoodRecordRef *lowerBound, - const RedwoodRecordRef *upperBound, - const RedwoodRecordRef *decodeLowerBound, - const RedwoodRecordRef *decodeUpperBound, - int skipLen = 0 - ) { - //skipLen = lowerBound->getCommonPrefixLen(*upperBound, skipLen); + VersionedBTree* self, MutationBuffer* mutationBuffer, + // MutationBuffer::const_iterator iMutationBoundary, // = mutationBuffer->upper_bound(lowerBound->key); + // --iMutationBoundary; MutationBuffer::const_iterator iMutationBoundaryEnd, // = + // mutationBuffer->lower_bound(upperBound->key); + Reference snapshot, BTreePageID rootID, bool isLeaf, const RedwoodRecordRef* lowerBound, + const RedwoodRecordRef* upperBound, const RedwoodRecordRef* decodeLowerBound, + const RedwoodRecordRef* decodeUpperBound, int skipLen = 0) { + // skipLen = lowerBound->getCommonPrefixLen(*upperBound, skipLen); state std::string context; - if(REDWOOD_DEBUG) { + if (REDWOOD_DEBUG) { context = format("CommitSubtree(root=%s): ", toString(rootID).c_str()); } state Version writeVersion = self->getLastCommittedVersion() + 1; state Standalone result; - debug_printf("%s lower=%s upper=%s\n", context.c_str(), lowerBound->toString().c_str(), upperBound->toString().c_str()); - debug_printf("%s decodeLower=%s decodeUpper=%s\n", context.c_str(), decodeLowerBound->toString().c_str(), decodeUpperBound->toString().c_str()); + debug_printf("%s lower=%s upper=%s\n", context.c_str(), lowerBound->toString().c_str(), + upperBound->toString().c_str()); + debug_printf("%s decodeLower=%s decodeUpper=%s\n", context.c_str(), decodeLowerBound->toString().c_str(), + decodeUpperBound->toString().c_str()); self->counts.commitToPageStart++; // Find the slice of the mutation buffer that is relevant to this subtree @@ -3813,12 +3672,13 @@ private: --iMutationBoundary; state MutationBuffer::const_iterator iMutationBoundaryEnd = mutationBuffer->lower_bound(upperBound->key); - if(REDWOOD_DEBUG) { + if (REDWOOD_DEBUG) { debug_printf("%s ---------MUTATION BUFFER SLICE ---------------------\n", context.c_str()); auto begin = iMutationBoundary; - while(1) { - debug_printf("%s Mutation: '%s': %s\n", context.c_str(), printable(begin.key()).c_str(), begin.mutation().toString().c_str()); - if(begin == iMutationBoundaryEnd) { + while (1) { + debug_printf("%s Mutation: '%s': %s\n", context.c_str(), printable(begin.key()).c_str(), + begin.mutation().toString().c_str()); + if (begin == iMutationBoundaryEnd) { break; } ++begin; @@ -3833,7 +3693,7 @@ private: // unmodified, or possibly/partially modified. MutationBuffer::const_iterator iMutationBoundaryNext = iMutationBoundary; ++iMutationBoundaryNext; - if(iMutationBoundaryNext == iMutationBoundaryEnd) { + if (iMutationBoundaryNext == iMutationBoundaryEnd) { // Cleared means the entire range covering the subtree was cleared. It is assumed true // if the range starting after the lower mutation boundary was cleared, and then proven false // below if possible. @@ -3845,29 +3705,30 @@ private: // If the lower mutation boundary key is the same as the subtree lower bound then whether or not // that key is being changed or cleared affects this subtree. - if(iMutationBoundary.key() == lowerBound->key) { - // If subtree will be cleared (so far) but the lower boundary key is not cleared then the subtree is not cleared - if(cleared && !iMutationBoundary.mutation().boundaryCleared()) { + if (iMutationBoundary.key() == lowerBound->key) { + // If subtree will be cleared (so far) but the lower boundary key is not cleared then the subtree is not + // cleared + if (cleared && !iMutationBoundary.mutation().boundaryCleared()) { cleared = false; debug_printf("%s cleared=%d unchanged=%d\n", context.c_str(), cleared, unchanged); } - // If the subtree looked unchanged (so far) but the lower boundary is is changed then the subtree is changed - if(unchanged && iMutationBoundary.mutation().boundaryChanged) { + // If the subtree looked unchanged (so far) but the lower boundary is is changed then the subtree is + // changed + if (unchanged && iMutationBoundary.mutation().boundaryChanged) { unchanged = false; debug_printf("%s cleared=%d unchanged=%d\n", context.c_str(), cleared, unchanged); } } - // If the higher mutation boundary key is the same as the subtree upper bound key then whether + // If the higher mutation boundary key is the same as the subtree upper bound key then whether // or not it is being changed or cleared affects this subtree. - if((cleared || unchanged) && iMutationBoundaryEnd.key() == upperBound->key) { + if ((cleared || unchanged) && iMutationBoundaryEnd.key() == upperBound->key) { // If the key is being changed then the records in this subtree with the same key must be removed // so the subtree is definitely not unchanged, though it may be cleared to achieve the same effect. - if(iMutationBoundaryEnd.mutation().boundaryChanged) { + if (iMutationBoundaryEnd.mutation().boundaryChanged) { unchanged = false; debug_printf("%s cleared=%d unchanged=%d\n", context.c_str(), cleared, unchanged); - } - else { + } else { // If the key is not being changed then the records in this subtree can't be removed so the // subtree is not being cleared. cleared = false; @@ -3879,20 +3740,21 @@ private: ASSERT(!(cleared && unchanged)); // If no changes in subtree - if(unchanged) { + if (unchanged) { result.contents() = ChildLinksRef(decodeLowerBound, decodeUpperBound); - debug_printf("%s no changes on this subtree, returning %s\n", context.c_str(), toString(result).c_str()); + debug_printf("%s no changes on this subtree, returning %s\n", context.c_str(), + toString(result).c_str()); return result; } // If subtree is cleared - if(cleared) { - debug_printf("%s %s cleared, deleting it, returning %s\n", context.c_str(), isLeaf ? "Page" : "Subtree", toString(result).c_str()); - if(isLeaf) { + if (cleared) { + debug_printf("%s %s cleared, deleting it, returning %s\n", context.c_str(), isLeaf ? "Page" : "Subtree", + toString(result).c_str()); + if (isLeaf) { self->freeBtreePage(rootID, writeVersion); - } - else { - self->m_lazyDeleteQueue.pushBack(LazyDeleteQueueEntry{writeVersion, rootID}); + } else { + self->m_lazyDeleteQueue.pushBack(LazyDeleteQueueEntry{ writeVersion, rootID }); } return result; } @@ -3900,18 +3762,21 @@ private: self->counts.commitToPage++; state Reference page = wait(readPage(snapshot, rootID, decodeLowerBound, decodeUpperBound)); - state BTreePage *btPage = (BTreePage *)page->begin(); + state BTreePage* btPage = (BTreePage*)page->begin(); ASSERT(isLeaf == btPage->isLeaf()); - debug_printf("%s commitSubtree(): %s\n", context.c_str(), btPage->toString(false, rootID, snapshot->getVersion(), decodeLowerBound, decodeUpperBound).c_str()); + debug_printf( + "%s commitSubtree(): %s\n", context.c_str(), + btPage->toString(false, rootID, snapshot->getVersion(), decodeLowerBound, decodeUpperBound).c_str()); state BTreePage::BinaryTree::Cursor cursor; - if(REDWOOD_DEBUG) { + if (REDWOOD_DEBUG) { debug_printf("%s ---------MUTATION BUFFER SLICE ---------------------\n", context.c_str()); auto begin = iMutationBoundary; - while(1) { - debug_printf("%s Mutation: '%s': %s\n", context.c_str(), printable(begin.key()).c_str(), begin.mutation().toString().c_str()); - if(begin == iMutationBoundaryEnd) { + while (1) { + debug_printf("%s Mutation: '%s': %s\n", context.c_str(), printable(begin.key()).c_str(), + begin.mutation().toString().c_str()); + if (begin == iMutationBoundaryEnd) { break; } ++begin; @@ -3920,33 +3785,33 @@ private: } // Leaf Page - if(isLeaf) { + if (isLeaf) { // Try to update page unless it's an oversized page or empty or the boundaries have changed // TODO: Caller already knows if boundaries are the same. - bool updating = btPage->tree().numItems > 0 && !(*decodeLowerBound != *lowerBound || *decodeUpperBound != *upperBound); + bool updating = + btPage->tree().numItems > 0 && !(*decodeLowerBound != *lowerBound || *decodeUpperBound != *upperBound); - state Reference newPage; - // If replacement pages are written they will be at the minimum version seen in the mutations for this leaf + state Reference newPage; + // If replacement pages are written they will be at the minimum version seen in the mutations for this leaf bool changesMade = false; // If attempting an in-place page update, clone the page and read/modify the copy - if(updating) { + if (updating) { newPage = self->cloneForUpdate(page); - cursor = getCursor(newPage); - } - else { + cursor = getCursor(newPage); + } else { // Otherwise read the old page cursor = getCursor(page); } - // Couldn't make changes in place, so now do a linear merge and build new pages. - state Standalone> merged; + // Couldn't make changes in place, so now do a linear merge and build new pages. + state Standalone> merged; auto switchToLinearMerge = [&]() { updating = false; auto c = cursor; c.moveFirst(); - while(c != cursor) { + while (c != cursor) { debug_printf("%s catch-up adding %s\n", context.c_str(), c.get().toString().c_str()); merged.push_back(merged.arena(), c.get()); c.moveNext(); @@ -3955,40 +3820,46 @@ private: // The first mutation buffer boundary has a key <= the first key in the page. - cursor.moveFirst(); - debug_printf("%s Leaf page, applying changes.\n", context.c_str()); + cursor.moveFirst(); + debug_printf("%s Leaf page, applying changes.\n", context.c_str()); // Now, process each mutation range and merge changes with existing data. bool firstMutationBoundary = true; - while(iMutationBoundary != iMutationBoundaryEnd) { - debug_printf("%s New mutation boundary: '%s': %s\n", context.c_str(), printable(iMutationBoundary.key()).c_str(), iMutationBoundary.mutation().toString().c_str()); + while (iMutationBoundary != iMutationBoundaryEnd) { + debug_printf("%s New mutation boundary: '%s': %s\n", context.c_str(), + printable(iMutationBoundary.key()).c_str(), + iMutationBoundary.mutation().toString().c_str()); // Apply the change to the mutation buffer start boundary key only if // - there actually is a change (whether a set or a clear, old records are to be removed) // - either this is not the first boundary or it is but its key matches our lower bound key - bool applyBoundaryChange = iMutationBoundary.mutation().boundaryChanged && (!firstMutationBoundary || iMutationBoundary.key() >= lowerBound->key); + bool applyBoundaryChange = iMutationBoundary.mutation().boundaryChanged && + (!firstMutationBoundary || iMutationBoundary.key() >= lowerBound->key); firstMutationBoundary = false; - - // Iterate over records for the mutation boundary key, keep them unless the boundary key was changed or we are not applying it - while(cursor.valid() && cursor.get().key == iMutationBoundary.key()) { + + // Iterate over records for the mutation boundary key, keep them unless the boundary key was changed or + // we are not applying it + while (cursor.valid() && cursor.get().key == iMutationBoundary.key()) { // If there were no changes to the key or we're not applying it - if(!applyBoundaryChange) { - // If not updating, add to the output set, otherwise skip ahead past the records for the mutation boundary - if(!updating) { + if (!applyBoundaryChange) { + // If not updating, add to the output set, otherwise skip ahead past the records for the + // mutation boundary + if (!updating) { merged.push_back(merged.arena(), cursor.get()); - debug_printf("%s Added %s [existing, boundary start]\n", context.c_str(), cursor.get().toString().c_str()); + debug_printf("%s Added %s [existing, boundary start]\n", context.c_str(), + cursor.get().toString().c_str()); } cursor.moveNext(); - } - else { + } else { changesMade = true; // If updating, erase from the page, otherwise do not add to the output set - if(updating) { - debug_printf("%s Erasing %s [existing, boundary start]\n", context.c_str(), cursor.get().toString().c_str()); + if (updating) { + debug_printf("%s Erasing %s [existing, boundary start]\n", context.c_str(), + cursor.get().toString().c_str()); cursor.erase(); - } - else { - debug_printf("%s Skipped %s [existing, boundary start]\n", context.c_str(), cursor.get().toString().c_str()); + } else { + debug_printf("%s Skipped %s [existing, boundary start]\n", context.c_str(), + cursor.get().toString().c_str()); cursor.moveNext(); } } @@ -3997,25 +3868,28 @@ private: constexpr int maxHeightAllowed = 8; // Write the new record(s) for the mutation boundary start key if its value has been set - // Clears of this key will have been processed above by not being erased from the updated page or excluded from the merge output - if(applyBoundaryChange && iMutationBoundary.mutation().boundarySet()) { + // Clears of this key will have been processed above by not being erased from the updated page or + // excluded from the merge output + if (applyBoundaryChange && iMutationBoundary.mutation().boundarySet()) { RedwoodRecordRef rec(iMutationBoundary.key(), 0, iMutationBoundary.mutation().boundaryValue.get()); changesMade = true; // If updating, add to the page, else add to the output set - if(updating) { - if(cursor.mirror->insert(rec, skipLen, maxHeightAllowed)) { - debug_printf("%s Inserted %s [mutation, boundary start]\n", context.c_str(), rec.toString().c_str()); - } - else { - debug_printf("%s Inserted failed for %s [mutation, boundary start]\n", context.c_str(), rec.toString().c_str()); + if (updating) { + if (cursor.mirror->insert(rec, skipLen, maxHeightAllowed)) { + debug_printf("%s Inserted %s [mutation, boundary start]\n", context.c_str(), + rec.toString().c_str()); + } else { + debug_printf("%s Inserted failed for %s [mutation, boundary start]\n", context.c_str(), + rec.toString().c_str()); switchToLinearMerge(); } } - if(!updating) { + if (!updating) { merged.push_back(merged.arena(), rec); - debug_printf("%s Added %s [mutation, boundary start]\n", context.c_str(), rec.toString().c_str()); + debug_printf("%s Added %s [mutation, boundary start]\n", context.c_str(), + rec.toString().c_str()); } } @@ -4023,39 +3897,41 @@ private: bool remove = iMutationBoundary.mutation().clearAfterBoundary; // Advance to the next boundary because we need to know the end key for the current range. ++iMutationBoundary; - if(iMutationBoundary == iMutationBoundaryEnd) { + if (iMutationBoundary == iMutationBoundaryEnd) { skipLen = 0; } - debug_printf("%s Mutation range end: '%s'\n", context.c_str(), printable(iMutationBoundary.key()).c_str()); + debug_printf("%s Mutation range end: '%s'\n", context.c_str(), + printable(iMutationBoundary.key()).c_str()); // Now handle the records up through but not including the next mutation boundary key RedwoodRecordRef end(iMutationBoundary.key()); // If the records are being removed and we're not doing an in-place update // OR if we ARE doing an update but the records are NOT being removed, then just skip them. - if(remove != updating) { - // If not updating, then the records, if any exist, are being removed. We don't know if there actually are any - // but we must assume there are. - if(!updating) { + if (remove != updating) { + // If not updating, then the records, if any exist, are being removed. We don't know if there + // actually are any but we must assume there are. + if (!updating) { changesMade = true; } - debug_printf("%s Seeking forward to next boundary (remove=%d updating=%d) %s\n", context.c_str(), remove, updating, iMutationBoundary.key().toString().c_str()); + debug_printf("%s Seeking forward to next boundary (remove=%d updating=%d) %s\n", context.c_str(), + remove, updating, iMutationBoundary.key().toString().c_str()); cursor.seekGreaterThanOrEqual(end, skipLen); - } - else { - // Otherwise we must visit the records. If updating, the visit is to erase them, and if doing a + } else { + // Otherwise we must visit the records. If updating, the visit is to erase them, and if doing a // linear merge than the visit is to add them to the output set. - while(cursor.valid() && cursor.get().compare(end, skipLen) < 0) { - if(updating) { - debug_printf("%s Erasing %s [existing, boundary start]\n", context.c_str(), cursor.get().toString().c_str()); + while (cursor.valid() && cursor.get().compare(end, skipLen) < 0) { + if (updating) { + debug_printf("%s Erasing %s [existing, boundary start]\n", context.c_str(), + cursor.get().toString().c_str()); cursor.erase(); changesMade = true; - } - else { - merged.push_back(merged.arena(), cursor.get()); - debug_printf("%s Added %s [existing, middle]\n", context.c_str(), merged.back().toString().c_str()); + } else { + merged.push_back(merged.arena(), cursor.get()); + debug_printf("%s Added %s [existing, middle]\n", context.c_str(), + merged.back().toString().c_str()); cursor.moveNext(); } } @@ -4063,87 +3939,92 @@ private: } // If there are still more records, they have the same key as the end boundary - if(cursor.valid()) { + if (cursor.valid()) { // If the end boundary is changing, we must remove the remaining records in this page bool remove = iMutationBoundaryEnd.mutation().boundaryChanged; - if(remove) { + if (remove) { changesMade = true; } // If we don't have to remove the records and we are updating, do nothing. // If we do have to remove the records and we are not updating, do nothing. - if(remove != updating) { - debug_printf("%s Ignoring remaining records, remove=%d updating=%d\n", context.c_str(), remove, updating); - } - else { + if (remove != updating) { + debug_printf("%s Ignoring remaining records, remove=%d updating=%d\n", context.c_str(), remove, + updating); + } else { // If updating and the key is changing, we must visit the records to erase them. - // If not updating and the key is not changing, we must visit the records to add them to the output set. - while(cursor.valid()) { - if(updating) { - debug_printf("%s Erasing %s and beyond [existing, matches changed upper mutation boundary]\n", context.c_str(), cursor.get().toString().c_str()); + // If not updating and the key is not changing, we must visit the records to add them to the output + // set. + while (cursor.valid()) { + if (updating) { + debug_printf( + "%s Erasing %s and beyond [existing, matches changed upper mutation boundary]\n", + context.c_str(), cursor.get().toString().c_str()); cursor.erase(); - } - else { + } else { merged.push_back(merged.arena(), cursor.get()); - debug_printf("%s Added %s [existing, tail]\n", context.c_str(), merged.back().toString().c_str()); + debug_printf("%s Added %s [existing, tail]\n", context.c_str(), + merged.back().toString().c_str()); cursor.moveNext(); } } } - } - else { + } else { debug_printf("%s No records matching mutation buffer end boundary key\n", context.c_str()); } - // No changes were actually made. This could happen if the only mutations are clear ranges which do not match any records. - if(!changesMade) { + // No changes were actually made. This could happen if the only mutations are clear ranges which do not + // match any records. + if (!changesMade) { result.contents() = ChildLinksRef(decodeLowerBound, decodeUpperBound); - debug_printf("%s No changes were made during mutation merge, returning %s\n", context.c_str(), toString(result).c_str()); + debug_printf("%s No changes were made during mutation merge, returning %s\n", context.c_str(), + toString(result).c_str()); return result; - } - else { + } else { debug_printf("%s Changes were made, writing.\n", context.c_str()); } writeVersion = self->getLastCommittedVersion() + 1; - if(updating) { - const BTreePage::BinaryTree &deltaTree = ((const BTreePage *)newPage->begin())->tree(); - if(deltaTree.numItems == 0) { - debug_printf("%s Page updates cleared all entries, returning %s\n", context.c_str(), toString(result).c_str()); + if (updating) { + const BTreePage::BinaryTree& deltaTree = ((const BTreePage*)newPage->begin())->tree(); + if (deltaTree.numItems == 0) { + debug_printf("%s Page updates cleared all entries, returning %s\n", context.c_str(), + toString(result).c_str()); self->freeBtreePage(rootID, writeVersion); return result; - } - else { - // Otherwise update it. - BTreePageID newID = wait(self->updateBtreePage(self, rootID, &result.arena(), newPage, writeVersion)); + } else { + // Otherwise update it. + BTreePageID newID = + wait(self->updateBtreePage(self, rootID, &result.arena(), newPage, writeVersion)); - // Set the child page ID, which has already been allocated in result.arena() - RedwoodRecordRef *rec = new (result.arena()) RedwoodRecordRef(decodeLowerBound->withoutValue()); - rec->setChildPage(newID); + // Set the child page ID, which has already been allocated in result.arena() + RedwoodRecordRef* rec = new (result.arena()) RedwoodRecordRef(decodeLowerBound->withoutValue()); + rec->setChildPage(newID); - result.contents() = ChildLinksRef(rec, decodeUpperBound); - debug_printf("%s Page updated in-place, returning %s\n", context.c_str(), toString(result).c_str()); + result.contents() = ChildLinksRef(rec, decodeUpperBound); + debug_printf("%s Page updated in-place, returning %s\n", context.c_str(), toString(result).c_str()); ++counts.pageUpdates; - return result; - } + return result; + } } // If everything in the page was deleted then this page should be deleted as of the new version // Note that if a single range clear covered the entire page then we should not get this far - if(merged.empty()) { - debug_printf("%s All leaf page contents were cleared, returning %s\n", context.c_str(), toString(result).c_str()); + if (merged.empty()) { + debug_printf("%s All leaf page contents were cleared, returning %s\n", context.c_str(), + toString(result).c_str()); self->freeBtreePage(rootID, writeVersion); return result; } - state Standalone> entries = wait(writePages(self, lowerBound, upperBound, merged, btPage->height, writeVersion, rootID)); + state Standalone> entries = + wait(writePages(self, lowerBound, upperBound, merged, btPage->height, writeVersion, rootID)); result.arena().dependsOn(entries.arena()); result.contents() = ChildLinksRef(entries, *upperBound); debug_printf("%s Merge complete, returning %s\n", context.c_str(), toString(result).c_str()); return result; - } - else { + } else { // Internal Page ASSERT(!isLeaf); state std::vector>> futureChildren; @@ -4152,9 +4033,9 @@ private: cursor.moveFirst(); bool first = true; - while(cursor.valid()) { + while (cursor.valid()) { // The lower bound for the first child is the lowerBound arg - const RedwoodRecordRef &childLowerBound = first ? *lowerBound : cursor.get(); + const RedwoodRecordRef& childLowerBound = first ? *lowerBound : cursor.get(); first = false; // At this point we should never be at a null child page entry because the first entry of a page @@ -4162,54 +4043,60 @@ private: ASSERT(cursor.get().value.present()); // The decode lower bound is always the key of the child link record - const RedwoodRecordRef &decodeChildLowerBound = cursor.get(); + const RedwoodRecordRef& decodeChildLowerBound = cursor.get(); BTreePageID pageID = cursor.get().getChildPage(); ASSERT(!pageID.empty()); - // The decode upper bound is always the next key after the child link, or the decode upper bound for this page - const RedwoodRecordRef &decodeChildUpperBound = cursor.moveNext() ? cursor.get() : *decodeUpperBound; + // The decode upper bound is always the next key after the child link, or the decode upper bound for + // this page + const RedwoodRecordRef& decodeChildUpperBound = cursor.moveNext() ? cursor.get() : *decodeUpperBound; // But the decode upper bound might be a placeholder record with a null child link because // the subtree was previously deleted but the key needed to exist to enable decoding of the // previous child page which has not since been rewritten. - if(cursor.valid() && !cursor.get().value.present()) { + if (cursor.valid() && !cursor.get().value.present()) { // There should only be one null child link entry, followed by a present link or the end of the page ASSERT(!cursor.moveNext() || cursor.get().value.present()); } - const RedwoodRecordRef &childUpperBound = cursor.valid() ? cursor.get() : *upperBound; + const RedwoodRecordRef& childUpperBound = cursor.valid() ? cursor.get() : *upperBound; - debug_printf("%s recursing to %s lower=%s upper=%s decodeLower=%s decodeUpper=%s\n", - context.c_str(), toString(pageID).c_str(), childLowerBound.toString().c_str(), childUpperBound.toString().c_str(), decodeChildLowerBound.toString().c_str(), decodeChildUpperBound.toString().c_str()); + debug_printf("%s recursing to %s lower=%s upper=%s decodeLower=%s decodeUpper=%s\n", context.c_str(), + toString(pageID).c_str(), childLowerBound.toString().c_str(), + childUpperBound.toString().c_str(), decodeChildLowerBound.toString().c_str(), + decodeChildUpperBound.toString().c_str()); // If this page has height of 2 then its children are leaf nodes - futureChildren.push_back(self->commitSubtree(self, mutationBuffer, snapshot, pageID, btPage->height == 2, &childLowerBound, &childUpperBound, &decodeChildLowerBound, &decodeChildUpperBound)); + futureChildren.push_back(self->commitSubtree(self, mutationBuffer, snapshot, pageID, + btPage->height == 2, &childLowerBound, &childUpperBound, + &decodeChildLowerBound, &decodeChildUpperBound)); } // Waiting one at a time makes debugging easier // TODO: Is it better to use waitForAll()? state int k; - for(k = 0; k < futureChildren.size(); ++k) { + for (k = 0; k < futureChildren.size(); ++k) { wait(success(futureChildren[k])); } - if(REDWOOD_DEBUG) { - debug_printf("%s Subtree update results\n", context.c_str()); - for(int i = 0; i < futureChildren.size(); ++i) { + if (REDWOOD_DEBUG) { + debug_printf("%s Subtree update results\n", context.c_str()); + for (int i = 0; i < futureChildren.size(); ++i) { debug_printf("%s subtree result %s\n", context.c_str(), toString(futureChildren[i].get()).c_str()); } } - // All of the things added to pageBuilder will exist in the arenas inside futureChildren or will be upperBound + // All of the things added to pageBuilder will exist in the arenas inside futureChildren or will be + // upperBound BTreePage::BinaryTree::Cursor c = getCursor(page); c.moveFirst(); InternalPageBuilder pageBuilder(c); - for(int i = 0; i < futureChildren.size(); ++i) { + for (int i = 0; i < futureChildren.size(); ++i) { ChildLinksRef c = futureChildren[i].get(); - if(!c.children.empty()) { + if (!c.children.empty()) { pageBuilder.addEntries(c); } } @@ -4217,29 +4104,33 @@ private: pageBuilder.finalize(*upperBound, *decodeUpperBound); // If page contents have changed - if(pageBuilder.modified) { + if (pageBuilder.modified) { // If the page now has no children - if(pageBuilder.childPageCount == 0) { - debug_printf("%s All internal page children were deleted so deleting this page too, returning %s\n", context.c_str(), toString(result).c_str()); + if (pageBuilder.childPageCount == 0) { + debug_printf("%s All internal page children were deleted so deleting this page too, returning %s\n", + context.c_str(), toString(result).c_str()); self->freeBtreePage(rootID, writeVersion); return result; - } - else { + } else { debug_printf("%s Internal page modified, creating replacements.\n", context.c_str()); - debug_printf("%s newChildren=%s lastUpperBound=%s upperBound=%s\n", context.c_str(), toString(pageBuilder.entries).c_str(), pageBuilder.lastUpperBound.toString().c_str(), upperBound->toString().c_str()); + debug_printf("%s newChildren=%s lastUpperBound=%s upperBound=%s\n", context.c_str(), + toString(pageBuilder.entries).c_str(), pageBuilder.lastUpperBound.toString().c_str(), + upperBound->toString().c_str()); debug_printf("pagebuilder entries: %s\n", ::toString(pageBuilder.entries).c_str()); - ASSERT(!pageBuilder.entries.back().value.present() || pageBuilder.lastUpperBound.sameExceptValue(*upperBound)); + ASSERT(!pageBuilder.entries.back().value.present() || + pageBuilder.lastUpperBound.sameExceptValue(*upperBound)); - Standalone> childEntries = wait(holdWhile(pageBuilder.entries, writePages(self, lowerBound, upperBound, pageBuilder.entries, btPage->height, writeVersion, rootID))); + Standalone> childEntries = wait( + holdWhile(pageBuilder.entries, writePages(self, lowerBound, upperBound, pageBuilder.entries, + btPage->height, writeVersion, rootID))); result.arena().dependsOn(childEntries.arena()); result.contents() = ChildLinksRef(childEntries, *upperBound); debug_printf("%s Internal modified, returning %s\n", context.c_str(), toString(result).c_str()); return result; } - } - else { + } else { result.contents() = ChildLinksRef(decodeLowerBound, decodeUpperBound); debug_printf("%s Page has no changes, returning %s\n", context.c_str(), toString(result).c_str()); return result; @@ -4247,8 +4138,8 @@ private: } } - ACTOR static Future commit_impl(VersionedBTree *self) { - state MutationBuffer *mutations = self->m_pBuffer; + ACTOR static Future commit_impl(VersionedBTree* self) { + state MutationBuffer* mutations = self->m_pBuffer; // No more mutations are allowed to be written to this mutation buffer we will commit // at m_writeVersion, which we must save locally because it could change during commit. @@ -4267,7 +4158,8 @@ private: wait(previousCommit); self->m_pager->setOldestVersion(self->m_newOldestVersion); - debug_printf("%s: Beginning commit of version %" PRId64 ", new oldest version set to %" PRId64 "\n", self->m_name.c_str(), writeVersion, self->m_newOldestVersion); + debug_printf("%s: Beginning commit of version %" PRId64 ", new oldest version set to %" PRId64 "\n", + self->m_name.c_str(), writeVersion, self->m_newOldestVersion); state bool lazyDeleteStop = false; state Future lazyDelete = incrementalSubtreeClear(self, &lazyDeleteStop); @@ -4278,27 +4170,29 @@ private: state Standalone rootPageID = self->m_header.root.get(); state RedwoodRecordRef lowerBound = dbBegin.withPageID(rootPageID); - Standalone newRootChildren = wait(commitSubtree(self, mutations, self->m_pager->getReadSnapshot(latestVersion), rootPageID, self->m_header.height == 1, &lowerBound, &dbEnd, &lowerBound, &dbEnd)); - debug_printf("CommitSubtree(root %s) returned %s\n", toString(rootPageID).c_str(), toString(newRootChildren).c_str()); + Standalone newRootChildren = + wait(commitSubtree(self, mutations, self->m_pager->getReadSnapshot(latestVersion), rootPageID, + self->m_header.height == 1, &lowerBound, &dbEnd, &lowerBound, &dbEnd)); + debug_printf("CommitSubtree(root %s) returned %s\n", toString(rootPageID).c_str(), + toString(newRootChildren).c_str()); // If the old root was deleted, write a new empty tree root node and free the old roots - if(newRootChildren.children.empty()) { + if (newRootChildren.children.empty()) { debug_printf("Writing new empty root.\n"); LogicalPageID newRootID = wait(self->m_pager->newPageID()); Reference page = self->m_pager->newPageBuffer(); makeEmptyRoot(page); self->m_header.height = 1; self->m_pager->updatePage(newRootID, page); - rootPageID = BTreePageID((LogicalPageID *)&newRootID, 1); - } - else { - Standalone> newRootLevel(newRootChildren.children, newRootChildren.arena()); - if(newRootLevel.size() == 1) { + rootPageID = BTreePageID((LogicalPageID*)&newRootID, 1); + } else { + Standalone> newRootLevel(newRootChildren.children, newRootChildren.arena()); + if (newRootLevel.size() == 1) { rootPageID = newRootLevel.front().getChildPage(); - } - else { + } else { // If the new root level's size is not 1 then build new root level(s) - Standalone> newRootPage = wait(buildNewRoot(self, latestVersion, newRootLevel, self->m_header.height)); + Standalone> newRootPage = + wait(buildNewRoot(self, latestVersion, newRootLevel, self->m_header.height)); rootPageID = newRootPage.front().getChildPage(); } } @@ -4333,11 +4227,10 @@ private: return Void(); } - public: - +public: // InternalCursor is for seeking to and iterating over the leaf-level RedwoodRecordRef records in the tree. - // The records could represent multiple values for the same key at different versions, including a non-present value representing a clear. - // Currently, however, all records are at version 0 and no clears are present in the tree. + // The records could represent multiple values for the same key at different versions, including a non-present value + // representing a clear. Currently, however, all records are at version 0 and no clears are present in the tree. struct InternalCursor { private: // Each InternalCursor's position is represented by a reference counted PageCursor, which links @@ -4345,52 +4238,46 @@ private: // PageCursors can be shared by many InternalCursors, making InternalCursor copying low overhead struct PageCursor : ReferenceCounted, FastAllocated { Reference parent; - BTreePageID pageID; // Only needed for debugging purposes + BTreePageID pageID; // Only needed for debugging purposes Reference page; BTreePage::BinaryTree::Cursor cursor; // id will normally reference memory owned by the parent, which is okay because a reference to the parent // will be held in the cursor PageCursor(BTreePageID id, Reference page, Reference parent = {}) - : pageID(id), page(page), parent(parent), cursor(getCursor(page)) - { - } + : pageID(id), page(page), parent(parent), cursor(getCursor(page)) {} - PageCursor(const PageCursor &toCopy) : parent(toCopy.parent), pageID(toCopy.pageID), page(toCopy.page), cursor(toCopy.cursor) { - } + PageCursor(const PageCursor& toCopy) + : parent(toCopy.parent), pageID(toCopy.pageID), page(toCopy.page), cursor(toCopy.cursor) {} // Convenience method for copying a PageCursor - Reference copy() const { - return Reference(new PageCursor(*this)); - } + Reference copy() const { return Reference(new PageCursor(*this)); } - const BTreePage * btPage() const { - return (const BTreePage *)page->begin(); - } + const BTreePage* btPage() const { return (const BTreePage*)page->begin(); } - bool isLeaf() const { - return btPage()->isLeaf(); - } + bool isLeaf() const { return btPage()->isLeaf(); } Future> getChild(Reference pager, int readAheadBytes = 0) { ASSERT(!isLeaf()); BTreePage::BinaryTree::Cursor next = cursor; next.moveNext(); - const RedwoodRecordRef &rec = cursor.get(); + const RedwoodRecordRef& rec = cursor.get(); BTreePageID id = rec.getChildPage(); Future> child = readPage(pager, id, &rec, &next.getOrUpperBound()); // Read ahead siblings at level 2 - // TODO: Application of readAheadBytes is not taking into account the size of the current page or any of the adjacent pages it is preloading. - if(readAheadBytes > 0 && btPage()->height == 2 && next.valid()) { + // TODO: Application of readAheadBytes is not taking into account the size of the current page or any + // of the adjacent pages it is preloading. + if (readAheadBytes > 0 && btPage()->height == 2 && next.valid()) { do { - debug_printf("preloading %s %d bytes left\n", ::toString(next.get().getChildPage()).c_str(), readAheadBytes); + debug_printf("preloading %s %d bytes left\n", ::toString(next.get().getChildPage()).c_str(), + readAheadBytes); // If any part of the page was already loaded then stop - if(next.get().value.present()) { + if (next.get().value.present()) { preLoadPage(pager.getPtr(), next.get().getChildPage()); readAheadBytes -= page->size(); } - } while(readAheadBytes > 0 && next.moveNext()); + } while (readAheadBytes > 0 && next.moveNext()); } return map(child, [=](Reference page) { @@ -4399,7 +4286,8 @@ private: } std::string toString() const { - return format("%s, %s", ::toString(pageID).c_str(), cursor.valid() ? cursor.get().toString().c_str() : ""); + return format("%s, %s", ::toString(pageID).c_str(), + cursor.valid() ? cursor.get().toString().c_str() : ""); } }; @@ -4408,26 +4296,23 @@ private: Reference pageCursor; public: - InternalCursor() { - } + InternalCursor() {} - InternalCursor(Reference pager, BTreePageID root) - : pager(pager), rootPageID(root) { - } + InternalCursor(Reference pager, BTreePageID root) : pager(pager), rootPageID(root) {} std::string toString() const { std::string r; Reference c = pageCursor; int maxDepth = 0; - while(c) { + while (c) { c = c->parent; ++maxDepth; } c = pageCursor; int depth = maxDepth; - while(c) { + while (c) { r = format("[%d/%d: %s] ", depth--, maxDepth, c->toString().c_str()) + r; c = c->parent; } @@ -4435,47 +4320,35 @@ private: } // Returns true if cursor position is a valid leaf page record - bool valid() const { - return pageCursor && pageCursor->isLeaf() && pageCursor->cursor.valid(); - } + bool valid() const { return pageCursor && pageCursor->isLeaf() && pageCursor->cursor.valid(); } -// Returns true if cursor position is valid() and has a present record value -bool present() const { - return valid() && pageCursor->cursor.get().value.present(); -} + // Returns true if cursor position is valid() and has a present record value + bool present() const { return valid() && pageCursor->cursor.get().value.present(); } -// Returns true if cursor position is present() and has an effective version <= v -bool presentAtVersion(Version v) { - return present() && pageCursor->cursor.get().version <= v; -} + // Returns true if cursor position is present() and has an effective version <= v + bool presentAtVersion(Version v) { return present() && pageCursor->cursor.get().version <= v; } -// This is to enable an optimization for the case where all internal records are at the -// same version and there are no implicit clears -// *this MUST be valid() -bool presentAtExactVersion(Version v) const { - return present() && pageCursor->cursor.get().version == v; -} + // This is to enable an optimization for the case where all internal records are at the + // same version and there are no implicit clears + // *this MUST be valid() + bool presentAtExactVersion(Version v) const { return present() && pageCursor->cursor.get().version == v; } -// Returns true if cursor position is present() and has an effective version <= v -bool validAtVersion(Version v) { - return valid() && pageCursor->cursor.get().version <= v; -} + // Returns true if cursor position is present() and has an effective version <= v + bool validAtVersion(Version v) { return valid() && pageCursor->cursor.get().version <= v; } - const RedwoodRecordRef & get() const { - return pageCursor->cursor.get(); - } + const RedwoodRecordRef& get() const { return pageCursor->cursor.get(); } // Ensure that pageCursor is not shared with other cursors so we can modify it void ensureUnshared() { - if(!pageCursor->isSoleOwner()) { + if (!pageCursor->isSoleOwner()) { pageCursor = pageCursor->copy(); } } Future moveToRoot() { // If pageCursor exists follow parent links to the root - if(pageCursor) { - while(pageCursor->parent) { + if (pageCursor) { + while (pageCursor->parent) { pageCursor = pageCursor->parent; } return Void(); @@ -4489,10 +4362,10 @@ bool validAtVersion(Version v) { }); } - ACTOR Future seekLessThan_impl(InternalCursor *self, RedwoodRecordRef query, int prefetchBytes) { + ACTOR Future seekLessThan_impl(InternalCursor* self, RedwoodRecordRef query, int prefetchBytes) { Future f = self->moveToRoot(); // f will almost always be ready - if(!f.isReady()) { + if (!f.isReady()) { wait(f); } @@ -4502,23 +4375,22 @@ bool validAtVersion(Version v) { bool success = self->pageCursor->cursor.seekLessThan(query); // Skip backwards over internal page entries that do not link to child pages - if(!isLeaf) { + if (!isLeaf) { // While record has no value, move again - while(success && !self->pageCursor->cursor.get().value.present()) { + while (success && !self->pageCursor->cursor.get().value.present()) { success = self->pageCursor->cursor.movePrev(); } } - if(success) { + if (success) { // If we found a record < query at a leaf page then return success - if(isLeaf) { + if (isLeaf) { return true; } Reference child = wait(self->pageCursor->getChild(self->pager, prefetchBytes)); self->pageCursor = child; - } - else { + } else { // No records < query on this page, so move to immediate previous record at leaf level bool success = wait(self->move(false)); return success; @@ -4530,22 +4402,23 @@ bool validAtVersion(Version v) { return seekLessThan_impl(this, query, prefetchBytes); } - ACTOR Future move_impl(InternalCursor *self, bool forward) { + ACTOR Future move_impl(InternalCursor* self, bool forward) { // Try to move pageCursor, if it fails to go parent, repeat until it works or root cursor can't be moved - while(1) { + while (1) { self->ensureUnshared(); - bool success = self->pageCursor->cursor.valid() && (forward ? self->pageCursor->cursor.moveNext() : self->pageCursor->cursor.movePrev()); + bool success = self->pageCursor->cursor.valid() && + (forward ? self->pageCursor->cursor.moveNext() : self->pageCursor->cursor.movePrev()); // Skip over internal page entries that do not link to child pages - if(!self->pageCursor->isLeaf()) { + if (!self->pageCursor->isLeaf()) { // While record has no value, move again - while(success && !self->pageCursor->cursor.get().value.present()) { + while (success && !self->pageCursor->cursor.get().value.present()) { success = forward ? self->pageCursor->cursor.moveNext() : self->pageCursor->cursor.movePrev(); } } // Stop if successful or there's no parent to move to - if(success || !self->pageCursor->parent) { + if (success || !self->pageCursor->parent) { break; } @@ -4554,16 +4427,16 @@ bool validAtVersion(Version v) { } // If pageCursor not valid we've reached an end of the tree - if(!self->pageCursor->cursor.valid()) { + if (!self->pageCursor->cursor.valid()) { return false; } // While not on a leaf page, move down to get to one. - while(!self->pageCursor->isLeaf()) { + while (!self->pageCursor->isLeaf()) { // Skip over internal page entries that do not link to child pages - while(!self->pageCursor->cursor.get().value.present()) { + while (!self->pageCursor->cursor.get().value.present()) { bool success = forward ? self->pageCursor->cursor.moveNext() : self->pageCursor->cursor.movePrev(); - if(!success) { + if (!success) { return false; } } @@ -4576,16 +4449,14 @@ bool validAtVersion(Version v) { return true; } - Future move(bool forward) { - return move_impl(this, forward); - } + Future move(bool forward) { return move_impl(this, forward); } // Move to the first or last record of the database. - ACTOR Future move_end(InternalCursor *self, bool begin) { + ACTOR Future move_end(InternalCursor* self, bool begin) { Future f = self->moveToRoot(); // f will almost always be ready - if(!f.isReady()) { + if (!f.isReady()) { wait(f); } @@ -4596,47 +4467,37 @@ bool validAtVersion(Version v) { bool success = begin ? self->pageCursor->cursor.moveFirst() : self->pageCursor->cursor.moveLast(); // Skip over internal page entries that do not link to child pages - if(!self->pageCursor->isLeaf()) { + if (!self->pageCursor->isLeaf()) { // While record has no value, move past it - while(success && !self->pageCursor->cursor.get().value.present()) { + while (success && !self->pageCursor->cursor.get().value.present()) { success = begin ? self->pageCursor->cursor.moveNext() : self->pageCursor->cursor.movePrev(); } } // If it worked, return true if we've reached a leaf page otherwise go to the next child - if(success) { - if(self->pageCursor->isLeaf()) { + if (success) { + if (self->pageCursor->isLeaf()) { return true; } Reference child = wait(self->pageCursor->getChild(self->pager)); self->pageCursor = child; - } - else { + } else { return false; } } } - Future moveFirst() { - return move_end(this, true); - } - Future moveLast() { - return move_end(this, false); - } - + Future moveFirst() { return move_end(this, true); } + Future moveLast() { return move_end(this, false); } }; // Cursor is for reading and interating over user visible KV pairs at a specific version // KeyValueRefs returned become invalid once the cursor is moved - class Cursor : public IStoreCursor, public ReferenceCounted, public FastAllocated, NonCopyable { + class Cursor : public IStoreCursor, public ReferenceCounted, public FastAllocated, NonCopyable { public: Cursor(Reference pageSource, BTreePageID root, Version internalRecordVersion) - : m_version(internalRecordVersion), - m_cur1(pageSource, root), - m_cur2(m_cur1) - { - } + : m_version(internalRecordVersion), m_cur1(pageSource, root), m_cur2(m_cur1) {} void addref() { ReferenceCounted::addref(); } void delref() { ReferenceCounted::delref(); } @@ -4657,9 +4518,7 @@ bool validAtVersion(Version v) { Optional m_kv; public: - Future findEqual(KeyRef key) override { - return find_impl(this, key, 0); - } + Future findEqual(KeyRef key) override { return find_impl(this, key, 0); } Future findFirstEqualOrGreater(KeyRef key, int prefetchBytes) override { return find_impl(this, key, 1, prefetchBytes); } @@ -4667,43 +4526,32 @@ bool validAtVersion(Version v) { return find_impl(this, key, -1, prefetchBytes); } - Future next() override { - return move(this, true); - } - Future prev() override { - return move(this, false); - } + Future next() override { return move(this, true); } + Future prev() override { return move(this, false); } - bool isValid() override { - return m_kv.present(); - } + bool isValid() override { return m_kv.present(); } - KeyRef getKey() override { - return m_kv.get().key; - } + KeyRef getKey() override { return m_kv.get().key; } - ValueRef getValue() override { - return m_kv.get().value; - } + ValueRef getValue() override { return m_kv.get().value; } std::string toString(bool includePaths = false) const { std::string r; r += format("Cursor(%p) ver: %" PRId64 " ", this, m_version); - if(m_kv.present()) { - r += format(" KV: '%s' -> '%s'", m_kv.get().key.printable().c_str(), m_kv.get().value.printable().c_str()); - } - else { + if (m_kv.present()) { + r += format(" KV: '%s' -> '%s'", m_kv.get().key.printable().c_str(), + m_kv.get().value.printable().c_str()); + } else { r += " KV: "; } - if(includePaths) { + if (includePaths) { r += format("\n Cur1: %s", m_cur1.toString().c_str()); r += format("\n Cur2: %s", m_cur2.toString().c_str()); - } - else { - if(m_cur1.valid()) { + } else { + if (m_cur1.valid()) { r += format("\n Cur1: %s", m_cur1.get().toString().c_str()); } - if(m_cur2.valid()) { + if (m_cur2.valid()) { r += format("\n Cur2: %s", m_cur2.get().toString().c_str()); } } @@ -4716,48 +4564,48 @@ bool validAtVersion(Version v) { // for less than or equal use cmp < 0 // for greater than or equal use cmp > 0 // for equal use cmp == 0 - ACTOR static Future find_impl(Cursor *self, KeyRef key, int cmp, int prefetchBytes = 0) { + ACTOR static Future find_impl(Cursor* self, KeyRef key, int cmp, int prefetchBytes = 0) { state RedwoodRecordRef query(key, self->m_version + 1); self->m_kv.reset(); wait(success(self->m_cur1.seekLessThan(query, prefetchBytes))); - debug_printf("find%sE(%s): %s\n", cmp > 0 ? "GT" : (cmp == 0 ? "" : "LT"), query.toString().c_str(), self->toString().c_str()); + debug_printf("find%sE(%s): %s\n", cmp > 0 ? "GT" : (cmp == 0 ? "" : "LT"), query.toString().c_str(), + self->toString().c_str()); // If we found the target key with a present value then return it as it is valid for any cmp type - if(self->m_cur1.present() && self->m_cur1.get().key == key) { + if (self->m_cur1.present() && self->m_cur1.get().key == key) { debug_printf("Target key found. Cursor: %s\n", self->toString().c_str()); self->m_kv = self->m_cur1.get().toKeyValueRef(); return Void(); } // If cmp type is Equal and we reached here, we didn't find it - if(cmp == 0) { + if (cmp == 0) { return Void(); } // cmp mode is GreaterThanOrEqual, so if we've reached here an equal key was not found and cur1 either // points to a lesser key or is invalid. - if(cmp > 0) { + if (cmp > 0) { // If cursor is invalid, query was less than the first key in database so go to the first record - if(!self->m_cur1.valid()) { + if (!self->m_cur1.valid()) { bool valid = wait(self->m_cur1.moveFirst()); - if(!valid) { + if (!valid) { self->m_kv.reset(); return Void(); } - } - else { + } else { // Otherwise, move forward until we find a key greater than the target key. // If multiversion data is present, the next record could have the same key as the initial // record found but be at a newer version. loop { bool valid = wait(self->m_cur1.move(true)); - if(!valid) { + if (!valid) { self->m_kv.reset(); return Void(); } - if(self->m_cur1.get().key > key) { + if (self->m_cur1.get().key > key) { break; } } @@ -4765,10 +4613,10 @@ bool validAtVersion(Version v) { // Get the next present key at the target version. Handles invalid cursor too. wait(self->next()); - } - else if(cmp < 0) { - // cmp mode is LessThanOrEqual. An equal key to the target key was already checked above, and the search was for LessThan query, so cur1 is already in the right place. - if(!self->m_cur1.valid()) { + } else if (cmp < 0) { + // cmp mode is LessThanOrEqual. An equal key to the target key was already checked above, and the + // search was for LessThan query, so cur1 is already in the right place. + if (!self->m_cur1.valid()) { self->m_kv.reset(); return Void(); } @@ -4780,19 +4628,19 @@ bool validAtVersion(Version v) { return Void(); } - ACTOR static Future move(Cursor *self, bool fwd) { + ACTOR static Future move(Cursor* self, bool fwd) { debug_printf("Cursor::move(%d): Start %s\n", fwd, self->toString().c_str()); ASSERT(self->m_cur1.valid()); // If kv is present then the key/version at cur1 was already returned so move to a new key // Move cur1 until failure or a new key is found, keeping prior record visited in cur2 - if(self->m_kv.present()) { + if (self->m_kv.present()) { ASSERT(self->m_cur1.valid()); loop { self->m_cur2 = self->m_cur1; debug_printf("Cursor::move(%d): Advancing cur1 %s\n", fwd, self->toString().c_str()); bool valid = wait(self->m_cur1.move(fwd)); - if(!valid || self->m_cur1.get().key != self->m_cur2.get().key) { + if (!valid || self->m_cur1.get().key != self->m_cur2.get().key) { break; } } @@ -4806,36 +4654,33 @@ bool validAtVersion(Version v) { // exists at the version (but could be the empty string) while valid just means the internal // record is in effect at that version but it could indicate that the key was cleared and // no longer exists from the user's perspective at that version - if(self->m_cur1.valid()) { + if (self->m_cur1.valid()) { self->m_cur2 = self->m_cur1; debug_printf("Cursor::move(%d): Advancing cur2 %s\n", fwd, self->toString().c_str()); wait(success(self->m_cur2.move(true))); } - while(self->m_cur1.valid()) { + while (self->m_cur1.valid()) { - if(self->m_cur1.get().version == self->m_version || - (self->m_cur1.presentAtVersion(self->m_version) && - (!self->m_cur2.validAtVersion(self->m_version) || - self->m_cur2.get().key != self->m_cur1.get().key)) - ) { + if (self->m_cur1.get().version == self->m_version || + (self->m_cur1.presentAtVersion(self->m_version) && + (!self->m_cur2.validAtVersion(self->m_version) || + self->m_cur2.get().key != self->m_cur1.get().key))) { self->m_kv = self->m_cur1.get().toKeyValueRef(); return Void(); } - if(fwd) { + if (fwd) { // Moving forward, move cur2 forward and keep cur1 pointing to the prior (predecessor) record debug_printf("Cursor::move(%d): Moving forward %s\n", fwd, self->toString().c_str()); self->m_cur1 = self->m_cur2; wait(success(self->m_cur2.move(true))); - } - else { + } else { // Moving backward, move cur1 backward and keep cur2 pointing to the prior (successor) record debug_printf("Cursor::move(%d): Moving backward %s\n", fwd, self->toString().c_str()); self->m_cur2 = self->m_cur1; wait(success(self->m_cur1.move(false))); } - } debug_printf("Cursor::move(%d): Exit, end of db reached. Cursor = %s\n", fwd, self->toString().c_str()); @@ -4844,7 +4689,6 @@ bool validAtVersion(Version v) { return Void(); } }; - }; #include "art_impl.h" @@ -4857,16 +4701,14 @@ class KeyValueStoreRedwoodUnversioned : public IKeyValueStore { public: KeyValueStoreRedwoodUnversioned(std::string filePrefix, UID logID) : m_filePrefix(filePrefix) { // TODO: This constructor should really just take an IVersionedStore - IPager2 *pager = new DWALPager(4096, filePrefix, 0); + IPager2* pager = new DWALPager(4096, filePrefix, 0); m_tree = new VersionedBTree(pager, filePrefix); m_init = catchError(init_impl(this)); } - Future init() { - return m_init; - } + Future init() { return m_init; } - ACTOR Future init_impl(KeyValueStoreRedwoodUnversioned *self) { + ACTOR Future init_impl(KeyValueStoreRedwoodUnversioned* self) { TraceEvent(SevInfo, "RedwoodInit").detail("FilePrefix", self->m_filePrefix); wait(self->m_tree->init()); Version v = self->m_tree->getLatestVersion(); @@ -4875,34 +4717,30 @@ public: return Void(); } - ACTOR void shutdown(KeyValueStoreRedwoodUnversioned *self, bool dispose) { + ACTOR void shutdown(KeyValueStoreRedwoodUnversioned* self, bool dispose) { TraceEvent(SevInfo, "RedwoodShutdown").detail("FilePrefix", self->m_filePrefix).detail("Dispose", dispose); - if(self->m_error.canBeSet()) { - self->m_error.sendError(actor_cancelled()); // Ideally this should be shutdown_in_progress + if (self->m_error.canBeSet()) { + self->m_error.sendError(actor_cancelled()); // Ideally this should be shutdown_in_progress } self->m_init.cancel(); Future closedFuture = self->m_tree->onClosed(); - if(dispose) + if (dispose) self->m_tree->dispose(); else self->m_tree->close(); wait(closedFuture); self->m_closed.send(Void()); - TraceEvent(SevInfo, "RedwoodShutdownComplete").detail("FilePrefix", self->m_filePrefix).detail("Dispose", dispose); + TraceEvent(SevInfo, "RedwoodShutdownComplete") + .detail("FilePrefix", self->m_filePrefix) + .detail("Dispose", dispose); delete self; } - void close() { - shutdown(this, false); - } + void close() { shutdown(this, false); } - void dispose() { - shutdown(this, true); - } + void dispose() { shutdown(this, true); } - Future< Void > onClosed() { - return m_closed.getFuture(); - } + Future onClosed() { return m_closed.getFuture(); } Future commit(bool sequential = false) { Future c = m_tree->commit(); @@ -4911,40 +4749,35 @@ public: return catchError(c); } - KeyValueStoreType getType() { - return KeyValueStoreType::SSD_REDWOOD_V1; - } + KeyValueStoreType getType() { return KeyValueStoreType::SSD_REDWOOD_V1; } - StorageBytes getStorageBytes() { - return m_tree->getStorageBytes(); - } + StorageBytes getStorageBytes() { return m_tree->getStorageBytes(); } - Future< Void > getError() { - return delayed(m_error.getFuture()); - }; + Future getError() { return delayed(m_error.getFuture()); }; void clear(KeyRangeRef range, const Arena* arena = 0) { debug_printf("CLEAR %s\n", printable(range).c_str()); m_tree->clear(range); } - void set( KeyValueRef keyValue, const Arena* arena = NULL ) { + void set(KeyValueRef keyValue, const Arena* arena = NULL) { debug_printf("SET %s\n", printable(keyValue).c_str()); m_tree->set(keyValue); } - Future< Standalone< RangeResultRef > > readRange(KeyRangeRef keys, int rowLimit = 1<<30, int byteLimit = 1<<30) { + Future> readRange(KeyRangeRef keys, int rowLimit = 1 << 30, int byteLimit = 1 << 30) { debug_printf("READRANGE %s\n", printable(keys).c_str()); return catchError(readRange_impl(this, keys, rowLimit, byteLimit)); } - ACTOR static Future< Standalone< RangeResultRef > > readRange_impl(KeyValueStoreRedwoodUnversioned *self, KeyRange keys, int rowLimit, int byteLimit) { + ACTOR static Future> readRange_impl(KeyValueStoreRedwoodUnversioned* self, KeyRange keys, + int rowLimit, int byteLimit) { self->m_tree->counts.getRanges++; state Standalone result; state int accumulatedBytes = 0; - ASSERT( byteLimit > 0 ); + ASSERT(byteLimit > 0); - if(rowLimit == 0) { + if (rowLimit == 0) { return result; } @@ -4952,27 +4785,26 @@ public: // Prefetch is currently only done in the forward direction state int prefetchBytes = rowLimit > 1 ? byteLimit : 0; - if(rowLimit > 0) { + if (rowLimit > 0) { wait(cur->findFirstEqualOrGreater(keys.begin, prefetchBytes)); - while(cur->isValid() && cur->getKey() < keys.end) { + while (cur->isValid() && cur->getKey() < keys.end) { KeyValueRef kv(KeyRef(result.arena(), cur->getKey()), ValueRef(result.arena(), cur->getValue())); accumulatedBytes += kv.expectedSize(); result.push_back(result.arena(), kv); - if(--rowLimit == 0 || accumulatedBytes >= byteLimit) { + if (--rowLimit == 0 || accumulatedBytes >= byteLimit) { break; } wait(cur->next()); } } else { wait(cur->findLastLessOrEqual(keys.end)); - if(cur->isValid() && cur->getKey() == keys.end) - wait(cur->prev()); + if (cur->isValid() && cur->getKey() == keys.end) wait(cur->prev()); - while(cur->isValid() && cur->getKey() >= keys.begin) { + while (cur->isValid() && cur->getKey() >= keys.begin) { KeyValueRef kv(KeyRef(result.arena(), cur->getKey()), ValueRef(result.arena(), cur->getValue())); accumulatedBytes += kv.expectedSize(); result.push_back(result.arena(), kv); - if(++rowLimit == 0 || accumulatedBytes >= byteLimit) { + if (++rowLimit == 0 || accumulatedBytes >= byteLimit) { break; } wait(cur->prev()); @@ -4980,34 +4812,36 @@ public: } result.more = rowLimit == 0 || accumulatedBytes >= byteLimit; - if(result.more) { + if (result.more) { ASSERT(result.size() > 0); - result.readThrough = result[result.size()-1].key; + result.readThrough = result[result.size() - 1].key; } return result; } - ACTOR static Future< Optional > readValue_impl(KeyValueStoreRedwoodUnversioned *self, Key key, Optional< UID > debugID) { + ACTOR static Future> readValue_impl(KeyValueStoreRedwoodUnversioned* self, Key key, + Optional debugID) { self->m_tree->counts.gets++; state Reference cur = self->m_tree->readAtVersion(self->m_tree->getLastCommittedVersion()); wait(cur->findEqual(key)); - if(cur->isValid()) { + if (cur->isValid()) { return cur->getValue(); } return Optional(); } - Future< Optional< Value > > readValue(KeyRef key, Optional< UID > debugID = Optional()) { + Future> readValue(KeyRef key, Optional debugID = Optional()) { return catchError(readValue_impl(this, key, debugID)); } - ACTOR static Future< Optional > readValuePrefix_impl(KeyValueStoreRedwoodUnversioned *self, Key key, int maxLength, Optional< UID > debugID) { + ACTOR static Future> readValuePrefix_impl(KeyValueStoreRedwoodUnversioned* self, Key key, + int maxLength, Optional debugID) { self->m_tree->counts.gets++; state Reference cur = self->m_tree->readAtVersion(self->m_tree->getLastCommittedVersion()); wait(cur->findEqual(key)); - if(cur->isValid()) { + if (cur->isValid()) { Value v = cur->getValue(); int len = std::min(v.size(), maxLength); return Value(cur->getValue().substr(0, len)); @@ -5015,26 +4849,26 @@ public: return Optional(); } - Future< Optional< Value > > readValuePrefix(KeyRef key, int maxLength, Optional< UID > debugID = Optional()) { + Future> readValuePrefix(KeyRef key, int maxLength, Optional debugID = Optional()) { return catchError(readValuePrefix_impl(this, key, maxLength, debugID)); } - virtual ~KeyValueStoreRedwoodUnversioned() { - }; + virtual ~KeyValueStoreRedwoodUnversioned(){}; private: std::string m_filePrefix; - VersionedBTree *m_tree; + VersionedBTree* m_tree; Future m_init; Promise m_closed; Promise m_error; - template inline Future catchError(Future f) { + template + inline Future catchError(Future f) { return forwardError(f, m_error); } }; -IKeyValueStore* keyValueStoreRedwoodV1( std::string const& filename, UID logID) { +IKeyValueStore* keyValueStoreRedwoodV1(std::string const& filename, UID logID) { return new KeyValueStoreRedwoodUnversioned(filename, logID); } @@ -5043,18 +4877,18 @@ int randomSize(int max) { return n; } -StringRef randomString(Arena &arena, int len, char firstChar = 'a', char lastChar = 'z') { +StringRef randomString(Arena& arena, int len, char firstChar = 'a', char lastChar = 'z') { ++lastChar; StringRef s = makeString(len, arena); - for(int i = 0; i < len; ++i) { - *(uint8_t *)(s.begin() + i) = (uint8_t)deterministicRandom()->randomInt(firstChar, lastChar); + for (int i = 0; i < len; ++i) { + *(uint8_t*)(s.begin() + i) = (uint8_t)deterministicRandom()->randomInt(firstChar, lastChar); } return s; } Standalone randomString(int len, char firstChar = 'a', char lastChar = 'z') { Standalone s; - (StringRef &)s = randomString(s.arena(), len, firstChar, lastChar); + (StringRef&)s = randomString(s.arena(), len, firstChar, lastChar); return s; } @@ -5065,80 +4899,84 @@ KeyValue randomKV(int maxKeySize = 10, int maxValueSize = 5) { KeyValue kv; kv.key = randomString(kv.arena(), kLen, 'a', 'm'); - for(int i = 0; i < kLen; ++i) - mutateString(kv.key)[i] = (uint8_t)deterministicRandom()->randomInt('a', 'm'); + for (int i = 0; i < kLen; ++i) mutateString(kv.key)[i] = (uint8_t)deterministicRandom()->randomInt('a', 'm'); - if(vLen > 0) { + if (vLen > 0) { kv.value = randomString(kv.arena(), vLen, 'n', 'z'); - for(int i = 0; i < vLen; ++i) - mutateString(kv.value)[i] = (uint8_t)deterministicRandom()->randomInt('o', 'z'); + for (int i = 0; i < vLen; ++i) mutateString(kv.value)[i] = (uint8_t)deterministicRandom()->randomInt('o', 'z'); } return kv; } -ACTOR Future verifyRange(VersionedBTree *btree, Key start, Key end, Version v, std::map, Optional> *written, int *pErrorCount) { +ACTOR Future verifyRange(VersionedBTree* btree, Key start, Key end, Version v, + std::map, Optional>* written, + int* pErrorCount) { state int errors = 0; - if(end <= start) - end = keyAfter(start); + if (end <= start) end = keyAfter(start); - state std::map, Optional>::const_iterator i = written->lower_bound(std::make_pair(start.toString(), 0)); - state std::map, Optional>::const_iterator iEnd = written->upper_bound(std::make_pair(end.toString(), 0)); + state std::map, Optional>::const_iterator i = + written->lower_bound(std::make_pair(start.toString(), 0)); + state std::map, Optional>::const_iterator iEnd = + written->upper_bound(std::make_pair(end.toString(), 0)); state std::map, Optional>::const_iterator iLast; state Reference cur = btree->readAtVersion(v); - debug_printf("VerifyRange(@%" PRId64 ", %s, %s): Start cur=%p\n", v, start.toHexString().c_str(), end.toHexString().c_str(), cur.getPtr()); + debug_printf("VerifyRange(@%" PRId64 ", %s, %s): Start cur=%p\n", v, start.toHexString().c_str(), + end.toHexString().c_str(), cur.getPtr()); // Randomly use the cursor for something else first. - if(deterministicRandom()->coinflip()) { + if (deterministicRandom()->coinflip()) { state Key randomKey = randomKV().key; - debug_printf("VerifyRange(@%" PRId64 ", %s, %s): Dummy seek to '%s'\n", v, start.toHexString().c_str(), end.toHexString().c_str(), randomKey.toString().c_str()); - wait(deterministicRandom()->coinflip() ? cur->findFirstEqualOrGreater(randomKey) : cur->findLastLessOrEqual(randomKey)); + debug_printf("VerifyRange(@%" PRId64 ", %s, %s): Dummy seek to '%s'\n", v, start.toHexString().c_str(), + end.toHexString().c_str(), randomKey.toString().c_str()); + wait(deterministicRandom()->coinflip() ? cur->findFirstEqualOrGreater(randomKey) + : cur->findLastLessOrEqual(randomKey)); } - debug_printf("VerifyRange(@%" PRId64 ", %s, %s): Actual seek\n", v, start.toHexString().c_str(), end.toHexString().c_str()); + debug_printf("VerifyRange(@%" PRId64 ", %s, %s): Actual seek\n", v, start.toHexString().c_str(), + end.toHexString().c_str()); wait(cur->findFirstEqualOrGreater(start)); state std::vector results; - while(cur->isValid() && cur->getKey() < end) { + while (cur->isValid() && cur->getKey() < end) { // Find the next written kv pair that would be present at this version - while(1) { + while (1) { iLast = i; - if(i == iEnd) - break; + if (i == iEnd) break; ++i; - if(iLast->first.second <= v - && iLast->second.present() - && ( - i == iEnd - || i->first.first != iLast->first.first - || i->first.second > v - ) - ) { - debug_printf("VerifyRange(@%" PRId64 ", %s, %s) Found key in written map: %s\n", v, start.toHexString().c_str(), end.toHexString().c_str(), iLast->first.first.c_str()); + if (iLast->first.second <= v && iLast->second.present() && + (i == iEnd || i->first.first != iLast->first.first || i->first.second > v)) { + debug_printf("VerifyRange(@%" PRId64 ", %s, %s) Found key in written map: %s\n", v, + start.toHexString().c_str(), end.toHexString().c_str(), iLast->first.first.c_str()); break; } } - if(iLast == iEnd) { + if (iLast == iEnd) { ++errors; ++*pErrorCount; - printf("VerifyRange(@%" PRId64 ", %s, %s) ERROR: Tree key '%s' vs nothing in written map.\n", v, start.toHexString().c_str(), end.toHexString().c_str(), cur->getKey().toString().c_str()); + printf("VerifyRange(@%" PRId64 ", %s, %s) ERROR: Tree key '%s' vs nothing in written map.\n", v, + start.toHexString().c_str(), end.toHexString().c_str(), cur->getKey().toString().c_str()); break; } - if(cur->getKey() != iLast->first.first) { + if (cur->getKey() != iLast->first.first) { ++errors; ++*pErrorCount; - printf("VerifyRange(@%" PRId64 ", %s, %s) ERROR: Tree key '%s' vs written '%s'\n", v, start.toHexString().c_str(), end.toHexString().c_str(), cur->getKey().toString().c_str(), iLast->first.first.c_str()); + printf("VerifyRange(@%" PRId64 ", %s, %s) ERROR: Tree key '%s' vs written '%s'\n", v, + start.toHexString().c_str(), end.toHexString().c_str(), cur->getKey().toString().c_str(), + iLast->first.first.c_str()); break; } - if(cur->getValue() != iLast->second.get()) { + if (cur->getValue() != iLast->second.get()) { ++errors; ++*pErrorCount; - printf("VerifyRange(@%" PRId64 ", %s, %s) ERROR: Tree key '%s' has tree value '%s' vs written '%s'\n", v, start.toHexString().c_str(), end.toHexString().c_str(), cur->getKey().toString().c_str(), cur->getValue().toString().c_str(), iLast->second.get().c_str()); + printf("VerifyRange(@%" PRId64 ", %s, %s) ERROR: Tree key '%s' has tree value '%s' vs written '%s'\n", v, + start.toHexString().c_str(), end.toHexString().c_str(), cur->getKey().toString().c_str(), + cur->getValue().toString().c_str(), iLast->second.get().c_str()); break; } @@ -5149,60 +4987,61 @@ ACTOR Future verifyRange(VersionedBTree *btree, Key start, Key end, Version } // Make sure there are no further written kv pairs that would be present at this version. - while(1) { + while (1) { iLast = i; - if(i == iEnd) - break; + if (i == iEnd) break; ++i; - if(iLast->first.second <= v - && iLast->second.present() - && ( - i == iEnd - || i->first.first != iLast->first.first - || i->first.second > v - ) - ) + if (iLast->first.second <= v && iLast->second.present() && + (i == iEnd || i->first.first != iLast->first.first || i->first.second > v)) break; } - if(iLast != iEnd) { + if (iLast != iEnd) { ++errors; ++*pErrorCount; - printf("VerifyRange(@%" PRId64 ", %s, %s) ERROR: Tree range ended but written has @%" PRId64 " '%s'\n", v, start.toHexString().c_str(), end.toHexString().c_str(), iLast->first.second, iLast->first.first.c_str()); + printf("VerifyRange(@%" PRId64 ", %s, %s) ERROR: Tree range ended but written has @%" PRId64 " '%s'\n", v, + start.toHexString().c_str(), end.toHexString().c_str(), iLast->first.second, iLast->first.first.c_str()); } - debug_printf("VerifyRangeReverse(@%" PRId64 ", %s, %s): start\n", v, start.toHexString().c_str(), end.toHexString().c_str()); + debug_printf("VerifyRangeReverse(@%" PRId64 ", %s, %s): start\n", v, start.toHexString().c_str(), + end.toHexString().c_str()); - // Randomly use a new cursor at the same version for the reverse range read, if the version is still available for opening new cursors - if(v >= btree->getOldestVersion() && deterministicRandom()->coinflip()) { + // Randomly use a new cursor at the same version for the reverse range read, if the version is still available for + // opening new cursors + if (v >= btree->getOldestVersion() && deterministicRandom()->coinflip()) { cur = btree->readAtVersion(v); } // Now read the range from the tree in reverse order and compare to the saved results wait(cur->findLastLessOrEqual(end)); - if(cur->isValid() && cur->getKey() == end) - wait(cur->prev()); + if (cur->isValid() && cur->getKey() == end) wait(cur->prev()); state std::vector::const_reverse_iterator r = results.rbegin(); - while(cur->isValid() && cur->getKey() >= start) { - if(r == results.rend()) { + while (cur->isValid() && cur->getKey() >= start) { + if (r == results.rend()) { ++errors; ++*pErrorCount; - printf("VerifyRangeReverse(@%" PRId64 ", %s, %s) ERROR: Tree key '%s' vs nothing in written map.\n", v, start.toHexString().c_str(), end.toHexString().c_str(), cur->getKey().toString().c_str()); + printf("VerifyRangeReverse(@%" PRId64 ", %s, %s) ERROR: Tree key '%s' vs nothing in written map.\n", v, + start.toHexString().c_str(), end.toHexString().c_str(), cur->getKey().toString().c_str()); break; } - if(cur->getKey() != r->key) { + if (cur->getKey() != r->key) { ++errors; ++*pErrorCount; - printf("VerifyRangeReverse(@%" PRId64 ", %s, %s) ERROR: Tree key '%s' vs written '%s'\n", v, start.toHexString().c_str(), end.toHexString().c_str(), cur->getKey().toString().c_str(), r->key.toString().c_str()); + printf("VerifyRangeReverse(@%" PRId64 ", %s, %s) ERROR: Tree key '%s' vs written '%s'\n", v, + start.toHexString().c_str(), end.toHexString().c_str(), cur->getKey().toString().c_str(), + r->key.toString().c_str()); break; } - if(cur->getValue() != r->value) { + if (cur->getValue() != r->value) { ++errors; ++*pErrorCount; - printf("VerifyRangeReverse(@%" PRId64 ", %s, %s) ERROR: Tree key '%s' has tree value '%s' vs written '%s'\n", v, start.toHexString().c_str(), end.toHexString().c_str(), cur->getKey().toString().c_str(), cur->getValue().toString().c_str(), r->value.toString().c_str()); + printf("VerifyRangeReverse(@%" PRId64 + ", %s, %s) ERROR: Tree key '%s' has tree value '%s' vs written '%s'\n", + v, start.toHexString().c_str(), end.toHexString().c_str(), cur->getKey().toString().c_str(), + cur->getValue().toString().c_str(), r->value.toString().c_str()); break; } @@ -5210,47 +5049,54 @@ ACTOR Future verifyRange(VersionedBTree *btree, Key start, Key end, Version wait(cur->prev()); } - if(r != results.rend()) { + if (r != results.rend()) { ++errors; ++*pErrorCount; - printf("VerifyRangeReverse(@%" PRId64 ", %s, %s) ERROR: Tree range ended but written has '%s'\n", v, start.toHexString().c_str(), end.toHexString().c_str(), r->key.toString().c_str()); + printf("VerifyRangeReverse(@%" PRId64 ", %s, %s) ERROR: Tree range ended but written has '%s'\n", v, + start.toHexString().c_str(), end.toHexString().c_str(), r->key.toString().c_str()); } return errors; } // Verify the result of point reads for every set or cleared key at the given version -ACTOR Future seekAll(VersionedBTree *btree, Version v, std::map, Optional> *written, int *pErrorCount) { +ACTOR Future seekAll(VersionedBTree* btree, Version v, + std::map, Optional>* written, int* pErrorCount) { state std::map, Optional>::const_iterator i = written->cbegin(); state std::map, Optional>::const_iterator iEnd = written->cend(); state int errors = 0; state Reference cur = btree->readAtVersion(v); - while(i != iEnd) { + while (i != iEnd) { state std::string key = i->first.first; state Version ver = i->first.second; - if(ver == v) { + if (ver == v) { state Optional val = i->second; debug_printf("Verifying @%" PRId64 " '%s'\n", ver, key.c_str()); state Arena arena; wait(cur->findEqual(KeyRef(arena, key))); - if(val.present()) { - if(!(cur->isValid() && cur->getKey() == key && cur->getValue() == val.get())) { + if (val.present()) { + if (!(cur->isValid() && cur->getKey() == key && cur->getValue() == val.get())) { ++errors; ++*pErrorCount; - if(!cur->isValid()) - printf("Verify ERROR: key_not_found: '%s' -> '%s' @%" PRId64 "\n", key.c_str(), val.get().c_str(), ver); - else if(cur->getKey() != key) - printf("Verify ERROR: key_incorrect: found '%s' expected '%s' @%" PRId64 "\n", cur->getKey().toString().c_str(), key.c_str(), ver); - else if(cur->getValue() != val.get()) - printf("Verify ERROR: value_incorrect: for '%s' found '%s' expected '%s' @%" PRId64 "\n", cur->getKey().toString().c_str(), cur->getValue().toString().c_str(), val.get().c_str(), ver); + if (!cur->isValid()) + printf("Verify ERROR: key_not_found: '%s' -> '%s' @%" PRId64 "\n", key.c_str(), + val.get().c_str(), ver); + else if (cur->getKey() != key) + printf("Verify ERROR: key_incorrect: found '%s' expected '%s' @%" PRId64 "\n", + cur->getKey().toString().c_str(), key.c_str(), ver); + else if (cur->getValue() != val.get()) + printf("Verify ERROR: value_incorrect: for '%s' found '%s' expected '%s' @%" PRId64 "\n", + cur->getKey().toString().c_str(), cur->getValue().toString().c_str(), val.get().c_str(), + ver); } } else { - if(cur->isValid() && cur->getKey() == key) { + if (cur->isValid() && cur->getKey() == key) { ++errors; ++*pErrorCount; - printf("Verify ERROR: cleared_key_found: '%s' -> '%s' @%" PRId64 "\n", key.c_str(), cur->getValue().toString().c_str(), ver); + printf("Verify ERROR: cleared_key_found: '%s' -> '%s' @%" PRId64 "\n", key.c_str(), + cur->getValue().toString().c_str(), ver); } } } @@ -5259,7 +5105,9 @@ ACTOR Future seekAll(VersionedBTree *btree, Version v, std::map verify(VersionedBTree *btree, FutureStream vStream, std::map, Optional> *written, int *pErrorCount, bool serial) { +ACTOR Future verify(VersionedBTree* btree, FutureStream vStream, + std::map, Optional>* written, int* pErrorCount, + bool serial) { state Future fRangeAll; state Future fRangeRandom; state Future fSeekAll; @@ -5273,33 +5121,37 @@ ACTOR Future verify(VersionedBTree *btree, FutureStream vStream, committedVersions.push_back(v); // Remove expired versions - while(!committedVersions.empty() && committedVersions.front() < btree->getOldestVersion()) { + while (!committedVersions.empty() && committedVersions.front() < btree->getOldestVersion()) { committedVersions.pop_front(); } - // Choose a random committed version, or sometimes the latest (which could be ahead of the latest version from vStream) - v = (committedVersions.empty() || deterministicRandom()->random01() < 0.25) ? btree->getLastCommittedVersion() : committedVersions[deterministicRandom()->randomInt(0, committedVersions.size())]; + // Choose a random committed version, or sometimes the latest (which could be ahead of the latest version + // from vStream) + v = (committedVersions.empty() || deterministicRandom()->random01() < 0.25) + ? btree->getLastCommittedVersion() + : committedVersions[deterministicRandom()->randomInt(0, committedVersions.size())]; debug_printf("Using committed version %" PRId64 "\n", v); // Get a cursor at v so that v doesn't get expired between the possibly serial steps below. state Reference cur = btree->readAtVersion(v); debug_printf("Verifying entire key range at version %" PRId64 "\n", v); fRangeAll = verifyRange(btree, LiteralStringRef(""), LiteralStringRef("\xff\xff"), v, written, pErrorCount); - if(serial) { + if (serial) { wait(success(fRangeAll)); } Key begin = randomKV().key; Key end = randomKV().key; - debug_printf("Verifying range (%s, %s) at version %" PRId64 "\n", toString(begin).c_str(), toString(end).c_str(), v); + debug_printf("Verifying range (%s, %s) at version %" PRId64 "\n", toString(begin).c_str(), + toString(end).c_str(), v); fRangeRandom = verifyRange(btree, begin, end, v, written, pErrorCount); - if(serial) { + if (serial) { wait(success(fRangeRandom)); } debug_printf("Verifying seeks to each changed key at version %" PRId64 "\n", v); fSeekAll = seekAll(btree, v, written, pErrorCount); - if(serial) { + if (serial) { wait(success(fSeekAll)); } @@ -5307,11 +5159,10 @@ ACTOR Future verify(VersionedBTree *btree, FutureStream vStream, printf("Verified version %" PRId64 ", %d errors\n", v, *pErrorCount); - if(*pErrorCount != 0) - break; + if (*pErrorCount != 0) break; } - } catch(Error &e) { - if(e.code() != error_code_end_of_stream && e.code() != error_code_transaction_too_old) { + } catch (Error& e) { + if (e.code() != error_code_end_of_stream && e.code() != error_code_transaction_too_old) { throw; } } @@ -5319,12 +5170,12 @@ ACTOR Future verify(VersionedBTree *btree, FutureStream vStream, } // Does a random range read, doesn't trap/report errors -ACTOR Future randomReader(VersionedBTree *btree) { +ACTOR Future randomReader(VersionedBTree* btree) { try { state Reference cur; loop { wait(yield()); - if(!cur || deterministicRandom()->random01() > .01) { + if (!cur || deterministicRandom()->random01() > .01) { Version v = btree->getLastCommittedVersion(); cur = btree->readAtVersion(v); } @@ -5332,14 +5183,13 @@ ACTOR Future randomReader(VersionedBTree *btree) { state KeyValue kv = randomKV(10, 0); wait(cur->findFirstEqualOrGreater(kv.key)); state int c = deterministicRandom()->randomInt(0, 100); - while(cur->isValid() && c-- > 0) { + while (cur->isValid() && c-- > 0) { wait(success(cur->next())); wait(yield()); } } - } - catch(Error &e) { - if(e.code() != error_code_transaction_too_old) { + } catch (Error& e) { + if (e.code() != error_code_transaction_too_old) { throw e; } } @@ -5351,9 +5201,7 @@ struct IntIntPair { IntIntPair() {} IntIntPair(int k, int v) : k(k), v(v) {} - IntIntPair(Arena &arena, const IntIntPair &toCopy) { - *this = toCopy; - } + IntIntPair(Arena& arena, const IntIntPair& toCopy) { *this = toCopy; } struct Delta { bool prefixSource; @@ -5361,39 +5209,28 @@ struct IntIntPair { int dk; int dv; - IntIntPair apply(const IntIntPair &base, Arena &arena) { - return {base.k + dk, base.v + dv}; - } + IntIntPair apply(const IntIntPair& base, Arena& arena) { return { base.k + dk, base.v + dv }; } - void setPrefixSource(bool val) { - prefixSource = val; - } + void setPrefixSource(bool val) { prefixSource = val; } - bool getPrefixSource() const { - return prefixSource; - } + bool getPrefixSource() const { return prefixSource; } - void setDeleted(bool val) { - deleted = val; - } + void setDeleted(bool val) { deleted = val; } - bool getDeleted() const { - return deleted; - } + bool getDeleted() const { return deleted; } - int size() const { - return sizeof(Delta); - } + int size() const { return sizeof(Delta); } std::string toString() const { - return format("DELTA{prefixSource=%d deleted=%d dk=%d(0x%x) dv=%d(0x%x)}", prefixSource, deleted, dk, dk, dv, dv); + return format("DELTA{prefixSource=%d deleted=%d dk=%d(0x%x) dv=%d(0x%x)}", prefixSource, deleted, dk, dk, + dv, dv); } }; // For IntIntPair, skipLen will be in units of fields, not bytes - int getCommonPrefixLen(const IntIntPair &other, int skip = 0) const { - if(k == other.k) { - if(v == other.v) { + int getCommonPrefixLen(const IntIntPair& other, int skip = 0) const { + if (k == other.k) { + if (v == other.v) { return 2; } return 1; @@ -5401,31 +5238,25 @@ struct IntIntPair { return 0; } - int compare(const IntIntPair &rhs, int skip = 0) const { - if(skip == 2) { + int compare(const IntIntPair& rhs, int skip = 0) const { + if (skip == 2) { return 0; } int cmp = (skip > 0) ? 0 : (k - rhs.k); - if(cmp == 0) { + if (cmp == 0) { cmp = v - rhs.v; } return cmp; } - bool operator==(const IntIntPair &rhs) const { - return compare(rhs) == 0; - } + bool operator==(const IntIntPair& rhs) const { return compare(rhs) == 0; } - bool operator<(const IntIntPair &rhs) const { - return compare(rhs) < 0; - } + bool operator<(const IntIntPair& rhs) const { return compare(rhs) < 0; } - int deltaSize(const IntIntPair &base, int skipLen, bool worstcase) const { - return sizeof(Delta); - } + int deltaSize(const IntIntPair& base, int skipLen, bool worstcase) const { return sizeof(Delta); } - int writeDelta(Delta &d, const IntIntPair &base, int commonPrefix = -1) const { + int writeDelta(Delta& d, const IntIntPair& base, int commonPrefix = -1) const { d.prefixSource = false; d.deleted = false; d.dk = k - base.k; @@ -5436,21 +5267,19 @@ struct IntIntPair { int k; int v; - std::string toString() const { - return format("{k=%d(0x%x) v=%d(0x%x)}", k, k, v, v); - } + std::string toString() const { return format("{k=%d(0x%x) v=%d(0x%x)}", k, k, v, v); } }; int deltaTest(RedwoodRecordRef rec, RedwoodRecordRef base) { std::vector buf(rec.key.size() + rec.value.orDefault(StringRef()).size() + 20); - RedwoodRecordRef::Delta &d = *(RedwoodRecordRef::Delta *)&buf.front(); + RedwoodRecordRef::Delta& d = *(RedwoodRecordRef::Delta*)&buf.front(); Arena mem; int expectedSize = rec.deltaSize(base, 0, false); int deltaSize = rec.writeDelta(d, base); RedwoodRecordRef decoded = d.apply(base, mem); - if(decoded != rec || expectedSize != deltaSize || d.size() != deltaSize) { + if (decoded != rec || expectedSize != deltaSize || d.size() != deltaSize) { printf("\n"); printf("Base: %s\n", base.toString().c_str()); printf("Record: %s\n", rec.toString().c_str()); @@ -5466,15 +5295,15 @@ int deltaTest(RedwoodRecordRef rec, RedwoodRecordRef base) { return deltaSize; } -RedwoodRecordRef randomRedwoodRecordRef(const std::string &keyBuffer, const std::string &valueBuffer) { +RedwoodRecordRef randomRedwoodRecordRef(const std::string& keyBuffer, const std::string& valueBuffer) { RedwoodRecordRef rec; - rec.key = StringRef((uint8_t *)keyBuffer.data(), deterministicRandom()->randomInt(0, keyBuffer.size())); - if(deterministicRandom()->coinflip()) { - rec.value = StringRef((uint8_t *)valueBuffer.data(), deterministicRandom()->randomInt(0, valueBuffer.size())); + rec.key = StringRef((uint8_t*)keyBuffer.data(), deterministicRandom()->randomInt(0, keyBuffer.size())); + if (deterministicRandom()->coinflip()) { + rec.value = StringRef((uint8_t*)valueBuffer.data(), deterministicRandom()->randomInt(0, valueBuffer.size())); } int versionIntSize = deterministicRandom()->randomInt(0, 8) * 8; - if(versionIntSize > 0) { + if (versionIntSize > 0) { --versionIntSize; int64_t max = ((int64_t)1 << versionIntSize) - 1; rec.version = deterministicRandom()->randomInt64(0, max); @@ -5496,7 +5325,7 @@ TEST_CASE("!/redwood/correctness/unit/RedwoodRecordRef") { // Test pageID stuff. { - LogicalPageID ids[] = {1, 5}; + LogicalPageID ids[] = { 1, 5 }; BTreePageID id(ids, 2); RedwoodRecordRef r; r.setChildPage(id); @@ -5509,44 +5338,34 @@ TEST_CASE("!/redwood/correctness/unit/RedwoodRecordRef") { } deltaTest(RedwoodRecordRef(LiteralStringRef(""), 0, LiteralStringRef("")), - RedwoodRecordRef(LiteralStringRef(""), 0, LiteralStringRef("")) - ); + RedwoodRecordRef(LiteralStringRef(""), 0, LiteralStringRef(""))); deltaTest(RedwoodRecordRef(LiteralStringRef("abc"), 0, LiteralStringRef("")), - RedwoodRecordRef(LiteralStringRef("abc"), 0, LiteralStringRef("")) - ); + RedwoodRecordRef(LiteralStringRef("abc"), 0, LiteralStringRef(""))); - deltaTest(RedwoodRecordRef(LiteralStringRef("abc"), 0, LiteralStringRef("")), - RedwoodRecordRef(LiteralStringRef("abcd"), 0, LiteralStringRef("")) - ); + deltaTest(RedwoodRecordRef(LiteralStringRef("abc"), 0, LiteralStringRef("")), + RedwoodRecordRef(LiteralStringRef("abcd"), 0, LiteralStringRef(""))); deltaTest(RedwoodRecordRef(LiteralStringRef("abcd"), 2, LiteralStringRef("")), - RedwoodRecordRef(LiteralStringRef("abc"), 2, LiteralStringRef("")) - ); + RedwoodRecordRef(LiteralStringRef("abc"), 2, LiteralStringRef(""))); deltaTest(RedwoodRecordRef(std::string(300, 'k'), 2, std::string(1e6, 'v')), - RedwoodRecordRef(std::string(300, 'k'), 2, LiteralStringRef("")) - ); + RedwoodRecordRef(std::string(300, 'k'), 2, LiteralStringRef(""))); deltaTest(RedwoodRecordRef(LiteralStringRef(""), 2, LiteralStringRef("")), - RedwoodRecordRef(LiteralStringRef(""), 1, LiteralStringRef("")) - ); + RedwoodRecordRef(LiteralStringRef(""), 1, LiteralStringRef(""))); deltaTest(RedwoodRecordRef(LiteralStringRef(""), 0xffff, LiteralStringRef("")), - RedwoodRecordRef(LiteralStringRef(""), 1, LiteralStringRef("")) - ); + RedwoodRecordRef(LiteralStringRef(""), 1, LiteralStringRef(""))); deltaTest(RedwoodRecordRef(LiteralStringRef(""), 1, LiteralStringRef("")), - RedwoodRecordRef(LiteralStringRef(""), 0xffff, LiteralStringRef("")) - ); + RedwoodRecordRef(LiteralStringRef(""), 0xffff, LiteralStringRef(""))); deltaTest(RedwoodRecordRef(LiteralStringRef(""), 0xffffff, LiteralStringRef("")), - RedwoodRecordRef(LiteralStringRef(""), 1, LiteralStringRef("")) - ); + RedwoodRecordRef(LiteralStringRef(""), 1, LiteralStringRef(""))); deltaTest(RedwoodRecordRef(LiteralStringRef(""), 1, LiteralStringRef("")), - RedwoodRecordRef(LiteralStringRef(""), 0xffffff, LiteralStringRef("")) - ); + RedwoodRecordRef(LiteralStringRef(""), 0xffffff, LiteralStringRef(""))); Arena mem; double start; @@ -5560,9 +5379,9 @@ TEST_CASE("!/redwood/correctness/unit/RedwoodRecordRef") { start = timer(); count = 1000; bytes = 0; - for(i = 0; i < count; ++i) { + for (i = 0; i < count; ++i) { RedwoodRecordRef a = randomRedwoodRecordRef(keyBuffer, valueBuffer); - RedwoodRecordRef b = randomRedwoodRecordRef(keyBuffer, valueBuffer); + RedwoodRecordRef b = randomRedwoodRecordRef(keyBuffer, valueBuffer); bytes += deltaTest(a, b); } double elapsed = timer() - start; @@ -5573,9 +5392,9 @@ TEST_CASE("!/redwood/correctness/unit/RedwoodRecordRef") { start = timer(); count = 1e6; bytes = 0; - for(i = 0; i < count; ++i) { + for (i = 0; i < count; ++i) { RedwoodRecordRef a = randomRedwoodRecordRef(keyBuffer, valueBuffer); - RedwoodRecordRef b = randomRedwoodRecordRef(keyBuffer, valueBuffer); + RedwoodRecordRef b = randomRedwoodRecordRef(keyBuffer, valueBuffer); bytes += deltaTest(a, b); } printf("DeltaTest() on random small records %g M/s %g MB/s\n", count / elapsed / 1e6, bytes / elapsed / 1e6); @@ -5592,7 +5411,7 @@ TEST_CASE("!/redwood/correctness/unit/RedwoodRecordRef") { start = timer(); total = 0; count = 100e6; - for(i = 0; i < count; ++i) { + for (i = 0; i < count; ++i) { total += rec1.getCommonPrefixLen(rec2, 50); } printf("%" PRId64 " getCommonPrefixLen(skip=50) %g M/s\n", total, count / (timer() - start) / 1e6); @@ -5600,20 +5419,20 @@ TEST_CASE("!/redwood/correctness/unit/RedwoodRecordRef") { start = timer(); total = 0; count = 100e6; - for(i = 0; i < count; ++i) { + for (i = 0; i < count; ++i) { total += rec1.getCommonPrefixLen(rec2, 0); } printf("%" PRId64 " getCommonPrefixLen(skip=0) %g M/s\n", total, count / (timer() - start) / 1e6); char buf[1000]; - RedwoodRecordRef::Delta &d = *(RedwoodRecordRef::Delta *)buf; + RedwoodRecordRef::Delta& d = *(RedwoodRecordRef::Delta*)buf; start = timer(); total = 0; count = 100e6; int commonPrefix = rec1.getCommonPrefixLen(rec2, 0); - for(i = 0; i < count; ++i) { + for (i = 0; i < count; ++i) { total += rec1.writeDelta(d, rec2, commonPrefix); } printf("%" PRId64 " writeDelta(commonPrefix=%d) %g M/s\n", total, commonPrefix, count / (timer() - start) / 1e6); @@ -5621,7 +5440,7 @@ TEST_CASE("!/redwood/correctness/unit/RedwoodRecordRef") { start = timer(); total = 0; count = 10e6; - for(i = 0; i < count; ++i) { + for (i = 0; i < count; ++i) { total += rec1.writeDelta(d, rec2); } printf("%" PRId64 " writeDelta() %g M/s\n", total, count / (timer() - start) / 1e6); @@ -5643,16 +5462,18 @@ TEST_CASE("!/redwood/correctness/unit/deltaTree/RedwoodRecordRef") { std::set uniqueItems; // Add random items to uniqueItems until its size is N - while(uniqueItems.size() < N) { + while (uniqueItems.size() < N) { std::string k = deterministicRandom()->randomAlphaNumeric(30); std::string v = deterministicRandom()->randomAlphaNumeric(30); RedwoodRecordRef rec; rec.key = StringRef(arena, k); - rec.version = deterministicRandom()->coinflip() ? deterministicRandom()->randomInt64(0, std::numeric_limits::max()) : invalidVersion; - if(deterministicRandom()->coinflip()) { + rec.version = deterministicRandom()->coinflip() + ? deterministicRandom()->randomInt64(0, std::numeric_limits::max()) + : invalidVersion; + if (deterministicRandom()->coinflip()) { rec.value = StringRef(arena, v); } - if(uniqueItems.count(rec) == 0) { + if (uniqueItems.count(rec) == 0) { uniqueItems.insert(rec); } } @@ -5660,19 +5481,20 @@ TEST_CASE("!/redwood/correctness/unit/deltaTree/RedwoodRecordRef") { int bufferSize = N * 100; bool largeTree = bufferSize > DeltaTree::SmallSizeLimit; - DeltaTree *tree = (DeltaTree *) new uint8_t[bufferSize]; + DeltaTree* tree = (DeltaTree*)new uint8_t[bufferSize]; tree->build(bufferSize, &items[0], &items[items.size()], &prev, &next); - printf("Count=%d Size=%d InitialHeight=%d largeTree=%d\n", (int)items.size(), (int)tree->size(), (int)tree->initialHeight, largeTree); - debug_printf("Data(%p): %s\n", tree, StringRef((uint8_t *)tree, tree->size()).toHexString().c_str()); + printf("Count=%d Size=%d InitialHeight=%d largeTree=%d\n", (int)items.size(), (int)tree->size(), + (int)tree->initialHeight, largeTree); + debug_printf("Data(%p): %s\n", tree, StringRef((uint8_t*)tree, tree->size()).toHexString().c_str()); DeltaTree::Mirror r(tree, &prev, &next); // Test delete/insert behavior for each item, making no net changes printf("Testing seek/delete/insert for existing keys with random values\n"); ASSERT(tree->numItems == items.size()); - for(auto rec : items) { + for (auto rec : items) { // Insert existing should fail ASSERT(!r.insert(rec)); ASSERT(tree->numItems == items.size()); @@ -5706,24 +5528,27 @@ TEST_CASE("!/redwood/correctness/unit/deltaTree/RedwoodRecordRef") { ASSERT(rev.moveLast()); int i = 0; - while(1) { - if(fwd.get() != items[i]) { - printf("forward iterator i=%d\n %s found\n %s expected\n", i, fwd.get().toString().c_str(), items[i].toString().c_str()); + while (1) { + if (fwd.get() != items[i]) { + printf("forward iterator i=%d\n %s found\n %s expected\n", i, fwd.get().toString().c_str(), + items[i].toString().c_str()); printf("Delta: %s\n", fwd.node->raw->delta(largeTree).toString().c_str()); ASSERT(false); } - if(rev.get() != items[items.size() - 1 - i]) { - printf("reverse iterator i=%d\n %s found\n %s expected\n", i, rev.get().toString().c_str(), items[items.size() - 1 - i].toString().c_str()); + if (rev.get() != items[items.size() - 1 - i]) { + printf("reverse iterator i=%d\n %s found\n %s expected\n", i, rev.get().toString().c_str(), + items[items.size() - 1 - i].toString().c_str()); printf("Delta: %s\n", rev.node->raw->delta(largeTree).toString().c_str()); ASSERT(false); } - if(fwdValueOnly.get().value != items[i].value) { - printf("forward values-only iterator i=%d\n %s found\n %s expected\n", i, fwdValueOnly.get().toString().c_str(), items[i].toString().c_str()); + if (fwdValueOnly.get().value != items[i].value) { + printf("forward values-only iterator i=%d\n %s found\n %s expected\n", i, + fwdValueOnly.get().toString().c_str(), items[i].toString().c_str()); printf("Delta: %s\n", fwdValueOnly.node->raw->delta(largeTree).toString().c_str()); ASSERT(false); } ++i; - + bool more = fwd.moveNext(); ASSERT(fwdValueOnly.moveNext() == more); ASSERT(rev.movePrev() == more); @@ -5732,7 +5557,7 @@ TEST_CASE("!/redwood/correctness/unit/deltaTree/RedwoodRecordRef") { ASSERT(fwdValueOnly.valid() == more); ASSERT(rev.valid() == more); - if(!fwd.valid()) { + if (!fwd.valid()) { break; } } @@ -5744,15 +5569,16 @@ TEST_CASE("!/redwood/correctness/unit/deltaTree/RedwoodRecordRef") { printf("Doing 20M random seeks using the same cursor from the same mirror.\n"); double start = timer(); - - for(int i = 0; i < 20000000; ++i) { - const RedwoodRecordRef &query = items[deterministicRandom()->randomInt(0, items.size())]; - if(!c.seekLessThanOrEqual(query)) { + + for (int i = 0; i < 20000000; ++i) { + const RedwoodRecordRef& query = items[deterministicRandom()->randomInt(0, items.size())]; + if (!c.seekLessThanOrEqual(query)) { printf("Not found! query=%s\n", query.toString().c_str()); ASSERT(false); } - if(c.get() != query) { - printf("Found incorrect node! query=%s found=%s\n", query.toString().c_str(), c.get().toString().c_str()); + if (c.get() != query) { + printf("Found incorrect node! query=%s found=%s\n", query.toString().c_str(), + c.get().toString().c_str()); ASSERT(false); } } @@ -5763,22 +5589,23 @@ TEST_CASE("!/redwood/correctness/unit/deltaTree/RedwoodRecordRef") { { printf("Doing 5M random seeks using 10k random cursors, each from a different mirror.\n"); double start = timer(); - std::vector::Mirror *> mirrors; + std::vector::Mirror*> mirrors; std::vector::Cursor> cursors; - for(int i = 0; i < 10000; ++i) { + for (int i = 0; i < 10000; ++i) { mirrors.push_back(new DeltaTree::Mirror(tree, &prev, &next)); cursors.push_back(mirrors.back()->getCursor()); } - for(int i = 0; i < 5000000; ++i) { - const RedwoodRecordRef &query = items[deterministicRandom()->randomInt(0, items.size())]; - DeltaTree::Cursor &c = cursors[deterministicRandom()->randomInt(0, cursors.size())]; - if(!c.seekLessThanOrEqual(query)) { + for (int i = 0; i < 5000000; ++i) { + const RedwoodRecordRef& query = items[deterministicRandom()->randomInt(0, items.size())]; + DeltaTree::Cursor& c = cursors[deterministicRandom()->randomInt(0, cursors.size())]; + if (!c.seekLessThanOrEqual(query)) { printf("Not found! query=%s\n", query.toString().c_str()); ASSERT(false); } - if(c.get() != query) { - printf("Found incorrect node! query=%s found=%s\n", query.toString().c_str(), c.get().toString().c_str()); + if (c.get() != query) { + printf("Found incorrect node! query=%s found=%s\n", query.toString().c_str(), + c.get().toString().c_str()); ASSERT(false); } } @@ -5791,18 +5618,19 @@ TEST_CASE("!/redwood/correctness/unit/deltaTree/RedwoodRecordRef") { TEST_CASE("!/redwood/correctness/unit/deltaTree/IntIntPair") { const int N = 200; - IntIntPair prev = {1, 0}; - IntIntPair next = {10000, 10000}; + IntIntPair prev = { 1, 0 }; + IntIntPair next = { 10000, 10000 }; state std::function randomPair = [&]() { - return IntIntPair({deterministicRandom()->randomInt(prev.k, next.k), deterministicRandom()->randomInt(prev.v, next.v)}); + return IntIntPair( + { deterministicRandom()->randomInt(prev.k, next.k), deterministicRandom()->randomInt(prev.v, next.v) }); }; // Build a set of N unique items std::set uniqueItems; - while(uniqueItems.size() < N) { + while (uniqueItems.size() < N) { IntIntPair p = randomPair(); - if(uniqueItems.count(p) == 0) { + if (uniqueItems.count(p) == 0) { uniqueItems.insert(p); } } @@ -5810,7 +5638,7 @@ TEST_CASE("!/redwood/correctness/unit/deltaTree/IntIntPair") { // Build tree of items std::vector items(uniqueItems.begin(), uniqueItems.end()); int bufferSize = N * 2 * 20; - DeltaTree *tree = (DeltaTree *) new uint8_t[bufferSize]; + DeltaTree* tree = (DeltaTree*)new uint8_t[bufferSize]; int builtSize = tree->build(bufferSize, &items[0], &items[items.size()], &prev, &next); ASSERT(builtSize <= bufferSize); @@ -5818,17 +5646,17 @@ TEST_CASE("!/redwood/correctness/unit/deltaTree/IntIntPair") { // Grow uniqueItems until tree is full, adding half of new items to toDelete std::vector toDelete; - while(1) { + while (1) { IntIntPair p = randomPair(); - if(uniqueItems.count(p) == 0) { - if(!r.insert(p)) { + if (uniqueItems.count(p) == 0) { + if (!r.insert(p)) { break; }; uniqueItems.insert(p); - if(deterministicRandom()->coinflip()) { + if (deterministicRandom()->coinflip()) { toDelete.push_back(p); } - //printf("Inserted %s size=%d\n", items.back().toString().c_str(), tree->size()); + // printf("Inserted %s size=%d\n", items.back().toString().c_str(), tree->size()); } } @@ -5839,13 +5667,14 @@ TEST_CASE("!/redwood/correctness/unit/deltaTree/IntIntPair") { items = std::vector(uniqueItems.begin(), uniqueItems.end()); auto printItems = [&] { - for(int k = 0; k < items.size(); ++k) { + for (int k = 0; k < items.size(); ++k) { printf("%d %s\n", k, items[k].toString().c_str()); } }; - printf("Count=%d Size=%d InitialHeight=%d MaxHeight=%d\n", (int)items.size(), (int)tree->size(), (int)tree->initialHeight, (int)tree->maxHeight); - debug_printf("Data(%p): %s\n", tree, StringRef((uint8_t *)tree, tree->size()).toHexString().c_str()); + printf("Count=%d Size=%d InitialHeight=%d MaxHeight=%d\n", (int)items.size(), (int)tree->size(), + (int)tree->initialHeight, (int)tree->maxHeight); + debug_printf("Data(%p): %s\n", tree, StringRef((uint8_t*)tree, tree->size()).toHexString().c_str()); // Iterate through items and tree forward and backward, verifying tree contents. auto scanAndVerify = [&]() { @@ -5856,15 +5685,17 @@ TEST_CASE("!/redwood/correctness/unit/deltaTree/IntIntPair") { ASSERT(fwd.moveFirst()); ASSERT(rev.moveLast()); - for(int i = 0; i < items.size(); ++i) { - if(fwd.get() != items[i]) { + for (int i = 0; i < items.size(); ++i) { + if (fwd.get() != items[i]) { printItems(); - printf("forward iterator i=%d\n %s found\n %s expected\n", i, fwd.get().toString().c_str(), items[i].toString().c_str()); + printf("forward iterator i=%d\n %s found\n %s expected\n", i, fwd.get().toString().c_str(), + items[i].toString().c_str()); ASSERT(false); } - if(rev.get() != items[items.size() - 1 - i]) { + if (rev.get() != items[items.size() - 1 - i]) { printItems(); - printf("reverse iterator i=%d\n %s found\n %s expected\n", i, rev.get().toString().c_str(), items[items.size() - 1 - i].toString().c_str()); + printf("reverse iterator i=%d\n %s found\n %s expected\n", i, rev.get().toString().c_str(), + items[items.size() - 1 - i].toString().c_str()); ASSERT(false); } @@ -5877,7 +5708,7 @@ TEST_CASE("!/redwood/correctness/unit/deltaTree/IntIntPair") { ASSERT(fwd.valid() == !end); ASSERT(rev.valid() == !end); - if(end) { + if (end) { break; } } @@ -5892,7 +5723,7 @@ TEST_CASE("!/redwood/correctness/unit/deltaTree/IntIntPair") { // For each randomly selected new item to be deleted, delete it from the DeltaTree and from uniqueItems printf("Deleting some items\n"); - for(auto p : toDelete) { + for (auto p : toDelete) { uniqueItems.erase(p); DeltaTree::Cursor c = r.getCursor(); ASSERT(c.seekLessThanOrEqual(p)); @@ -5906,7 +5737,7 @@ TEST_CASE("!/redwood/correctness/unit/deltaTree/IntIntPair") { printf("Verifying insert/erase behavior for existing items\n"); // Test delete/insert behavior for each item, making no net changes - for(auto p : items) { + for (auto p : items) { // Insert existing should fail ASSERT(!r.insert(p)); @@ -5930,80 +5761,85 @@ TEST_CASE("!/redwood/correctness/unit/deltaTree/IntIntPair") { DeltaTree::Cursor s = r.getCursor(); // SeekLTE to each element - for(int i = 0; i < items.size(); ++i) { + for (int i = 0; i < items.size(); ++i) { IntIntPair p = items[i]; IntIntPair q = p; ASSERT(s.seekLessThanOrEqual(q)); - if(s.get() != p) { + if (s.get() != p) { printItems(); - printf("seekLessThanOrEqual(%s) found %s expected %s\n", q.toString().c_str(), s.get().toString().c_str(), p.toString().c_str()); + printf("seekLessThanOrEqual(%s) found %s expected %s\n", q.toString().c_str(), s.get().toString().c_str(), + p.toString().c_str()); ASSERT(false); } } // SeekGTE to each element - for(int i = 0; i < items.size(); ++i) { + for (int i = 0; i < items.size(); ++i) { IntIntPair p = items[i]; IntIntPair q = p; ASSERT(s.seekGreaterThanOrEqual(q)); - if(s.get() != p) { + if (s.get() != p) { printItems(); - printf("seekGreaterThanOrEqual(%s) found %s expected %s\n", q.toString().c_str(), s.get().toString().c_str(), p.toString().c_str()); + printf("seekGreaterThanOrEqual(%s) found %s expected %s\n", q.toString().c_str(), + s.get().toString().c_str(), p.toString().c_str()); ASSERT(false); } } // SeekLTE to the next possible int pair value after each element to make sure the base element is found - for(int i = 0; i < items.size(); ++i) { + for (int i = 0; i < items.size(); ++i) { IntIntPair p = items[i]; IntIntPair q = p; q.v++; ASSERT(s.seekLessThanOrEqual(q)); - if(s.get() != p) { + if (s.get() != p) { printItems(); - printf("seekLessThanOrEqual(%s) found %s expected %s\n", q.toString().c_str(), s.get().toString().c_str(), p.toString().c_str()); + printf("seekLessThanOrEqual(%s) found %s expected %s\n", q.toString().c_str(), s.get().toString().c_str(), + p.toString().c_str()); ASSERT(false); } } // SeekGTE to the previous possible int pair value after each element to make sure the base element is found - for(int i = 0; i < items.size(); ++i) { + for (int i = 0; i < items.size(); ++i) { IntIntPair p = items[i]; IntIntPair q = p; q.v--; ASSERT(s.seekGreaterThanOrEqual(q)); - if(s.get() != p) { + if (s.get() != p) { printItems(); - printf("seekGreaterThanOrEqual(%s) found %s expected %s\n", q.toString().c_str(), s.get().toString().c_str(), p.toString().c_str()); + printf("seekGreaterThanOrEqual(%s) found %s expected %s\n", q.toString().c_str(), + s.get().toString().c_str(), p.toString().c_str()); ASSERT(false); } } // SeekLTE to each element N times, using every element as a hint - for(int i = 0; i < items.size(); ++i) { + for (int i = 0; i < items.size(); ++i) { IntIntPair p = items[i]; IntIntPair q = p; - for(int j = 0; j < items.size(); ++j) { + for (int j = 0; j < items.size(); ++j) { ASSERT(s.seekLessThanOrEqual(items[j])); ASSERT(s.seekLessThanOrEqual(q, 0, &s)); - if(s.get() != p) { + if (s.get() != p) { printItems(); printf("i=%d j=%d\n", i, j); - printf("seekLessThanOrEqual(%s) found %s expected %s\n", q.toString().c_str(), s.get().toString().c_str(), p.toString().c_str()); + printf("seekLessThanOrEqual(%s) found %s expected %s\n", q.toString().c_str(), + s.get().toString().c_str(), p.toString().c_str()); ASSERT(false); } } } // SeekLTE to each element's next possible value, using each element as a hint - for(int i = 0; i < items.size(); ++i) { + for (int i = 0; i < items.size(); ++i) { IntIntPair p = items[i]; IntIntPair q = p; q.v++; - for(int j = 0; j < items.size(); ++j) { + for (int j = 0; j < items.size(); ++j) { ASSERT(s.seekLessThanOrEqual(items[j])); ASSERT(s.seekLessThanOrEqual(q, 0, &s)); - if(s.get() != p) { + if (s.get() != p) { printItems(); printf("i=%d j=%d\n", i, j); ASSERT(false); @@ -6018,36 +5854,34 @@ TEST_CASE("!/redwood/correctness/unit/deltaTree/IntIntPair") { s.moveFirst(); auto first = s; int pos = 0; - for(int c = 0; c < count; ++c) { + for (int c = 0; c < count; ++c) { int jump = deterministicRandom()->randomInt(0, jumpMax); int newPos = pos + jump; - if(newPos >= items.size()) { + if (newPos >= items.size()) { pos = 0; newPos = jump; s = first; } IntIntPair q = items[newPos]; ++q.v; - if(old) { - if(useHint) { + if (old) { + if (useHint) { s.seekLessThanOrEqualOld(q, 0, &s, newPos - pos); - } - else { + } else { s.seekLessThanOrEqualOld(q, 0, nullptr, 0); } - } - else { - if(useHint) { + } else { + if (useHint) { s.seekLessThanOrEqual(q, 0, &s, newPos - pos); - } - else { + } else { s.seekLessThanOrEqual(q); } } pos = newPos; } double elapsed = timer() - start; - printf("Seek/skip test, jumpMax=%d, items=%d, oldSeek=%d useHint=%d: Elapsed %f s\n", jumpMax, items.size(), old, useHint, elapsed); + printf("Seek/skip test, jumpMax=%d, items=%d, oldSeek=%d useHint=%d: Elapsed %f s\n", jumpMax, items.size(), + old, useHint, elapsed); }; // Compare seeking to nearby elements with and without hints, using the old and new SeekLessThanOrEqual methods. @@ -6059,22 +5893,21 @@ TEST_CASE("!/redwood/correctness/unit/deltaTree/IntIntPair") { // Repeatedly seek for one of a set of pregenerated random pairs and time it. std::vector randomPairs; - for(int i = 0; i < 10 * N; ++i) { + for (int i = 0; i < 10 * N; ++i) { randomPairs.push_back(randomPair()); } // Random seeks double start = timer(); - for(int i = 0; i < 20000000; ++i) { + for (int i = 0; i < 20000000; ++i) { IntIntPair p = randomPairs[i % randomPairs.size()]; // Verify the result is less than or equal, and if seek fails then p must be lower than lowest (first) item - if(!s.seekLessThanOrEqual(p)) { - if(p >= items.front()) { + if (!s.seekLessThanOrEqual(p)) { + if (p >= items.front()) { printf("Seek failed! query=%s front=%s\n", p.toString().c_str(), items.front().toString().c_str()); ASSERT(false); } - } - else if(s.get() > p) { + } else if (s.get() > p) { printf("Found incorrect node! query=%s found=%s\n", p.toString().c_str(), s.get().toString().c_str()); ASSERT(false); } @@ -6112,14 +5945,14 @@ TEST_CASE("!/redwood/performance/mutationBuffer") { printf("Generating %d strings...\n", count); Arena arena; std::vector strings; - while(strings.size() < count) { + while (strings.size() < count) { strings.push_back(randomString(arena, 5)); } printf("Inserting and then finding each string...\n", count); double start = timer(); VersionedBTree::MutationBuffer m; - for(int i = 0; i < count; ++i) { + for (int i = 0; i < count; ++i) { KeyRef key = strings[i]; auto a = m.insert(key); auto b = m.lower_bound(key); @@ -6135,12 +5968,13 @@ TEST_CASE("!/redwood/performance/mutationBuffer") { TEST_CASE("!/redwood/correctness/btree") { state std::string pagerFile = "unittest_pageFile.redwood"; - IPager2 *pager; + IPager2* pager; state bool serialTest = deterministicRandom()->coinflip(); state bool shortTest = deterministicRandom()->coinflip(); - state int pageSize = shortTest ? 200 : (deterministicRandom()->coinflip() ? 4096 : deterministicRandom()->randomInt(200, 400)); + state int pageSize = + shortTest ? 200 : (deterministicRandom()->coinflip() ? 4096 : deterministicRandom()->randomInt(200, 400)); // We must be able to fit at least two any two keys plus overhead in a page to prevent // a situation where the tree cannot be grown upward with decreasing level size. @@ -6176,7 +6010,7 @@ TEST_CASE("!/redwood/correctness/btree") { printf("Initializing...\n"); state double startTime = now(); pager = new DWALPager(pageSize, pagerFile, 0); - state VersionedBTree *btree = new VersionedBTree(pager, pagerFile); + state VersionedBTree* btree = new VersionedBTree(pager, pagerFile); wait(btree->init()); state std::map, Optional> written; @@ -6204,66 +6038,66 @@ TEST_CASE("!/redwood/correctness/btree") { state Future commit = Void(); - while(mutationBytes.get() < mutationBytesTarget && (now() - startTime) < maxDuration) { - if(now() - startTime > 600) { + while (mutationBytes.get() < mutationBytesTarget && (now() - startTime) < maxDuration) { + if (now() - startTime > 600) { mutationBytesTarget = mutationBytes.get(); } // Sometimes advance the version - if(deterministicRandom()->random01() < 0.10) { + if (deterministicRandom()->random01() < 0.10) { ++version; btree->setWriteVersion(version); } // Sometimes do a clear range - if(deterministicRandom()->random01() < clearProbability) { + if (deterministicRandom()->random01() < clearProbability) { Key start = randomKV(maxKeySize, 1).key; Key end = (deterministicRandom()->random01() < .01) ? keyAfter(start) : randomKV(maxKeySize, 1).key; // Sometimes replace start and/or end with a close actual (previously used) value - if(deterministicRandom()->random01() < .10) { + if (deterministicRandom()->random01() < .10) { auto i = keys.upper_bound(start); - if(i != keys.end()) - start = *i; + if (i != keys.end()) start = *i; } - if(deterministicRandom()->random01() < .10) { + if (deterministicRandom()->random01() < .10) { auto i = keys.upper_bound(end); - if(i != keys.end()) - end = *i; + if (i != keys.end()) end = *i; } - // Do a single key clear based on probability or end being randomly chosen to be the same as begin (unlikely) - if(deterministicRandom()->random01() < clearSingleKeyProbability || end == start) { + // Do a single key clear based on probability or end being randomly chosen to be the same as begin + // (unlikely) + if (deterministicRandom()->random01() < clearSingleKeyProbability || end == start) { end = keyAfter(start); - } - else if(end < start) { + } else if (end < start) { std::swap(end, start); } // Apply clear range to verification map ++rangeClears; KeyRangeRef range(start, end); - debug_printf(" Mutation: Clear '%s' to '%s' @%" PRId64 "\n", start.toString().c_str(), end.toString().c_str(), version); + debug_printf(" Mutation: Clear '%s' to '%s' @%" PRId64 "\n", start.toString().c_str(), + end.toString().c_str(), version); auto e = written.lower_bound(std::make_pair(start.toString(), 0)); - if(e != written.end()) { + if (e != written.end()) { auto last = e; auto eEnd = written.lower_bound(std::make_pair(end.toString(), 0)); - while(e != eEnd) { + while (e != eEnd) { auto w = *e; ++e; // If e key is different from last and last was present then insert clear for last's key at version - if(last != eEnd && ((e == eEnd || e->first.first != last->first.first) && last->second.present())) { - debug_printf(" Mutation: Clearing key '%s' @%" PRId64 "\n", last->first.first.c_str(), version); + if (last != eEnd && + ((e == eEnd || e->first.first != last->first.first) && last->second.present())) { + debug_printf(" Mutation: Clearing key '%s' @%" PRId64 "\n", last->first.first.c_str(), + version); keyBytesCleared += last->first.first.size(); mutationBytes += last->first.first.size(); mutationBytesThisCommit += last->first.first.size(); // If the last set was at version then just make it not present - if(last->first.second == version) { + if (last->first.second == version) { last->second.reset(); - } - else { + } else { written[std::make_pair(last->first.first, version)].reset(); } } @@ -6274,24 +6108,23 @@ TEST_CASE("!/redwood/correctness/btree") { btree->clear(range); // Sometimes set the range start after the clear - if(deterministicRandom()->random01() < clearPostSetProbability) { + if (deterministicRandom()->random01() < clearPostSetProbability) { KeyValue kv = randomKV(0, maxValueSize); kv.key = range.begin; btree->set(kv); written[std::make_pair(kv.key.toString(), version)] = kv.value.toString(); } - } - else { + } else { // Set a key KeyValue kv = randomKV(maxKeySize, maxValueSize); // Sometimes change key to a close previously used key - if(deterministicRandom()->random01() < .01) { + if (deterministicRandom()->random01() < .01) { auto i = keys.upper_bound(kv.key); - if(i != keys.end()) - kv.key = StringRef(kv.arena(), *i); + if (i != keys.end()) kv.key = StringRef(kv.arena(), *i); } - debug_printf(" Mutation: Set '%s' -> '%s' @%" PRId64 "\n", kv.key.toString().c_str(), kv.value.toString().c_str(), version); + debug_printf(" Mutation: Set '%s' -> '%s' @%" PRId64 "\n", kv.key.toString().c_str(), + kv.value.toString().c_str(), version); ++sets; keyBytesInserted += kv.key.size(); @@ -6305,24 +6138,24 @@ TEST_CASE("!/redwood/correctness/btree") { } // Commit at end or after this commit's mutation bytes are reached - if(mutationBytes.get() >= mutationBytesTarget || mutationBytesThisCommit >= mutationBytesTargetThisCommit) { + if (mutationBytes.get() >= mutationBytesTarget || mutationBytesThisCommit >= mutationBytesTargetThisCommit) { // Wait for previous commit to finish wait(commit); - printf("Committed. Next commit %d bytes, %" PRId64 "/%d (%.2f%%) Stats: Insert %.2f MB/s ClearedKeys %.2f MB/s Total %.2f\n", - mutationBytesThisCommit, - mutationBytes.get(), - mutationBytesTarget, - (double)mutationBytes.get() / mutationBytesTarget * 100, - (keyBytesInserted.rate() + valueBytesInserted.rate()) / 1e6, - keyBytesCleared.rate() / 1e6, - mutationBytes.rate() / 1e6 - ); + printf("Committed. Next commit %d bytes, %" PRId64 + "/%d (%.2f%%) Stats: Insert %.2f MB/s ClearedKeys %.2f MB/s Total %.2f\n", + mutationBytesThisCommit, mutationBytes.get(), mutationBytesTarget, + (double)mutationBytes.get() / mutationBytesTarget * 100, + (keyBytesInserted.rate() + valueBytesInserted.rate()) / 1e6, keyBytesCleared.rate() / 1e6, + mutationBytes.rate() / 1e6); - Version v = version; // Avoid capture of version as a member of *this + Version v = version; // Avoid capture of version as a member of *this - // Sometimes advance the oldest version to close the gap between the oldest and latest versions by a random amount. - if(deterministicRandom()->random01() < advanceOldVersionProbability) { - btree->setOldestVersion(btree->getLastCommittedVersion() - deterministicRandom()->randomInt(0, btree->getLastCommittedVersion() - btree->getOldestVersion() + 1)); + // Sometimes advance the oldest version to close the gap between the oldest and latest versions by a random + // amount. + if (deterministicRandom()->random01() < advanceOldVersionProbability) { + btree->setOldestVersion(btree->getLastCommittedVersion() - + deterministicRandom()->randomInt(0, btree->getLastCommittedVersion() - + btree->getOldestVersion() + 1)); } commit = map(btree->commit(), [=](Void) { @@ -6332,7 +6165,7 @@ TEST_CASE("!/redwood/correctness/btree") { return Void(); }); - if(serialTest) { + if (serialTest) { // Wait for commit, wait for verification, then start new verification wait(commit); committedVersions.sendError(end_of_stream()); @@ -6346,7 +6179,7 @@ TEST_CASE("!/redwood/correctness/btree") { mutationBytesTargetThisCommit = randomSize(maxCommitSize); // Recover from disk at random - if(!serialTest && deterministicRandom()->random01() < coldStartProbability) { + if (!serialTest && deterministicRandom()->random01() < coldStartProbability) { printf("Recovering from disk after next commit.\n"); // Wait for outstanding commit @@ -6364,7 +6197,7 @@ TEST_CASE("!/redwood/correctness/btree") { wait(closedFuture); printf("Reopening btree from disk.\n"); - IPager2 *pager = new DWALPager(pageSize, pagerFile, 0); + IPager2* pager = new DWALPager(pageSize, pagerFile, 0); btree = new VersionedBTree(pager, pagerFile); wait(btree->init()); @@ -6383,8 +6216,7 @@ TEST_CASE("!/redwood/correctness/btree") { } // Check for errors - if(errorCount != 0) - throw internal_error(); + if (errorCount != 0) throw internal_error(); } debug_printf("Waiting for outstanding commit\n"); @@ -6395,8 +6227,7 @@ TEST_CASE("!/redwood/correctness/btree") { wait(verifyTask); // Check for errors - if(errorCount != 0) - throw internal_error(); + if (errorCount != 0) throw internal_error(); wait(btree->destroyAndCheckSanity()); @@ -6408,13 +6239,13 @@ TEST_CASE("!/redwood/correctness/btree") { return Void(); } -ACTOR Future randomSeeks(VersionedBTree *btree, int count, char firstChar, char lastChar) { +ACTOR Future randomSeeks(VersionedBTree* btree, int count, char firstChar, char lastChar) { state Version readVer = btree->getLatestVersion(); state int c = 0; state double readStart = timer(); printf("Executing %d random seeks\n", count); state Reference cur = btree->readAtVersion(readVer); - while(c < count) { + while (c < count) { state Key k = randomString(20, firstChar, lastChar); wait(success(cur->findFirstEqualOrGreater(k))); ++c; @@ -6424,7 +6255,8 @@ ACTOR Future randomSeeks(VersionedBTree *btree, int count, char firstChar, return Void(); } -ACTOR Future randomScans(VersionedBTree *btree, int count, int width, int readAhead, char firstChar, char lastChar) { +ACTOR Future randomScans(VersionedBTree* btree, int count, int width, int readAhead, char firstChar, + char lastChar) { state Version readVer = btree->getLatestVersion(); state int c = 0; state double readStart = timer(); @@ -6432,14 +6264,14 @@ ACTOR Future randomScans(VersionedBTree *btree, int count, int width, int state Reference cur = btree->readAtVersion(readVer); state bool adaptive = readAhead < 0; state int totalScanBytes = 0; - while(c++ < count) { + while (c++ < count) { state Key k = randomString(20, firstChar, lastChar); wait(success(cur->findFirstEqualOrGreater(k, readAhead))); - if(adaptive) { + if (adaptive) { readAhead = totalScanBytes / c; } state int w = width; - while(w > 0 && cur->isValid()) { + while (w > 0 && cur->isValid()) { totalScanBytes += cur->getKey().size(); totalScanBytes += cur->getValue().size(); wait(cur->next()); @@ -6447,7 +6279,8 @@ ACTOR Future randomScans(VersionedBTree *btree, int count, int width, int } } double elapsed = timer() - readStart; - printf("Completed %d scans: readAhead=%d width=%d bytesRead=%d scansRate=%d/s\n", count, readAhead, width, totalScanBytes, int(count / elapsed)); + printf("Completed %d scans: readAhead=%d width=%d bytesRead=%d scansRate=%d/s\n", count, readAhead, width, + totalScanBytes, int(count / elapsed)); return Void(); } @@ -6457,7 +6290,7 @@ TEST_CASE("!/redwood/correctness/pager/cow") { deleteFile(pagerFile); int pageSize = 4096; - state IPager2 *pager = new DWALPager(pageSize, pagerFile, 0); + state IPager2* pager = new DWALPager(pageSize, pagerFile, 0); wait(success(pager->init())); state LogicalPageID id = wait(pager->newPageID()); @@ -6486,15 +6319,15 @@ TEST_CASE("!/redwood/performance/set") { state bool reload = getenv("TESTFILE") == nullptr; state std::string pagerFile = reload ? "unittest.redwood" : getenv("TESTFILE"); - if(reload) { + if (reload) { printf("Deleting old test data\n"); deleteFile(pagerFile); } state int pageSize = 4096; state int64_t pageCacheBytes = FLOW_KNOBS->PAGE_CACHE_4K; - DWALPager *pager = new DWALPager(pageSize, pagerFile, pageCacheBytes); - state VersionedBTree *btree = new VersionedBTree(pager, pagerFile); + DWALPager* pager = new DWALPager(pageSize, pagerFile, pageCacheBytes); + state VersionedBTree* btree = new VersionedBTree(pager, pagerFile); wait(btree->init()); state int nodeCount = 1e9; @@ -6534,8 +6367,8 @@ TEST_CASE("!/redwood/performance/set") { state double intervalStart = timer(); state double start = intervalStart; - if(reload) { - while(kvBytesTotal < kvBytesTarget) { + if (reload) { + while (kvBytesTotal < kvBytesTarget) { wait(yield()); Version lastVer = btree->getLatestVersion(); @@ -6543,15 +6376,19 @@ TEST_CASE("!/redwood/performance/set") { btree->setWriteVersion(version); int changes = deterministicRandom()->randomInt(0, maxChangesPerVersion); - while(changes > 0 && kvBytes < commitTarget) { + while (changes > 0 && kvBytes < commitTarget) { KeyValue kv; - kv.key = randomString(kv.arena(), deterministicRandom()->randomInt(minKeyPrefixBytes + sizeof(uint32_t), maxKeyPrefixBytes + sizeof(uint32_t) + 1), firstKeyChar, lastKeyChar); + kv.key = randomString(kv.arena(), + deterministicRandom()->randomInt(minKeyPrefixBytes + sizeof(uint32_t), + maxKeyPrefixBytes + sizeof(uint32_t) + 1), + firstKeyChar, lastKeyChar); int32_t index = deterministicRandom()->randomInt(0, nodeCount); int runLength = deterministicRandom()->randomInt(minConsecutiveRun, maxConsecutiveRun + 1); - while(runLength > 0 && changes > 0) { - *(uint32_t *)(kv.key.end() - sizeof(uint32_t)) = bigEndian32(index++); - kv.value = StringRef((uint8_t *)value.data(), deterministicRandom()->randomInt(minValueSize, maxValueSize + 1)); + while (runLength > 0 && changes > 0) { + *(uint32_t*)(kv.key.end() - sizeof(uint32_t)) = bigEndian32(index++); + kv.value = StringRef((uint8_t*)value.data(), + deterministicRandom()->randomInt(minValueSize, maxValueSize + 1)); btree->set(kv); @@ -6562,22 +6399,25 @@ TEST_CASE("!/redwood/performance/set") { } } - if(kvBytes >= commitTarget) { + if (kvBytes >= commitTarget) { btree->setOldestVersion(btree->getLastCommittedVersion()); wait(commit); - printf("Cumulative %.2f MB keyValue bytes written at %.2f MB/s\n", kvBytesTotal / 1e6, kvBytesTotal / (timer() - start) / 1e6); + printf("Cumulative %.2f MB keyValue bytes written at %.2f MB/s\n", kvBytesTotal / 1e6, + kvBytesTotal / (timer() - start) / 1e6); // Avoid capturing via this to freeze counter values int recs = records; int kvb = kvBytes; - // Capturing invervalStart via this->intervalStart makes IDE's unhappy as they do not know about the actor state object - double *pIntervalStart = &intervalStart; + // Capturing invervalStart via this->intervalStart makes IDE's unhappy as they do not know about the + // actor state object + double* pIntervalStart = &intervalStart; commit = map(btree->commit(), [=](Void result) { printf("Committed: %s\n", VersionedBTree::counts.toString(true).c_str()); double elapsed = timer() - *pIntervalStart; - printf("Committed %d kvBytes in %d records in %f seconds, %.2f MB/s\n", kvb, recs, elapsed, kvb / elapsed / 1e6); + printf("Committed %d kvBytes in %d records in %f seconds, %.2f MB/s\n", kvb, recs, elapsed, + kvb / elapsed / 1e6); *pIntervalStart = timer(); return Void(); }); @@ -6589,14 +6429,15 @@ TEST_CASE("!/redwood/performance/set") { } wait(commit); - printf("Cumulative %.2f MB keyValue bytes written at %.2f MB/s\n", kvBytesTotal / 1e6, kvBytesTotal / (timer() - start) / 1e6); + printf("Cumulative %.2f MB keyValue bytes written at %.2f MB/s\n", kvBytesTotal / 1e6, + kvBytesTotal / (timer() - start) / 1e6); } int seeks = 1e6; printf("Warming cache with seeks\n"); - actors.add(randomSeeks(btree, seeks/3, firstKeyChar, lastKeyChar)); - actors.add(randomSeeks(btree, seeks/3, firstKeyChar, lastKeyChar)); - actors.add(randomSeeks(btree, seeks/3, firstKeyChar, lastKeyChar)); + actors.add(randomSeeks(btree, seeks / 3, firstKeyChar, lastKeyChar)); + actors.add(randomSeeks(btree, seeks / 3, firstKeyChar, lastKeyChar)); + actors.add(randomSeeks(btree, seeks / 3, firstKeyChar, lastKeyChar)); wait(actors.signalAndReset()); printf("Stats: %s\n", VersionedBTree::counts.toString(true).c_str()); @@ -6650,9 +6491,7 @@ struct PrefixSegment { int length; int cardinality; - std::string toString() const { - return format("{%d bytes, %d choices}", length, cardinality); - } + std::string toString() const { return format("{%d bytes, %d choices}", length, cardinality); } }; // Utility class for generating kv pairs under a prefix pattern @@ -6666,42 +6505,42 @@ struct KVSource { std::vector desc; std::vector> segments; std::vector prefixes; - std::vector prefixesSorted; + std::vector prefixesSorted; std::string valueData; int prefixLen; int lastIndex; - KVSource(const std::vector &desc, int numPrefixes = 0) : desc(desc) { - if(numPrefixes == 0) { + KVSource(const std::vector& desc, int numPrefixes = 0) : desc(desc) { + if (numPrefixes == 0) { numPrefixes = 1; - for(auto &p : desc) { + for (auto& p : desc) { numPrefixes *= p.cardinality; } } prefixLen = 0; - for(auto &s : desc) { + for (auto& s : desc) { prefixLen += s.length; std::vector parts; - while(parts.size() < s.cardinality) { + while (parts.size() < s.cardinality) { parts.push_back(deterministicRandom()->randomAlphaNumeric(s.length)); } segments.push_back(std::move(parts)); } - while(prefixes.size() < numPrefixes) { + while (prefixes.size() < numPrefixes) { std::string p; - for(auto &s : segments) { + for (auto& s : segments) { p.append(s[deterministicRandom()->randomInt(0, s.size())]); } - prefixes.push_back(PrefixRef((uint8_t *)p.data(), p.size())); + prefixes.push_back(PrefixRef((uint8_t*)p.data(), p.size())); } - for(auto &p : prefixes) { + for (auto& p : prefixes) { prefixesSorted.push_back(&p); } - std::sort(prefixesSorted.begin(), prefixesSorted.end(), [](const Prefix *a, const Prefix *b) { - return KeyRef((uint8_t *)a->begin(), a->size()) < KeyRef((uint8_t *)b->begin(), b->size()); + std::sort(prefixesSorted.begin(), prefixesSorted.end(), [](const Prefix* a, const Prefix* b) { + return KeyRef((uint8_t*)a->begin(), a->size()) < KeyRef((uint8_t*)b->begin(), b->size()); }); valueData = deterministicRandom()->randomAlphaNumeric(100000); @@ -6710,13 +6549,11 @@ struct KVSource { // Expands the chosen prefix in the prefix list to hold suffix, // fills suffix with random bytes, and returns a reference to the string - KeyRef getKeyRef(int suffixLen) { - return makeKey(randomPrefix(), suffixLen); - } + KeyRef getKeyRef(int suffixLen) { return makeKey(randomPrefix(), suffixLen); } // Like getKeyRef but uses the same prefix as the last randomly chosen prefix KeyRef getAnotherKeyRef(int suffixLen, bool sorted = false) { - Prefix &p = sorted ? *prefixesSorted[lastIndex] : prefixes[lastIndex]; + Prefix& p = sorted ? *prefixesSorted[lastIndex] : prefixes[lastIndex]; return makeKey(p, suffixLen); } @@ -6724,51 +6561,48 @@ struct KVSource { KeyRangeRef getRangeRef(int prefixesCovered, int suffixLen) { prefixesCovered = std::min(prefixesCovered, prefixes.size()); int i = deterministicRandom()->randomInt(0, prefixesSorted.size() - prefixesCovered); - Prefix *begin = prefixesSorted[i]; - Prefix *end = prefixesSorted[i + prefixesCovered]; + Prefix* begin = prefixesSorted[i]; + Prefix* end = prefixesSorted[i + prefixesCovered]; return KeyRangeRef(makeKey(*begin, suffixLen), makeKey(*end, suffixLen)); } - KeyRef getValue(int len) { - return KeyRef(valueData).substr(0, len); - } + KeyRef getValue(int len) { return KeyRef(valueData).substr(0, len); } // Move lastIndex to the next position, wrapping around to 0 void nextPrefix() { ++lastIndex; - if(lastIndex == prefixes.size()) { + if (lastIndex == prefixes.size()) { lastIndex = 0; } } - Prefix & randomPrefix() { + Prefix& randomPrefix() { lastIndex = deterministicRandom()->randomInt(0, prefixes.size()); return prefixes[lastIndex]; } - static KeyRef makeKey(Prefix &p, int suffixLen) { + static KeyRef makeKey(Prefix& p, int suffixLen) { p.reserve(p.arena(), p.size() + suffixLen); - uint8_t *wptr = p.end(); - for(int i = 0; i < suffixLen; ++i) { + uint8_t* wptr = p.end(); + for (int i = 0; i < suffixLen; ++i) { *wptr++ = (uint8_t)deterministicRandom()->randomAlphaNumeric(); } return KeyRef(p.begin(), p.size() + suffixLen); } - int numPrefixes() const { - return prefixes.size(); - }; + int numPrefixes() const { return prefixes.size(); }; std::string toString() const { return format("{prefixLen=%d prefixes=%d format=%s}", prefixLen, numPrefixes(), ::toString(desc).c_str()); } }; -std::string toString(const StorageBytes &sb) { - return format("{%.2f MB total, %.2f MB free, %.2f MB available, %.2f MB used}", sb.total / 1e6, sb.free / 1e6, sb.available / 1e6, sb.used / 1e6); +std::string toString(const StorageBytes& sb) { + return format("{%.2f MB total, %.2f MB free, %.2f MB available, %.2f MB used}", sb.total / 1e6, sb.free / 1e6, + sb.available / 1e6, sb.used / 1e6); } -ACTOR Future getStableStorageBytes(IKeyValueStore *kvs) { +ACTOR Future getStableStorageBytes(IKeyValueStore* kvs) { state StorageBytes sb = kvs->getStorageBytes(); // Wait for StorageBytes used metric to stabilize @@ -6777,7 +6611,7 @@ ACTOR Future getStableStorageBytes(IKeyValueStore *kvs) { StorageBytes sb2 = kvs->getStorageBytes(); bool stable = sb2.used == sb.used; sb = sb2; - if(stable) { + if (stable) { break; } } @@ -6785,7 +6619,8 @@ ACTOR Future getStableStorageBytes(IKeyValueStore *kvs) { return sb; } -ACTOR Future prefixClusteredInsert(IKeyValueStore *kvs, int suffixSize, int valueSize, KVSource source, int recordCountTarget, bool usePrefixesInOrder) { +ACTOR Future prefixClusteredInsert(IKeyValueStore* kvs, int suffixSize, int valueSize, KVSource source, + int recordCountTarget, bool usePrefixesInOrder) { state int commitTarget = 5e6; state int recordSize = source.prefixLen + suffixSize + valueSize; @@ -6816,26 +6651,27 @@ ACTOR Future prefixClusteredInsert(IKeyValueStore *kvs, int suffixSize, in state std::function stats = [&]() { double elapsed = timer() - start; - printf("Cumulative stats: %.2f seconds %.2f MB keyValue bytes %d records %.2f MB/s %.2f rec/s\r", elapsed, kvBytesTotal / 1e6, records, kvBytesTotal / elapsed / 1e6, records / elapsed); + printf("Cumulative stats: %.2f seconds %.2f MB keyValue bytes %d records %.2f MB/s %.2f rec/s\r", elapsed, + kvBytesTotal / 1e6, records, kvBytesTotal / elapsed / 1e6, records / elapsed); fflush(stdout); }; - while(kvBytesTotal < kvBytesTarget) { + while (kvBytesTotal < kvBytesTarget) { wait(yield()); state int i; - for(i = 0; i < recordsPerPrefix; ++i) { + for (i = 0; i < recordsPerPrefix; ++i) { KeyValueRef kv(source.getAnotherKeyRef(4, usePrefixesInOrder), source.getValue(valueSize)); kvs->set(kv); kvBytes += kv.expectedSize(); ++records; - if(kvBytes >= commitTarget) { + if (kvBytes >= commitTarget) { wait(commit); stats(); commit = kvs->commit(); kvBytesTotal += kvBytes; - if(kvBytesTotal >= kvBytesTarget) { + if (kvBytesTotal >= kvBytesTarget) { break; } kvBytes = 0; @@ -6858,15 +6694,16 @@ ACTOR Future prefixClusteredInsert(IKeyValueStore *kvs, int suffixSize, in intervalStart = timer(); kvs->clear(KeyRangeRef(LiteralStringRef(""), LiteralStringRef("\xff"))); state StorageBytes sbClear = wait(getStableStorageBytes(kvs)); - printf("Cleared all keys in %.2f seconds, final storageByte: %s\n", timer() - intervalStart, toString(sbClear).c_str()); + printf("Cleared all keys in %.2f seconds, final storageByte: %s\n", timer() - intervalStart, + toString(sbClear).c_str()); return Void(); } -ACTOR Future sequentialInsert(IKeyValueStore *kvs, int prefixLen, int valueSize, int recordCountTarget) { +ACTOR Future sequentialInsert(IKeyValueStore* kvs, int prefixLen, int valueSize, int recordCountTarget) { state int commitTarget = 5e6; - state KVSource source({{prefixLen, 1}}); + state KVSource source({ { prefixLen, 1 } }); state int recordSize = source.prefixLen + sizeof(uint64_t) + valueSize; state int64_t kvBytesTarget = (int64_t)recordCountTarget * recordSize; @@ -6890,27 +6727,28 @@ ACTOR Future sequentialInsert(IKeyValueStore *kvs, int prefixLen, int valu state std::function stats = [&]() { double elapsed = timer() - start; - printf("Cumulative stats: %.2f seconds %.2f MB keyValue bytes %d records %.2f MB/s %.2f rec/s\r", elapsed, kvBytesTotal / 1e6, records, kvBytesTotal / elapsed / 1e6, records / elapsed); + printf("Cumulative stats: %.2f seconds %.2f MB keyValue bytes %d records %.2f MB/s %.2f rec/s\r", elapsed, + kvBytesTotal / 1e6, records, kvBytesTotal / elapsed / 1e6, records / elapsed); fflush(stdout); }; state uint64_t c = 0; state Key key = source.getKeyRef(sizeof(uint64_t)); - while(kvBytesTotal < kvBytesTarget) { + while (kvBytesTotal < kvBytesTarget) { wait(yield()); - *(uint64_t *)(key.end() - sizeof(uint64_t)) = bigEndian64(c); + *(uint64_t*)(key.end() - sizeof(uint64_t)) = bigEndian64(c); KeyValueRef kv(key, source.getValue(valueSize)); kvs->set(kv); kvBytes += kv.expectedSize(); ++records; - if(kvBytes >= commitTarget) { + if (kvBytes >= commitTarget) { wait(commit); stats(); commit = kvs->commit(); kvBytesTotal += kvBytes; - if(kvBytesTotal >= kvBytesTarget) { + if (kvBytesTotal >= kvBytesTarget) { break; } kvBytes = 0; @@ -6925,18 +6763,19 @@ ACTOR Future sequentialInsert(IKeyValueStore *kvs, int prefixLen, int valu return Void(); } -Future closeKVS(IKeyValueStore *kvs) { +Future closeKVS(IKeyValueStore* kvs) { Future closed = kvs->onClosed(); kvs->close(); return closed; } -ACTOR Future doPrefixInsertComparison(int suffixSize, int valueSize, int recordCountTarget, bool usePrefixesInOrder, KVSource source) { +ACTOR Future doPrefixInsertComparison(int suffixSize, int valueSize, int recordCountTarget, + bool usePrefixesInOrder, KVSource source) { VersionedBTree::counts.clear(); deleteFile("test.redwood"); wait(delay(5)); - state IKeyValueStore *redwood = openKVStore(KeyValueStoreType::SSD_REDWOOD_V1, "test.redwood", UID(), 0); + state IKeyValueStore* redwood = openKVStore(KeyValueStoreType::SSD_REDWOOD_V1, "test.redwood", UID(), 0); wait(prefixClusteredInsert(redwood, suffixSize, valueSize, source, recordCountTarget, usePrefixesInOrder)); wait(closeKVS(redwood)); printf("\n"); @@ -6944,7 +6783,7 @@ ACTOR Future doPrefixInsertComparison(int suffixSize, int valueSize, int r deleteFile("test.sqlite"); deleteFile("test.sqlite-wal"); wait(delay(5)); - state IKeyValueStore *sqlite = openKVStore(KeyValueStoreType::SSD_BTREE_V2, "test.sqlite", UID(), 0); + state IKeyValueStore* sqlite = openKVStore(KeyValueStoreType::SSD_BTREE_V2, "test.sqlite", UID(), 0); wait(prefixClusteredInsert(sqlite, suffixSize, valueSize, source, recordCountTarget, usePrefixesInOrder)); wait(closeKVS(sqlite)); printf("\n"); @@ -6958,10 +6797,14 @@ TEST_CASE("!/redwood/performance/prefixSizeComparison") { state int recordCountTarget = 100e6; state int usePrefixesInOrder = false; - wait(doPrefixInsertComparison(suffixSize, valueSize, recordCountTarget, usePrefixesInOrder, KVSource({{10, 100000}}))); - wait(doPrefixInsertComparison(suffixSize, valueSize, recordCountTarget, usePrefixesInOrder, KVSource({{16, 100000}}))); - wait(doPrefixInsertComparison(suffixSize, valueSize, recordCountTarget, usePrefixesInOrder, KVSource({{32, 100000}}))); - wait(doPrefixInsertComparison(suffixSize, valueSize, recordCountTarget, usePrefixesInOrder, KVSource({{4, 5}, {12, 1000}, {8, 5}, {8, 4}}))); + wait(doPrefixInsertComparison(suffixSize, valueSize, recordCountTarget, usePrefixesInOrder, + KVSource({ { 10, 100000 } }))); + wait(doPrefixInsertComparison(suffixSize, valueSize, recordCountTarget, usePrefixesInOrder, + KVSource({ { 16, 100000 } }))); + wait(doPrefixInsertComparison(suffixSize, valueSize, recordCountTarget, usePrefixesInOrder, + KVSource({ { 32, 100000 } }))); + wait(doPrefixInsertComparison(suffixSize, valueSize, recordCountTarget, usePrefixesInOrder, + KVSource({ { 4, 5 }, { 12, 1000 }, { 8, 5 }, { 8, 4 } }))); return Void(); } @@ -6973,11 +6816,10 @@ TEST_CASE("!/redwood/performance/sequentialInsert") { deleteFile("test.redwood"); wait(delay(5)); - state IKeyValueStore *redwood = openKVStore(KeyValueStoreType::SSD_REDWOOD_V1, "test.redwood", UID(), 0); + state IKeyValueStore* redwood = openKVStore(KeyValueStoreType::SSD_REDWOOD_V1, "test.redwood", UID(), 0); wait(sequentialInsert(redwood, prefixLen, valueSize, recordCountTarget)); wait(closeKVS(redwood)); printf("\n"); return Void(); } - From d654f75c46556e458b983d94bfbc861037d4fe73 Mon Sep 17 00:00:00 2001 From: tclinken Date: Sat, 25 Apr 2020 12:24:06 -0700 Subject: [PATCH 1540/1604] Move instead of copy in VectorRef::reallocate --- flow/Arena.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/Arena.h b/flow/Arena.h index 0344929b0e..2dbec1fdc3 100644 --- a/flow/Arena.h +++ b/flow/Arena.h @@ -964,7 +964,7 @@ private: // SOMEDAY: Maybe we are right at the end of the arena and can expand cheaply T* newData = (T*)new (p) uint8_t[requiredCapacity * sizeof(T)]; if (m_size > 0) { - std::copy(data, data + m_size, newData); + std::move(data, data + m_size, newData); } data = newData; m_capacity = requiredCapacity; From fee9a5117ff4d284ccd2862c0c7612f1a2f6ddca Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Sat, 25 Apr 2020 17:59:17 -0700 Subject: [PATCH 1541/1604] Don't config TLS if OpenSSL is missing OPENSSL_INIT_NO_ATEXIT --- FDBLibTLS/CMakeLists.txt | 2 +- cmake/FDBComponents.cmake | 29 ++++++++++++++++------------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/FDBLibTLS/CMakeLists.txt b/FDBLibTLS/CMakeLists.txt index cd22748648..62ea4d5cad 100644 --- a/FDBLibTLS/CMakeLists.txt +++ b/FDBLibTLS/CMakeLists.txt @@ -9,4 +9,4 @@ set(SRCS FDBLibTLSVerify.h) add_library(FDBLibTLS STATIC ${SRCS}) -target_link_libraries(FDBLibTLS PUBLIC LibreSSL boost_target PRIVATE flow) +target_link_libraries(FDBLibTLS PUBLIC OpenSSL::SSL boost_target PRIVATE flow) diff --git a/cmake/FDBComponents.cmake b/cmake/FDBComponents.cmake index 6ada101d39..817d173f4f 100644 --- a/cmake/FDBComponents.cmake +++ b/cmake/FDBComponents.cmake @@ -12,25 +12,28 @@ endif() # SSL ################################################################################ -set(DISABLE_TLS OFF CACHE BOOL "Don't try to find LibreSSL and always build without TLS support") +set(DISABLE_TLS OFF CACHE BOOL "Don't try to find OpenSSL and always build without TLS support") if(DISABLE_TLS) set(WITH_TLS OFF) else() set(OPENSSL_USE_STATIC_LIBS TRUE) find_package(OpenSSL) - if(NOT OPENSSL_FOUND) - set(LIBRESSL_USE_STATIC_LIBS TRUE) - find_package(LibreSSL) - if (LIBRESSL_FOUND) - add_library(OpenSSL::SSL ALIAS LibreSSL) - endif() - endif() - if(OPENSSL_FOUND OR LIBRESSL_FOUND) - set(WITH_TLS ON) - add_compile_options(-DHAVE_OPENSSL) + if(OPENSSL_FOUND) + set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) + CHECK_CXX_SOURCE_COMPILES( + "#include + int main() { (void) OPENSSL_INIT_NO_ATEXIT; }" OPENSSL_HAS_NO_ATEXIT) + if(OPENSSL_HAS_NO_ATEXIT) + set(WITH_TLS ON) + add_compile_options(-DHAVE_OPENSSL) + else() + message(STATUS "An OpenSSL version was found, but it doesn't support OPENSSL_INIT_NO_ATEXIT - Will compile without TLS Support") + message(STATUS "You can set OPENSSL_ROOT_DIR to help cmake find it") + set(WITH_TLS OFF) + endif() else() - message(STATUS "Neither OpenSSL nor LibreSSL were found - Will compile without TLS Support") - message(STATUS "You can set OPENSSL_ROOT_DIR or LibreSSL_ROOT to the LibreSSL install directory to help cmake find it") + message(STATUS "OpenSSL was not found - Will compile without TLS Support") + message(STATUS "You can set OPENSSL_ROOT_DIR to help cmake find it") set(WITH_TLS OFF) endif() if(WIN32) From 0a8474e995f5f700d634f20741b5030ccb7fa6c2 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Sun, 26 Apr 2020 20:52:10 +0000 Subject: [PATCH 1542/1604] Install /var/lib/foundationdb/data This is what the old buildrpms.sh and builddebs.sh seemed to do --- cmake/InstallLayout.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/InstallLayout.cmake b/cmake/InstallLayout.cmake index b1fb33c68e..76a9889dc9 100644 --- a/cmake/InstallLayout.cmake +++ b/cmake/InstallLayout.cmake @@ -131,9 +131,9 @@ set(install_destination_for_log_el6 "var/log/foundationdb") set(install_destination_for_log_el7 "var/log/foundationdb") set(install_destination_for_log_pm "") set(install_destination_for_data_tgz "lib/foundationdb") -set(install_destination_for_data_deb "var/lib/foundationdb") -set(install_destination_for_data_el6 "var/lib/foundationdb") -set(install_destination_for_data_el7 "var/lib/foundationdb") +set(install_destination_for_data_deb "var/lib/foundationdb/data") +set(install_destination_for_data_el6 "var/lib/foundationdb/data") +set(install_destination_for_data_el7 "var/lib/foundationdb/data") set(install_destination_for_data_pm "") set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated") From 110edc745361454988f0488e0484b2d986a0945a Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Sun, 26 Apr 2020 20:53:08 +0000 Subject: [PATCH 1543/1604] Fix build on gcc9 --- fdbserver/MasterProxyServer.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/MasterProxyServer.actor.cpp b/fdbserver/MasterProxyServer.actor.cpp index af9823a1fe..16be0e352a 100644 --- a/fdbserver/MasterProxyServer.actor.cpp +++ b/fdbserver/MasterProxyServer.actor.cpp @@ -157,7 +157,7 @@ struct TransactionRateInfo { } void setRate(double rate) { - ASSERT(rate >= 0 && rate != std::numeric_limits::infinity() && !isnan(rate)); + ASSERT(rate >= 0 && rate != std::numeric_limits::infinity() && !std::isnan(rate)); this->rate = rate; if(disabled) { From 16f3a2480b46bd9aca07682d6612fc575aa6ed81 Mon Sep 17 00:00:00 2001 From: Pieter Joost Date: Fri, 6 Dec 2019 22:24:58 +0100 Subject: [PATCH 1544/1604] implement fmt.Stringer interface on Tuple --- bindings/go/src/fdb/tuple/tuple.go | 6 ++++++ bindings/go/src/fdb/tuple/tuple_test.go | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/bindings/go/src/fdb/tuple/tuple.go b/bindings/go/src/fdb/tuple/tuple.go index a37ce5f3e8..6101a49fdf 100644 --- a/bindings/go/src/fdb/tuple/tuple.go +++ b/bindings/go/src/fdb/tuple/tuple.go @@ -66,6 +66,12 @@ type TupleElement interface{} // packing T (modulo type normalization to []byte, uint64, and int64). type Tuple []TupleElement +// String implements the fmt.Stringer interface and return the tuple +// as a human readable byte string provided by fdb.Printable. +func (t Tuple) String() string { + return fdb.Printable(t.Pack()) +} + // UUID wraps a basic byte array as a UUID. We do not provide any special // methods for accessing or generating the UUID, but as Go does not provide // a built-in UUID type, this simple wrapper allows for other libraries diff --git a/bindings/go/src/fdb/tuple/tuple_test.go b/bindings/go/src/fdb/tuple/tuple_test.go index 59c37bc7bc..e4dfbe71dc 100644 --- a/bindings/go/src/fdb/tuple/tuple_test.go +++ b/bindings/go/src/fdb/tuple/tuple_test.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/gob" "flag" + "fmt" "math/rand" "os" "testing" @@ -118,3 +119,12 @@ func BenchmarkTuplePacking(b *testing.B) { }) } } + +func TestTupleString(t *testing.T) { + printed := fmt.Sprint(Tuple{[]byte("hello"), "world", 42, 0x99}) + expected := "\\x01hello\\x00\\x02world\\x00\\x15*\\x15\\x99" + + if printed != expected { + t.Fatalf("printed tuple result differs, expected %v, got %v", expected, printed) + } +} From 2a618231061ab4052940313e2cc54916677a6bfb Mon Sep 17 00:00:00 2001 From: Pieter Joost Date: Fri, 6 Dec 2019 22:25:32 +0100 Subject: [PATCH 1545/1604] iimplement fmt.Stringer interface on Subspace --- bindings/go/src/fdb/subspace/subspace.go | 10 ++++++++++ bindings/go/src/fdb/subspace/subspace_test.go | 15 +++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 bindings/go/src/fdb/subspace/subspace_test.go diff --git a/bindings/go/src/fdb/subspace/subspace.go b/bindings/go/src/fdb/subspace/subspace.go index 353d377e42..b621d058c7 100644 --- a/bindings/go/src/fdb/subspace/subspace.go +++ b/bindings/go/src/fdb/subspace/subspace.go @@ -35,6 +35,8 @@ package subspace import ( "bytes" "errors" + "fmt" + "github.com/apple/foundationdb/bindings/go/src/fdb" "github.com/apple/foundationdb/bindings/go/src/fdb/tuple" ) @@ -42,6 +44,8 @@ import ( // Subspace represents a well-defined region of keyspace in a FoundationDB // database. type Subspace interface { + fmt.Stringer + // Sub returns a new Subspace whose prefix extends this Subspace with the // encoding of the provided element(s). If any of the elements are not a // valid tuple.TupleElement, Sub will panic. @@ -105,6 +109,12 @@ func FromBytes(b []byte) Subspace { return subspace{s} } +// String implements the fmt.Stringer interface and return the subspace +// as a human readable byte string provided by fdb.Printable. +func (s subspace) String() string { + return fdb.Printable(s.b) +} + func (s subspace) Sub(el ...tuple.TupleElement) Subspace { return subspace{concat(s.Bytes(), tuple.Tuple(el).Pack()...)} } diff --git a/bindings/go/src/fdb/subspace/subspace_test.go b/bindings/go/src/fdb/subspace/subspace_test.go new file mode 100644 index 0000000000..33a4164697 --- /dev/null +++ b/bindings/go/src/fdb/subspace/subspace_test.go @@ -0,0 +1,15 @@ +package subspace + +import ( + "fmt" + "testing" +) + +func TestSubspaceString(t *testing.T) { + printed := fmt.Sprint(Sub([]byte("hello"), "world", 42, 0x99)) + expected := "\\x01hello\\x00\\x02world\\x00\\x15*\\x15\\x99" + + if printed != expected { + t.Fatalf("printed subspace result differs, expected %v, got %v", expected, printed) + } +} From 642840a3f4aee453d14588b7ccea63338e7084c8 Mon Sep 17 00:00:00 2001 From: Vishesh Yadav Date: Sun, 26 Apr 2020 17:45:43 -0700 Subject: [PATCH 1546/1604] go: Update tuple/subspace String() to output readable strings --- bindings/go/src/fdb/subspace/subspace.go | 2 +- bindings/go/src/fdb/subspace/subspace_test.go | 2 +- bindings/go/src/fdb/tuple/tuple.go | 51 +++++++++++++++++-- bindings/go/src/fdb/tuple/tuple_test.go | 31 +++++++++-- 4 files changed, 76 insertions(+), 10 deletions(-) diff --git a/bindings/go/src/fdb/subspace/subspace.go b/bindings/go/src/fdb/subspace/subspace.go index b621d058c7..0ac6e6da62 100644 --- a/bindings/go/src/fdb/subspace/subspace.go +++ b/bindings/go/src/fdb/subspace/subspace.go @@ -112,7 +112,7 @@ func FromBytes(b []byte) Subspace { // String implements the fmt.Stringer interface and return the subspace // as a human readable byte string provided by fdb.Printable. func (s subspace) String() string { - return fdb.Printable(s.b) + return fmt.Sprintf("Subspace(rawPrefix=%s)", fdb.Printable(s.b)) } func (s subspace) Sub(el ...tuple.TupleElement) Subspace { diff --git a/bindings/go/src/fdb/subspace/subspace_test.go b/bindings/go/src/fdb/subspace/subspace_test.go index 33a4164697..abc713a2fc 100644 --- a/bindings/go/src/fdb/subspace/subspace_test.go +++ b/bindings/go/src/fdb/subspace/subspace_test.go @@ -7,7 +7,7 @@ import ( func TestSubspaceString(t *testing.T) { printed := fmt.Sprint(Sub([]byte("hello"), "world", 42, 0x99)) - expected := "\\x01hello\\x00\\x02world\\x00\\x15*\\x15\\x99" + expected := "Subspace(rawPrefix=\\x01hello\\x00\\x02world\\x00\\x15*\\x15\\x99)" if printed != expected { t.Fatalf("printed subspace result differs, expected %v, got %v", expected, printed) diff --git a/bindings/go/src/fdb/tuple/tuple.go b/bindings/go/src/fdb/tuple/tuple.go index 6101a49fdf..714111d161 100644 --- a/bindings/go/src/fdb/tuple/tuple.go +++ b/bindings/go/src/fdb/tuple/tuple.go @@ -43,6 +43,8 @@ import ( "fmt" "math" "math/big" + "strconv" + "strings" "github.com/apple/foundationdb/bindings/go/src/fdb" ) @@ -66,10 +68,47 @@ type TupleElement interface{} // packing T (modulo type normalization to []byte, uint64, and int64). type Tuple []TupleElement -// String implements the fmt.Stringer interface and return the tuple -// as a human readable byte string provided by fdb.Printable. -func (t Tuple) String() string { - return fdb.Printable(t.Pack()) +// String implements the fmt.Stringer interface and returns human-readable +// string representation of this tuple. For most elements, we use the +// object's default string representation. +func (tuple Tuple) String() string { + sb := strings.Builder{} + printTuple(tuple, &sb) + return sb.String() +} + +func printTuple(tuple Tuple, sb *strings.Builder) { + // TODO: Add VersionStamp printer + sb.WriteString("(") + + for i, t := range tuple { + switch t := t.(type) { + case Tuple: + printTuple(t, sb) + case nil: + sb.WriteString("") + case string: + sb.WriteString(strconv.Quote(t)) + case UUID: + sb.WriteString("UUID(") + sb.WriteString(t.String()) + sb.WriteString(")") + case []byte: + sb.WriteString("b\"") + sb.WriteString(fdb.Printable(t)) + sb.WriteString("\"") + default: + // For user-defined and standard types, we use standard Go + // printer, which itself uses Stringer interface. + fmt.Fprintf(sb, "%v", t) + } + + if (i < len(tuple) - 1) { + sb.WriteString(", ") + } + } + + sb.WriteString(")") } // UUID wraps a basic byte array as a UUID. We do not provide any special @@ -79,6 +118,10 @@ func (t Tuple) String() string { // an instance of this type. type UUID [16]byte +func (uuid UUID) String() string { + return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]) +} + // Versionstamp is struct for a FoundationDB verionstamp. Versionstamps are // 12 bytes long composed of a 10 byte transaction version and a 2 byte user // version. The transaction version is filled in at commit time and the user diff --git a/bindings/go/src/fdb/tuple/tuple_test.go b/bindings/go/src/fdb/tuple/tuple_test.go index e4dfbe71dc..ad226c66b2 100644 --- a/bindings/go/src/fdb/tuple/tuple_test.go +++ b/bindings/go/src/fdb/tuple/tuple_test.go @@ -121,10 +121,33 @@ func BenchmarkTuplePacking(b *testing.B) { } func TestTupleString(t *testing.T) { - printed := fmt.Sprint(Tuple{[]byte("hello"), "world", 42, 0x99}) - expected := "\\x01hello\\x00\\x02world\\x00\\x15*\\x15\\x99" + testCases :=[ ]struct { + input Tuple + expected string + }{ + { + Tuple{[]byte("hello"), "world", 42, 0x99}, + "(b\"hello\", \"world\", 42, 153)", + }, + { + Tuple{nil, Tuple{"Ok", Tuple{1, 2}, "Go"}, 42, 0x99}, + "(, (\"Ok\", (1, 2), \"Go\"), 42, 153)", + }, + { + Tuple{"Bool", true, false}, + "(\"Bool\", true, false)", + }, + { + Tuple{"UUID", testUUID}, + "(\"UUID\", UUID(1100aabb-ccdd-eeff-1100-aabbccddeeff))", + }, + // TODO: Add VersionStamp testcase + } - if printed != expected { - t.Fatalf("printed tuple result differs, expected %v, got %v", expected, printed) + for _, testCase := range testCases { + printed := fmt.Sprint(testCase.input) + if printed != testCase.expected { + t.Fatalf("printed tuple result differs, expected %v, got %v", testCase.expected, printed) + } } } From e5929015518b74270d1e46b0b4f2dcb7ce31b579 Mon Sep 17 00:00:00 2001 From: Vishesh Yadav Date: Sun, 26 Apr 2020 18:56:38 -0700 Subject: [PATCH 1547/1604] go: Rename Subspace member `b` to `rawPrefix` Naming stays consistent with other Python/Java. --- bindings/go/src/fdb/subspace/subspace.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/bindings/go/src/fdb/subspace/subspace.go b/bindings/go/src/fdb/subspace/subspace.go index 0ac6e6da62..eb349433ab 100644 --- a/bindings/go/src/fdb/subspace/subspace.go +++ b/bindings/go/src/fdb/subspace/subspace.go @@ -86,7 +86,7 @@ type Subspace interface { } type subspace struct { - b []byte + rawPrefix []byte } // AllKeys returns the Subspace corresponding to all keys in a FoundationDB @@ -112,7 +112,7 @@ func FromBytes(b []byte) Subspace { // String implements the fmt.Stringer interface and return the subspace // as a human readable byte string provided by fdb.Printable. func (s subspace) String() string { - return fmt.Sprintf("Subspace(rawPrefix=%s)", fdb.Printable(s.b)) + return fmt.Sprintf("Subspace(rawPrefix=%s)", fdb.Printable(s.rawPrefix)) } func (s subspace) Sub(el ...tuple.TupleElement) Subspace { @@ -120,35 +120,35 @@ func (s subspace) Sub(el ...tuple.TupleElement) Subspace { } func (s subspace) Bytes() []byte { - return s.b + return s.rawPrefix } func (s subspace) Pack(t tuple.Tuple) fdb.Key { - return fdb.Key(concat(s.b, t.Pack()...)) + return fdb.Key(concat(s.rawPrefix, t.Pack()...)) } func (s subspace) PackWithVersionstamp(t tuple.Tuple) (fdb.Key, error) { - return t.PackWithVersionstamp(s.b) + return t.PackWithVersionstamp(s.rawPrefix) } func (s subspace) Unpack(k fdb.KeyConvertible) (tuple.Tuple, error) { key := k.FDBKey() - if !bytes.HasPrefix(key, s.b) { + if !bytes.HasPrefix(key, s.rawPrefix) { return nil, errors.New("key is not in subspace") } - return tuple.Unpack(key[len(s.b):]) + return tuple.Unpack(key[len(s.rawPrefix):]) } func (s subspace) Contains(k fdb.KeyConvertible) bool { - return bytes.HasPrefix(k.FDBKey(), s.b) + return bytes.HasPrefix(k.FDBKey(), s.rawPrefix) } func (s subspace) FDBKey() fdb.Key { - return fdb.Key(s.b) + return fdb.Key(s.rawPrefix) } func (s subspace) FDBRangeKeys() (fdb.KeyConvertible, fdb.KeyConvertible) { - return fdb.Key(concat(s.b, 0x00)), fdb.Key(concat(s.b, 0xFF)) + return fdb.Key(concat(s.rawPrefix, 0x00)), fdb.Key(concat(s.rawPrefix, 0xFF)) } func (s subspace) FDBRangeKeySelectors() (fdb.Selectable, fdb.Selectable) { From 22fa93264ff5e78d7a19a3f204439169b9902b86 Mon Sep 17 00:00:00 2001 From: Vishesh Yadav Date: Sun, 26 Apr 2020 20:31:49 -0700 Subject: [PATCH 1548/1604] go: Implment Stringer interface for DirectorySubspace --- bindings/go/src/fdb/directory/directorySubspace.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/bindings/go/src/fdb/directory/directorySubspace.go b/bindings/go/src/fdb/directory/directorySubspace.go index 2dd927b2da..f67c46bc33 100644 --- a/bindings/go/src/fdb/directory/directorySubspace.go +++ b/bindings/go/src/fdb/directory/directorySubspace.go @@ -23,6 +23,8 @@ package directory import ( + "fmt" + "strings" "github.com/apple/foundationdb/bindings/go/src/fdb" "github.com/apple/foundationdb/bindings/go/src/fdb/subspace" ) @@ -43,6 +45,18 @@ type directorySubspace struct { layer []byte } +// String implements the fmt.Stringer interface and returns human-readable +// string representation of this object. +func (ds directorySubspace) String() string { + var path string + if len(ds.path) > 0 { + path = "(" + strings.Join(ds.path, ",") + ")" + } else { + path = "nil" + } + return fmt.Sprintf("DirectorySubspace(%s, %s)", path, fdb.Printable(ds.Bytes())) +} + func (d directorySubspace) CreateOrOpen(t fdb.Transactor, path []string, layer []byte) (DirectorySubspace, error) { return d.dl.CreateOrOpen(t, d.dl.partitionSubpath(d.path, path), layer) } From ecb1d9b8c6829a891064d737f46896089e05d0c6 Mon Sep 17 00:00:00 2001 From: Vishesh Yadav Date: Sun, 26 Apr 2020 20:44:23 -0700 Subject: [PATCH 1549/1604] go: Subspace doesn't have to inherit fmt.Stringer Go does duck-typing, so any `struct` can choose to implement String() and it will automatically be Stringer(). There is no need to enforce in out interface. --- bindings/go/src/fdb/subspace/subspace.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/bindings/go/src/fdb/subspace/subspace.go b/bindings/go/src/fdb/subspace/subspace.go index eb349433ab..65f97048c8 100644 --- a/bindings/go/src/fdb/subspace/subspace.go +++ b/bindings/go/src/fdb/subspace/subspace.go @@ -44,8 +44,6 @@ import ( // Subspace represents a well-defined region of keyspace in a FoundationDB // database. type Subspace interface { - fmt.Stringer - // Sub returns a new Subspace whose prefix extends this Subspace with the // encoding of the provided element(s). If any of the elements are not a // valid tuple.TupleElement, Sub will panic. From 8c9a6467448b9000a95748bcd2124bb932c692c4 Mon Sep 17 00:00:00 2001 From: Vishesh Yadav Date: Sun, 26 Apr 2020 22:06:57 -0700 Subject: [PATCH 1550/1604] go: Don't complain about unsafe pointer conversion in stringRefToSlice Running Go bindings with `-race` argument will enable instrumentation to check pointer conversions, and complain about potential unsafe conversion in `stringRefToSlice`. This patch adds annotation `go:nocheckptr` annotation to skip that check, since our conversion is safe, and can let users take advantage of check for their code without this false positive. It will be useful to check if, we can potentially rewrite this code to obey Go pointer conversion rules. FIXES #2843 Signed-off-by: Vishesh Yadav --- bindings/go/src/fdb/futures.go | 1 + 1 file changed, 1 insertion(+) diff --git a/bindings/go/src/fdb/futures.go b/bindings/go/src/fdb/futures.go index aa58e7c81b..c5157d9ebd 100644 --- a/bindings/go/src/fdb/futures.go +++ b/bindings/go/src/fdb/futures.go @@ -273,6 +273,7 @@ type futureKeyValueArray struct { *future } +//go:nocheckptr func stringRefToSlice(ptr unsafe.Pointer) []byte { size := *((*C.int)(unsafe.Pointer(uintptr(ptr) + 8))) From f5e8345496389bade3e3ae4cca0d22cc7c68d77c Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Mon, 27 Apr 2020 22:07:45 -0700 Subject: [PATCH 1551/1604] FastRestoreAgent:Use atomicParallelRestore to kick off restore Replace the handcrafted version with atomicParallelRestore actor which is simulation tested --- fdbbackup/backup.actor.cpp | 127 +++++----------------------- fdbclient/BackupAgent.actor.h | 4 - fdbclient/FileBackupAgent.actor.cpp | 23 +++++ 3 files changed, 45 insertions(+), 109 deletions(-) diff --git a/fdbbackup/backup.actor.cpp b/fdbbackup/backup.actor.cpp index 7eef8ebc15..e83073bf1f 100644 --- a/fdbbackup/backup.actor.cpp +++ b/fdbbackup/backup.actor.cpp @@ -2192,8 +2192,7 @@ ACTOR Future runRestore(Database db, std::string originalClusterFile, std: // Fast restore agent that kicks off the restore: send restore requests to restore workers. ACTOR Future runFastRestoreAgent(Database db, std::string tagName, std::string container, Standalone> ranges, Version dbVersion, - bool performRestore, bool verbose, bool waitForDone, std::string addPrefix, - std::string removePrefix) { + bool performRestore, bool verbose, bool waitForDone) { try { state FileBackupAgent backupAgent; state Version restoreVersion = invalidVersion; @@ -2219,9 +2218,26 @@ ACTOR Future runFastRestoreAgent(Database db, std::string tagName, std::st dbVersion = desc.maxRestorableVersion.get(); TraceEvent("FastRestoreAgent").detail("TargetRestoreVersion", dbVersion); } - Version _restoreVersion = wait(fastRestore(db, KeyRef(tagName), KeyRef(container), waitForDone, dbVersion, - verbose, range, KeyRef(addPrefix), KeyRef(removePrefix))); - restoreVersion = _restoreVersion; + state UID randomUID = deterministicRandom()->randomUniqueID(); + TraceEvent("FastRestoreAgent") + .detail("SubmitRestoreRequests", ranges.size()) + .detail("RestoreUID", randomUID); + wait(backupAgent.submitParallelRestore(db, KeyRef(tagName), ranges, KeyRef(container), dbVersion, true, + randomUID)); + if (waitForDone) { + // Wait for parallel restore to finish and unlock DB after that + TraceEvent("FastRestoreAgent").detail("BackupAndParallelRestore", "WaitForRestoreToFinish"); + wait(backupAgent.parallelRestoreFinish(db, randomUID)); + TraceEvent("FastRestoreAgent").detail("BackupAndParallelRestore", "RestoreFinished"); + } else { + TraceEvent("FastRestoreAgent") + .detail("RestoreUID", randomUID) + .detail("OperationGuide", "Manually unlock DB when restore finishes"); + printf("WARNING: DB will be in locked state after restore. Need UID:%s to unlock DB\n", + randomUID.toString()); + } + + restoreVersion = dbVersion; } else { state Reference bc = IBackupContainer::openContainer(container); state BackupDescription description = wait(bc->describeBackup()); @@ -3740,7 +3756,7 @@ int main(int argc, char* argv[]) { switch (restoreType) { case RESTORE_START: f = stopAfter(runFastRestoreAgent(db, tagName, restoreContainer, backupKeys, restoreVersion, !dryRun, - !quietDisplay, waitForDone, addPrefix, removePrefix)); + !quietDisplay, waitForDone)); break; case RESTORE_WAIT: printf("[TODO][ERROR] FastRestore does not support RESTORE_WAIT yet!\n"); @@ -3887,102 +3903,3 @@ int main(int argc, char* argv[]) { flushAndExit(status); } - -//------Restore Agent: Kick off the restore by sending the restore requests -ACTOR static Future waitFastRestore(Database cx, Key tagName, bool verbose) { - // We should wait on all restore to finish before proceeds - TraceEvent("FastRestore").detail("Progress", "WaitForRestoreToFinish"); - state ReadYourWritesTransaction tr(cx); - state Future fRestoreRequestDone; - state bool restoreRequestDone = false; - - loop { - try { - tr.reset(); - tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); - tr.setOption(FDBTransactionOptions::LOCK_AWARE); - tr.setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE); - // In case restoreRequestDoneKey is already set before we set watch on it - Optional restoreRequestDoneKeyValue = wait(tr.get(restoreRequestDoneKey)); - if (restoreRequestDoneKeyValue.present()) { - restoreRequestDone = true; - tr.clear(restoreRequestDoneKey); - wait(tr.commit()); - break; - } else if (!restoreRequestDone) { - fRestoreRequestDone = tr.watch(restoreRequestDoneKey); - wait(tr.commit()); - wait(fRestoreRequestDone); - } else { - break; - } - } catch (Error& e) { - wait(tr.onError(e)); - } - } - - TraceEvent("FastRestore").detail("Progress", "RestoreFinished"); - - return FileBackupAgent::ERestoreState::COMPLETED; -} - -ACTOR static Future _fastRestore(Database cx, Key tagName, Key url, bool waitForComplete, - Version targetVersion, bool verbose, KeyRange range, Key addPrefix, - Key removePrefix) { - state Reference bc = IBackupContainer::openContainer(url.toString()); - state BackupDescription desc = wait(bc->describeBackup()); - wait(desc.resolveVersionTimes(cx)); - - if (targetVersion == invalidVersion && desc.maxRestorableVersion.present()) - targetVersion = desc.maxRestorableVersion.get(); - - Optional restoreSet = wait(bc->getRestoreSet(targetVersion)); - TraceEvent("FastRestore").detail("BackupDesc", desc.toString()).detail("TargetVersion", targetVersion); - - if (!restoreSet.present()) { - TraceEvent(SevWarn, "FileBackupAgentRestoreNotPossible") - .detail("BackupContainer", bc->getURL()) - .detail("TargetVersion", targetVersion); - throw restore_invalid_version(); - } - - // NOTE: The restore agent makes sure we only support 1 restore range for each restore request for now! - // The simulation test did test restoring multiple restore ranges in one restore request though. - state Reference tr(new ReadYourWritesTransaction(cx)); - state int restoreIndex = 0; - loop { - try { - tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); - tr->setOption(FDBTransactionOptions::LOCK_AWARE); - Standalone restoreTag(tagName.toString() + "_" + std::to_string(restoreIndex)); - bool locked = true; - struct RestoreRequest restoreRequest(restoreIndex, restoreTag, KeyRef(bc->getURL()), true, targetVersion, - true, range, Key(), Key(), locked, - deterministicRandom()->randomUniqueID()); - tr->set(restoreRequestKeyFor(restoreRequest.index), restoreRequestValue(restoreRequest)); - // backupRanges.size = 1 because we only support restoring 1 range in real mode for now - tr->set(restoreRequestTriggerKey, restoreRequestTriggerValue(deterministicRandom()->randomUniqueID(),1)); - wait(tr->commit()); // Trigger fast restore - break; - } catch (Error& e) { - if (e.code() != error_code_restore_duplicate_tag) { - wait(tr->onError(e)); - } - } - } - - if (waitForComplete) { - FileBackupAgent::ERestoreState finalState = wait(waitFastRestore(cx, tagName, verbose)); - if (finalState != FileBackupAgent::ERestoreState::COMPLETED) throw restore_error(); - } - - return targetVersion; -} - -ACTOR Future fastRestore(Database cx, Standalone tagName, Standalone url, - bool waitForComplete, long targetVersion, bool verbose, Standalone range, - Standalone addPrefix, Standalone removePrefix) { - Version result = - wait(_fastRestore(cx, tagName, url, waitForComplete, targetVersion, verbose, range, addPrefix, removePrefix)); - return result; -} diff --git a/fdbclient/BackupAgent.actor.h b/fdbclient/BackupAgent.actor.h index f728bcd488..4699aa480b 100644 --- a/fdbclient/BackupAgent.actor.h +++ b/fdbclient/BackupAgent.actor.h @@ -893,10 +893,6 @@ public: } }; -ACTOR Future fastRestore(Database cx, Standalone tagName, Standalone url, - bool waitForComplete, long targetVersion, bool verbose, Standalone range, - Standalone addPrefix, Standalone removePrefix); - // Helper class for reading restore data from a buffer and throwing the right errors. struct StringRefReader { StringRefReader(StringRef s = StringRef(), Error e = Error()) : rptr(s.begin()), end(s.end()), failure_error(e) {} diff --git a/fdbclient/FileBackupAgent.actor.cpp b/fdbclient/FileBackupAgent.actor.cpp index 59f1837374..b4cefe9eb6 100644 --- a/fdbclient/FileBackupAgent.actor.cpp +++ b/fdbclient/FileBackupAgent.actor.cpp @@ -3628,6 +3628,29 @@ public: ACTOR static Future submitParallelRestore(Database cx, Key backupTag, Standalone> backupRanges, KeyRef bcUrl, Version targetVersion, bool lockDB, UID randomUID) { + // Sanity check backup is valid + state Reference bc = IBackupContainer::openContainer(bcUrl.toString()); + state BackupDescription desc = wait(bc->describeBackup()); + wait(desc.resolveVersionTimes(cx)); + + Optional restoreSet = wait(bc->getRestoreSet(targetVersion)); + + if (!restoreSet.present()) { + TraceEvent(SevWarn, "FileBackupAgentRestoreNotPossible") + .detail("BackupContainer", bc->getURL()) + .detail("TargetVersion", targetVersion); + throw restore_invalid_version(); + } + + if (targetVersion == invalidVersion && desc.maxRestorableVersion.present()) { + targetVersion = desc.maxRestorableVersion.get(); + TraceEvent(SevWarn, "FastRestoreSubmitRestoreRequestWithInvalidTargetVersion") + .detail("OverrideTargetVersion", targetVersion); + } + TraceEvent("FastRestoreSubmitRestoreRequest") + .detail("BackupDesc", desc.toString()) + .detail("TargetVersion", targetVersion); + state Reference tr(new ReadYourWritesTransaction(cx)); state int restoreIndex = 0; state int numTries = 0; From 50ad4a33febf423369f72d989074aea11b4698de Mon Sep 17 00:00:00 2001 From: Markus Pilman Date: Tue, 28 Apr 2020 16:19:04 +0000 Subject: [PATCH 1552/1604] Added possibility to include and exclude tests Description Testing --- cmake/AddFdbTest.cmake | 3 +++ tests/CMakeLists.txt | 2 ++ 2 files changed, 5 insertions(+) diff --git a/cmake/AddFdbTest.cmake b/cmake/AddFdbTest.cmake index a8fae7837b..83d9a5646b 100644 --- a/cmake/AddFdbTest.cmake +++ b/cmake/AddFdbTest.cmake @@ -87,6 +87,9 @@ function(add_fdb_test) if (NOT "${ADD_FDB_TEST_TEST_NAME}" STREQUAL "") set(test_name ${ADD_FDB_TEST_TEST_NAME}) endif() + if((NOT test_name MATCHES "${TEST_INCLUDE}") OR (test_name MATCHES "${TEST_EXCLUDE}")) + return() + endif() math(EXPR test_idx "${CURRENT_TEST_INDEX} + ${NUM_TEST_FILES}") set(CURRENT_TEST_INDEX "${test_idx}" PARENT_SCOPE) # set( PARENT_SCOPE) doesn't set the diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d2a92a00c4..a8bc214b0b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -7,6 +7,8 @@ set(TEST_KEEP_LOGS "FAILED" CACHE STRING "Which logs to keep (NONE, FAILED, ALL) set(TEST_KEEP_SIMDIR "NONE" CACHE STRING "Which simfdb directories to keep (NONE, FAILED, ALL)") set(TEST_AGGREGATE_TRACES "NONE" CACHE STRING "Create aggregated trace files (NONE, FAILED, ALL)") set(TEST_LOG_FORMAT "xml" CACHE STRING "Format for test trace files (xml, json)") +set(TEST_INCLUDE ".*" CACHE STRING "Include only tests that match the given regex") +set(TEST_EXCLUDE ".^" CACHE STRING "Exclude all tests matching the given regex") # for the restart test we optimally want to use the last stable fdbserver # to test upgrades From b315163033d1606c90967672e6c4baf3c68f043d Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Mon, 27 Apr 2020 11:18:04 -0700 Subject: [PATCH 1553/1604] Fix a memory corruption error --- fdbclient/BackupContainer.h | 6 ------ fdbserver/RestoreLoader.actor.cpp | 2 +- fdbserver/RestoreMaster.actor.cpp | 2 +- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/fdbclient/BackupContainer.h b/fdbclient/BackupContainer.h index 92c03b1985..c9bb1477dc 100644 --- a/fdbclient/BackupContainer.h +++ b/fdbclient/BackupContainer.h @@ -108,12 +108,6 @@ struct RangeFile { std::string fileName; int64_t fileSize; - RangeFile() {} - RangeFile(Version v, uint32_t bSize, std::string name, int64_t size) - : version(v), blockSize(bSize), fileName(name), fileSize(size) {} - RangeFile(const RangeFile& f) - : version(f.version), blockSize(f.blockSize), fileName(f.fileName), fileSize(f.fileSize) {} - // Order by version, break ties with name bool operator< (const RangeFile &rhs) const { return version == rhs.version ? fileName < rhs.fileName : version < rhs.version; diff --git a/fdbserver/RestoreLoader.actor.cpp b/fdbserver/RestoreLoader.actor.cpp index 5bf49f3352..51befe3279 100644 --- a/fdbserver/RestoreLoader.actor.cpp +++ b/fdbserver/RestoreLoader.actor.cpp @@ -817,7 +817,7 @@ ACTOR static Future _parseRangeFileToMutationsOnLoader( // The set of key value version is rangeFile.version. the key-value set in the same range file has the same version Reference inFile = wait(bc->readFile(asset.filename)); - state VectorRef blockData; + state Standalone> blockData; try { Standalone> kvs = wait(fileBackup::decodeRangeFileBlock(inFile, asset.offset, asset.len)); diff --git a/fdbserver/RestoreMaster.actor.cpp b/fdbserver/RestoreMaster.actor.cpp index a13bf9e54b..9e0d749a5c 100644 --- a/fdbserver/RestoreMaster.actor.cpp +++ b/fdbserver/RestoreMaster.actor.cpp @@ -717,7 +717,7 @@ ACTOR static Future collectBackupFiles(Reference bc, ACTOR static Future insertRangeVersion(KeyRangeMap* pRangeVersions, RestoreFileFR* file, Reference bc) { TraceEvent("FastRestoreMasterDecodeRangeVersion").detail("File", file->toString()); - RangeFile rangeFile(file->version, file->blockSize, file->fileName, file->fileSize); + RangeFile rangeFile = { file->version, (uint32_t)file->blockSize, file->fileName, file->fileSize }; // First and last key are the range for this file: endKey is exclusive KeyRange fileRange = wait(bc->getSnapshotFileKeyRange(rangeFile)); From 7d59e533494c41dac6c19b443775efd394fb7f54 Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Mon, 27 Apr 2020 13:59:45 -0700 Subject: [PATCH 1554/1604] Consolidate makePadding() --- fdbbackup/FileConverter.actor.cpp | 13 +------------ fdbclient/BackupAgent.actor.h | 3 +++ fdbclient/BackupContainer.h | 3 +++ fdbclient/FileBackupAgent.actor.cpp | 7 ++++--- fdbserver/BackupWorker.actor.cpp | 13 +------------ 5 files changed, 12 insertions(+), 27 deletions(-) diff --git a/fdbbackup/FileConverter.actor.cpp b/fdbbackup/FileConverter.actor.cpp index 006b311f87..67f0e3493d 100644 --- a/fdbbackup/FileConverter.actor.cpp +++ b/fdbbackup/FileConverter.actor.cpp @@ -373,17 +373,6 @@ struct LogFileWriter { return wr.toValue(); } - // Return a block of contiguous padding bytes, growing if needed. - static Value makePadding(int size) { - static Value pad; - if (pad.size() < size) { - pad = makeString(size); - memset(mutateString(pad), '\xff', pad.size()); - } - - return pad.substr(0, size); - } - // Start a new block if needed, then write the key and value ACTOR static Future writeKV_impl(LogFileWriter* self, Key k, Value v) { // If key and value do not fit in this block, end it and start a new one @@ -392,7 +381,7 @@ struct LogFileWriter { // Write padding if needed int bytesLeft = self->blockEnd - self->file->size(); if (bytesLeft > 0) { - state Value paddingFFs = makePadding(bytesLeft); + state Value paddingFFs = fileBackup::makePadding(bytesLeft); wait(self->file->append(paddingFFs.begin(), bytesLeft)); } diff --git a/fdbclient/BackupAgent.actor.h b/fdbclient/BackupAgent.actor.h index f728bcd488..400b4e9a6f 100644 --- a/fdbclient/BackupAgent.actor.h +++ b/fdbclient/BackupAgent.actor.h @@ -937,6 +937,9 @@ struct StringRefReader { namespace fileBackup { ACTOR Future>> decodeRangeFileBlock(Reference file, int64_t offset, int len); + +// Return a block of contiguous padding bytes "\0xff" for backup files, growing if needed. +Value makePadding(int size); } #include "flow/unactorcompiler.h" diff --git a/fdbclient/BackupContainer.h b/fdbclient/BackupContainer.h index c9bb1477dc..8ac79937dd 100644 --- a/fdbclient/BackupContainer.h +++ b/fdbclient/BackupContainer.h @@ -68,6 +68,9 @@ static const uint32_t BACKUP_AGENT_MLOG_VERSION = 2001; // Mutation log version written by BackupWorker static const uint32_t PARTITIONED_MLOG_VERSION = 4110; +// Snapshot file version written by FileBackupAgent +static const uint32_t BACKUP_AGENT_SNAPSHOT_FILE_VERSION = 1001; + struct LogFile { Version beginVersion; Version endVersion; diff --git a/fdbclient/FileBackupAgent.actor.cpp b/fdbclient/FileBackupAgent.actor.cpp index 59f1837374..f7368e3e8b 100644 --- a/fdbclient/FileBackupAgent.actor.cpp +++ b/fdbclient/FileBackupAgent.actor.cpp @@ -461,7 +461,8 @@ namespace fileBackup { // then the space after the final key to the next 1MB boundary would // just be padding anyway. struct RangeFileWriter { - RangeFileWriter(Reference file = Reference(), int blockSize = 0) : file(file), blockSize(blockSize), blockEnd(0), fileVersion(1001) {} + RangeFileWriter(Reference file = Reference(), int blockSize = 0) + : file(file), blockSize(blockSize), blockEnd(0), fileVersion(BACKUP_AGENT_SNAPSHOT_FILE_VERSION) {} // Handles the first block and internal blocks. Ends current block if needed. // The final flag is used in simulation to pad the file's final block to a whole block size @@ -557,8 +558,8 @@ namespace fileBackup { state StringRefReader reader(buf, restore_corrupted_data()); try { - // Read header, currently only decoding version 1001 - if(reader.consume() != 1001) + // Read header, currently only decoding BACKUP_AGENT_SNAPSHOT_FILE_VERSION + if(reader.consume() != BACKUP_AGENT_SNAPSHOT_FILE_VERSION) throw restore_unsupported_file_version(); // Read begin key, if this fails then block was invalid. diff --git a/fdbserver/BackupWorker.actor.cpp b/fdbserver/BackupWorker.actor.cpp index c6d0ba8d10..2ff0502188 100644 --- a/fdbserver/BackupWorker.actor.cpp +++ b/fdbserver/BackupWorker.actor.cpp @@ -571,17 +571,6 @@ ACTOR Future saveProgress(BackupData* self, Version backupVersion) { } } -// Return a block of contiguous padding bytes, growing if needed. -static Value makePadding(int size) { - static Value pad; - if (pad.size() < size) { - pad = makeString(size); - memset(mutateString(pad), '\xff', pad.size()); - } - - return pad.substr(0, size); -} - // Write a mutation to a log file. Note the mutation can be different from // message.message for clear mutations. ACTOR Future addMutation(Reference logFile, VersionedMessage message, StringRef mutation, @@ -602,7 +591,7 @@ ACTOR Future addMutation(Reference logFile, VersionedMessage // Write padding if needed const int bytesLeft = *blockEnd - logFile->size(); if (bytesLeft > 0) { - state Value paddingFFs = makePadding(bytesLeft); + state Value paddingFFs = fileBackup::makePadding(bytesLeft); wait(logFile->append(paddingFFs.begin(), bytesLeft)); } From ba261eda36a316fc96a0912a427052a04396f374 Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Mon, 27 Apr 2020 16:49:01 -0700 Subject: [PATCH 1555/1604] Fix a backup container unit test Write a valid range file instead of random data so that checking its content is fine. --- fdbclient/BackupContainer.actor.cpp | 43 +++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/fdbclient/BackupContainer.actor.cpp b/fdbclient/BackupContainer.actor.cpp index 2fd21ebb73..7110f40323 100644 --- a/fdbclient/BackupContainer.actor.cpp +++ b/fdbclient/BackupContainer.actor.cpp @@ -1373,7 +1373,7 @@ public: wait(bc->readKeyspaceSnapshot(snapshot.get())); restorable.ranges = std::move(results.first); restorable.keyRanges = std::move(results.second); - if (false && g_network->isSimulated()) { // TODO: Reenable sanity check + if (g_network->isSimulated()) { // Sanity check key ranges state std::map::iterator rit; for (rit = restorable.keyRanges.begin(); rit != restorable.keyRanges.end(); rit++) { @@ -2097,6 +2097,8 @@ ACTOR Future> timeKeeperEpochsFromVersion(Version v, Reference return found.first + (v - found.second) / CLIENT_KNOBS->CORE_VERSIONSPERSECOND; } +namespace backup_test { + int chooseFileSize(std::vector &sizes) { int size = 1000; if(!sizes.empty()) { @@ -2134,7 +2136,30 @@ Version nextVersion(Version v) { return v + increment; } -ACTOR Future testBackupContainer(std::string url) { +// Write a snapshot file with only begin & end key +ACTOR static Future testWriteSnapshotFile(Reference file, Key begin, Key end, uint32_t blockSize) { + ASSERT(blockSize > 3 * sizeof(uint32_t) + begin.size() + end.size()); + + uint32_t fileVersion = BACKUP_AGENT_SNAPSHOT_FILE_VERSION; + // write Header + wait(file->append((uint8_t*)&fileVersion, sizeof(fileVersion))); + + // write begin key length and key + wait(file->appendStringRefWithLen(begin)); + + // write end key length and key + wait(file->appendStringRefWithLen(end)); + + int bytesLeft = blockSize - file->size(); + if (bytesLeft > 0) { + Value paddings = fileBackup::makePadding(bytesLeft); + wait(file->append(paddings.begin(), bytesLeft)); + } + wait(file->finish()); + return Void(); +} + +ACTOR static Future testBackupContainer(std::string url) { printf("BackupContainerTest URL %s\n", url.c_str()); state Reference c = IBackupContainer::openContainer(url); @@ -2163,6 +2188,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(""); while(kvfiles > 0) { if(snapshots.empty()) { @@ -2173,15 +2200,17 @@ ACTOR Future testBackupContainer(std::string url) { v = nextVersion(v); } } - Reference range = wait(c->writeRangeFile(snapshots.rbegin()->first, 0, v, 10)); + Reference range = wait(c->writeRangeFile(snapshots.rbegin()->first, 0, v, 16)); ++nRangeFiles; v = nextVersion(v); snapshots.rbegin()->second.push_back(range->getFileName()); - snapshotBeginEndKeys.rbegin()->second.emplace_back(LiteralStringRef(""), LiteralStringRef("")); + snapshotBeginEndKeys.rbegin()->second.emplace_back(begin, end); int size = chooseFileSize(fileSizes); snapshotSizes.rbegin()->second += size; - writes.push_back(writeAndVerifyFile(c, range, size)); + // Write in actual range file format, instead of random data. + // writes.push_back(writeAndVerifyFile(c, range, size)); + wait(testWriteSnapshotFile(range, begin, end, 16)); if(deterministicRandom()->random01() < .2) { writes.push_back(c->writeKeyspaceSnapshotFile( @@ -2377,4 +2406,6 @@ TEST_CASE("/backup/continuous") { ASSERT(BackupContainerFileSystem::getPartitionedLogsContinuousEndVersion(files, 250) == 399); return Void(); -} \ No newline at end of file +} + +} // namespace backup_test From 364142d02c4f9462d029cf188e6464525dfe2594 Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Tue, 28 Apr 2020 13:31:07 -0700 Subject: [PATCH 1556/1604] Fix backupWorkerEnabled flag not set bug for first backup This can cause restore failures because the latestLogEndVersion could be wrong. --- fdbclient/FileBackupAgent.actor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/fdbclient/FileBackupAgent.actor.cpp b/fdbclient/FileBackupAgent.actor.cpp index f7368e3e8b..83fb5d69bd 100644 --- a/fdbclient/FileBackupAgent.actor.cpp +++ b/fdbclient/FileBackupAgent.actor.cpp @@ -2407,6 +2407,7 @@ namespace fileBackup { state bool backupWorkerEnabled = dbConfig.backupWorkerEnabled; if (!backupWorkerEnabled) { wait(success(changeConfig(cx, "backup_worker_enabled:=1", true))); + backupWorkerEnabled = true; } // Set the "backupStartedKey" and wait for all backup worker started From 8dd05405ebfd455ba92586334a5875179ac18ef4 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Tue, 28 Apr 2020 16:05:35 -0700 Subject: [PATCH 1557/1604] FastRestore:Guard knob with BUGGIFY Prevent knob to be randomly set. --- fdbserver/Knobs.cpp | 26 +++++++++++++------------- fdbserver/RestoreWorker.actor.cpp | 5 ++++- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index dc63d21a48..24cdec69ce 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -566,19 +566,19 @@ void ServerKnobs::initialize(bool randomize, ClientKnobs* clientKnobs, bool isSi // Fast Restore init( FASTRESTORE_FAILURE_TIMEOUT, 3600 ); init( FASTRESTORE_HEARTBEAT_INTERVAL, 60 ); - init( FASTRESTORE_SAMPLING_PERCENT, 1 ); if( randomize ) { FASTRESTORE_SAMPLING_PERCENT = deterministicRandom()->random01() * 100; } - init( FASTRESTORE_NUM_LOADERS, 3 ); if( randomize ) { FASTRESTORE_NUM_LOADERS = deterministicRandom()->random01() * 10 + 1; } - init( FASTRESTORE_NUM_APPLIERS, 3 ); if( randomize ) { FASTRESTORE_NUM_APPLIERS = deterministicRandom()->random01() * 10 + 1; } - init( FASTRESTORE_TXN_BATCH_MAX_BYTES, 512.0 ); if( randomize ) { FASTRESTORE_TXN_BATCH_MAX_BYTES = deterministicRandom()->random01() * 1024.0 * 1024.0 + 1.0; } - init( FASTRESTORE_VERSIONBATCH_MAX_BYTES, 10.0 * 1024.0 * 1024.0 ); if( randomize ) { FASTRESTORE_VERSIONBATCH_MAX_BYTES = deterministicRandom()->random01() * 10.0 * 1024.0 * 1024.0 * 1024.0; } - init( FASTRESTORE_VB_PARALLELISM, 3 ); if( randomize ) { FASTRESTORE_VB_PARALLELISM = deterministicRandom()->random01() * 20 + 1; } - init( FASTRESTORE_VB_MONITOR_DELAY, 5 ); if( randomize ) { FASTRESTORE_VB_MONITOR_DELAY = deterministicRandom()->random01() * 20 + 1; } - init( FASTRESTORE_VB_LAUNCH_DELAY, 5 ); if( randomize ) { FASTRESTORE_VB_LAUNCH_DELAY = deterministicRandom()->random01() * 60 + 1; } - init( FASTRESTORE_ROLE_LOGGING_DELAY, 5 ); if( randomize ) { FASTRESTORE_ROLE_LOGGING_DELAY = deterministicRandom()->random01() * 60 + 1; } - init( FASTRESTORE_UPDATE_PROCESS_STATS_INTERVAL, 5 ); if( randomize ) { FASTRESTORE_UPDATE_PROCESS_STATS_INTERVAL = deterministicRandom()->random01() * 60 + 1; } - init( FASTRESTORE_ATOMICOP_WEIGHT, 100 ); if( randomize ) { FASTRESTORE_ATOMICOP_WEIGHT = deterministicRandom()->random01() * 200 + 1; } - init( FASTRESTORE_APPLYING_PARALLELISM, 100 ); if( randomize ) { FASTRESTORE_APPLYING_PARALLELISM = deterministicRandom()->random01() * 10 + 1; } - init( FASTRESTORE_MONITOR_LEADER_DELAY, 5 ); if( randomize ) { FASTRESTORE_MONITOR_LEADER_DELAY = deterministicRandom()->random01() * 100; } + init( FASTRESTORE_SAMPLING_PERCENT, 1 ); if( randomize && BUGGIFY ) { FASTRESTORE_SAMPLING_PERCENT = deterministicRandom()->random01() * 100; } + init( FASTRESTORE_NUM_LOADERS, 3 ); if( randomize && BUGGIFY ) { FASTRESTORE_NUM_LOADERS = deterministicRandom()->random01() * 10 + 1; } + init( FASTRESTORE_NUM_APPLIERS, 3 ); if( randomize && BUGGIFY ) { FASTRESTORE_NUM_APPLIERS = deterministicRandom()->random01() * 10 + 1; } + init( FASTRESTORE_TXN_BATCH_MAX_BYTES, 512.0 ); if( randomize && BUGGIFY ) { FASTRESTORE_TXN_BATCH_MAX_BYTES = deterministicRandom()->random01() * 1024.0 * 1024.0 + 1.0; } + init( FASTRESTORE_VERSIONBATCH_MAX_BYTES, 10.0 * 1024.0 * 1024.0 ); if( randomize && BUGGIFY ) { FASTRESTORE_VERSIONBATCH_MAX_BYTES = deterministicRandom()->random01() * 10.0 * 1024.0 * 1024.0 * 1024.0; } + init( FASTRESTORE_VB_PARALLELISM, 3 ); if( randomize && BUGGIFY ) { FASTRESTORE_VB_PARALLELISM = deterministicRandom()->random01() * 20 + 1; } + init( FASTRESTORE_VB_MONITOR_DELAY, 5 ); if( randomize && BUGGIFY ) { FASTRESTORE_VB_MONITOR_DELAY = deterministicRandom()->random01() * 20 + 1; } + init( FASTRESTORE_VB_LAUNCH_DELAY, 5 ); if( randomize && BUGGIFY ) { FASTRESTORE_VB_LAUNCH_DELAY = deterministicRandom()->random01() * 60 + 1; } + init( FASTRESTORE_ROLE_LOGGING_DELAY, 5 ); if( randomize && BUGGIFY ) { FASTRESTORE_ROLE_LOGGING_DELAY = deterministicRandom()->random01() * 60 + 1; } + init( FASTRESTORE_UPDATE_PROCESS_STATS_INTERVAL, 5 ); if( randomize && BUGGIFY ) { FASTRESTORE_UPDATE_PROCESS_STATS_INTERVAL = deterministicRandom()->random01() * 60 + 1; } + init( FASTRESTORE_ATOMICOP_WEIGHT, 100 ); if( randomize && BUGGIFY ) { FASTRESTORE_ATOMICOP_WEIGHT = deterministicRandom()->random01() * 200 + 1; } + init( FASTRESTORE_APPLYING_PARALLELISM, 100 ); if( randomize && BUGGIFY ) { FASTRESTORE_APPLYING_PARALLELISM = deterministicRandom()->random01() * 10 + 1; } + init( FASTRESTORE_MONITOR_LEADER_DELAY, 5 ); if( randomize && BUGGIFY ) { FASTRESTORE_MONITOR_LEADER_DELAY = deterministicRandom()->random01() * 100; } init( FASTRESTORE_STRAGGLER_THRESHOLD_SECONDS, 60 ); if( randomize && BUGGIFY ) { FASTRESTORE_STRAGGLER_THRESHOLD_SECONDS = deterministicRandom()->random01() * 240 + 10; } init( FASTRESTORE_TRACK_REQUEST_LATENCY, true ); if( randomize && BUGGIFY ) { FASTRESTORE_TRACK_REQUEST_LATENCY = false; } init( FASTRESTORE_TRACK_LOADER_SEND_REQUESTS, false ); if( randomize && BUGGIFY ) { FASTRESTORE_TRACK_LOADER_SEND_REQUESTS = true; } diff --git a/fdbserver/RestoreWorker.actor.cpp b/fdbserver/RestoreWorker.actor.cpp index 77494c1fcf..6d78dad9ef 100644 --- a/fdbserver/RestoreWorker.actor.cpp +++ b/fdbserver/RestoreWorker.actor.cpp @@ -147,7 +147,10 @@ ACTOR Future collectRestoreWorkerInterface(Reference se } break; } - TraceEvent("FastRestore").suppressFor(10.0).detail("NotEnoughWorkers", agentValues.size()); + TraceEvent("FastRestore") + .suppressFor(10.0) + .detail("NotEnoughWorkers", agentValues.size()) + .detail("MinWorkers", min_num_workers); wait(delay(5.0)); } catch (Error& e) { wait(tr.onError(e)); From 562456028c65ee16593288ef8a49a1026dc43a12 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Tue, 28 Apr 2020 16:33:10 -0700 Subject: [PATCH 1558/1604] Fix the interface of special-key-space, only keys in (\xff\xff, \xff\xff\xff) are valid --- fdbclient/ReadYourWrites.actor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fdbclient/ReadYourWrites.actor.cpp b/fdbclient/ReadYourWrites.actor.cpp index f47a01f54a..539b858419 100644 --- a/fdbclient/ReadYourWrites.actor.cpp +++ b/fdbclient/ReadYourWrites.actor.cpp @@ -1230,7 +1230,7 @@ Future< Optional > ReadYourWritesTransaction::get( const Key& key, bool s } // special key space are only allowed to query if both begin and end start with \xff\xff - if (key.startsWith(specialKeys.begin)) + if (specialKeys.contains(key)) return getDatabase()->specialKeySpace->get(Reference::addRef(this), key); if(checkUsedDuringCommit()) { @@ -1285,7 +1285,7 @@ Future< Standalone > ReadYourWritesTransaction::getRange( } // special key space are only allowed to query if both begin and end start with \xff\xff - if (begin.getKey().startsWith(specialKeys.begin) && end.getKey().startsWith(specialKeys.begin)) + if (specialKeys.contains(begin.getKey()) && specialKeys.contains(end.getKey())) return getDatabase()->specialKeySpace->getRange(Reference::addRef(this), begin, end, limits, reverse); From 3adbc895100b181328028e01b0ea3ef1affaece0 Mon Sep 17 00:00:00 2001 From: chaoguang <13974480+zjuLcg@users.noreply.github.com> Date: Tue, 28 Apr 2020 16:50:24 -0700 Subject: [PATCH 1559/1604] update special-key-space argument --- fdbclient/ReadYourWrites.actor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fdbclient/ReadYourWrites.actor.cpp b/fdbclient/ReadYourWrites.actor.cpp index 539b858419..ff0b5dba3e 100644 --- a/fdbclient/ReadYourWrites.actor.cpp +++ b/fdbclient/ReadYourWrites.actor.cpp @@ -1229,7 +1229,7 @@ Future< Optional > ReadYourWritesTransaction::get( const Key& key, bool s return Optional(); } - // special key space are only allowed to query if both begin and end start with \xff\xff + // special key space are only allowed to query if both begin and end are in \xff\xff, \xff\xff\xff if (specialKeys.contains(key)) return getDatabase()->specialKeySpace->get(Reference::addRef(this), key); @@ -1284,7 +1284,7 @@ Future< Standalone > ReadYourWritesTransaction::getRange( } } - // special key space are only allowed to query if both begin and end start with \xff\xff + // special key space are only allowed to query if both begin and end are in \xff\xff, \xff\xff\xff if (specialKeys.contains(begin.getKey()) && specialKeys.contains(end.getKey())) return getDatabase()->specialKeySpace->getRange(Reference::addRef(this), begin, end, limits, reverse); From d0cb3ec53869215319479663e03c8954c347444b Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Tue, 28 Apr 2020 17:01:35 -0700 Subject: [PATCH 1560/1604] FuzzApiCorrectness no longer expects an error when reading the special keys keyspace --- .../workloads/FuzzApiCorrectness.actor.cpp | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/fdbserver/workloads/FuzzApiCorrectness.actor.cpp b/fdbserver/workloads/FuzzApiCorrectness.actor.cpp index 7a979352a0..0f9b67d8ca 100644 --- a/fdbserver/workloads/FuzzApiCorrectness.actor.cpp +++ b/fdbserver/workloads/FuzzApiCorrectness.actor.cpp @@ -595,7 +595,7 @@ struct FuzzApiCorrectnessWorkload : TestWorkload { TestGet(unsigned int id, FuzzApiCorrectnessWorkload *workload) : BaseTest(id, workload, "TestGet") { key = makeKey(); contract = { - std::make_pair( error_code_key_outside_legal_range, ExceptionContract::requiredIf((key >= (workload->useSystemKeys ? systemKeys.end : normalKeys.end))) ), + std::make_pair( error_code_key_outside_legal_range, ExceptionContract::requiredIf((key >= (workload->useSystemKeys ? systemKeys.end : normalKeys.end)) && !specialKeys.contains(key)) ), std::make_pair( error_code_client_invalid_operation, ExceptionContract::Possible ), std::make_pair( error_code_accessed_unreadable, ExceptionContract::Possible ) }; @@ -652,12 +652,15 @@ struct FuzzApiCorrectnessWorkload : TestWorkload { limit = deterministicRandom()->randomInt(0, INT_MAX)+1; } + bool isSpecialKeyRange = specialKeys.contains(keysel1.getKey()) && specialKeys.contains(keysel2.getKey()); + contract = { std::make_pair( error_code_range_limits_invalid, ExceptionContract::possibleButRequiredIf(limit < 0) ), std::make_pair( error_code_client_invalid_operation, ExceptionContract::Possible ), std::make_pair( error_code_key_outside_legal_range, ExceptionContract::requiredIf( - (keysel1.getKey() > (workload->useSystemKeys ? systemKeys.end : normalKeys.end)) || - (keysel2.getKey() > (workload->useSystemKeys ? systemKeys.end : normalKeys.end))) ), + ((keysel1.getKey() > (workload->useSystemKeys ? systemKeys.end : normalKeys.end)) || + (keysel2.getKey() > (workload->useSystemKeys ? systemKeys.end : normalKeys.end))) && + !isSpecialKeyRange) ), std::make_pair( error_code_accessed_unreadable, ExceptionContract::Possible ) }; } @@ -681,12 +684,16 @@ struct FuzzApiCorrectnessWorkload : TestWorkload { keysel1 = makeKeySel(); keysel2 = makeKeySel(); limits = makeRangeLimits(); + + bool isSpecialKeyRange = specialKeys.contains(keysel1.getKey()) && specialKeys.contains(keysel2.getKey()); + contract = { std::make_pair( error_code_range_limits_invalid, ExceptionContract::possibleButRequiredIf( !limits.isReached() && !limits.isValid()) ), std::make_pair( error_code_client_invalid_operation, ExceptionContract::Possible ), std::make_pair( error_code_key_outside_legal_range, ExceptionContract::requiredIf( - (keysel1.getKey() > (workload->useSystemKeys ? systemKeys.end : normalKeys.end)) || - (keysel2.getKey() > (workload->useSystemKeys ? systemKeys.end : normalKeys.end))) ), + ((keysel1.getKey() > (workload->useSystemKeys ? systemKeys.end : normalKeys.end)) || + (keysel2.getKey() > (workload->useSystemKeys ? systemKeys.end : normalKeys.end))) && + !isSpecialKeyRange) ), std::make_pair( error_code_accessed_unreadable, ExceptionContract::Possible ) }; } @@ -721,13 +728,17 @@ struct FuzzApiCorrectnessWorkload : TestWorkload { else limit = deterministicRandom()->randomInt(0, INT_MAX)+1; } + + bool isSpecialKeyRange = specialKeys.contains(key1) && specialKeys.contains(key2); + contract = { std::make_pair( error_code_inverted_range, ExceptionContract::requiredIf(key1 > key2) ), std::make_pair( error_code_range_limits_invalid, ExceptionContract::possibleButRequiredIf(limit < 0) ), std::make_pair( error_code_client_invalid_operation, ExceptionContract::Possible ), std::make_pair( error_code_key_outside_legal_range, ExceptionContract::requiredIf( - (key1 > (workload->useSystemKeys ? systemKeys.end : normalKeys.end)) || - (key2 > (workload->useSystemKeys ? systemKeys.end : normalKeys.end))) ), + ((key1 > (workload->useSystemKeys ? systemKeys.end : normalKeys.end)) || + (key2 > (workload->useSystemKeys ? systemKeys.end : normalKeys.end))) + && !isSpecialKeyRange) ), std::make_pair( error_code_accessed_unreadable, ExceptionContract::Possible ) }; } @@ -752,13 +763,17 @@ struct FuzzApiCorrectnessWorkload : TestWorkload { key1 = makeKey(); key2 = makeKey(); limits = makeRangeLimits(); + + bool isSpecialKeyRange = specialKeys.contains(key1) && specialKeys.contains(key2); + contract = { std::make_pair( error_code_inverted_range, ExceptionContract::requiredIf(key1 > key2) ), std::make_pair( error_code_range_limits_invalid, ExceptionContract::possibleButRequiredIf( !limits.isReached() && !limits.isValid()) ), std::make_pair( error_code_client_invalid_operation, ExceptionContract::Possible ), std::make_pair( error_code_key_outside_legal_range, ExceptionContract::requiredIf( - (key1 > (workload->useSystemKeys ? systemKeys.end : normalKeys.end)) || - (key2 > (workload->useSystemKeys ? systemKeys.end : normalKeys.end))) ), + ((key1 > (workload->useSystemKeys ? systemKeys.end : normalKeys.end)) || + (key2 > (workload->useSystemKeys ? systemKeys.end : normalKeys.end))) && + !isSpecialKeyRange) ), std::make_pair( error_code_accessed_unreadable, ExceptionContract::Possible ) }; } From 5b1d99bc3bd86bea6c6d898898c5e17281bb47ee Mon Sep 17 00:00:00 2001 From: Vishesh Yadav Date: Tue, 28 Apr 2020 17:00:38 -0700 Subject: [PATCH 1561/1604] go: Implement fmt.Stringer for Versionstamp --- bindings/go/src/fdb/tuple/tuple.go | 6 +++++- bindings/go/src/fdb/tuple/tuple_test.go | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/bindings/go/src/fdb/tuple/tuple.go b/bindings/go/src/fdb/tuple/tuple.go index 714111d161..46b17061ba 100644 --- a/bindings/go/src/fdb/tuple/tuple.go +++ b/bindings/go/src/fdb/tuple/tuple.go @@ -78,7 +78,6 @@ func (tuple Tuple) String() string { } func printTuple(tuple Tuple, sb *strings.Builder) { - // TODO: Add VersionStamp printer sb.WriteString("(") for i, t := range tuple { @@ -131,6 +130,11 @@ type Versionstamp struct { UserVersion uint16 } +// Returns a human-readable string for this Versionstamp. +func (vs Versionstamp) String() string { + return fmt.Sprintf("Versionstamp(%s, %d)", fdb.Printable(vs.TransactionVersion[:]), vs.UserVersion) +} + var incompleteTransactionVersion = [10]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} const versionstampLength = 12 diff --git a/bindings/go/src/fdb/tuple/tuple_test.go b/bindings/go/src/fdb/tuple/tuple_test.go index ad226c66b2..602bdfe915 100644 --- a/bindings/go/src/fdb/tuple/tuple_test.go +++ b/bindings/go/src/fdb/tuple/tuple_test.go @@ -141,7 +141,10 @@ func TestTupleString(t *testing.T) { Tuple{"UUID", testUUID}, "(\"UUID\", UUID(1100aabb-ccdd-eeff-1100-aabbccddeeff))", }, - // TODO: Add VersionStamp testcase + { + Tuple{"Versionstamp", Versionstamp{[10]byte{0, 0, 0, 0xaa, 0, 0xbb, 0, 0xcc, 0, 0xdd}, 620}}, + "(\"Versionstamp\", Versionstamp(\\x00\\x00\\x00\\xaa\\x00\\xbb\\x00\\xcc\\x00\\xdd, 620))", + }, } for _, testCase := range testCases { From 8742cc0ab6586db6eb9392d4885b2ac6dc100bdb Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Tue, 28 Apr 2020 19:12:27 -0700 Subject: [PATCH 1562/1604] FastRestore:Fix windows build --- fdbbackup/backup.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbbackup/backup.actor.cpp b/fdbbackup/backup.actor.cpp index e83073bf1f..1354ab2685 100644 --- a/fdbbackup/backup.actor.cpp +++ b/fdbbackup/backup.actor.cpp @@ -2234,7 +2234,7 @@ ACTOR Future runFastRestoreAgent(Database db, std::string tagName, std::st .detail("RestoreUID", randomUID) .detail("OperationGuide", "Manually unlock DB when restore finishes"); printf("WARNING: DB will be in locked state after restore. Need UID:%s to unlock DB\n", - randomUID.toString()); + randomUID.toString().c_str()); } restoreVersion = dbVersion; From a8becb90275a36b131b22e8e004d69548427c17e Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Tue, 28 Apr 2020 21:13:18 -0700 Subject: [PATCH 1563/1604] Use calculated block size for range files in unit tests --- fdbclient/BackupContainer.actor.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fdbclient/BackupContainer.actor.cpp b/fdbclient/BackupContainer.actor.cpp index 7110f40323..0fabe6e83a 100644 --- a/fdbclient/BackupContainer.actor.cpp +++ b/fdbclient/BackupContainer.actor.cpp @@ -2190,6 +2190,7 @@ ACTOR static Future testBackupContainer(std::string url) { state int kvfiles = deterministicRandom()->randomInt(0, 3); state Key begin = LiteralStringRef(""); state Key end = LiteralStringRef(""); + state int blockSize = 3 * sizeof(uint32_t) + begin.size() + end.size() + 8; while(kvfiles > 0) { if(snapshots.empty()) { @@ -2200,7 +2201,7 @@ ACTOR static Future testBackupContainer(std::string url) { v = nextVersion(v); } } - Reference range = wait(c->writeRangeFile(snapshots.rbegin()->first, 0, v, 16)); + Reference range = wait(c->writeRangeFile(snapshots.rbegin()->first, 0, v, blockSize)); ++nRangeFiles; v = nextVersion(v); snapshots.rbegin()->second.push_back(range->getFileName()); @@ -2210,7 +2211,7 @@ ACTOR static Future testBackupContainer(std::string url) { snapshotSizes.rbegin()->second += size; // Write in actual range file format, instead of random data. // writes.push_back(writeAndVerifyFile(c, range, size)); - wait(testWriteSnapshotFile(range, begin, end, 16)); + wait(testWriteSnapshotFile(range, begin, end, blockSize)); if(deterministicRandom()->random01() < .2) { writes.push_back(c->writeKeyspaceSnapshotFile( From 17221b3e9a2d2f6ddf870674e5575e76a6f0c9b8 Mon Sep 17 00:00:00 2001 From: Vishesh Yadav Date: Mon, 27 Apr 2020 18:17:20 -0700 Subject: [PATCH 1564/1604] cmake: Copy generated files to bindingtester bundle Source generated by vexilographer were not copied into bindingtester. This patch manually adds those files in `cmake/AddFdbTest.cmake`, since there doesn't seem to be any easy way to make bindingtester script know about these generated files (except copying these files to source directory). FIXES #3029 --- cmake/AddFdbTest.cmake | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/cmake/AddFdbTest.cmake b/cmake/AddFdbTest.cmake index a8fae7837b..07c694f689 100644 --- a/cmake/AddFdbTest.cmake +++ b/cmake/AddFdbTest.cmake @@ -277,7 +277,41 @@ function(package_bindingtester) COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bindings ${CMAKE_BINARY_DIR}/bindingtester/tests COMMAND ${CMAKE_COMMAND} -E touch "${CMAKE_BINARY_DIR}/bindingtester.touch" COMMENT "Copy test files for bindingtester") - add_custom_target(copy_bindingtester_binaries DEPENDS ${outfiles} "${CMAKE_BINARY_DIR}/bindingtester.touch") + + add_custom_target(copy_generated_files DEPENDS ${CMAKE_BINARY_DIR}/bindingtester.touch python_binding) + set(generated_binding_files python/fdb/fdboptions.py) + if(WITH_JAVA) + add_dependencies(copy_generated_files fdb_java) + set(java_dir java/src/main/com/apple/foundationdb) + set(generated_binding_files ${generated_binding_files} + ${java_dir}/ConflictRangeType.java + ${java_dir}/DatabaseOptions.java + ${java_dir}/MutationType.java + ${java_dir}/NetworkOptions.java + ${java_dir}/StreamingMode.java + ${java_dir}/TransactionOptions.java + ${java_dir}/FDBException.java) + endif() + + foreach(generated IN LISTS generated_binding_files) + add_custom_command( + TARGET copy_generated_files + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/bindings/${generated} ${bdir}/tests/${generated} + COMMENT "Copy ${generated} to bindingtester") + endforeach() + + if(WITH_GO AND NOT OPEN_FOR_IDE) + add_dependencies(copy_generated_files fdb_go) + add_custom_command( + TARGET copy_generated_files + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_BINARY_DIR}/bindings/go/src/github.com/apple/foundationdb/bindings/go/src/fdb/generated.go # SRC + ${bdir}/tests/go/src/fdb/ # DEST + COMMENT "Copy generated.go for bindingtester") + endif() + + add_custom_target(copy_bindingtester_binaries + DEPENDS ${outfiles} "${CMAKE_BINARY_DIR}/bindingtester.touch" copy_generated_files) add_dependencies(copy_bindingtester_binaries strip_only_fdbserver strip_only_fdbcli strip_only_fdb_c) set(tar_file ${CMAKE_BINARY_DIR}/packages/bindingtester-${CMAKE_PROJECT_VERSION}.tar.gz) add_custom_command( From fab4d698dcc21cba926f781cdd1d741d1e127f31 Mon Sep 17 00:00:00 2001 From: Vishesh Yadav Date: Tue, 28 Apr 2020 16:24:27 -0700 Subject: [PATCH 1565/1604] build: Add JAR and Go's _stacktester to bindingtester bundle --- cmake/AddFdbTest.cmake | 52 +++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/cmake/AddFdbTest.cmake b/cmake/AddFdbTest.cmake index 07c694f689..c8a7c122a9 100644 --- a/cmake/AddFdbTest.cmake +++ b/cmake/AddFdbTest.cmake @@ -278,40 +278,50 @@ function(package_bindingtester) COMMAND ${CMAKE_COMMAND} -E touch "${CMAKE_BINARY_DIR}/bindingtester.touch" COMMENT "Copy test files for bindingtester") - add_custom_target(copy_generated_files DEPENDS ${CMAKE_BINARY_DIR}/bindingtester.touch python_binding) + add_custom_target(copy_binding_output_files DEPENDS ${CMAKE_BINARY_DIR}/bindingtester.touch python_binding fdb_flow_tester) + add_custom_command( + TARGET copy_binding_output_files + COMMAND ${CMAKE_COMMAND} -E copy $ ${bdir}/tests/flow/bin/fdb_flow_tester + COMMENT "Copy Flow tester for bindingtester") + set(generated_binding_files python/fdb/fdboptions.py) if(WITH_JAVA) - add_dependencies(copy_generated_files fdb_java) - set(java_dir java/src/main/com/apple/foundationdb) - set(generated_binding_files ${generated_binding_files} - ${java_dir}/ConflictRangeType.java - ${java_dir}/DatabaseOptions.java - ${java_dir}/MutationType.java - ${java_dir}/NetworkOptions.java - ${java_dir}/StreamingMode.java - ${java_dir}/TransactionOptions.java - ${java_dir}/FDBException.java) + if(NOT FDB_RELEASE) + set(prerelease_string "-PRERELEASE") + else() + set(prerelease_string "") + endif() + add_custom_command( + TARGET copy_binding_output_files + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_BINARY_DIR}/packages/fdb-java-${CMAKE_PROJECT_VERSION}${prerelease_string}.jar + ${bdir}/tests/java/foundationdb-client.jar + COMMENT "Copy Java bindings for bindingtester") + add_dependencies(copy_binding_output_files fat-jar) + add_dependencies(copy_binding_output_files foundationdb-tests) + set(generated_binding_files ${generated_binding_files} java/foundationdb-tests.jar) endif() - foreach(generated IN LISTS generated_binding_files) - add_custom_command( - TARGET copy_generated_files - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/bindings/${generated} ${bdir}/tests/${generated} - COMMENT "Copy ${generated} to bindingtester") - endforeach() - if(WITH_GO AND NOT OPEN_FOR_IDE) - add_dependencies(copy_generated_files fdb_go) + add_dependencies(copy_binding_output_files fdb_go_tester fdb_go) add_custom_command( - TARGET copy_generated_files + TARGET copy_binding_output_files + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/bindings/go/bin/_stacktester ${bdir}/tests/go/build/bin/_stacktester COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/bindings/go/src/github.com/apple/foundationdb/bindings/go/src/fdb/generated.go # SRC ${bdir}/tests/go/src/fdb/ # DEST COMMENT "Copy generated.go for bindingtester") endif() + foreach(generated IN LISTS generated_binding_files) + add_custom_command( + TARGET copy_binding_output_files + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/bindings/${generated} ${bdir}/tests/${generated} + COMMENT "Copy ${generated} to bindingtester") + endforeach() + add_custom_target(copy_bindingtester_binaries - DEPENDS ${outfiles} "${CMAKE_BINARY_DIR}/bindingtester.touch" copy_generated_files) + DEPENDS ${outfiles} "${CMAKE_BINARY_DIR}/bindingtester.touch" copy_binding_output_files) add_dependencies(copy_bindingtester_binaries strip_only_fdbserver strip_only_fdbcli strip_only_fdb_c) set(tar_file ${CMAKE_BINARY_DIR}/packages/bindingtester-${CMAKE_PROJECT_VERSION}.tar.gz) add_custom_command( From ebf7adfa002366b8bf9ad5837342b87809fe0cd2 Mon Sep 17 00:00:00 2001 From: Vishesh Yadav Date: Wed, 29 Apr 2020 00:39:47 -0700 Subject: [PATCH 1566/1604] Revert "Merge pull request #1910 from ryanworl/ryanworl/remove-finalizers" This reverts commit cef556bbee20c323a3142f310d7e6febd2b8a00f, reversing changes made to a6fe8c1d1f8bb30f53d895f327a116155249de37. --- bindings/go/src/fdb/database.go | 13 +- .../go/src/fdb/directory/directoryLayer.go | 2 - bindings/go/src/fdb/fdb_test.go | 1 - bindings/go/src/fdb/futures.go | 140 ++++++------------ bindings/go/src/fdb/range.go | 14 -- bindings/go/src/fdb/transaction.go | 12 +- documentation/sphinx/source/release-notes.rst | 2 - 7 files changed, 53 insertions(+), 131 deletions(-) diff --git a/bindings/go/src/fdb/database.go b/bindings/go/src/fdb/database.go index 6d914d7928..c9bf818fab 100644 --- a/bindings/go/src/fdb/database.go +++ b/bindings/go/src/fdb/database.go @@ -27,7 +27,7 @@ package fdb import "C" import ( - "sync" + "runtime" ) // Database is a handle to a FoundationDB database. Database is a lightweight @@ -74,14 +74,13 @@ func (d Database) CreateTransaction() (Transaction, error) { return Transaction{}, Error{int(err)} } - t := &transaction{outt, d, sync.Once{}} + t := &transaction{outt, d} + runtime.SetFinalizer(t, (*transaction).destroy) return Transaction{t}, nil } -func retryable(t Transaction, wrapped func() (interface{}, error), onError func(Error) FutureNil) (ret interface{}, e error) { - defer t.Close() - +func retryable(wrapped func() (interface{}, error), onError func(Error) FutureNil) (ret interface{}, e error) { for { ret, e = wrapped() @@ -141,7 +140,7 @@ func (d Database) Transact(f func(Transaction) (interface{}, error)) (interface{ return } - return retryable(tr, wrapped, tr.OnError) + return retryable(wrapped, tr.OnError) } // ReadTransact runs a caller-provided function inside a retry loop, providing @@ -181,7 +180,7 @@ func (d Database) ReadTransact(f func(ReadTransaction) (interface{}, error)) (in return } - return retryable(tr, wrapped, tr.OnError) + return retryable(wrapped, tr.OnError) } // Options returns a DatabaseOptions instance suitable for setting options diff --git a/bindings/go/src/fdb/directory/directoryLayer.go b/bindings/go/src/fdb/directory/directoryLayer.go index 5be70e5dd1..63574d9148 100644 --- a/bindings/go/src/fdb/directory/directoryLayer.go +++ b/bindings/go/src/fdb/directory/directoryLayer.go @@ -417,7 +417,6 @@ func (dl directoryLayer) subdirNames(rtr fdb.ReadTransaction, node subspace.Subs rr := rtr.GetRange(sd, fdb.RangeOptions{}) ri := rr.Iterator() - defer ri.Close() var ret []string @@ -443,7 +442,6 @@ func (dl directoryLayer) subdirNodes(tr fdb.Transaction, node subspace.Subspace) rr := tr.GetRange(sd, fdb.RangeOptions{}) ri := rr.Iterator() - defer ri.Close() var ret []subspace.Subspace diff --git a/bindings/go/src/fdb/fdb_test.go b/bindings/go/src/fdb/fdb_test.go index 2c10100e30..ed9478878a 100644 --- a/bindings/go/src/fdb/fdb_test.go +++ b/bindings/go/src/fdb/fdb_test.go @@ -246,7 +246,6 @@ func ExampleRangeIterator() { rr := tr.GetRange(fdb.KeyRange{fdb.Key(""), fdb.Key{0xFF}}, fdb.RangeOptions{}) ri := rr.Iterator() - defer ri.Close() // Advance will return true until the iterator is exhausted for ri.Advance() { diff --git a/bindings/go/src/fdb/futures.go b/bindings/go/src/fdb/futures.go index c5157d9ebd..17ae1d70a4 100644 --- a/bindings/go/src/fdb/futures.go +++ b/bindings/go/src/fdb/futures.go @@ -39,6 +39,7 @@ package fdb import "C" import ( + "runtime" "sync" "unsafe" ) @@ -74,7 +75,9 @@ type future struct { } func newFuture(ptr *C.FDBFuture) *future { - return &future{ptr} + f := &future{ptr} + runtime.SetFinalizer(f, func(f *future) { C.fdb_future_destroy(f.ptr) }) + return f } // Note: This function guarantees the callback will be executed **at most once**. @@ -97,14 +100,17 @@ func fdb_future_block_until_ready(f *C.FDBFuture) { } func (f *future) BlockUntilReady() { + defer runtime.KeepAlive(f) fdb_future_block_until_ready(f.ptr) } func (f *future) IsReady() bool { + defer runtime.KeepAlive(f) return C.fdb_future_is_ready(f.ptr) != 0 } func (f *future) Cancel() { + defer runtime.KeepAlive(f) C.fdb_future_cancel(f.ptr) } @@ -136,7 +142,7 @@ type futureByteSlice struct { func (f *futureByteSlice) Get() ([]byte, error) { f.o.Do(func() { - defer C.fdb_future_destroy(f.ptr) + defer runtime.KeepAlive(f.future) var present C.fdb_bool_t var value *C.uint8_t @@ -150,14 +156,10 @@ func (f *futureByteSlice) Get() ([]byte, error) { } if present != 0 { - // Copy the native `value` into a Go byte slice so the underlying - // native Future can be freed. This avoids the need for finalizers. - valueDestination := make([]byte, length) - valueSource := C.GoBytes(unsafe.Pointer(value), length) - copy(valueDestination, valueSource) - - f.v = valueDestination + f.v = C.GoBytes(unsafe.Pointer(value), length) } + + C.fdb_future_release_memory(f.ptr) }) return f.v, f.e @@ -197,7 +199,7 @@ type futureKey struct { func (f *futureKey) Get() (Key, error) { f.o.Do(func() { - defer C.fdb_future_destroy(f.ptr) + defer runtime.KeepAlive(f.future) var value *C.uint8_t var length C.int @@ -209,11 +211,8 @@ func (f *futureKey) Get() (Key, error) { return } - keySource := C.GoBytes(unsafe.Pointer(value), length) - keyDestination := make([]byte, length) - copy(keyDestination, keySource) - - f.k = keyDestination + f.k = C.GoBytes(unsafe.Pointer(value), length) + C.fdb_future_release_memory(f.ptr) }) return f.k, f.e @@ -246,21 +245,17 @@ type FutureNil interface { type futureNil struct { *future - o sync.Once - e error } func (f *futureNil) Get() error { - f.o.Do(func() { - defer C.fdb_future_destroy(f.ptr) + defer runtime.KeepAlive(f.future) - f.BlockUntilReady() - if err := C.fdb_future_get_error(f.ptr); err != 0 { - f.e = Error{int(err)} - } - }) + f.BlockUntilReady() + if err := C.fdb_future_get_error(f.ptr); err != 0 { + return Error{int(err)} + } - return f.e + return nil } func (f *futureNil) MustGet() { @@ -287,6 +282,8 @@ func stringRefToSlice(ptr unsafe.Pointer) []byte { } func (f *futureKeyValueArray) Get() ([]KeyValue, bool, error) { + defer runtime.KeepAlive(f.future) + f.BlockUntilReady() var kvs *C.FDBKeyValue @@ -297,42 +294,13 @@ func (f *futureKeyValueArray) Get() ([]KeyValue, bool, error) { return nil, false, Error{int(err)} } - // To minimize the number of individual allocations, we first calculate the - // final size used by all keys and values returned from this iteration, - // then perform one larger allocation and slice within it. - - poolSize := 0 - for i := 0; i < int(count); i++ { - kvptr := unsafe.Pointer(uintptr(unsafe.Pointer(kvs)) + uintptr(i*24)) - - poolSize += len(stringRefToSlice(kvptr)) - poolSize += len(stringRefToSlice(unsafe.Pointer(uintptr(kvptr) + 12))) - } - - poolOffset := 0 - pool := make([]byte, poolSize) - ret := make([]KeyValue, int(count)) for i := 0; i < int(count); i++ { kvptr := unsafe.Pointer(uintptr(unsafe.Pointer(kvs)) + uintptr(i*24)) - keySource := stringRefToSlice(kvptr) - valueSource := stringRefToSlice(unsafe.Pointer(uintptr(kvptr) + 12)) - - keyDestination := pool[poolOffset : poolOffset+len(keySource)] - poolOffset += len(keySource) - - valueDestination := pool[poolOffset : poolOffset+len(valueSource)] - poolOffset += len(valueSource) - - copy(keyDestination, keySource) - copy(valueDestination, valueSource) - - ret[i] = KeyValue{ - Key: keyDestination, - Value: valueDestination, - } + ret[i].Key = stringRefToSlice(kvptr) + ret[i].Value = stringRefToSlice(unsafe.Pointer(uintptr(kvptr) + 12)) } return ret, (more != 0), nil @@ -357,28 +325,19 @@ type FutureInt64 interface { type futureInt64 struct { *future - o sync.Once - e error - v int64 } func (f *futureInt64) Get() (int64, error) { - f.o.Do(func() { - defer C.fdb_future_destroy(f.ptr) + defer runtime.KeepAlive(f.future) - f.BlockUntilReady() + f.BlockUntilReady() - var ver C.int64_t - if err := C.fdb_future_get_int64(f.ptr, &ver); err != 0 { - f.v = 0 - f.e = Error{int(err)} - return - } + var ver C.int64_t + if err := C.fdb_future_get_int64(f.ptr, &ver); err != 0 { + return 0, Error{int(err)} + } - f.v = int64(ver) - }) - - return f.v, f.e + return int64(ver), nil } func (f *futureInt64) MustGet() int64 { @@ -409,40 +368,27 @@ type FutureStringSlice interface { type futureStringSlice struct { *future - o sync.Once - e error - v []string } func (f *futureStringSlice) Get() ([]string, error) { - f.o.Do(func() { - defer C.fdb_future_destroy(f.ptr) + defer runtime.KeepAlive(f.future) - f.BlockUntilReady() + f.BlockUntilReady() - var strings **C.char - var count C.int + var strings **C.char + var count C.int - if err := C.fdb_future_get_string_array(f.ptr, (***C.char)(unsafe.Pointer(&strings)), &count); err != 0 { - f.e = Error{int(err)} - return - } + if err := C.fdb_future_get_string_array(f.ptr, (***C.char)(unsafe.Pointer(&strings)), &count); err != 0 { + return nil, Error{int(err)} + } - ret := make([]string, int(count)) + ret := make([]string, int(count)) - for i := 0; i < int(count); i++ { - source := C.GoString((*C.char)(*(**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(strings)) + uintptr(i*8))))) + for i := 0; i < int(count); i++ { + ret[i] = C.GoString((*C.char)(*(**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(strings)) + uintptr(i*8))))) + } - destination := make([]byte, len(source)) - copy(destination, source) - - ret[i] = string(destination) - } - - f.v = ret - }) - - return f.v, f.e + return ret, nil } func (f *futureStringSlice) MustGet() []string { diff --git a/bindings/go/src/fdb/range.go b/bindings/go/src/fdb/range.go index 5d9e635b39..67a45c63b2 100644 --- a/bindings/go/src/fdb/range.go +++ b/bindings/go/src/fdb/range.go @@ -28,7 +28,6 @@ import "C" import ( "fmt" - "sync" ) // KeyValue represents a single key-value pair in the database. @@ -141,7 +140,6 @@ func (rr RangeResult) GetSliceWithError() ([]KeyValue, error) { var ret []KeyValue ri := rr.Iterator() - defer ri.Close() if rr.options.Limit != 0 { ri.options.Mode = StreamingModeExact @@ -209,18 +207,6 @@ type RangeIterator struct { index int err error snapshot bool - o sync.Once -} - -// Close releases the underlying native resources for all the `KeyValue`s -// ever returned by this iterator. The `KeyValue`s themselves are copied -// before they're returned, so they are still safe to use after calling -// this function. This is instended to be called with `defer` inside -// your transaction function. -func (ri *RangeIterator) Close() { - ri.o.Do(func() { - C.fdb_future_destroy(ri.f.ptr) - }) } // Advance attempts to advance the iterator to the next key-value pair. Advance diff --git a/bindings/go/src/fdb/transaction.go b/bindings/go/src/fdb/transaction.go index cbd079a216..4102a0556b 100644 --- a/bindings/go/src/fdb/transaction.go +++ b/bindings/go/src/fdb/transaction.go @@ -25,7 +25,6 @@ package fdb // #define FDB_API_VERSION 630 // #include import "C" -import "sync" // A ReadTransaction can asynchronously read from a FoundationDB // database. Transaction and Snapshot both satisfy the ReadTransaction @@ -71,7 +70,6 @@ type Transaction struct { type transaction struct { ptr *C.FDBTransaction db Database - o sync.Once } // TransactionOptions is a handle with which to set options that affect a @@ -87,18 +85,16 @@ func (opt TransactionOptions) setOpt(code int, param []byte) error { }, param) } +func (t *transaction) destroy() { + C.fdb_transaction_destroy(t.ptr) +} + // GetDatabase returns a handle to the database with which this transaction is // interacting. func (t Transaction) GetDatabase() Database { return t.transaction.db } -func (t Transaction) Close() { - t.o.Do(func() { - C.fdb_transaction_destroy(t.ptr) - }) -} - // Transact executes the caller-provided function, passing it the Transaction // receiver object. // diff --git a/documentation/sphinx/source/release-notes.rst b/documentation/sphinx/source/release-notes.rst index afd5e1b440..72e0df1b5c 100644 --- a/documentation/sphinx/source/release-notes.rst +++ b/documentation/sphinx/source/release-notes.rst @@ -28,8 +28,6 @@ Bindings * Java: Introduced ``keyAfter`` utility function that can be used to create the immediate next key for a given byte array. `(PR #2458) `_ * C: The ``FDBKeyValue`` struct's ``key`` and ``value`` members have changed type from ``void*`` to ``uint8_t*``. `(PR #2622) `_ * Deprecated ``enable_slow_task_profiling`` transaction option and replaced it with ``enable_run_loop_profiling``. `(PR #2608) `_ -* Go: Added a ``Close`` function to ``RangeIterator`` which **must** be called to free resources returned from ``Transaction.GetRange``. `(PR #1910) `_. -* Go: Finalizers are no longer used to clean up native resources. ``Future`` results are now copied from the native heap to the Go heap, and native resources are freed immediately. `(PR #1910) `_. Other Changes ------------- From 7cebe743f9316f3df97114959380fa3db722507e Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 29 Apr 2020 13:50:13 -0700 Subject: [PATCH 1567/1604] A number of bug fixes of rare correctness errors --- fdbclient/DatabaseBackupAgent.actor.cpp | 9 ++++++--- fdbclient/ReadYourWrites.actor.cpp | 7 ++++++- fdbserver/Knobs.cpp | 2 +- fdbserver/LogRouter.actor.cpp | 2 +- fdbserver/OldTLogServer_6_0.actor.cpp | 4 ++++ fdbserver/OldTLogServer_6_2.actor.cpp | 4 ++++ fdbserver/TLogServer.actor.cpp | 4 ++++ fdbserver/workloads/WriteDuringRead.actor.cpp | 2 +- 8 files changed, 27 insertions(+), 7 deletions(-) diff --git a/fdbclient/DatabaseBackupAgent.actor.cpp b/fdbclient/DatabaseBackupAgent.actor.cpp index 8d2fe7ea4e..72c8529b79 100644 --- a/fdbclient/DatabaseBackupAgent.actor.cpp +++ b/fdbclient/DatabaseBackupAgent.actor.cpp @@ -1490,6 +1490,12 @@ namespace dbBackup { Version bVersion = wait(srcTr->getReadVersion()); beginVersionKey = BinaryWriter::toValue(bVersion, Unversioned()); + state Key versionKey = logUidValue.withPrefix(destUidValue).withPrefix(backupLatestVersionsPrefix); + Optional versionRecord = wait( scrTr->get(versionKey) ); + if(!versionRecord.present()) { + srcTr->set(versionKey, beginVersionKey); + } + task->params[BackupAgentBase::destUid] = destUidValue; wait(srcTr->commit()); @@ -1539,9 +1545,6 @@ namespace dbBackup { if(v.present() && BinaryReader::fromStringRef(v.get(), Unversioned()) >= BinaryReader::fromStringRef(task->params[DatabaseBackupAgent::keyFolderId], Unversioned())) return Void(); - Key versionKey = logUidValue.withPrefix(destUidValue).withPrefix(backupLatestVersionsPrefix); - srcTr2->set(versionKey, beginVersionKey); - srcTr2->set( Subspace(databaseBackupPrefixRange.begin).get(BackupAgentBase::keySourceTagName).pack(task->params[BackupAgentBase::keyTagName]), logUidValue ); srcTr2->set( sourceStates.pack(DatabaseBackupAgent::keyFolderId), task->params[DatabaseBackupAgent::keyFolderId] ); srcTr2->set( sourceStates.pack(DatabaseBackupAgent::keyStateStatus), StringRef(BackupAgentBase::getStateText(BackupAgentBase::STATE_RUNNING))); diff --git a/fdbclient/ReadYourWrites.actor.cpp b/fdbclient/ReadYourWrites.actor.cpp index ff0b5dba3e..4ff52e426f 100644 --- a/fdbclient/ReadYourWrites.actor.cpp +++ b/fdbclient/ReadYourWrites.actor.cpp @@ -1019,7 +1019,12 @@ public: return Void(); } - watchFuture = ryw->tr.watch(watch); // throws if there are too many outstanding watches + try { + watchFuture = ryw->tr.watch(watch); // throws if there are too many outstanding watches + } catch( Error &e ) { + done.send(Void()); + throw; + } done.send(Void()); wait(watchFuture); diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index a194ac3deb..5e889e307b 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -461,7 +461,7 @@ void ServerKnobs::initialize(bool randomize, ClientKnobs* clientKnobs, bool isSi init( SPRING_BYTES_TLOG_BATCH, 300e6 ); if( smallTlogTarget ) SPRING_BYTES_TLOG_BATCH = 150e3; init( TLOG_SPILL_THRESHOLD, 1500e6 ); if( smallTlogTarget ) TLOG_SPILL_THRESHOLD = 1500e3; if( randomize && BUGGIFY ) TLOG_SPILL_THRESHOLD = 0; init( REFERENCE_SPILL_UPDATE_STORAGE_BYTE_LIMIT, 20e6 ); if( (randomize && BUGGIFY) || smallTlogTarget ) REFERENCE_SPILL_UPDATE_STORAGE_BYTE_LIMIT = 1e6; - init( TLOG_HARD_LIMIT_BYTES, 3000e6 ); if( smallTlogTarget ) TLOG_HARD_LIMIT_BYTES = 3000e3; + init( TLOG_HARD_LIMIT_BYTES, 3000e6 ); if( smallTlogTarget ) TLOG_HARD_LIMIT_BYTES = 30e6; init( TLOG_RECOVER_MEMORY_LIMIT, TARGET_BYTES_PER_TLOG + SPRING_BYTES_TLOG ); init( MAX_TRANSACTIONS_PER_BYTE, 1000 ); diff --git a/fdbserver/LogRouter.actor.cpp b/fdbserver/LogRouter.actor.cpp index 4dcb0c60be..6f6e876519 100644 --- a/fdbserver/LogRouter.actor.cpp +++ b/fdbserver/LogRouter.actor.cpp @@ -347,7 +347,7 @@ ACTOR Future logRouterPeekMessages( LogRouterData* self, TLogPeekRequest r peekId = req.sequence.get().first; sequence = req.sequence.get().second; if (sequence >= SERVER_KNOBS->PARALLEL_GET_MORE_REQUESTS && self->peekTracker.find(peekId) == self->peekTracker.end()) { - throw timed_out(); + throw operation_obsolete(); } auto& trackerData = self->peekTracker[peekId]; if (sequence == 0 && trackerData.sequence_version.find(0) == trackerData.sequence_version.end()) { diff --git a/fdbserver/OldTLogServer_6_0.actor.cpp b/fdbserver/OldTLogServer_6_0.actor.cpp index 4754bf6dad..838c32dd79 100644 --- a/fdbserver/OldTLogServer_6_0.actor.cpp +++ b/fdbserver/OldTLogServer_6_0.actor.cpp @@ -1088,6 +1088,10 @@ ACTOR Future tLogPeekMessages( TLogData* self, TLogPeekRequest req, Refere state UID peekId; state double queueStart = now(); + if(req.tag.locality == tagLocalityTxs && req.tag.id >= logData->txsTags && logData->txsTags > 0) { + req.tag.id = req.tag.id % logData->txsTags; + } + if(req.sequence.present()) { try { peekId = req.sequence.get().first; diff --git a/fdbserver/OldTLogServer_6_2.actor.cpp b/fdbserver/OldTLogServer_6_2.actor.cpp index 4a9f9c9158..c33d1c3471 100644 --- a/fdbserver/OldTLogServer_6_2.actor.cpp +++ b/fdbserver/OldTLogServer_6_2.actor.cpp @@ -1391,6 +1391,10 @@ ACTOR Future tLogPeekMessages( TLogData* self, TLogPeekRequest req, Refere state int sequence = -1; state UID peekId; state double queueStart = now(); + + if(req.tag.locality == tagLocalityTxs && req.tag.id >= logData->txsTags && logData->txsTags > 0) { + req.tag.id = req.tag.id % logData->txsTags; + } if(req.sequence.present()) { try { diff --git a/fdbserver/TLogServer.actor.cpp b/fdbserver/TLogServer.actor.cpp index ae53590cba..242d5f90cf 100644 --- a/fdbserver/TLogServer.actor.cpp +++ b/fdbserver/TLogServer.actor.cpp @@ -1404,6 +1404,10 @@ ACTOR Future tLogPeekMessages( TLogData* self, TLogPeekRequest req, Refere state int sequence = -1; state UID peekId; state double queueStart = now(); + + if(req.tag.locality == tagLocalityTxs && req.tag.id >= logData->txsTags && logData->txsTags > 0) { + req.tag.id = req.tag.id % logData->txsTags; + } if(req.sequence.present()) { try { diff --git a/fdbserver/workloads/WriteDuringRead.actor.cpp b/fdbserver/workloads/WriteDuringRead.actor.cpp index a446d093c8..24be079176 100644 --- a/fdbserver/workloads/WriteDuringRead.actor.cpp +++ b/fdbserver/workloads/WriteDuringRead.actor.cpp @@ -827,7 +827,7 @@ struct WriteDuringReadWorkload : TestWorkload { self->addedConflicts.insert(allKeys, false); return Void(); } - if( e.code() == error_code_not_committed || e.code() == error_code_commit_unknown_result || e.code() == error_code_transaction_too_large || e.code() == error_code_key_too_large || e.code() == error_code_value_too_large || cancelled ) + if( e.code() == error_code_not_committed || e.code() == error_code_commit_unknown_result || e.code() == error_code_transaction_too_large || e.code() == error_code_key_too_large || e.code() == error_code_value_too_large || e.code() == error_code_too_many_watches || cancelled ) throw not_committed(); try { wait( tr.onError(e) ); From 0420b3e786484026e3d92ebb132cd878ad7c7aa0 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Wed, 29 Apr 2020 14:05:53 -0700 Subject: [PATCH 1568/1604] fix compile error --- fdbclient/DatabaseBackupAgent.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbclient/DatabaseBackupAgent.actor.cpp b/fdbclient/DatabaseBackupAgent.actor.cpp index 72c8529b79..1da07379e7 100644 --- a/fdbclient/DatabaseBackupAgent.actor.cpp +++ b/fdbclient/DatabaseBackupAgent.actor.cpp @@ -1491,7 +1491,7 @@ namespace dbBackup { beginVersionKey = BinaryWriter::toValue(bVersion, Unversioned()); state Key versionKey = logUidValue.withPrefix(destUidValue).withPrefix(backupLatestVersionsPrefix); - Optional versionRecord = wait( scrTr->get(versionKey) ); + Optional versionRecord = wait( srcTr->get(versionKey) ); if(!versionRecord.present()) { srcTr->set(versionKey, beginVersionKey); } From c62a4aef974eb9808097bde1fa803a4972498712 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Wed, 29 Apr 2020 15:06:02 -0700 Subject: [PATCH 1569/1604] Upgrade TLS symbol not found message to WARNING --- cmake/FDBComponents.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/FDBComponents.cmake b/cmake/FDBComponents.cmake index 817d173f4f..2e5777d263 100644 --- a/cmake/FDBComponents.cmake +++ b/cmake/FDBComponents.cmake @@ -27,7 +27,7 @@ else() set(WITH_TLS ON) add_compile_options(-DHAVE_OPENSSL) else() - message(STATUS "An OpenSSL version was found, but it doesn't support OPENSSL_INIT_NO_ATEXIT - Will compile without TLS Support") + message(WARNING "An OpenSSL version was found, but it doesn't support OPENSSL_INIT_NO_ATEXIT - Will compile without TLS Support") message(STATUS "You can set OPENSSL_ROOT_DIR to help cmake find it") set(WITH_TLS OFF) endif() From 7659b1bff6fdf18dae130353f6a401bcaf56ffe1 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Wed, 29 Apr 2020 15:11:35 -0700 Subject: [PATCH 1570/1604] Use check_symbol_exists --- cmake/FDBComponents.cmake | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cmake/FDBComponents.cmake b/cmake/FDBComponents.cmake index 2e5777d263..2b5d132c74 100644 --- a/cmake/FDBComponents.cmake +++ b/cmake/FDBComponents.cmake @@ -20,9 +20,7 @@ else() find_package(OpenSSL) if(OPENSSL_FOUND) set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) - CHECK_CXX_SOURCE_COMPILES( - "#include - int main() { (void) OPENSSL_INIT_NO_ATEXIT; }" OPENSSL_HAS_NO_ATEXIT) + check_symbol_exists("OPENSSL_INIT_NO_ATEXIT" "openssl/crypto.h" OPENSSL_HAS_NO_ATEXIT) if(OPENSSL_HAS_NO_ATEXIT) set(WITH_TLS ON) add_compile_options(-DHAVE_OPENSSL) From 05614d33ccf859a73eda4b6b86519f86d800c732 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Wed, 29 Apr 2020 22:16:43 +0000 Subject: [PATCH 1571/1604] Add include(CheckSymbolExists) --- cmake/FDBComponents.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/FDBComponents.cmake b/cmake/FDBComponents.cmake index 2b5d132c74..38fe3ecd98 100644 --- a/cmake/FDBComponents.cmake +++ b/cmake/FDBComponents.cmake @@ -11,7 +11,8 @@ endif() ################################################################################ # SSL ################################################################################ - +include(CheckSymbolExists) + set(DISABLE_TLS OFF CACHE BOOL "Don't try to find OpenSSL and always build without TLS support") if(DISABLE_TLS) set(WITH_TLS OFF) From 33dedaab359e6c1a0cf0d507f1326a060c2fa92c Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Wed, 29 Apr 2020 15:44:54 -0700 Subject: [PATCH 1572/1604] FastRestore:Fix submitParallelRestore when targetVersion is unset --- fdbclient/FileBackupAgent.actor.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/fdbclient/FileBackupAgent.actor.cpp b/fdbclient/FileBackupAgent.actor.cpp index b4cefe9eb6..f1607c8f5c 100644 --- a/fdbclient/FileBackupAgent.actor.cpp +++ b/fdbclient/FileBackupAgent.actor.cpp @@ -3633,6 +3633,12 @@ public: state BackupDescription desc = wait(bc->describeBackup()); wait(desc.resolveVersionTimes(cx)); + if (targetVersion == invalidVersion && desc.maxRestorableVersion.present()) { + targetVersion = desc.maxRestorableVersion.get(); + TraceEvent(SevWarn, "FastRestoreSubmitRestoreRequestWithInvalidTargetVersion") + .detail("OverrideTargetVersion", targetVersion); + } + Optional restoreSet = wait(bc->getRestoreSet(targetVersion)); if (!restoreSet.present()) { @@ -3642,11 +3648,6 @@ public: throw restore_invalid_version(); } - if (targetVersion == invalidVersion && desc.maxRestorableVersion.present()) { - targetVersion = desc.maxRestorableVersion.get(); - TraceEvent(SevWarn, "FastRestoreSubmitRestoreRequestWithInvalidTargetVersion") - .detail("OverrideTargetVersion", targetVersion); - } TraceEvent("FastRestoreSubmitRestoreRequest") .detail("BackupDesc", desc.toString()) .detail("TargetVersion", targetVersion); From 27043ee92f6d24cdf7ce2b96359e5617810a9b78 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Wed, 29 Apr 2020 15:45:33 -0700 Subject: [PATCH 1573/1604] Update cmake/FDBComponents.cmake Co-Authored-By: Markus Pilman --- cmake/FDBComponents.cmake | 1 - 1 file changed, 1 deletion(-) diff --git a/cmake/FDBComponents.cmake b/cmake/FDBComponents.cmake index 38fe3ecd98..f3df9331dc 100644 --- a/cmake/FDBComponents.cmake +++ b/cmake/FDBComponents.cmake @@ -27,7 +27,6 @@ else() add_compile_options(-DHAVE_OPENSSL) else() message(WARNING "An OpenSSL version was found, but it doesn't support OPENSSL_INIT_NO_ATEXIT - Will compile without TLS Support") - message(STATUS "You can set OPENSSL_ROOT_DIR to help cmake find it") set(WITH_TLS OFF) endif() else() From 519ac70a2ad72dc3bc1823a52a3c8bfa4526b9f4 Mon Sep 17 00:00:00 2001 From: Evan Tschannen <36455792+etschannen@users.noreply.github.com> Date: Wed, 29 Apr 2020 15:51:29 -0700 Subject: [PATCH 1574/1604] Revert "Enable -Wclass-memaccess and fix warnings" --- cmake/ConfigureCompiler.cmake | 1 - fdbclient/FDBTypes.h | 5 ----- fdbserver/DiskQueue.actor.cpp | 2 +- fdbserver/VFSAsync.cpp | 2 +- flow/Arena.h | 35 ++++++++++++++++------------------- 5 files changed, 18 insertions(+), 27 deletions(-) diff --git a/cmake/ConfigureCompiler.cmake b/cmake/ConfigureCompiler.cmake index 8533aa5be3..33b749c0ee 100644 --- a/cmake/ConfigureCompiler.cmake +++ b/cmake/ConfigureCompiler.cmake @@ -242,7 +242,6 @@ else() -fvisibility=hidden -Wreturn-type -fPIC) - add_compile_options($<$:-Wclass-memaccess>) if (GPERFTOOLS_FOUND AND GCC) add_compile_options( -fno-builtin-malloc diff --git a/fdbclient/FDBTypes.h b/fdbclient/FDBTypes.h index 4a6341aca6..318832a227 100644 --- a/fdbclient/FDBTypes.h +++ b/fdbclient/FDBTypes.h @@ -26,7 +26,6 @@ #include #include -#include "flow/Arena.h" #include "flow/flow.h" #include "fdbclient/Knobs.h" @@ -78,10 +77,6 @@ struct Tag { serializer(ar, locality, id); } }; - -template <> -struct non_flow_ref : std::integral_constant {}; - #pragma pack(pop) template void load( Ar& ar, Tag& tag ) { tag.serialize_unversioned(ar); } diff --git a/fdbserver/DiskQueue.actor.cpp b/fdbserver/DiskQueue.actor.cpp index 9ec422ad7c..1f38dfb8ee 100644 --- a/fdbserver/DiskQueue.actor.cpp +++ b/fdbserver/DiskQueue.actor.cpp @@ -1013,7 +1013,7 @@ private: ASSERT( nextPageSeq%sizeof(Page)==0 ); auto& p = backPage(); - memset(static_cast(&p), 0, sizeof(Page)); // FIXME: unnecessary? + memset(&p, 0, sizeof(Page)); // FIXME: unnecessary? p.magic = 0xFDB; switch (diskQueueVersion) { case DiskQueueVersion::V0: diff --git a/fdbserver/VFSAsync.cpp b/fdbserver/VFSAsync.cpp index 0a1feff976..3d53aaccfb 100644 --- a/fdbserver/VFSAsync.cpp +++ b/fdbserver/VFSAsync.cpp @@ -531,7 +531,7 @@ static int asyncOpen( if (flags & SQLITE_OPEN_WAL) oflags |= IAsyncFile::OPEN_LARGE_PAGES; oflags |= IAsyncFile::OPEN_LOCK; - memset(static_cast(p), 0, sizeof(VFSAsyncFile)); + memset(p, 0, sizeof(VFSAsyncFile)); new (p) VFSAsyncFile(zName, flags); try { // Note that SQLiteDB::open also opens the db file, so its flags and modes are important, too diff --git a/flow/Arena.h b/flow/Arena.h index cad0b13083..cfc756506d 100644 --- a/flow/Arena.h +++ b/flow/Arena.h @@ -699,18 +699,15 @@ inline bool operator != (const StringRef& lhs, const StringRef& rhs ) { return ! inline bool operator <= ( const StringRef& lhs, const StringRef& rhs ) { return !(lhs>rhs); } inline bool operator >= ( const StringRef& lhs, const StringRef& rhs ) { return !(lhs -struct non_flow_ref : std::is_fundamental {}; +struct memcpy_able : std::is_trivial {}; template <> -struct non_flow_ref : std::integral_constant {}; - -template -struct non_flow_ref> : std::integral_constant {}; +struct memcpy_able : std::integral_constant {}; template struct string_serialized_traits : std::false_type { @@ -786,7 +783,7 @@ public: using value_type = T; static_assert(SerStrategy == VecSerStrategy::FlatBuffers || string_serialized_traits::value); - // T must be trivially destructible! + // T must be trivially destructible (and copyable)! VectorRef() : data(0), m_size(0), m_capacity(0) {} template @@ -801,19 +798,19 @@ public: return *this; } - // Arena constructor for non-Ref types, identified by non_flow_ref + // Arena constructor for non-Ref types, identified by memcpy_able template - VectorRef(Arena& p, const VectorRef& toCopy, typename std::enable_if::value, int>::type = 0) + VectorRef(Arena& p, const VectorRef& toCopy, typename std::enable_if::value, int>::type = 0) : VPS(toCopy), data((T*)new (p) uint8_t[sizeof(T) * toCopy.size()]), m_size(toCopy.size()), m_capacity(toCopy.size()) { if (m_size > 0) { - std::copy(toCopy.data, toCopy.data + m_size, data); + memcpy(data, toCopy.data, m_size * sizeof(T)); } } // Arena constructor for Ref types, which must have an Arena constructor template - VectorRef(Arena& p, const VectorRef& toCopy, typename std::enable_if::value, int>::type = 0) + VectorRef(Arena& p, const VectorRef& toCopy, typename std::enable_if::value, int>::type = 0) : VPS(), data((T*)new (p) uint8_t[sizeof(T) * toCopy.size()]), m_size(toCopy.size()), m_capacity(toCopy.size()) { for (int i = 0; i < m_size; i++) { auto ptr = new (&data[i]) T(p, toCopy[i]); @@ -903,7 +900,7 @@ public: if (m_size + count > m_capacity) reallocate(p, m_size + count); VPS::invalidate(); if (count > 0) { - std::copy(begin, begin + count, data + m_size); + memcpy(data + m_size, begin, sizeof(T) * count); } m_size += count; } @@ -943,15 +940,15 @@ public: if (size > m_capacity) reallocate(p, size); } - // expectedSize() for non-Ref types, identified by non_flow_ref + // expectedSize() for non-Ref types, identified by memcpy_able template - typename std::enable_if::value, size_t>::type expectedSize() const { + typename std::enable_if::value, size_t>::type expectedSize() const { return sizeof(T) * m_size; } // expectedSize() for Ref types, which must in turn have expectedSize() implemented. template - typename std::enable_if::value, size_t>::type expectedSize() const { + typename std::enable_if::value, size_t>::type expectedSize() const { size_t t = sizeof(T) * m_size; for (int i = 0; i < m_size; i++) t += data[i].expectedSize(); return t; @@ -970,7 +967,7 @@ private: // SOMEDAY: Maybe we are right at the end of the arena and can expand cheaply T* newData = (T*)new (p) uint8_t[requiredCapacity * sizeof(T)]; if (m_size > 0) { - std::move(data, data + m_size, newData); + memcpy(newData, data, m_size * sizeof(T)); } data = newData; m_capacity = requiredCapacity; From 9da76c35ea77814a8b9c267bf4faf250de58fa0d Mon Sep 17 00:00:00 2001 From: Jingyu Zhou Date: Wed, 29 Apr 2020 15:55:34 -0700 Subject: [PATCH 1575/1604] Fix a memory corruption error The backup container URL should be Key instead of KeyRef. --- fdbclient/BackupAgent.actor.h | 2 +- fdbclient/FileBackupAgent.actor.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fdbclient/BackupAgent.actor.h b/fdbclient/BackupAgent.actor.h index 400b4e9a6f..b0d95f23cb 100644 --- a/fdbclient/BackupAgent.actor.h +++ b/fdbclient/BackupAgent.actor.h @@ -278,7 +278,7 @@ public: // parallel restore Future parallelRestoreFinish(Database cx, UID randomUID); Future submitParallelRestore(Database cx, Key backupTag, Standalone> backupRanges, - KeyRef bcUrl, Version targetVersion, bool lockDB, UID randomUID); + Key bcUrl, Version targetVersion, bool lockDB, UID randomUID); Future atomicParallelRestore(Database cx, Key tagName, Standalone> ranges, Key addPrefix, Key removePrefix); diff --git a/fdbclient/FileBackupAgent.actor.cpp b/fdbclient/FileBackupAgent.actor.cpp index 83fb5d69bd..38d8bf01eb 100644 --- a/fdbclient/FileBackupAgent.actor.cpp +++ b/fdbclient/FileBackupAgent.actor.cpp @@ -3628,7 +3628,7 @@ public: } ACTOR static Future submitParallelRestore(Database cx, Key backupTag, - Standalone> backupRanges, KeyRef bcUrl, + Standalone> backupRanges, Key bcUrl, Version targetVersion, bool lockDB, UID randomUID) { state Reference tr(new ReadYourWritesTransaction(cx)); state int restoreIndex = 0; @@ -4608,7 +4608,7 @@ Future FileBackupAgent::parallelRestoreFinish(Database cx, UID randomUID) } Future FileBackupAgent::submitParallelRestore(Database cx, Key backupTag, - Standalone> backupRanges, KeyRef bcUrl, + Standalone> backupRanges, Key bcUrl, Version targetVersion, bool lockDB, UID randomUID) { return FileBackupAgentImpl::submitParallelRestore(cx, backupTag, backupRanges, bcUrl, targetVersion, lockDB, randomUID); From 7a8cb3ffcf1fe00083b2e3083ed8e483ff29cb48 Mon Sep 17 00:00:00 2001 From: Dave Cottlehuber Date: Sat, 1 Feb 2020 17:52:24 +0000 Subject: [PATCH 1576/1604] add headers --- fdbrpc/libeio/config.h.FreeBSD | 142 +++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100755 fdbrpc/libeio/config.h.FreeBSD diff --git a/fdbrpc/libeio/config.h.FreeBSD b/fdbrpc/libeio/config.h.FreeBSD new file mode 100755 index 0000000000..4770bace5f --- /dev/null +++ b/fdbrpc/libeio/config.h.FreeBSD @@ -0,0 +1,142 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* fdatasync(2) is available */ +#define HAVE_FDATASYNC 1 + +/* futimes(2) is available */ +#define HAVE_FUTIMES 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* fallocate(2) is available */ +/* #undef HAVE_LINUX_FALLOCATE */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LINUX_FIEMAP_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LINUX_FS_H */ + +/* splice/vmsplice/tee(2) are available */ +/* #undef HAVE_LINUX_SPLICE */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* posix_fadvise(2) is available */ +#define HAVE_POSIX_FADVISE 1 + +/* posix_madvise(2) is available */ +#define HAVE_POSIX_MADVISE 1 + +/* prctl(PR_SET_NAME) is available */ +/* #undef HAVE_PRCTL_SET_NAME */ + +/* readahead(2) is available (linux) */ +/* #undef HAVE_READAHEAD */ + +/* sendfile(2) is available and supported */ +#define HAVE_SENDFILE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* sync_file_range(2) is available */ +/* #undef HAVE_SYNC_FILE_RANGE */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_PRCTL_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* syscall(__NR_syncfs) is available */ +/* #undef HAVE_SYS_SYNCFS */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SYSCALL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* utimes(2) is available */ +#define HAVE_UTIMES 1 + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "libeio" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif + + +/* Version number of package */ +#define VERSION "1.0" + +/* Define to 1 if on MINIX. */ +/* #undef _MINIX */ + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +/* #undef _POSIX_SOURCE */ From e227b4aba31112c5206b755a76e6fdb5f0e0af87 Mon Sep 17 00:00:00 2001 From: Dave Cottlehuber Date: Sat, 1 Feb 2020 17:53:53 +0000 Subject: [PATCH 1577/1604] tweak bashisms --- bindings/bindingtester/run_binding_tester.sh | 2 +- bindings/bindingtester/run_tester_loop.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/bindingtester/run_binding_tester.sh b/bindings/bindingtester/run_binding_tester.sh index f676e7796b..06c3f0a710 100644 --- a/bindings/bindingtester/run_binding_tester.sh +++ b/bindings/bindingtester/run_binding_tester.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash ###################################################### # # FoundationDB Binding Test Script diff --git a/bindings/bindingtester/run_tester_loop.sh b/bindings/bindingtester/run_tester_loop.sh index d78915a489..2bdcee44b3 100755 --- a/bindings/bindingtester/run_tester_loop.sh +++ b/bindings/bindingtester/run_tester_loop.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash LOGGING_LEVEL=WARNING From 98a6979f5b0bdd713dcec1d96402a9dd459e5f35 Mon Sep 17 00:00:00 2001 From: Dave Cottlehuber Date: Sat, 1 Feb 2020 17:55:31 +0000 Subject: [PATCH 1578/1604] update link options in CMakeLists --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f013d57e6..3578ab51f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -173,6 +173,10 @@ else() include(CPack) endif() +if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + add_link_options(-lexecinfo) +endif() + ################################################################################ # process compile commands for IDE ################################################################################ From 98639645b1ab5544307d88146c13aefebacddd51 Mon Sep 17 00:00:00 2001 From: Dave Cottlehuber Date: Sat, 1 Feb 2020 18:00:06 +0000 Subject: [PATCH 1579/1604] fdbserver: update headers --- fdbserver/fdbserver.actor.cpp | 7 ++++--- fdbserver/worker.actor.cpp | 8 +++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/fdbserver/fdbserver.actor.cpp b/fdbserver/fdbserver.actor.cpp index 78a6ad7211..fd0c6844be 100644 --- a/fdbserver/fdbserver.actor.cpp +++ b/fdbserver/fdbserver.actor.cpp @@ -60,7 +60,7 @@ #include "fdbmonitor/SimpleIni.h" -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) #include #include #ifdef ALLOC_INSTRUMENTATION @@ -75,6 +75,7 @@ #endif #include "flow/SimpleOpt.h" +#include #include "flow/actorcompiler.h" // This must be the last #include. // clang-format off @@ -291,7 +292,7 @@ public: throw platform_error(); } permission.set_permissions( &sa ); -#elif (defined(__linux__) || defined(__APPLE__)) +#elif (defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)) // There is nothing to do here, since the default permissions are fine #else #error Port me! @@ -301,7 +302,7 @@ public: virtual ~WorldReadablePermissions() { #ifdef _WIN32 LocalFree( sa.lpSecurityDescriptor ); -#elif (defined(__linux__) || defined(__APPLE__)) +#elif (defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)) // There is nothing to do here, since the default permissions are fine #else #error Port me! diff --git a/fdbserver/worker.actor.cpp b/fdbserver/worker.actor.cpp index 9beffc5ece..32c4252159 100644 --- a/fdbserver/worker.actor.cpp +++ b/fdbserver/worker.actor.cpp @@ -48,6 +48,8 @@ #include #include #include +#endif +#if defined(__linux__) || defined(__FreeBSD__) #ifdef USE_GPERFTOOLS #include "gperftools/profiler.h" #include "gperftools/heap-profiler.h" @@ -526,7 +528,7 @@ ACTOR Future registrationClient( } } -#if defined(__linux__) && defined(USE_GPERFTOOLS) +#if (defined(__linux__) || defined(__FreeBSD__)) && defined(USE_GPERFTOOLS) //A set of threads that should be profiled std::set profiledThreads; @@ -538,7 +540,7 @@ int filter_in_thread(void *arg) { //Enables the calling thread to be profiled void registerThreadForProfiling() { -#if defined(__linux__) && defined(USE_GPERFTOOLS) +#if (defined(__linux__) || defined(__FreeBSD__)) && defined(USE_GPERFTOOLS) //Not sure if this is actually needed, but a call to backtrace was advised here: //http://groups.google.com/group/google-perftools/browse_thread/thread/0dfd74532e038eb8/2686d9f24ac4365f?pli=1 profiledThreads.insert(std::this_thread::get_id()); @@ -552,7 +554,7 @@ void registerThreadForProfiling() { void updateCpuProfiler(ProfilerRequest req) { switch (req.type) { case ProfilerRequest::Type::GPROF: -#if defined(__linux__) && defined(USE_GPERFTOOLS) && !defined(VALGRIND) +#if (defined(__linux__) || defined(__FreeBSD__)) && defined(USE_GPERFTOOLS) && !defined(VALGRIND) switch (req.action) { case ProfilerRequest::Action::ENABLE: { const char *path = (const char*)req.outputFile.begin(); From a4131f88275025f5a206c9ee09e28efcb06a9430 Mon Sep 17 00:00:00 2001 From: Dave Cottlehuber Date: Sat, 1 Feb 2020 18:05:38 +0000 Subject: [PATCH 1580/1604] bindings: update mako --- bindings/c/test/mako/mako.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bindings/c/test/mako/mako.c b/bindings/c/test/mako/mako.c index f0e7688f04..26db163691 100644 --- a/bindings/c/test/mako/mako.c +++ b/bindings/c/test/mako/mako.c @@ -12,6 +12,9 @@ #if defined(__linux__) #include +#elif defined(__FreeBSD__) +#include +#define CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC_FAST #elif defined(__APPLE__) #include #define CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC From 673d186ce2768e693539f8e65d778941cf54110c Mon Sep 17 00:00:00 2001 From: Dave Cottlehuber Date: Sat, 1 Feb 2020 18:06:14 +0000 Subject: [PATCH 1581/1604] bindings: update go --- bindings/go/fdb-go-install.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bindings/go/fdb-go-install.sh b/bindings/go/fdb-go-install.sh index ff3c739cc8..148f4e50ea 100755 --- a/bindings/go/fdb-go-install.sh +++ b/bindings/go/fdb-go-install.sh @@ -25,6 +25,9 @@ platform=$(uname) if [[ "${platform}" == "Darwin" ]] ; then FDBLIBDIR="${FDBLIBDIR:-/usr/local/lib}" libfdbc="libfdb_c.dylib" +elif [[ "${platform}" == "FreeBSD" ]] ; then + FDBLIBDIR="${FDBLIBDIR:-/lib}" + libfdbc="libfdb_c.so" elif [[ "${platform}" == "Linux" ]] ; then libfdbc="libfdb_c.so" custom_libdir="${FDBLIBDIR:-}" @@ -248,8 +251,11 @@ else : elif [[ "${status}" -eq 0 ]] ; then echo "Building generated files." + if [[ "${platform}" == "FreeBSD" ]] ; then + cmd=( 'gmake' '-C' "${fdbdir}" 'bindings/c/foundationdb/fdb_c_options.g.h' ) + else cmd=( 'make' '-C' "${fdbdir}" 'bindings/c/foundationdb/fdb_c_options.g.h' ) - + fi echo "${cmd[*]}" if ! "${cmd[@]}" ; then let status="${status} + 1" From f46d452f38854f19a88b971f440c3f09515ea146 Mon Sep 17 00:00:00 2001 From: Dave Cottlehuber Date: Sat, 1 Feb 2020 18:06:49 +0000 Subject: [PATCH 1582/1604] bindings:update python --- bindings/python/fdb/impl.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bindings/python/fdb/impl.py b/bindings/python/fdb/impl.py index 12114b0e83..5076136f81 100644 --- a/bindings/python/fdb/impl.py +++ b/bindings/python/fdb/impl.py @@ -1231,6 +1231,8 @@ if platform.system() == 'Windows': capi_name = 'fdb_c.dll' elif platform.system() == 'Linux': capi_name = 'libfdb_c.so' +elif platform.system() == 'FreeBSD': + capi_name = 'libfdb_c.so' elif platform.system() == 'Darwin': capi_name = 'libfdb_c.dylib' elif sys.platform == 'win32': From 95bc24de116a571378aabeea851f86ad4c9acfc7 Mon Sep 17 00:00:00 2001 From: Dave Cottlehuber Date: Sat, 1 Feb 2020 21:23:53 +0000 Subject: [PATCH 1583/1604] flow: update headers and includes --- flow/CMakeLists.txt | 7 ++++ flow/FastAlloc.cpp | 6 ++++ flow/Net2.actor.cpp | 2 +- flow/Platform.cpp | 74 ++++++++++++++++++++++++++++----------- flow/Platform.h | 6 +++- flow/ThreadPrimitives.cpp | 8 ++--- flow/ThreadPrimitives.h | 4 +-- 7 files changed, 78 insertions(+), 29 deletions(-) diff --git a/flow/CMakeLists.txt b/flow/CMakeLists.txt index a7f68db07c..d6ef013b39 100644 --- a/flow/CMakeLists.txt +++ b/flow/CMakeLists.txt @@ -94,6 +94,13 @@ elseif(WIN32) target_link_libraries(flow PUBLIC winmm.lib) target_link_libraries(flow PUBLIC psapi.lib) endif() +if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + set (FLOW_LIBS ${FLOW_LIBS} execinfo devstat) + find_library(EIO eio) + if(EIO) + target_link_libraries(flow PUBLIC ${EIO}) + endif() +endif() target_link_libraries(flow PRIVATE ${FLOW_LIBS}) if(USE_VALGRIND) target_link_libraries(flow PUBLIC Valgrind) diff --git a/flow/FastAlloc.cpp b/flow/FastAlloc.cpp index 604fd7fbf5..47846bc76d 100644 --- a/flow/FastAlloc.cpp +++ b/flow/FastAlloc.cpp @@ -41,6 +41,10 @@ #include #endif +#ifdef __FreeBSD__ +#include +#endif + #define FAST_ALLOCATOR_DEBUG 0 #ifdef _MSC_VER @@ -54,6 +58,8 @@ #elif defined(__GNUG__) #ifdef __linux__ #define INIT_SEG __attribute__ ((init_priority (1000))) +#elif defined(__FreeBSD__) +#define INIT_SEG __attribute__ ((init_priority (1000))) #elif defined(__APPLE__) #pragma message "init_priority is not supported on this platform; will this be a problem?" #define INIT_SEG diff --git a/flow/Net2.actor.cpp b/flow/Net2.actor.cpp index a793eb6ea6..9d5bc07f02 100644 --- a/flow/Net2.actor.cpp +++ b/flow/Net2.actor.cpp @@ -55,7 +55,7 @@ intptr_t g_stackYieldLimit = 0; using namespace boost::asio::ip; -#if defined(__linux__) +#if defined(__linux__) || defined(__FreeBSD__) #include std::atomic net2RunLoopIterations(0); diff --git a/flow/Platform.cpp b/flow/Platform.cpp index 4ba02770a9..28f25935f7 100644 --- a/flow/Platform.cpp +++ b/flow/Platform.cpp @@ -104,6 +104,39 @@ #include #endif +#ifdef __FreeBSD__ +/* Needed for processor affinity */ +#include +/* Needed for getProcessorTime and setpriority */ +#include +/* Needed for setpriority */ +#include +/* Needed for crash handler */ +#include +/* Needed for proc info */ +#include +/* Needed for vm info */ +#include +#include +#include +#include +#include +/* Needed for sysctl info */ +#include +#include +/* Needed for network info */ +#include +#include +#include +#include +#include +#include +/* Needed for device info */ +#include +#include +#include +#endif + #ifdef __APPLE__ #include #include @@ -203,7 +236,7 @@ double getProcessorTimeThread() { throw platform_error(); } return FiletimeAsInt64(ftKernel) / double(1e7) + FiletimeAsInt64(ftUser) / double(1e7); -#elif defined(__linux__) +#elif defined(__linux__) || defined(__FreeBSD__) return getProcessorTimeGeneric(RUSAGE_THREAD); #elif defined(__APPLE__) /* No RUSAGE_THREAD so we use the lower level interface */ @@ -456,7 +489,7 @@ Error systemErrorCodeToError() { void getDiskBytes(std::string const& directory, int64_t& free, int64_t& total) { INJECT_FAULT( platform_error, "getDiskBytes" ); #if defined(__unixish__) -#ifdef __linux__ +#if defined (__linux__) || defined (__FreeBSD__) struct statvfs buf; if (statvfs(directory.c_str(), &buf)) { Error e = systemErrorCodeToError(); @@ -1277,7 +1310,7 @@ struct OffsetTimer { return offset + count * secondsPerCount; } }; -#elif defined(__linux__) +#elif defined(__linux__) || defined(__FreeBSD__) #define DOUBLETIME(ts) (double(ts.tv_sec) + (ts.tv_nsec * 1e-9)) #ifndef CLOCK_MONOTONIC_RAW #define CLOCK_MONOTONIC_RAW 4 // Confirmed safe to do with glibc >= 2.11 and kernel >= 2.6.28. No promises with older glibc. Older kernel definitely breaks it. @@ -1342,7 +1375,7 @@ double timer() { GetSystemTimeAsFileTime(&fileTime); static_assert( sizeof(fileTime) == sizeof(uint64_t), "FILETIME size wrong" ); return (*(uint64_t*)&fileTime - FILETIME_C_EPOCH) * 100e-9; -#elif defined(__linux__) +#elif defined(__linux__) || defined(__FreeBSD__) struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); return double(ts.tv_sec) + (ts.tv_nsec * 1e-9); @@ -1362,7 +1395,7 @@ uint64_t timer_int() { GetSystemTimeAsFileTime(&fileTime); static_assert( sizeof(fileTime) == sizeof(uint64_t), "FILETIME size wrong" ); return (*(uint64_t*)&fileTime - FILETIME_C_EPOCH); -#elif defined(__linux__) +#elif defined(__linux__) || defined(__FreeBSD__) struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); return uint64_t(ts.tv_sec) * 1e9 + ts.tv_nsec; @@ -1412,7 +1445,7 @@ void setMemoryQuota( size_t limit ) { } if (!AssignProcessToJobObject( job, GetCurrentProcess() )) TraceEvent(SevWarn, "FailedToSetMemoryLimit").GetLastError(); -#elif defined(__linux__) +#elif defined(__linux__) || defined(__FreeBSD__) struct rlimit rlim; if (getrlimit(RLIMIT_AS, &rlim)) { TraceEvent(SevError, "GetMemoryLimit").GetLastError(); @@ -1514,7 +1547,7 @@ static void *allocateInternal(size_t length, bool largePages) { flags |= MAP_HUGETLB; return mmap(NULL, length, PROT_READ|PROT_WRITE, flags, -1, 0); -#elif defined(__APPLE__) +#elif defined(__APPLE__) || defined(__FreeBSD__) int flags = MAP_PRIVATE|MAP_ANON; return mmap(NULL, length, PROT_READ|PROT_WRITE, flags, -1, 0); @@ -1648,7 +1681,7 @@ void renameFile( std::string const& fromPath, std::string const& toPath ) { //renamedFile(); return; } -#elif (defined(__linux__) || defined(__APPLE__)) +#elif (defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)) if (!rename( fromPath.c_str(), toPath.c_str() )) { //FIXME: We cannot inject faults after renaming the file, because we could end up with two asyncFileNonDurable open for the same file //renamedFile(); @@ -1814,7 +1847,7 @@ bool createDirectory( std::string const& directory ) { Error e = systemErrorCodeToError(); TraceEvent(SevError, "CreateDirectory").detail("Directory", directory).GetLastError().error(e); throw e; -#elif (defined(__linux__) || defined(__APPLE__)) +#elif (defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)) size_t sep = 0; do { sep = directory.find_first_of('/', sep + 1); @@ -1967,8 +2000,7 @@ std::string abspath( std::string const& path, bool resolveLinks, bool mustExist if (*x == '/') *x = CANONICAL_PATH_SEPARATOR; return nameBuffer; -#elif (defined(__linux__) || defined(__APPLE__)) - +#elif (defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)) char result[PATH_MAX]; // Must resolve links, so first try realpath on the whole thing const char *r = realpath( path.c_str(), result ); @@ -2031,7 +2063,7 @@ std::string getUserHomeDirectory() { #ifdef _WIN32 #define FILE_ATTRIBUTE_DATA DWORD -#elif (defined(__linux__) || defined(__APPLE__)) +#elif (defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)) #define FILE_ATTRIBUTE_DATA mode_t #else #error Port me! @@ -2040,7 +2072,7 @@ std::string getUserHomeDirectory() { bool acceptFile( FILE_ATTRIBUTE_DATA fileAttributes, std::string name, std::string extension ) { #ifdef _WIN32 return !(fileAttributes & FILE_ATTRIBUTE_DIRECTORY) && StringRef(name).endsWith(extension); -#elif (defined(__linux__) || defined(__APPLE__)) +#elif (defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)) return S_ISREG(fileAttributes) && StringRef(name).endsWith(extension); #else #error Port me! @@ -2050,7 +2082,7 @@ bool acceptFile( FILE_ATTRIBUTE_DATA fileAttributes, std::string name, std::stri bool acceptDirectory( FILE_ATTRIBUTE_DATA fileAttributes, std::string name, std::string extension ) { #ifdef _WIN32 return (fileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; -#elif (defined(__linux__) || defined(__APPLE__)) +#elif (defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)) return S_ISDIR(fileAttributes); #else #error Port me! @@ -2086,7 +2118,7 @@ std::vector findFiles( std::string const& directory, std::string co } FindClose(h); } -#elif (defined(__linux__) || defined(__APPLE__)) +#elif (defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)) DIR *dip; if ((dip = opendir(directory.c_str())) != NULL) { @@ -2150,7 +2182,7 @@ void findFilesRecursively(std::string path, std::vector &out) { void threadSleep( double seconds ) { #ifdef _WIN32 Sleep( (DWORD)(seconds * 1e3) ); -#elif (defined(__linux__) || defined(__APPLE__)) +#elif (defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)) struct timespec req, rem; req.tv_sec = seconds; @@ -2201,7 +2233,7 @@ void setCloseOnExec( int fd ) { THREAD_HANDLE startThread(void (*func) (void *), void *arg) { return (void *)_beginthread(func, 0, arg); } -#elif (defined(__linux__) || defined(__APPLE__)) +#elif (defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)) THREAD_HANDLE startThread(void *(*func) (void *), void *arg) { pthread_t t; pthread_create(&t, NULL, func, arg); @@ -2214,7 +2246,7 @@ THREAD_HANDLE startThread(void *(*func) (void *), void *arg) { void waitThread(THREAD_HANDLE thread) { #ifdef _WIN32 WaitForSingleObject(thread, INFINITE); -#elif (defined(__linux__) || defined(__APPLE__)) +#elif (defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)) pthread_join(thread, NULL); #else #error Port me! @@ -2256,7 +2288,7 @@ int64_t fileSize(std::string const& filename) { return 0; else return file_status.st_size; -#elif (defined(__linux__) || defined(__APPLE__)) +#elif (defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)) struct stat file_status; if(stat(filename.c_str(), &file_status) != 0) return 0; @@ -2701,7 +2733,7 @@ void* getImageOffset() { return NULL; } #endif bool isLibraryLoaded(const char* lib_path) { -#if !defined(__linux__) && !defined(__APPLE__) && !defined(_WIN32) +#if !defined(__linux__) && !defined(__APPLE__) && !defined(_WIN32) && !defined(__FreeBSD__) #error Port me! #endif @@ -2717,7 +2749,7 @@ bool isLibraryLoaded(const char* lib_path) { } void* loadLibrary(const char* lib_path) { -#if !defined(__linux__) && !defined(__APPLE__) && !defined(_WIN32) +#if !defined(__linux__) && !defined(__APPLE__) && !defined(_WIN32) && !defined(__FreeBSD__) #error Port me! #endif diff --git a/flow/Platform.h b/flow/Platform.h index 9838f9caa8..c5b93de1af 100644 --- a/flow/Platform.h +++ b/flow/Platform.h @@ -22,7 +22,7 @@ #define FLOW_PLATFORM_H #pragma once -#if (defined(__linux__) || defined(__APPLE__)) +#if (defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)) #define __unixish__ 1 #endif @@ -172,6 +172,8 @@ THREAD_HANDLE startThread(void *(func) (void *), void *arg); #define DYNAMIC_LIB_EXT ".dll" #elif defined(__linux) #define DYNAMIC_LIB_EXT ".so" +#elif defined(__FreeBSD__) +#define DYNAMIC_LIB_EXT ".so" #elif defined(__APPLE__) #define DYNAMIC_LIB_EXT ".dylib" #else @@ -531,6 +533,8 @@ inline static void aligned_free(void* ptr) { free(ptr); } #if (!defined(_ISOC11_SOURCE)) // old libc versions inline static void* aligned_alloc(size_t alignment, size_t size) { return memalign(alignment, size); } #endif +#elif defined(__FreeBSD__) +inline static void aligned_free(void* ptr) { free(ptr); } #elif defined(__APPLE__) #if !defined(HAS_ALIGNED_ALLOC) #include diff --git a/flow/ThreadPrimitives.cpp b/flow/ThreadPrimitives.cpp index 7317c500bf..2df0dad271 100644 --- a/flow/ThreadPrimitives.cpp +++ b/flow/ThreadPrimitives.cpp @@ -37,7 +37,7 @@ extern std::string format( const char *form, ... ); Event::Event() { #ifdef _WIN32 ev = CreateEvent(NULL, FALSE, FALSE, NULL); -#elif defined(__linux__) +#elif defined(__linux__) || defined(__FreeBSD__) int result = sem_init(&sem, 0, 0); if (result) criticalError(FDB_EXIT_INIT_SEMAPHORE, "UnableToInitializeSemaphore", format("Could not initialize semaphore - %s", strerror(errno)).c_str()); @@ -54,7 +54,7 @@ Event::Event() { Event::~Event() { #ifdef _WIN32 CloseHandle(ev); -#elif defined(__linux__) +#elif defined(__linux__) || defined(__FreeBSD__) sem_destroy(&sem); #elif defined(__APPLE__) semaphore_destroy(self, sem); @@ -66,7 +66,7 @@ Event::~Event() { void Event::set() { #ifdef _WIN32 SetEvent(ev); -#elif defined(__linux__) +#elif defined(__linux__) || defined(__FreeBSD__) sem_post(&sem); #elif defined(__APPLE__) semaphore_signal(sem); @@ -78,7 +78,7 @@ void Event::set() { void Event::block() { #ifdef _WIN32 WaitForSingleObject(ev, INFINITE); -#elif defined(__linux__) +#elif defined(__linux__) || defined(__FreeBSD__) int ret; do { ret = sem_wait(&sem); diff --git a/flow/ThreadPrimitives.h b/flow/ThreadPrimitives.h index f3e2468851..9a75be5fb0 100644 --- a/flow/ThreadPrimitives.h +++ b/flow/ThreadPrimitives.h @@ -25,7 +25,7 @@ #include "flow/Error.h" #include "flow/Trace.h" -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) #include #endif @@ -115,7 +115,7 @@ public: private: #ifdef _WIN32 void* ev; -#elif defined(__linux__) +#elif defined(__linux__) || defined(__FreeBSD__) sem_t sem; #elif defined(__APPLE__) mach_port_t self; From f6c5e207da6b3a18a0b9d4b591cf51407e029d92 Mon Sep 17 00:00:00 2001 From: Dave Cottlehuber Date: Sat, 1 Feb 2020 21:24:36 +0000 Subject: [PATCH 1584/1604] flow: provide rdtsc if missing --- flow/Platform.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/flow/Platform.h b/flow/Platform.h index c5b93de1af..21e34974f5 100644 --- a/flow/Platform.h +++ b/flow/Platform.h @@ -424,6 +424,16 @@ inline static uint64_t __rdtsc() { #endif #endif +#ifdef __FreeBSD__ +#if !(__has_builtin(__rdtsc)) +inline static uint64_t __rdtsc() { + uint64_t lo, hi; + asm( "rdtsc" : "=a" (lo), "=d" (hi) ); + return( lo | (hi << 32) ); +} +#endif +#endif + #ifdef _WIN32 #include inline static int32_t interlockedIncrement(volatile int32_t *a) { return _InterlockedIncrement((long*)a); } From 0fbcf258dea3e9d173ad5f07da02ae4eedf0c64d Mon Sep 17 00:00:00 2001 From: Dave Cottlehuber Date: Sat, 1 Feb 2020 21:24:54 +0000 Subject: [PATCH 1585/1604] flow: use cpuset --- flow/Platform.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/flow/Platform.cpp b/flow/Platform.cpp index 28f25935f7..4c418ea05d 100644 --- a/flow/Platform.cpp +++ b/flow/Platform.cpp @@ -1621,6 +1621,11 @@ void setAffinity(int proc) { CPU_ZERO(&set); CPU_SET(proc, &set); sched_setaffinity(0, sizeof(cpu_set_t), &set); +#elif defined(__FreeBSD__) + cpuset_t set; + CPU_ZERO(&set); + CPU_SET(proc, &set); + cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1,sizeof(set), &set); #endif } From 4bb65e6a3c0897c19ad62897f3568d48ce2df3e0 Mon Sep 17 00:00:00 2001 From: Dave Cottlehuber Date: Sat, 1 Feb 2020 18:13:08 +0000 Subject: [PATCH 1586/1604] flow: provide more FreeBSD-specific counters --- flow/Platform.cpp | 296 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 296 insertions(+) diff --git a/flow/Platform.cpp b/flow/Platform.cpp index 4c418ea05d..f44f35522d 100644 --- a/flow/Platform.cpp +++ b/flow/Platform.cpp @@ -288,6 +288,29 @@ uint64_t getResidentMemoryUsage() { rssize *= sysconf(_SC_PAGESIZE); + return rssize; +#elif defined(__FreeBSD__) + uint64_t rssize = 0; + + int status; + pid_t ppid = getpid(); + int pidinfo[4]; + pidinfo[0] = CTL_KERN; + pidinfo[1] = KERN_PROC; + pidinfo[2] = KERN_PROC_PID; + pidinfo[3] = (int)ppid; + + struct kinfo_proc procstk; + size_t len = sizeof(procstk); + + status = sysctl(pidinfo, nitems(pidinfo), &procstk, &len, NULL, 0); + if (status < 0){ + TraceEvent(SevError, "GetResidentMemoryUsage").GetLastError(); + throw platform_error(); + } + + rssize = (uint64_t)procstk.ki_rssize; + return rssize; #elif defined(_WIN32) PROCESS_MEMORY_COUNTERS_EX pmc; @@ -325,6 +348,29 @@ uint64_t getMemoryUsage() { vmsize *= sysconf(_SC_PAGESIZE); + return vmsize; +#elif defined(__FreeBSD__) + uint64_t vmsize = 0; + + int status; + pid_t ppid = getpid(); + int pidinfo[4]; + pidinfo[0] = CTL_KERN; + pidinfo[1] = KERN_PROC; + pidinfo[2] = KERN_PROC_PID; + pidinfo[3] = (int)ppid; + + struct kinfo_proc procstk; + size_t len = sizeof(procstk); + + status = sysctl(pidinfo, nitems(pidinfo), &procstk, &len, NULL, 0); + if (status < 0){ + TraceEvent(SevError, "GetMemoryUsage").GetLastError(); + throw platform_error(); + } + + vmsize = (uint64_t)procstk.ki_size >> PAGE_SHIFT; + return vmsize; #elif defined(_WIN32) PROCESS_MEMORY_COUNTERS_EX pmc; @@ -434,6 +480,52 @@ void getMachineRAMInfo(MachineRAMInfo& memInfo) { memInfo.available = 1024 * (std::max(0, (memFree-lowWatermark) + std::max(pageCache-lowWatermark, pageCache/2) + std::max(slabReclaimable-lowWatermark, slabReclaimable/2)) - usedSwap); } + memInfo.committed = memInfo.total - memInfo.available; +#elif defined(__FreeBSD__) + int status; + + u_int page_size; + u_int free_count; + u_int active_count; + u_int inactive_count; + u_int wire_count; + + size_t uint_size; + + uint_size = sizeof(page_size); + + status = sysctlbyname("vm.stats.vm.v_page_size", &page_size, &uint_size, NULL, 0); + if (status < 0){ + TraceEvent(SevError, "GetMachineMemInfo").GetLastError(); + throw platform_error(); + } + + status = sysctlbyname("vm.stats.vm.v_free_count", &free_count, &uint_size, NULL, 0); + if (status < 0){ + TraceEvent(SevError, "GetMachineMemInfo").GetLastError(); + throw platform_error(); + } + + status = sysctlbyname("vm.stats.vm.v_active_count", &active_count, &uint_size, NULL, 0); + if (status < 0){ + TraceEvent(SevError, "GetMachineMemInfo").GetLastError(); + throw platform_error(); + } + + status = sysctlbyname("vm.stats.vm.v_inactive_count", &inactive_count, &uint_size, NULL, 0); + if (status < 0){ + TraceEvent(SevError, "GetMachineMemInfo").GetLastError(); + throw platform_error(); + } + + status = sysctlbyname("vm.stats.vm.v_wire_count", &wire_count, &uint_size, NULL, 0); + if (status < 0){ + TraceEvent(SevError, "GetMachineMemInfo").GetLastError(); + throw platform_error(); + } + + memInfo.total = (int64_t)((free_count + active_count + inactive_count + wire_count) * (u_int64_t)(page_size)); + memInfo.available = (int64_t)(free_count * (u_int64_t)(page_size)); memInfo.committed = memInfo.total - memInfo.available; #elif defined(_WIN32) MEMORYSTATUSEX mem_status; @@ -788,6 +880,196 @@ dev_t getDeviceId(std::string path) { #endif +#if defined(__FreeBSD__) +void getNetworkTraffic(const IPAddress ip, uint64_t& bytesSent, uint64_t& bytesReceived, + uint64_t& outSegs, uint64_t& retransSegs) { + INJECT_FAULT( platform_error, "getNetworkTraffic" ); + + const char* ifa_name = nullptr; + try { + ifa_name = getInterfaceName(ip); + } + catch(Error &e) { + if(e.code() != error_code_platform_error) { + throw; + } + } + + if (!ifa_name) + return; + + struct ifaddrs *interfaces = NULL; + + if (getifaddrs(&interfaces)) + { + TraceEvent(SevError, "GetNetworkTrafficError").GetLastError(); + throw platform_error(); + } + + int if_count, i; + int mib[6]; + size_t ifmiblen; + struct ifmibdata ifmd; + + mib[0] = CTL_NET; + mib[1] = PF_LINK; + mib[2] = NETLINK_GENERIC; + mib[3] = IFMIB_IFDATA; + mib[4] = IFMIB_IFCOUNT; + mib[5] = IFDATA_GENERAL; + + ifmiblen = sizeof(ifmd); + + for (i = 1; i <= if_count; i++) + { + mib[4] = i; + + sysctl(mib, 6, &ifmd, &ifmiblen, (void *)0, 0); + + if (!strcmp(ifmd.ifmd_name, ifa_name)) + { + bytesSent = ifmd.ifmd_data.ifi_obytes; + bytesReceived = ifmd.ifmd_data.ifi_ibytes; + break; + } + } + + freeifaddrs(interfaces); + + struct tcpstat tcpstat; + size_t stat_len; + stat_len = sizeof(tcpstat); + int tcpstatus = sysctlbyname("net.inet.tcp.stats", &tcpstat, &stat_len, NULL, 0); + if (tcpstatus < 0) { + TraceEvent(SevError, "GetNetworkTrafficError").GetLastError(); + throw platform_error(); + } + + outSegs = tcpstat.tcps_sndtotal; + retransSegs = tcpstat.tcps_sndrexmitpack; +} + +void getMachineLoad(uint64_t& idleTime, uint64_t& totalTime, bool logDetails) { + INJECT_FAULT( platform_error, "getMachineLoad" ); + + long cur[CPUSTATES], last[CPUSTATES]; + size_t cur_sz = sizeof cur; + int cpustate; + long sum; + + memset(last, 0, sizeof last); + + if (sysctlbyname("kern.cp_time", &cur, &cur_sz, NULL, 0) < 0) + { + TraceEvent(SevError, "GetMachineLoad").GetLastError(); + throw platform_error(); + } + + sum = 0; + for (cpustate = 0; cpustate < CPUSTATES; cpustate++) + { + long tmp = cur[cpustate]; + cur[cpustate] -= last[cpustate]; + last[cpustate] = tmp; + sum += cur[cpustate]; + } + + totalTime = (uint64_t)(cur[CP_USER] + cur[CP_NICE] + cur[CP_SYS] + cur[CP_IDLE]); + + idleTime = (uint64_t)(cur[CP_IDLE]); + + //need to add logging here to TraceEvent + +} + +void getDiskStatistics(std::string const& directory, uint64_t& currentIOs, uint64_t& busyTicks, uint64_t& reads, uint64_t& writes, uint64_t& writeSectors, uint64_t& readSectors) { + INJECT_FAULT( platform_error, "getDiskStatistics" ); + currentIOs = 0; + busyTicks = 0; + reads = 0; + writes = 0; + writeSectors = 0; + readSectors = 0; + + struct stat buf; + if (stat(directory.c_str(), &buf)) { + TraceEvent(SevError, "GetDiskStatisticsStatError").detail("Directory", directory).GetLastError(); + throw platform_error(); + } + + static struct statinfo dscur; + double etime; + struct timespec ts; + static int num_devices; + + kvm_t *kd = NULL; + + etime = ts.tv_nsec * 1e-6;; + + int dn; + u_int64_t total_transfers_read, total_transfers_write; + u_int64_t total_blocks_read, total_blocks_write; + u_int64_t queue_len; + long double ms_per_transaction; + + dscur.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo)); + if (dscur.dinfo == NULL) { + TraceEvent(SevError, "GetDiskStatisticsStatError").GetLastError(); + throw platform_error(); + } + + if (devstat_getdevs(kd, &dscur) == -1) { + TraceEvent(SevError, "GetDiskStatisticsStatError").GetLastError(); + throw platform_error(); + } + + num_devices = dscur.dinfo->numdevs; + + for (dn = 0; dn < num_devices; dn++) + { + + if (devstat_compute_statistics(&dscur.dinfo->devices[dn], NULL, etime, + DSM_MS_PER_TRANSACTION, &ms_per_transaction, + DSM_TOTAL_TRANSFERS_READ, &total_transfers_read, + DSM_TOTAL_TRANSFERS_WRITE, &total_transfers_write, + DSM_TOTAL_BLOCKS_READ, &total_blocks_read, + DSM_TOTAL_BLOCKS_WRITE, &total_blocks_write, + DSM_QUEUE_LENGTH, &queue_len, + DSM_NONE) != 0) { + TraceEvent(SevError, "GetDiskStatisticsStatError").GetLastError(); + throw platform_error(); + } + + currentIOs = queue_len; + busyTicks = (u_int64_t)ms_per_transaction; + reads = total_transfers_read; + writes = total_transfers_write; + writeSectors = total_blocks_read; + readSectors = total_blocks_write; + } + +} + +dev_t getDeviceId(std::string path) { + struct stat statInfo; + + while (true) { + int returnValue = stat(path.c_str(), &statInfo); + if (!returnValue) break; + + if (errno == ENOENT) { + path = parentDirectory(path); + } else { + TraceEvent(SevError, "GetDeviceIdError").detail("Path", path).GetLastError(); + throw platform_error(); + } + } + + return statInfo.st_dev; +} + +#endif + #ifdef __APPLE__ void getNetworkTraffic(const IPAddress& ip, uint64_t& bytesSent, uint64_t& bytesReceived, uint64_t& outSegs, uint64_t& retransSegs) { @@ -2811,6 +3093,20 @@ std::string exePath() { } else { throw platform_error(); } +#elif defined(__FreeBSD__) + char binPath[2048]; + int mib[4]; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PATHNAME; + mib[3] = -1; + size_t len = sizeof(binPath); + if (sysctl(mib, 4, binPath, &len, NULL, 0) != 0) { + binPath[0] = '\0'; + return std::string(binPath); + } else { + throw platform_error(); + } #elif defined(__APPLE__) uint32_t bufSize = 1024; std::unique_ptr buf(new char[bufSize]); From a380a3779f036efd019a60a91c72e70bde4c7c93 Mon Sep 17 00:00:00 2001 From: Dave Cottlehuber Date: Sat, 1 Feb 2020 21:25:34 +0000 Subject: [PATCH 1587/1604] flow: look for plugins in the platform-specific place --- flow/Platform.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/Platform.cpp b/flow/Platform.cpp index f44f35522d..0e8aed22e7 100644 --- a/flow/Platform.cpp +++ b/flow/Platform.cpp @@ -2714,7 +2714,7 @@ std::string getDefaultConfigPath() { return _filepath + "\\foundationdb"; #elif defined(__linux__) return "/etc/foundationdb"; -#elif defined(__APPLE__) +#elif defined(__APPLE__) || defined(__FreeBSD__) return "/usr/local/etc/foundationdb"; #else #error Port me! From 5d1513cf26ebac4e95ad61c3a559174f9b5601fe Mon Sep 17 00:00:00 2001 From: Dave Cottlehuber Date: Tue, 7 Apr 2020 11:54:28 +0000 Subject: [PATCH 1588/1604] flow/platform.cpp --- flow/Platform.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/Platform.cpp b/flow/Platform.cpp index 0e8aed22e7..e9c0dfd248 100644 --- a/flow/Platform.cpp +++ b/flow/Platform.cpp @@ -2843,7 +2843,7 @@ int eraseDirectoryRecursive(std::string const& dir) { __eraseDirectoryRecurseiveCount = 0; #ifdef _WIN32 system( ("rd /s /q \"" + dir + "\"").c_str() ); -#elif defined(__linux__) || defined(__APPLE__) +#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) int error = nftw(dir.c_str(), [](const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) -> int { From ebead69182cce5773745caf140acb961b7a26653 Mon Sep 17 00:00:00 2001 From: Dave Cottlehuber Date: Sat, 1 Feb 2020 18:14:14 +0000 Subject: [PATCH 1589/1604] bindings: c asm --- bindings/c/generate_asm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/c/generate_asm.py b/bindings/c/generate_asm.py index cf06ef207e..284a6e6824 100755 --- a/bindings/c/generate_asm.py +++ b/bindings/c/generate_asm.py @@ -61,7 +61,7 @@ def write_windows_asm(asmfile, functions): def write_unix_asm(asmfile, functions, prefix): asmfile.write(".intel_syntax noprefix\n") - if platform == "linux": + if platform == "linux" or platform == "freebsd": asmfile.write("\n.data\n") for f in functions: asmfile.write("\t.extern fdb_api_ptr_%s\n" % f) From 99d6e9497b62e6de7bf73a96551d538034e6cc34 Mon Sep 17 00:00:00 2001 From: Dave Cottlehuber Date: Sat, 1 Feb 2020 18:13:41 +0000 Subject: [PATCH 1590/1604] cmake: skip OpenJDK on FreeBSD --- cmake/FDBComponents.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/FDBComponents.cmake b/cmake/FDBComponents.cmake index f3df9331dc..7e42871cce 100644 --- a/cmake/FDBComponents.cmake +++ b/cmake/FDBComponents.cmake @@ -47,7 +47,8 @@ endif() set(WITH_JAVA OFF) find_package(JNI 1.8) find_package(Java 1.8 COMPONENTS Development) -if(JNI_FOUND AND Java_FOUND AND Java_Development_FOUND) +# leave FreeBSD JVM compat for later +if(JNI_FOUND AND Java_FOUND AND Java_Development_FOUND AND NOT (CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")) set(WITH_JAVA ON) include(UseJava) enable_language(Java) From c14133a4eca0c71b55b790bfef4a00bb42b7ee41 Mon Sep 17 00:00:00 2001 From: Evan Tschannen Date: Thu, 30 Apr 2020 11:13:59 -0700 Subject: [PATCH 1591/1604] better serialization of empty StringRef --- fdbclient/CommitTransaction.h | 10 ++++------ fdbclient/FDBTypes.h | 6 ++---- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/fdbclient/CommitTransaction.h b/fdbclient/CommitTransaction.h index 02ab3bb23a..234a869731 100644 --- a/fdbclient/CommitTransaction.h +++ b/fdbclient/CommitTransaction.h @@ -111,15 +111,13 @@ struct MutationRef { template void serialize( Ar& ar ) { if (!ar.isDeserializing && type == ClearRange && equalsKeyAfter(param1, param2)) { - StringRef hold = param1; - param1 = StringRef(); - serializer(ar, type, param2, param1); - param1 = hold; + StringRef empty; + serializer(ar, type, param2, empty); } else { serializer(ar, type, param1, param2); } - if (ar.isDeserializing && type == ClearRange && param2 == StringRef()) { - ASSERT(param1.size() > 0 && param1[param1.size()-1] == '\x00'); + if (ar.isDeserializing && type == ClearRange && param2 == StringRef() && param1 != StringRef()) { + ASSERT(param1[param1.size()-1] == '\x00'); param2 = param1; param1 = param2.substr(0, param2.size()-1); } diff --git a/fdbclient/FDBTypes.h b/fdbclient/FDBTypes.h index 95f5b6602d..21b5d00dc5 100644 --- a/fdbclient/FDBTypes.h +++ b/fdbclient/FDBTypes.h @@ -283,10 +283,8 @@ struct KeyRangeRef { template force_inline void serialize(Ar& ar) { if (!ar.isDeserializing && equalsKeyAfter(begin, end)) { - KeyRef hold = begin; - const_cast(begin) = KeyRef(); - serializer(ar, const_cast(end), const_cast(begin)); - const_cast(begin) = hold; + StringRef empty; + serializer(ar, const_cast(end), empty); } else { serializer(ar, const_cast(begin), const_cast(end)); } From d7fe80b612ccca817e40bc0ea45015f230fe8360 Mon Sep 17 00:00:00 2001 From: Dave Cottlehuber Date: Sat, 1 Feb 2020 22:34:56 +0000 Subject: [PATCH 1592/1604] cmake: add dtrace toggle --- cmake/ConfigureCompiler.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/ConfigureCompiler.cmake b/cmake/ConfigureCompiler.cmake index 33b749c0ee..20960a61fa 100644 --- a/cmake/ConfigureCompiler.cmake +++ b/cmake/ConfigureCompiler.cmake @@ -1,6 +1,7 @@ include(CompilerChecks) env_set(USE_GPERFTOOLS OFF BOOL "Use gperfools for profiling") +env_set(USE_DTRACE ON BOOL "Enable dtrace probes on supported platforms") env_set(USE_VALGRIND OFF BOOL "Compile for valgrind usage") env_set(USE_VALGRIND_FOR_CTEST ${USE_VALGRIND} BOOL "Use valgrind for ctest") env_set(ALLOC_INSTRUMENTATION OFF BOOL "Instrument alloc") @@ -255,7 +256,7 @@ else() check_symbol_exists(DTRACE_PROBE sys/sdt.h SUPPORT_DTRACE) check_symbol_exists(aligned_alloc stdlib.h HAS_ALIGNED_ALLOC) message(STATUS "Has aligned_alloc: ${HAS_ALIGNED_ALLOC}") - if(SUPPORT_DTRACE) + if((SUPPORT_DTRACE) AND (USE_DTRACE)) add_compile_definitions(DTRACE_PROBES) endif() if(HAS_ALIGNED_ALLOC) From 9753013b65433385aea6f48371a5470652f62b49 Mon Sep 17 00:00:00 2001 From: Dave Cottlehuber Date: Sat, 1 Feb 2020 18:04:22 +0000 Subject: [PATCH 1593/1604] fdbrpc: update cmake & headers --- fdbrpc/CMakeLists.txt | 11 ++++++++++- fdbrpc/libeio/eio.c | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/fdbrpc/CMakeLists.txt b/fdbrpc/CMakeLists.txt index 200ff5fc37..5358221728 100644 --- a/fdbrpc/CMakeLists.txt +++ b/fdbrpc/CMakeLists.txt @@ -49,7 +49,16 @@ if(APPLE) list(APPEND FDBRPC_THIRD_PARTY_SRCS libcoroutine/asm.S) endif() if(NOT WIN32) - list(APPEND FDBRPC_THIRD_PARTY_SRCS libcoroutine/context.c libeio/eio.c) + if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + find_library(EIO eio) + if(EIO) + list(APPEND FDBRPC_THIRD_PARTY_SRCS libcoroutine/context.c) + else() + list(APPEND FDBRPC_THIRD_PARTY_SRCS libcoroutine/context.c libeio/eio.c) + endif() + else() + list(APPEND FDBRPC_THIRD_PARTY_SRCS libcoroutine/context.c libeio/eio.c) + endif() endif() add_library(thirdparty STATIC ${FDBRPC_THIRD_PARTY_SRCS}) diff --git a/fdbrpc/libeio/eio.c b/fdbrpc/libeio/eio.c index e961416690..8452303de3 100644 --- a/fdbrpc/libeio/eio.c +++ b/fdbrpc/libeio/eio.c @@ -39,6 +39,8 @@ #ifdef __linux__ #include "config.h.linux" +#elif defined(__FreeBSD__) +#include "config.h.FreeBSD" #elif defined(__APPLE__) #include "config.h.osx" #endif From 5064cc776b049a9b9bde385e6df11fa79d63e8fb Mon Sep 17 00:00:00 2001 From: Dave Cottlehuber Date: Sat, 1 Feb 2020 18:03:11 +0000 Subject: [PATCH 1594/1604] fdbmonitor: update headers & libraries --- fdbmonitor/fdbmonitor.cpp | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/fdbmonitor/fdbmonitor.cpp b/fdbmonitor/fdbmonitor.cpp index 6d91583295..e0ea1e5e0d 100644 --- a/fdbmonitor/fdbmonitor.cpp +++ b/fdbmonitor/fdbmonitor.cpp @@ -37,6 +37,10 @@ #include #endif +#ifdef __FreeBSD__ +#include +#endif + #ifdef __APPLE__ #include #include @@ -78,7 +82,7 @@ #ifdef __linux__ typedef fd_set* fdb_fd_set; -#elif defined __APPLE__ +#elif defined(__APPLE__) || defined(__FreeBSD__) typedef int fdb_fd_set; #endif @@ -89,7 +93,7 @@ void monitor_fd( fdb_fd_set list, int fd, int* maxfd, void* cmd ) { FD_SET( fd, list ); if ( fd > *maxfd ) *maxfd = fd; -#elif defined __APPLE__ +#elif defined(__APPLE__) || defined(__FreeBSD__) /* ignore maxfd */ struct kevent ev; EV_SET( &ev, fd, EVFILT_READ, EV_ADD, 0, 0, cmd ); @@ -100,7 +104,7 @@ void monitor_fd( fdb_fd_set list, int fd, int* maxfd, void* cmd ) { void unmonitor_fd( fdb_fd_set list, int fd ) { #ifdef __linux__ FD_CLR( fd, list ); -#elif defined __APPLE__ +#elif defined(__APPLE__) || defined(__FreeBSD__) struct kevent ev; EV_SET( &ev, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL ); kevent( list, &ev, 1, NULL, 0, NULL ); // FIXME: check? @@ -194,7 +198,7 @@ const char* get_value_multi(const CSimpleIni& ini, const char* key, ...) { } double timer() { -#if defined(__linux__) +#if defined(__linux__) || defined(__FreeBSD__) struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return double(ts.tv_sec) + (ts.tv_nsec * 1e-9); @@ -913,7 +917,7 @@ void read_child_output( Command* cmd, int pipe_idx, fdb_fd_set fds ) { } } -#ifdef __APPLE__ +#if defined(__APPLE__) || defined(__FreeBSD__) void watch_conf_dir( int kq, int* confd_fd, std::string confdir ) { struct kevent ev; std::string original = confdir; @@ -1266,12 +1270,12 @@ int main(int argc, char** argv) { #endif if (daemonize) { -#ifdef __APPLE__ +#if defined(__APPLE__) || defined(__FreeBSD__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif if (daemon(0, 0)) { -#ifdef __APPLE__ +#if defined(__APPLE__) || defined(__FreeBSD__) #pragma GCC diagnostic pop #endif log_err("daemon", errno, "Unable to daemonize"); @@ -1330,7 +1334,7 @@ int main(int argc, char** argv) { signal(SIGHUP, signal_handler); signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); -#elif defined(__APPLE__) +#elif defined(__APPLE__) || defined(__FreeBSD__) int kq = kqueue(); if ( kq < 0 ) { log_err( "kqueue", errno, "Unable to create kqueue" ); @@ -1375,11 +1379,11 @@ int main(int argc, char** argv) { /* normal will be restored in our main loop in the call to pselect, but none blocks all signals while processing events */ sigprocmask(SIG_SETMASK, &full_mask, &normal_mask); -#elif defined(__APPLE__) +#elif defined(__APPLE__) || defined(__FreeBSD__) sigprocmask(0, NULL, &normal_mask); #endif -#ifdef __APPLE__ +#if defined(__APPLE__) || defined(__FreeBSD__) struct stat st_buf; struct timespec mtimespec; @@ -1438,7 +1442,7 @@ int main(int argc, char** argv) { load_conf(confpath.c_str(), uid, gid, &normal_mask, &rfds, &maxfd); reload_additional_watches = false; -#elif defined(__APPLE__) +#elif defined(__APPLE__) || defined(__FreeBSD__) load_conf( confpath.c_str(), uid, gid, &normal_mask, watched_fds, &maxfd ); watch_conf_file( kq, &conff_fd, confpath.c_str() ); watch_conf_dir( kq, &confd_fd, confdir ); @@ -1476,7 +1480,7 @@ int main(int argc, char** argv) { if(nfds == 0) { reload = true; } -#elif defined(__APPLE__) +#elif defined(__APPLE__) || defined(__FreeBSD__) int nev = 0; if(timeout < 0) { nev = kevent( kq, NULL, 0, &ev, 1, NULL ); From b2eb93f5f4a6dc02967135849337d39eeaad83d3 Mon Sep 17 00:00:00 2001 From: Dave Cottlehuber Date: Tue, 7 Apr 2020 10:18:01 +0000 Subject: [PATCH 1595/1604] fdbmonitor: define O_EVTONLY for FreeBSD --- fdbmonitor/fdbmonitor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/fdbmonitor/fdbmonitor.cpp b/fdbmonitor/fdbmonitor.cpp index e0ea1e5e0d..a0d1be31f0 100644 --- a/fdbmonitor/fdbmonitor.cpp +++ b/fdbmonitor/fdbmonitor.cpp @@ -39,6 +39,7 @@ #ifdef __FreeBSD__ #include +#define O_EVTONLY O_RDONLY #endif #ifdef __APPLE__ From 28e58c672ade42149d883de67d071e93d9f437f1 Mon Sep 17 00:00:00 2001 From: Dave Cottlehuber Date: Tue, 7 Apr 2020 19:28:18 +0000 Subject: [PATCH 1596/1604] fdbmonitor: ifdef config path location --- CMakeLists.txt | 4 ++++ fdbmonitor/fdbmonitor.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3578ab51f4..00bdde8e1e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -146,6 +146,10 @@ set(SEED "0x${SEED_}" CACHE STRING "Random seed for testing") # components ################################################################################ +if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + include_directories(/usr/local/include) +endif() + include(CompileBoost) add_subdirectory(flow) add_subdirectory(fdbrpc) diff --git a/fdbmonitor/fdbmonitor.cpp b/fdbmonitor/fdbmonitor.cpp index a0d1be31f0..25f33d859c 100644 --- a/fdbmonitor/fdbmonitor.cpp +++ b/fdbmonitor/fdbmonitor.cpp @@ -1176,7 +1176,11 @@ int main(int argc, char** argv) { // testPathOps(); return -1; std::string lockfile = "/var/run/fdbmonitor.pid"; +#ifdef __FreeBSD__ + std::string _confpath = "/usr/local/etc/foundationdb/foundationdb.conf"; +#else std::string _confpath = "/etc/foundationdb/foundationdb.conf"; +#endif std::vector additional_watch_paths; From 15ba7edfb8ad52da8f5624301cd3f969d48997a2 Mon Sep 17 00:00:00 2001 From: Dave Cottlehuber Date: Thu, 30 Apr 2020 19:17:23 +0000 Subject: [PATCH 1597/1604] docs: add FreeBSD build steps --- README.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/README.md b/README.md index a3e7ef5979..e27dca73fc 100755 --- a/README.md +++ b/README.md @@ -123,6 +123,37 @@ cmake -G Xcode -DOPEN_FOR_IDE=ON You should create a second build-directory which you will use for building (probably with make or ninja) and debugging. +#### FreeBSD + +1. Check out this repo on your server. +1. Install compile-time dependencies from ports. +1. (Optional) Use tmpfs & ccache for significantly faster repeat builds +1. (Optional) Install a [JDK](https://www.freshports.org/java/openjdk8/) + for Java Bindings. FoundationDB currently builds with Java 8. +1. Navigate to the directory where you checked out the foundationdb + repo. +1. Build from source. + + ```shell + sudo pkg install -r FreeBSD \ + shells/bash devel/cmake devel/ninja devel/ccache \ + lang/mono lang/python3 \ + devel/boost-libs devel/libeio \ + security/openssl + mkdir .build && cd .build + cmake -G Ninja \ + -DUSE_CCACHE=on \ + -DDISABLE_TLS=off \ + -DUSE_DTRACE=off \ + .. + ninja -j 10 + # run fast tests + ctest -L fast + # run all tests + ctest --output-on-failure -v + ``` + + ### Linux There are no special requirements for Linux. A docker image can be pulled from From 6ee78aa3a4b173330cb25bb7616cf16eda595a86 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Thu, 30 Apr 2020 16:16:11 -0700 Subject: [PATCH 1598/1604] Fix:Disable sanity check backup metadata file Which can increase the false positive rate of TooManyFiles error --- fdbclient/BackupContainer.actor.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fdbclient/BackupContainer.actor.cpp b/fdbclient/BackupContainer.actor.cpp index 0fabe6e83a..2689ee8163 100644 --- a/fdbclient/BackupContainer.actor.cpp +++ b/fdbclient/BackupContainer.actor.cpp @@ -1373,7 +1373,8 @@ public: wait(bc->readKeyspaceSnapshot(snapshot.get())); restorable.ranges = std::move(results.first); restorable.keyRanges = std::move(results.second); - if (g_network->isSimulated()) { + // TODO: Reenable the sanity check after TooManyFiles error is resolved + if (false && g_network->isSimulated()) { // Sanity check key ranges state std::map::iterator rit; for (rit = restorable.keyRanges.begin(); rit != restorable.keyRanges.end(); rit++) { From 07a9a0568362543aef6ab2da6e401c968d130646 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Thu, 30 Apr 2020 16:20:20 -0700 Subject: [PATCH 1599/1604] FastRestore:Agent:Fix restore requests --- fdbbackup/backup.actor.cpp | 10 ++++++---- fdbclient/BackupContainer.actor.cpp | 1 - fdbserver/RestoreMaster.actor.cpp | 4 +++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/fdbbackup/backup.actor.cpp b/fdbbackup/backup.actor.cpp index 1354ab2685..5c9187d214 100644 --- a/fdbbackup/backup.actor.cpp +++ b/fdbbackup/backup.actor.cpp @@ -2198,13 +2198,15 @@ ACTOR Future runFastRestoreAgent(Database db, std::string tagName, std::st state Version restoreVersion = invalidVersion; if (ranges.size() > 1) { - fprintf(stderr, "Currently only a single restore range is supported!\n"); - throw restore_error(); + fprintf(stdout, "[WARNING] Currently only a single restore range is tested!\n"); } - state KeyRange range = (ranges.size() == 0) ? normalKeys : ranges.front(); + if (ranges.size() == 0) { + ranges.push_back(normalKeys); + } - printf("[INFO] runFastRestoreAgent: num_ranges:%d restore_range:%s\n", ranges.size(), range.toString().c_str()); + printf("[INFO] runFastRestoreAgent: restore_ranges:%d first range:%s\n", ranges.size(), + ranges.front().toString().c_str()); if (performRestore) { if (dbVersion == invalidVersion) { diff --git a/fdbclient/BackupContainer.actor.cpp b/fdbclient/BackupContainer.actor.cpp index 2689ee8163..6bc176df08 100644 --- a/fdbclient/BackupContainer.actor.cpp +++ b/fdbclient/BackupContainer.actor.cpp @@ -1777,7 +1777,6 @@ public: virtual ~BackupContainerBlobStore() {} Future> readFile(std::string path) final { - ASSERT(m_bstore->knobs.read_ahead_blocks > 0); return Reference( new AsyncFileReadAheadCache( Reference(new AsyncFileBlobStoreRead(m_bstore, m_bucket, dataPath(path))), diff --git a/fdbserver/RestoreMaster.actor.cpp b/fdbserver/RestoreMaster.actor.cpp index 9e0d749a5c..881ba43951 100644 --- a/fdbserver/RestoreMaster.actor.cpp +++ b/fdbserver/RestoreMaster.actor.cpp @@ -196,7 +196,7 @@ ACTOR Future startProcessRestoreRequests(Reference self state int numTries = 0; state int restoreIndex = 0; - TraceEvent("FastRestoreMasterWaitOnRestoreRequests", self->id()); + TraceEvent("FastRestoreMasterWaitOnRestoreRequests", self->id()).detail("RestoreRequests", restoreRequests.size()); // DB has been locked where restore request is submitted wait(clearDB(cx)); @@ -636,6 +636,8 @@ ACTOR static Future>> collectRestoreRequest TraceEvent("FastRestoreMasterPhaseCollectRestoreRequests") .detail("RestoreRequest", restoreRequests.back().toString()); } + } else { + TraceEvent(SevWarnAlways, "FastRestoreMasterPhaseCollectRestoreRequestsEmptyRequests"); } break; } From 37a537c2a3518c615e438a7cd7f6aa5f6d293229 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Thu, 30 Apr 2020 18:30:15 -0700 Subject: [PATCH 1600/1604] Fix MacOS compilation error --- fdbbackup/backup.actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbbackup/backup.actor.cpp b/fdbbackup/backup.actor.cpp index 5c9187d214..60622981b4 100644 --- a/fdbbackup/backup.actor.cpp +++ b/fdbbackup/backup.actor.cpp @@ -2202,7 +2202,7 @@ ACTOR Future runFastRestoreAgent(Database db, std::string tagName, std::st } if (ranges.size() == 0) { - ranges.push_back(normalKeys); + ranges.push_back(ranges.arena(), normalKeys); } printf("[INFO] runFastRestoreAgent: restore_ranges:%d first range:%s\n", ranges.size(), From 32c791a9b0f1352a5e98cdae1f458ca31574eca4 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Thu, 30 Apr 2020 18:47:13 -0700 Subject: [PATCH 1601/1604] FastRestore:Fix:memory threshold is set in MB not Bytes --- fdbserver/RestoreRoleCommon.actor.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fdbserver/RestoreRoleCommon.actor.cpp b/fdbserver/RestoreRoleCommon.actor.cpp index 2cef763001..dbd7fd6b7e 100644 --- a/fdbserver/RestoreRoleCommon.actor.cpp +++ b/fdbserver/RestoreRoleCommon.actor.cpp @@ -100,6 +100,7 @@ void updateProcessStats(Reference self) { // in increasing order of their version batch. ACTOR Future isSchedulable(Reference self, int actorBatchIndex, std::string name) { self->delayedActors++; + state double memoryThresholdBytes = SERVER_KNOBS->FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT * 1024 * 1024; loop { double memory = getSystemStatistics().processMemory; if (g_network->isSimulated() && BUGGIFY) { @@ -107,13 +108,13 @@ ACTOR Future isSchedulable(Reference self, int actorBatch // memory will be larger than threshold when deterministicRandom()->random01() > 1/2 memory = SERVER_KNOBS->FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT * 2 * deterministicRandom()->random01(); } - if (memory < SERVER_KNOBS->FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT || - self->finishedBatch.get() + 1 == actorBatchIndex) { - if (memory >= SERVER_KNOBS->FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT) { + if (memory < memoryThresholdBytes || self->finishedBatch.get() + 1 == actorBatchIndex) { + if (memory >= memoryThresholdBytes) { TraceEvent(SevWarn, "FastRestoreMemoryUsageAboveThreshold") .detail("BatchIndex", actorBatchIndex) .detail("FinishedBatch", self->finishedBatch.get()) - .detail("Actor", name); + .detail("Actor", name) + .detail("Memory", memory); } self->delayedActors--; break; From 6bd71560f0a9ebd45be9e18568a62366cfb330ea Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Thu, 30 Apr 2020 19:12:31 -0700 Subject: [PATCH 1602/1604] FastRestore:Reduce trace events in real cluster environment --- fdbserver/RestoreApplier.actor.cpp | 4 ++-- fdbserver/RestoreLoader.actor.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fdbserver/RestoreApplier.actor.cpp b/fdbserver/RestoreApplier.actor.cpp index 263a71a505..a8d35e6e99 100644 --- a/fdbserver/RestoreApplier.actor.cpp +++ b/fdbserver/RestoreApplier.actor.cpp @@ -112,7 +112,7 @@ ACTOR static Future handleSendMutationVectorRequest(RestoreSendVersionedMu // Note: Insert new items into processedFileState will not invalidate the reference. state NotifiedVersion& curMsgIndex = batchData->processedFileState[req.asset]; - TraceEvent(SevDebug, "FastRestoreApplierPhaseReceiveMutations", self->id()) + TraceEvent(SevInfo, "FastRestoreApplierPhaseReceiveMutations", self->id()) .detail("BatchIndex", req.batchIndex) .detail("RestoreAsset", req.asset.toString()) .detail("RestoreAssetMesssageIndex", curMsgIndex.get()) @@ -156,7 +156,7 @@ ACTOR static Future handleSendMutationVectorRequest(RestoreSendVersionedMu } req.reply.send(RestoreCommonReply(self->id(), isDuplicated)); - TraceEvent(SevDebug, "FastRestoreApplierPhaseReceiveMutationsDone", self->id()) + TraceEvent(SevInfo, "FastRestoreApplierPhaseReceiveMutationsDone", self->id()) .detail("BatchIndex", req.batchIndex) .detail("RestoreAsset", req.asset.toString()) .detail("ProcessedMessageIndex", curMsgIndex.get()) diff --git a/fdbserver/RestoreLoader.actor.cpp b/fdbserver/RestoreLoader.actor.cpp index 51befe3279..821e78b480 100644 --- a/fdbserver/RestoreLoader.actor.cpp +++ b/fdbserver/RestoreLoader.actor.cpp @@ -589,7 +589,7 @@ ACTOR Future sendMutationsToApplier(VersionedMutationsMap* pkvOps, int bat void splitMutation(std::map* pRangeToApplier, MutationRef m, Arena& mvector_arena, VectorRef& mvector, Arena& nodeIDs_arena, VectorRef& nodeIDs) { - TraceEvent(SevWarn, "FastRestoreSplitMutation").detail("Mutation", m.toString()); + TraceEvent(SevDebug, "FastRestoreSplitMutation").detail("Mutation", m.toString()); // mvector[i] should be mapped to nodeID[i] ASSERT(mvector.empty()); ASSERT(nodeIDs.empty()); From fb1c456a2d7df808da4c92a275007c4e28945a76 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Thu, 30 Apr 2020 19:15:32 -0700 Subject: [PATCH 1603/1604] FastRestore:Change default knob value --- fdbserver/Knobs.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fdbserver/Knobs.cpp b/fdbserver/Knobs.cpp index a194ac3deb..16d520662a 100644 --- a/fdbserver/Knobs.cpp +++ b/fdbserver/Knobs.cpp @@ -569,7 +569,7 @@ void ServerKnobs::initialize(bool randomize, ClientKnobs* clientKnobs, bool isSi // Fast Restore init( FASTRESTORE_FAILURE_TIMEOUT, 3600 ); init( FASTRESTORE_HEARTBEAT_INTERVAL, 60 ); - init( FASTRESTORE_SAMPLING_PERCENT, 1 ); if( randomize && BUGGIFY ) { FASTRESTORE_SAMPLING_PERCENT = deterministicRandom()->random01() * 100; } + init( FASTRESTORE_SAMPLING_PERCENT, 80 ); if( randomize && BUGGIFY ) { FASTRESTORE_SAMPLING_PERCENT = deterministicRandom()->random01() * 100; } init( FASTRESTORE_NUM_LOADERS, 3 ); if( randomize && BUGGIFY ) { FASTRESTORE_NUM_LOADERS = deterministicRandom()->random01() * 10 + 1; } init( FASTRESTORE_NUM_APPLIERS, 3 ); if( randomize && BUGGIFY ) { FASTRESTORE_NUM_APPLIERS = deterministicRandom()->random01() * 10 + 1; } init( FASTRESTORE_TXN_BATCH_MAX_BYTES, 512.0 ); if( randomize && BUGGIFY ) { FASTRESTORE_TXN_BATCH_MAX_BYTES = deterministicRandom()->random01() * 1024.0 * 1024.0 + 1.0; } @@ -583,7 +583,7 @@ void ServerKnobs::initialize(bool randomize, ClientKnobs* clientKnobs, bool isSi init( FASTRESTORE_APPLYING_PARALLELISM, 100 ); if( randomize && BUGGIFY ) { FASTRESTORE_APPLYING_PARALLELISM = deterministicRandom()->random01() * 10 + 1; } init( FASTRESTORE_MONITOR_LEADER_DELAY, 5 ); if( randomize && BUGGIFY ) { FASTRESTORE_MONITOR_LEADER_DELAY = deterministicRandom()->random01() * 100; } init( FASTRESTORE_STRAGGLER_THRESHOLD_SECONDS, 60 ); if( randomize && BUGGIFY ) { FASTRESTORE_STRAGGLER_THRESHOLD_SECONDS = deterministicRandom()->random01() * 240 + 10; } - init( FASTRESTORE_TRACK_REQUEST_LATENCY, true ); if( randomize && BUGGIFY ) { FASTRESTORE_TRACK_REQUEST_LATENCY = false; } + init( FASTRESTORE_TRACK_REQUEST_LATENCY, false ); if( randomize && BUGGIFY ) { FASTRESTORE_TRACK_REQUEST_LATENCY = false; } init( FASTRESTORE_TRACK_LOADER_SEND_REQUESTS, false ); if( randomize && BUGGIFY ) { FASTRESTORE_TRACK_LOADER_SEND_REQUESTS = true; } init( FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT, 6144 ); if( randomize && BUGGIFY ) { FASTRESTORE_MEMORY_THRESHOLD_MB_SOFT = 1; } init( FASTRESTORE_WAIT_FOR_MEMORY_LATENCY, 10 ); if( randomize && BUGGIFY ) { FASTRESTORE_WAIT_FOR_MEMORY_LATENCY = 60; } From 06935f247c9151a43b8c03148a365edb03a0dc27 Mon Sep 17 00:00:00 2001 From: Meng Xu Date: Thu, 30 Apr 2020 20:12:53 -0700 Subject: [PATCH 1604/1604] FastRestore:Change getBatchReplies from waitForAny to waitForAll waitForAny may cause busy waiting. For best performance, we should be able to control the number of outstanding requests to wait for. --- fdbserver/RestoreCommon.actor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdbserver/RestoreCommon.actor.h b/fdbserver/RestoreCommon.actor.h index 41df5495fd..4f7b8d395b 100644 --- a/fdbserver/RestoreCommon.actor.h +++ b/fdbserver/RestoreCommon.actor.h @@ -296,7 +296,7 @@ Future getBatchReplies(RequestStream Interface::*channel, std::ma if (ongoingReplies.empty()) { break; } else { - wait(waitForAny(ongoingReplies)); + wait(waitForAll(ongoingReplies)); } // At least one reply is received; Calculate the reply duration for (int j = 0; j < ongoingReplies.size(); ++j) {