Add an internal C API to support connection to a cluster using a connection string (#7438)

* Add an internal C API to support memory connection records

* Track shared state in the client using a unique and immutable cluster ID from the cluster

* Add missing code to store the clusterId in the database state object

* Update some arguments to pass by const&
This commit is contained in:
A.J. Beamon 2022-07-07 01:12:49 -07:00 committed by GitHub
parent f98d5ae9e9
commit c4b0f6eaae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 285 additions and 112 deletions

View File

@ -382,6 +382,12 @@ extern "C" DLLEXPORT fdb_error_t fdb_create_database(const char* cluster_file_pa
return fdb_create_database_impl(cluster_file_path, out_database); return fdb_create_database_impl(cluster_file_path, out_database);
} }
extern "C" DLLEXPORT fdb_error_t fdb_create_database_from_connection_string(const char* connection_string,
FDBDatabase** out_database) {
CATCH_AND_RETURN(*out_database =
(FDBDatabase*)API->createDatabaseFromConnectionString(connection_string).extractPtr(););
}
extern "C" DLLEXPORT fdb_error_t fdb_database_set_option(FDBDatabase* d, extern "C" DLLEXPORT fdb_error_t fdb_database_set_option(FDBDatabase* d,
FDBDatabaseOption option, FDBDatabaseOption option,
uint8_t const* value, uint8_t const* value,

View File

@ -46,6 +46,9 @@ DLLEXPORT void fdb_database_set_shared_state(FDBDatabase* db, DatabaseSharedStat
DLLEXPORT WARN_UNUSED_RESULT fdb_error_t fdb_future_get_shared_state(FDBFuture* f, DatabaseSharedState** outPtr); DLLEXPORT WARN_UNUSED_RESULT fdb_error_t fdb_future_get_shared_state(FDBFuture* f, DatabaseSharedState** outPtr);
DLLEXPORT WARN_UNUSED_RESULT fdb_error_t fdb_create_database_from_connection_string(const char* connection_string,
FDBDatabase** out_database);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -17,18 +17,22 @@ Users will also (by default) see a ``special_keys_cross_module_read`` error if t
The error is to save the user from the surprise of seeing the behavior of multiple modules in the same read. The error is to save the user from the surprise of seeing the behavior of multiple modules in the same read.
Users may opt out of these restrictions by setting the ``special_key_space_relaxed`` transaction option. Users may opt out of these restrictions by setting the ``special_key_space_relaxed`` transaction option.
Each special key that existed before api version 630 is its own module. These are Each special key that existed before api version 630 is its own module. These are:
#. ``\xff\xff/cluster_file_path`` See :ref:`cluster file client access <cluster-file-client-access>` #. ``\xff\xff/cluster_file_path`` - See :ref:`cluster file client access <cluster-file-client-access>`
#. ``\xff\xff/status/json`` See :doc:`Machine-readable status <mr-status>` #. ``\xff\xff/status/json`` - See :doc:`Machine-readable status <mr-status>`
Prior to api version 630, it was also possible to read a range starting at Prior to api version 630, it was also possible to read a range starting at ``\xff\xff/worker_interfaces``. This is mostly an implementation detail of fdbcli,
``\xff\xff/worker_interfaces``. This is mostly an implementation detail of fdbcli,
but it's available in api version 630 as a module with prefix ``\xff\xff/worker_interfaces/``. but it's available in api version 630 as a module with prefix ``\xff\xff/worker_interfaces/``.
Api version 630 includes two new modules with prefixes Api version 630 includes two new modules:
``\xff\xff/transaction/`` (information about the current transaction), and
``\xff\xff/metrics/`` (various metrics, not transactional). #. ``\xff\xff/transaction/`` - information about the current transaction
#. ``\xff\xff/metrics/`` - various metrics, not transactional
Api version 720 includes one new module:
#. ``\xff\xff/clusterId`` - returns an immutable unique ID for a cluster
Transaction module Transaction module
------------------ ------------------

View File

@ -40,6 +40,16 @@ ClusterConnectionFile::ClusterConnectionFile(std::string const& filename, Cluste
cs = contents; cs = contents;
} }
// Creates a cluster file from the given filename. If the filename is empty, attempts to load the default
// cluster file instead.
Reference<ClusterConnectionFile> ClusterConnectionFile::openOrDefault(std::string const& filename) {
return makeReference<ClusterConnectionFile>(lookupClusterFileName(filename).first);
}
Reference<ClusterConnectionFile> ClusterConnectionFile::openOrDefault(const char* filename) {
return openOrDefault(std::string(filename == nullptr ? "" : filename));
}
// Sets the connections string held by this object and persists it. // Sets the connections string held by this object and persists it.
Future<Void> ClusterConnectionFile::setAndPersistConnectionString(ClusterConnectionString const& conn) { Future<Void> ClusterConnectionFile::setAndPersistConnectionString(ClusterConnectionString const& conn) {
ASSERT(filename.size()); ASSERT(filename.size());

View File

@ -631,6 +631,11 @@ void DLApi::init() {
loadClientFunction(&api->runNetwork, lib, fdbCPath, "fdb_run_network", headerVersion >= 0); loadClientFunction(&api->runNetwork, lib, fdbCPath, "fdb_run_network", headerVersion >= 0);
loadClientFunction(&api->stopNetwork, lib, fdbCPath, "fdb_stop_network", headerVersion >= 0); loadClientFunction(&api->stopNetwork, lib, fdbCPath, "fdb_stop_network", headerVersion >= 0);
loadClientFunction(&api->createDatabase, lib, fdbCPath, "fdb_create_database", headerVersion >= 610); loadClientFunction(&api->createDatabase, lib, fdbCPath, "fdb_create_database", headerVersion >= 610);
loadClientFunction(&api->createDatabaseFromConnectionString,
lib,
fdbCPath,
"fdb_create_database_from_connection_string",
headerVersion >= 720);
loadClientFunction(&api->databaseOpenTenant, lib, fdbCPath, "fdb_database_open_tenant", headerVersion >= 710); loadClientFunction(&api->databaseOpenTenant, lib, fdbCPath, "fdb_database_open_tenant", headerVersion >= 710);
loadClientFunction( loadClientFunction(
@ -864,6 +869,16 @@ Reference<IDatabase> DLApi::createDatabase(const char* clusterFilePath) {
} }
} }
Reference<IDatabase> DLApi::createDatabaseFromConnectionString(const char* connectionString) {
if (api->createDatabaseFromConnectionString == nullptr) {
throw unsupported_operation();
}
FdbCApi::FDBDatabase* db;
throwIfError(api->createDatabaseFromConnectionString(connectionString, &db));
return Reference<IDatabase>(new DLDatabase(api, db));
}
void DLApi::addNetworkThreadCompletionHook(void (*hook)(void*), void* hookParameter) { void DLApi::addNetworkThreadCompletionHook(void (*hook)(void*), void* hookParameter) {
MutexHolder holder(lock); MutexHolder holder(lock);
threadCompletionHooks.emplace_back(hook, hookParameter); threadCompletionHooks.emplace_back(hook, hookParameter);
@ -1406,11 +1421,11 @@ void MultiVersionTenant::TenantState::close() {
// MultiVersionDatabase // MultiVersionDatabase
MultiVersionDatabase::MultiVersionDatabase(MultiVersionApi* api, MultiVersionDatabase::MultiVersionDatabase(MultiVersionApi* api,
int threadIdx, int threadIdx,
std::string clusterFilePath, ClusterConnectionRecord const& connectionRecord,
Reference<IDatabase> db, Reference<IDatabase> db,
Reference<IDatabase> versionMonitorDb, Reference<IDatabase> versionMonitorDb,
bool openConnectors) bool openConnectors)
: dbState(new DatabaseState(clusterFilePath, versionMonitorDb)) { : dbState(new DatabaseState(connectionRecord, versionMonitorDb)) {
dbState->db = db; dbState->db = db;
dbState->dbVar->set(db); dbState->dbVar->set(db);
if (openConnectors) { if (openConnectors) {
@ -1420,7 +1435,7 @@ MultiVersionDatabase::MultiVersionDatabase(MultiVersionApi* api,
api->runOnExternalClients(threadIdx, [this](Reference<ClientInfo> client) { dbState->addClient(client); }); api->runOnExternalClients(threadIdx, [this](Reference<ClientInfo> client) { dbState->addClient(client); });
api->runOnExternalClientsAllThreads([&clusterFilePath](Reference<ClientInfo> client) { api->runOnExternalClientsAllThreads([&connectionRecord](Reference<ClientInfo> client) {
// This creates a database to initialize some client state on the external library. // This creates a database to initialize some client state on the external library.
// We only do this on 6.2+ clients to avoid some bugs associated with older versions. // We only do this on 6.2+ clients to avoid some bugs associated with older versions.
// This deletes the new database immediately to discard its connections. // This deletes the new database immediately to discard its connections.
@ -1430,7 +1445,7 @@ MultiVersionDatabase::MultiVersionDatabase(MultiVersionApi* api,
// to run this initialization in case the other fails, and it's safe to run them in parallel. // to run this initialization in case the other fails, and it's safe to run them in parallel.
if (client->protocolVersion.hasCloseUnusedConnection() && !client->initialized) { if (client->protocolVersion.hasCloseUnusedConnection() && !client->initialized) {
try { try {
Reference<IDatabase> newDb = client->api->createDatabase(clusterFilePath.c_str()); Reference<IDatabase> newDb = connectionRecord.createDatabase(client->api);
client->initialized = true; client->initialized = true;
} catch (Error& e) { } catch (Error& e) {
// This connection is not initialized. It is still possible to connect with it, // This connection is not initialized. It is still possible to connect with it,
@ -1439,23 +1454,23 @@ MultiVersionDatabase::MultiVersionDatabase(MultiVersionApi* api,
TraceEvent(SevWarnAlways, "FailedToInitializeExternalClient") TraceEvent(SevWarnAlways, "FailedToInitializeExternalClient")
.error(e) .error(e)
.detail("LibraryPath", client->libPath) .detail("LibraryPath", client->libPath)
.detail("ClusterFilePath", clusterFilePath); .detail("ConnectionRecord", connectionRecord);
} }
} }
}); });
// For clients older than 6.2 we create and maintain our database connection // For clients older than 6.2 we create and maintain our database connection
api->runOnExternalClients(threadIdx, [this, &clusterFilePath](Reference<ClientInfo> client) { api->runOnExternalClients(threadIdx, [this, &connectionRecord](Reference<ClientInfo> client) {
if (!client->protocolVersion.hasCloseUnusedConnection()) { if (!client->protocolVersion.hasCloseUnusedConnection()) {
try { try {
dbState->legacyDatabaseConnections[client->protocolVersion] = dbState->legacyDatabaseConnections[client->protocolVersion] =
client->api->createDatabase(clusterFilePath.c_str()); connectionRecord.createDatabase(client->api);
} catch (Error& e) { } catch (Error& e) {
// This connection is discarded // This connection is discarded
TraceEvent(SevWarnAlways, "FailedToCreateLegacyDatabaseConnection") TraceEvent(SevWarnAlways, "FailedToCreateLegacyDatabaseConnection")
.error(e) .error(e)
.detail("LibraryPath", client->libPath) .detail("LibraryPath", client->libPath)
.detail("ClusterFilePath", clusterFilePath); .detail("ConnectionRecord", connectionRecord);
} }
} }
}); });
@ -1472,7 +1487,8 @@ MultiVersionDatabase::~MultiVersionDatabase() {
// Create a MultiVersionDatabase that wraps an already created IDatabase object // Create a MultiVersionDatabase that wraps an already created IDatabase object
// For internal use in testing // For internal use in testing
Reference<IDatabase> MultiVersionDatabase::debugCreateFromExistingDatabase(Reference<IDatabase> db) { Reference<IDatabase> MultiVersionDatabase::debugCreateFromExistingDatabase(Reference<IDatabase> db) {
return Reference<IDatabase>(new MultiVersionDatabase(MultiVersionApi::api, 0, "", db, db, false)); return Reference<IDatabase>(new MultiVersionDatabase(
MultiVersionApi::api, 0, ClusterConnectionRecord::fromConnectionString(""), db, db, false));
} }
Reference<ITenant> MultiVersionDatabase::openTenant(TenantNameRef tenantName) { Reference<ITenant> MultiVersionDatabase::openTenant(TenantNameRef tenantName) {
@ -1570,9 +1586,10 @@ ThreadFuture<ProtocolVersion> MultiVersionDatabase::getServerProtocol(Optional<P
return dbState->versionMonitorDb->getServerProtocol(expectedVersion); return dbState->versionMonitorDb->getServerProtocol(expectedVersion);
} }
MultiVersionDatabase::DatabaseState::DatabaseState(std::string clusterFilePath, Reference<IDatabase> versionMonitorDb) MultiVersionDatabase::DatabaseState::DatabaseState(ClusterConnectionRecord const& connectionRecord,
Reference<IDatabase> versionMonitorDb)
: dbVar(new ThreadSafeAsyncVar<Reference<IDatabase>>(Reference<IDatabase>(nullptr))), : dbVar(new ThreadSafeAsyncVar<Reference<IDatabase>>(Reference<IDatabase>(nullptr))),
clusterFilePath(clusterFilePath), versionMonitorDb(versionMonitorDb), closed(false) {} connectionRecord(connectionRecord), versionMonitorDb(versionMonitorDb), closed(false) {}
// Adds a client (local or externally loaded) that can be used to connect to the cluster // Adds a client (local or externally loaded) that can be used to connect to the cluster
void MultiVersionDatabase::DatabaseState::addClient(Reference<ClientInfo> client) { void MultiVersionDatabase::DatabaseState::addClient(Reference<ClientInfo> client) {
@ -1658,7 +1675,7 @@ void MultiVersionDatabase::DatabaseState::protocolVersionChanged(ProtocolVersion
// When the protocol version changes, clear the corresponding entry in the shared state map // When the protocol version changes, clear the corresponding entry in the shared state map
// so it can be re-initialized. Only do so if there was a valid previous protocol version. // so it can be re-initialized. Only do so if there was a valid previous protocol version.
if (dbProtocolVersion.present() && MultiVersionApi::apiVersionAtLeast(710)) { if (dbProtocolVersion.present() && MultiVersionApi::apiVersionAtLeast(710)) {
MultiVersionApi::api->clearClusterSharedStateMapEntry(clusterFilePath, dbProtocolVersion.get()); MultiVersionApi::api->clearClusterSharedStateMapEntry(clusterId, dbProtocolVersion.get());
} }
dbProtocolVersion = protocolVersion; dbProtocolVersion = protocolVersion;
@ -1673,13 +1690,13 @@ void MultiVersionDatabase::DatabaseState::protocolVersionChanged(ProtocolVersion
Reference<IDatabase> newDb; Reference<IDatabase> newDb;
try { try {
newDb = client->api->createDatabase(clusterFilePath.c_str()); newDb = connectionRecord.createDatabase(client->api);
} catch (Error& e) { } catch (Error& e) {
TraceEvent(SevWarnAlways, "MultiVersionClientFailedToCreateDatabase") TraceEvent(SevWarnAlways, "MultiVersionClientFailedToCreateDatabase")
.error(e) .error(e)
.detail("LibraryPath", client->libPath) .detail("LibraryPath", client->libPath)
.detail("External", client->external) .detail("External", client->external)
.detail("ClusterFilePath", clusterFilePath); .detail("ConnectionRecord", connectionRecord);
// Put the client in a disconnected state until the version changes again // Put the client in a disconnected state until the version changes again
updateDatabase(Reference<IDatabase>(), Reference<ClientInfo>()); updateDatabase(Reference<IDatabase>(), Reference<ClientInfo>());
@ -1748,35 +1765,45 @@ void MultiVersionDatabase::DatabaseState::updateDatabase(Reference<IDatabase> ne
} else { } else {
// For older clients that don't have an API to get the protocol version, we have to monitor it locally // For older clients that don't have an API to get the protocol version, we have to monitor it locally
try { try {
versionMonitorDb = MultiVersionApi::api->getLocalClient()->api->createDatabase(clusterFilePath.c_str()); versionMonitorDb = connectionRecord.createDatabase(MultiVersionApi::api->getLocalClient()->api);
} catch (Error& e) { } catch (Error& e) {
// We can't create a new database to monitor the cluster version. This means we will continue using the // We can't create a new database to monitor the cluster version. This means we will continue using the
// previous one, which should hopefully continue to work. // previous one, which should hopefully continue to work.
TraceEvent(SevWarnAlways, "FailedToCreateDatabaseForVersionMonitoring") TraceEvent(SevWarnAlways, "FailedToCreateDatabaseForVersionMonitoring")
.error(e) .error(e)
.detail("ClusterFilePath", clusterFilePath); .detail("ConnectionRecord", connectionRecord);
} }
} }
} else { } else {
// We don't have a database connection, so use the local client to monitor the protocol version // We don't have a database connection, so use the local client to monitor the protocol version
db = Reference<IDatabase>(); db = Reference<IDatabase>();
try { try {
versionMonitorDb = MultiVersionApi::api->getLocalClient()->api->createDatabase(clusterFilePath.c_str()); versionMonitorDb = connectionRecord.createDatabase(MultiVersionApi::api->getLocalClient()->api);
} catch (Error& e) { } catch (Error& e) {
// We can't create a new database to monitor the cluster version. This means we will continue using the // We can't create a new database to monitor the cluster version. This means we will continue using the
// previous one, which should hopefully continue to work. // previous one, which should hopefully continue to work.
TraceEvent(SevWarnAlways, "FailedToCreateDatabaseForVersionMonitoring") TraceEvent(SevWarnAlways, "FailedToCreateDatabaseForVersionMonitoring")
.error(e) .error(e)
.detail("ClusterFilePath", clusterFilePath); .detail("ConnectionRecord", connectionRecord);
} }
} }
if (db.isValid() && dbProtocolVersion.present() && MultiVersionApi::apiVersionAtLeast(710)) { if (db.isValid() && dbProtocolVersion.present() && MultiVersionApi::apiVersionAtLeast(710)) {
auto updateResult = Future<std::string> updateResult =
MultiVersionApi::api->updateClusterSharedStateMap(clusterFilePath, dbProtocolVersion.get(), db); MultiVersionApi::api->updateClusterSharedStateMap(connectionRecord, dbProtocolVersion.get(), db);
auto handler = mapThreadFuture<Void, Void>(updateResult, [this](ErrorOr<Void> result) { sharedStateUpdater = map(errorOr(updateResult), [this](ErrorOr<std::string> result) {
TraceEvent("ClusterSharedStateUpdated").detail("ClusterFilePath", clusterFilePath); if (result.present()) {
clusterId = result.get();
TraceEvent("ClusterSharedStateUpdated")
.detail("ClusterId", result.get())
.detail("ProtocolVersion", dbProtocolVersion.get());
} else {
TraceEvent(SevWarnAlways, "ClusterSharedStateUpdateError")
.error(result.getError())
.detail("ConnectionRecord", connectionRecord)
.detail("ProtocolVersion", dbProtocolVersion.get());
}
dbVar->set(db); dbVar->set(db);
return ErrorOr<Void>(Void()); return Void();
}); });
} else { } else {
dbVar->set(db); dbVar->set(db);
@ -2376,14 +2403,12 @@ void MultiVersionApi::addNetworkThreadCompletionHook(void (*hook)(void*), void*
} }
// Creates an IDatabase object that represents a connection to the cluster // Creates an IDatabase object that represents a connection to the cluster
Reference<IDatabase> MultiVersionApi::createDatabase(const char* clusterFilePath) { Reference<IDatabase> MultiVersionApi::createDatabase(ClusterConnectionRecord const& connectionRecord) {
lock.enter(); lock.enter();
if (!networkSetup) { if (!networkSetup) {
lock.leave(); lock.leave();
throw network_not_setup(); throw network_not_setup();
} }
std::string clusterFile(clusterFilePath);
if (localClientDisabled) { if (localClientDisabled) {
ASSERT(!bypassMultiClientApi); ASSERT(!bypassMultiClientApi);
@ -2391,23 +2416,32 @@ Reference<IDatabase> MultiVersionApi::createDatabase(const char* clusterFilePath
nextThread = (nextThread + 1) % threadCount; nextThread = (nextThread + 1) % threadCount;
lock.leave(); lock.leave();
Reference<IDatabase> localDb = localClient->api->createDatabase(clusterFilePath); Reference<IDatabase> localDb = connectionRecord.createDatabase(localClient->api);
return Reference<IDatabase>( return Reference<IDatabase>(
new MultiVersionDatabase(this, threadIdx, clusterFile, Reference<IDatabase>(), localDb)); new MultiVersionDatabase(this, threadIdx, connectionRecord, Reference<IDatabase>(), localDb));
} }
lock.leave(); lock.leave();
ASSERT_LE(threadCount, 1); ASSERT_LE(threadCount, 1);
Reference<IDatabase> localDb = localClient->api->createDatabase(clusterFilePath); Reference<IDatabase> localDb = connectionRecord.createDatabase(localClient->api);
if (bypassMultiClientApi) { if (bypassMultiClientApi) {
return localDb; return localDb;
} else { } else {
return Reference<IDatabase>(new MultiVersionDatabase(this, 0, clusterFile, Reference<IDatabase>(), localDb)); return Reference<IDatabase>(
new MultiVersionDatabase(this, 0, connectionRecord, Reference<IDatabase>(), localDb));
} }
} }
Reference<IDatabase> MultiVersionApi::createDatabase(const char* clusterFilePath) {
return createDatabase(ClusterConnectionRecord::fromFile(clusterFilePath));
}
Reference<IDatabase> MultiVersionApi::createDatabaseFromConnectionString(const char* connectionString) {
return createDatabase(ClusterConnectionRecord::fromConnectionString(connectionString));
}
void MultiVersionApi::updateSupportedVersions() { void MultiVersionApi::updateSupportedVersions() {
if (networkSetup) { if (networkSetup) {
Standalone<VectorRef<uint8_t>> versionStr; Standalone<VectorRef<uint8_t>> versionStr;
@ -2432,55 +2466,79 @@ void MultiVersionApi::updateSupportedVersions() {
} }
} }
ThreadFuture<Void> MultiVersionApi::updateClusterSharedStateMap(std::string clusterFilePath, // Must be called from the main thread
ProtocolVersion dbProtocolVersion, ACTOR Future<std::string> updateClusterSharedStateMapImpl(MultiVersionApi* self,
Reference<IDatabase> db) { ClusterConnectionRecord connectionRecord,
MutexHolder holder(lock); ProtocolVersion dbProtocolVersion,
if (clusterSharedStateMap.find(clusterFilePath) == clusterSharedStateMap.end()) { Reference<IDatabase> db) {
// The cluster ID will be the connection record string (either a filename or the connection string itself)
// in API versions before we could read the cluster ID.
state std::string clusterId = connectionRecord.toString();
if (MultiVersionApi::apiVersionAtLeast(720)) {
state Reference<ITransaction> tr = db->createTransaction();
loop {
try {
state ThreadFuture<Optional<Value>> clusterIdFuture = tr->get("\xff\xff/cluster_id"_sr);
Optional<Value> clusterIdVal = wait(safeThreadFutureToFuture(clusterIdFuture));
ASSERT(clusterIdVal.present());
clusterId = clusterIdVal.get().toString();
ASSERT(UID::fromString(clusterId).isValid());
break;
} catch (Error& e) {
wait(safeThreadFutureToFuture(tr->onError(e)));
}
}
}
if (self->clusterSharedStateMap.find(clusterId) == self->clusterSharedStateMap.end()) {
TraceEvent("CreatingClusterSharedState") TraceEvent("CreatingClusterSharedState")
.detail("ClusterFilePath", clusterFilePath) .detail("ClusterId", clusterId)
.detail("ProtocolVersion", dbProtocolVersion); .detail("ProtocolVersion", dbProtocolVersion);
clusterSharedStateMap[clusterFilePath] = { db->createSharedState(), dbProtocolVersion }; self->clusterSharedStateMap[clusterId] = { db->createSharedState(), dbProtocolVersion };
} else { } else {
auto& sharedStateInfo = clusterSharedStateMap[clusterFilePath]; auto& sharedStateInfo = self->clusterSharedStateMap[clusterId];
if (sharedStateInfo.protocolVersion != dbProtocolVersion) { if (sharedStateInfo.protocolVersion != dbProtocolVersion) {
// This situation should never happen, because we are connecting to the same cluster, // This situation should never happen, because we are connecting to the same cluster,
// so the protocol version must be the same // so the protocol version must be the same
TraceEvent(SevError, "ClusterStateProtocolVersionMismatch") TraceEvent(SevError, "ClusterStateProtocolVersionMismatch")
.detail("ClusterFilePath", clusterFilePath) .detail("ClusterId", clusterId)
.detail("ProtocolVersionExpected", dbProtocolVersion) .detail("ProtocolVersionExpected", dbProtocolVersion)
.detail("ProtocolVersionFound", sharedStateInfo.protocolVersion); .detail("ProtocolVersionFound", sharedStateInfo.protocolVersion);
return Void(); return clusterId;
} }
TraceEvent("SettingClusterSharedState") TraceEvent("SettingClusterSharedState")
.detail("ClusterFilePath", clusterFilePath) .detail("ClusterId", clusterId)
.detail("ProtocolVersion", dbProtocolVersion); .detail("ProtocolVersion", dbProtocolVersion);
ThreadFuture<DatabaseSharedState*> entry = sharedStateInfo.sharedStateFuture;
return mapThreadFuture<DatabaseSharedState*, Void>(entry, [db](ErrorOr<DatabaseSharedState*> result) { state ThreadFuture<DatabaseSharedState*> entry = sharedStateInfo.sharedStateFuture;
if (result.isError()) { DatabaseSharedState* sharedState = wait(safeThreadFutureToFuture(entry));
return ErrorOr<Void>(result.getError()); db->setSharedState(sharedState);
}
auto ssPtr = result.get();
db->setSharedState(ssPtr);
return ErrorOr<Void>(Void());
});
} }
return Void();
return clusterId;
} }
void MultiVersionApi::clearClusterSharedStateMapEntry(std::string clusterFilePath, ProtocolVersion dbProtocolVersion) { // Must be called from the main thread
MutexHolder holder(lock); Future<std::string> MultiVersionApi::updateClusterSharedStateMap(ClusterConnectionRecord const& connectionRecord,
auto mapEntry = clusterSharedStateMap.find(clusterFilePath); ProtocolVersion dbProtocolVersion,
// It can be that other database instances on the same cluster path are already upgraded and thus Reference<IDatabase> db) {
return updateClusterSharedStateMapImpl(this, connectionRecord, dbProtocolVersion, db);
}
// Must be called from the main thread
void MultiVersionApi::clearClusterSharedStateMapEntry(std::string clusterId, ProtocolVersion dbProtocolVersion) {
auto mapEntry = clusterSharedStateMap.find(clusterId);
// It can be that other database instances on the same cluster are already upgraded and thus
// have cleared or even created a new shared object entry // have cleared or even created a new shared object entry
if (mapEntry == clusterSharedStateMap.end()) { if (mapEntry == clusterSharedStateMap.end()) {
TraceEvent("ClusterSharedStateMapEntryNotFound").detail("ClusterFilePath", clusterFilePath); TraceEvent("ClusterSharedStateMapEntryNotFound").detail("ClusterId", clusterId);
return; return;
} }
auto sharedStateInfo = mapEntry->second; auto sharedStateInfo = mapEntry->second;
if (sharedStateInfo.protocolVersion != dbProtocolVersion) { if (sharedStateInfo.protocolVersion != dbProtocolVersion) {
TraceEvent("ClusterSharedStateClearSkipped") TraceEvent("ClusterSharedStateClearSkipped")
.detail("ClusterFilePath", clusterFilePath) .detail("ClusterId", clusterId)
.detail("ProtocolVersionExpected", dbProtocolVersion) .detail("ProtocolVersionExpected", dbProtocolVersion)
.detail("ProtocolVersionFound", sharedStateInfo.protocolVersion); .detail("ProtocolVersionFound", sharedStateInfo.protocolVersion);
return; return;
@ -2488,9 +2546,7 @@ void MultiVersionApi::clearClusterSharedStateMapEntry(std::string clusterFilePat
auto ssPtr = sharedStateInfo.sharedStateFuture.get(); auto ssPtr = sharedStateInfo.sharedStateFuture.get();
ssPtr->delRef(ssPtr); ssPtr->delRef(ssPtr);
clusterSharedStateMap.erase(mapEntry); clusterSharedStateMap.erase(mapEntry);
TraceEvent("ClusterSharedStateCleared") TraceEvent("ClusterSharedStateCleared").detail("ClusterId", clusterId).detail("ProtocolVersion", dbProtocolVersion);
.detail("ClusterFilePath", clusterFilePath)
.detail("ProtocolVersion", dbProtocolVersion);
} }
std::vector<std::string> parseOptionValues(std::string valueStr) { std::vector<std::string> parseOptionValues(std::string valueStr) {

View File

@ -1416,6 +1416,13 @@ KeyRangeRef toRelativeRange(KeyRangeRef range, KeyRef prefix) {
} }
} }
ACTOR Future<UID> getClusterId(Database db) {
while (!db->clientInfo->get().clusterId.isValid()) {
wait(db->clientInfo->onChange());
}
return db->clientInfo->get().clusterId;
}
DatabaseContext::DatabaseContext(Reference<AsyncVar<Reference<IClusterConnectionRecord>>> connectionRecord, DatabaseContext::DatabaseContext(Reference<AsyncVar<Reference<IClusterConnectionRecord>>> connectionRecord,
Reference<AsyncVar<ClientDBInfo>> clientInfo, Reference<AsyncVar<ClientDBInfo>> clientInfo,
Reference<AsyncVar<Optional<ClientLeaderRegInterface>> const> coordinator, Reference<AsyncVar<Optional<ClientLeaderRegInterface>> const> coordinator,
@ -1497,6 +1504,21 @@ DatabaseContext::DatabaseContext(Reference<AsyncVar<Reference<IClusterConnection
globalConfig = std::make_unique<GlobalConfig>(this); globalConfig = std::make_unique<GlobalConfig>(this);
if (apiVersionAtLeast(720)) { if (apiVersionAtLeast(720)) {
registerSpecialKeysImpl(
SpecialKeySpace::MODULE::CLUSTERID,
SpecialKeySpace::IMPLTYPE::READONLY,
std::make_unique<SingleSpecialKeyImpl>(
LiteralStringRef("\xff\xff/cluster_id"), [](ReadYourWritesTransaction* ryw) -> Future<Optional<Value>> {
try {
if (ryw->getDatabase().getPtr()) {
return map(getClusterId(ryw->getDatabase()),
[](UID id) { return Optional<Value>(StringRef(id.toString())); });
}
} catch (Error& e) {
return e;
}
return Optional<Value>();
}));
registerSpecialKeysImpl( registerSpecialKeysImpl(
SpecialKeySpace::MODULE::MANAGEMENT, SpecialKeySpace::MODULE::MANAGEMENT,
SpecialKeySpace::IMPLTYPE::READWRITE, SpecialKeySpace::IMPLTYPE::READWRITE,
@ -2259,8 +2281,7 @@ Database Database::createDatabase(std::string connFileName,
int apiVersion, int apiVersion,
IsInternal internal, IsInternal internal,
LocalityData const& clientLocality) { LocalityData const& clientLocality) {
Reference<IClusterConnectionRecord> rccr = Reference<IClusterConnectionRecord>( Reference<IClusterConnectionRecord> rccr = ClusterConnectionFile::openOrDefault(connFileName);
new ClusterConnectionFile(ClusterConnectionFile::lookupClusterFileName(connFileName).first));
return Database::createDatabase(rccr, apiVersion, internal, clientLocality); return Database::createDatabase(rccr, apiVersion, internal, clientLocality);
} }

View File

@ -78,7 +78,8 @@ std::unordered_map<SpecialKeySpace::MODULE, KeyRange> SpecialKeySpace::moduleToB
KeyRangeRef(LiteralStringRef("\xff\xff/actor_lineage/"), LiteralStringRef("\xff\xff/actor_lineage0")) }, KeyRangeRef(LiteralStringRef("\xff\xff/actor_lineage/"), LiteralStringRef("\xff\xff/actor_lineage0")) },
{ SpecialKeySpace::MODULE::ACTOR_PROFILER_CONF, { SpecialKeySpace::MODULE::ACTOR_PROFILER_CONF,
KeyRangeRef(LiteralStringRef("\xff\xff/actor_profiler_conf/"), KeyRangeRef(LiteralStringRef("\xff\xff/actor_profiler_conf/"),
LiteralStringRef("\xff\xff/actor_profiler_conf0")) } LiteralStringRef("\xff\xff/actor_profiler_conf0")) },
{ SpecialKeySpace::MODULE::CLUSTERID, singleKeyRange(LiteralStringRef("\xff\xff/cluster_id")) },
}; };
std::unordered_map<std::string, KeyRange> SpecialKeySpace::managementApiCommandToRange = { std::unordered_map<std::string, KeyRange> SpecialKeySpace::managementApiCommandToRange = {

View File

@ -20,6 +20,7 @@
#include "fdbclient/BlobGranuleFiles.h" #include "fdbclient/BlobGranuleFiles.h"
#include "fdbclient/ClusterConnectionFile.h" #include "fdbclient/ClusterConnectionFile.h"
#include "fdbclient/ClusterConnectionMemoryRecord.h"
#include "fdbclient/ThreadSafeTransaction.h" #include "fdbclient/ThreadSafeTransaction.h"
#include "fdbclient/DatabaseContext.h" #include "fdbclient/DatabaseContext.h"
#include "fdbclient/versions.h" #include "fdbclient/versions.h"
@ -142,19 +143,14 @@ ThreadFuture<Void> ThreadSafeDatabase::waitPurgeGranulesComplete(const KeyRef& p
return onMainThread([db, key]() -> Future<Void> { return db->waitPurgeGranulesComplete(key); }); return onMainThread([db, key]() -> Future<Void> { return db->waitPurgeGranulesComplete(key); });
} }
ThreadSafeDatabase::ThreadSafeDatabase(std::string connFilename, int apiVersion) { ThreadSafeDatabase::ThreadSafeDatabase(Reference<IClusterConnectionRecord> connectionRecord, int apiVersion) {
ClusterConnectionFile* connFile =
new ClusterConnectionFile(ClusterConnectionFile::lookupClusterFileName(connFilename).first);
// Allocate memory for the Database from this thread (so the pointer is known for subsequent method calls) // Allocate memory for the Database from this thread (so the pointer is known for subsequent method calls)
// but run its constructor on the main thread // but run its constructor on the main thread
DatabaseContext* db = this->db = DatabaseContext::allocateOnForeignThread(); DatabaseContext* db = this->db = DatabaseContext::allocateOnForeignThread();
onMainThreadVoid([db, connFile, apiVersion]() { onMainThreadVoid([db, connectionRecord, apiVersion]() {
try { try {
Database::createDatabase( Database::createDatabase(connectionRecord, apiVersion, IsInternal::False, LocalityData(), db).extractPtr();
Reference<ClusterConnectionFile>(connFile), apiVersion, IsInternal::False, LocalityData(), db)
.extractPtr();
} catch (Error& e) { } catch (Error& e) {
new (db) DatabaseContext(e); new (db) DatabaseContext(e);
} catch (...) { } catch (...) {
@ -635,7 +631,13 @@ void ThreadSafeApi::stopNetwork() {
} }
Reference<IDatabase> ThreadSafeApi::createDatabase(const char* clusterFilePath) { Reference<IDatabase> ThreadSafeApi::createDatabase(const char* clusterFilePath) {
return Reference<IDatabase>(new ThreadSafeDatabase(clusterFilePath, apiVersion)); return Reference<IDatabase>(
new ThreadSafeDatabase(ClusterConnectionFile::openOrDefault(clusterFilePath), apiVersion));
}
Reference<IDatabase> ThreadSafeApi::createDatabaseFromConnectionString(const char* connectionString) {
return Reference<IDatabase>(new ThreadSafeDatabase(
makeReference<ClusterConnectionMemoryRecord>(ClusterConnectionString(connectionString)), apiVersion));
} }
void ThreadSafeApi::addNetworkThreadCompletionHook(void (*hook)(void*), void* hookParameter) { void ThreadSafeApi::addNetworkThreadCompletionHook(void (*hook)(void*), void* hookParameter) {

View File

@ -33,6 +33,11 @@ public:
// Creates a cluster file with a given connection string and saves it to the specified file. // Creates a cluster file with a given connection string and saves it to the specified file.
explicit ClusterConnectionFile(std::string const& filename, ClusterConnectionString const& contents); explicit ClusterConnectionFile(std::string const& filename, ClusterConnectionString const& contents);
// Creates a cluster file from the given filename. If the filename is empty, attempts to load the default
// cluster file instead.
static Reference<ClusterConnectionFile> openOrDefault(std::string const& filename);
static Reference<ClusterConnectionFile> openOrDefault(const char* filename);
// Sets the connections string held by this object and persists it. // Sets the connections string held by this object and persists it.
Future<Void> setAndPersistConnectionString(ClusterConnectionString const&) override; Future<Void> setAndPersistConnectionString(ClusterConnectionString const&) override;

View File

@ -116,6 +116,7 @@ struct ClientDBInfo {
firstCommitProxy; // not serialized, used for commitOnFirstProxy when the commit proxies vector has been shrunk firstCommitProxy; // not serialized, used for commitOnFirstProxy when the commit proxies vector has been shrunk
Optional<Value> forward; Optional<Value> forward;
std::vector<VersionHistory> history; std::vector<VersionHistory> history;
UID clusterId;
TenantMode tenantMode; TenantMode tenantMode;
@ -129,7 +130,7 @@ struct ClientDBInfo {
if constexpr (!is_fb_function<Archive>) { if constexpr (!is_fb_function<Archive>) {
ASSERT(ar.protocolVersion().isValid()); ASSERT(ar.protocolVersion().isValid());
} }
serializer(ar, grvProxies, commitProxies, id, forward, history, tenantMode); serializer(ar, grvProxies, commitProxies, id, forward, history, tenantMode, clusterId);
} }
}; };

View File

@ -157,6 +157,11 @@ private:
bool connectionStringNeedsPersisted; bool connectionStringNeedsPersisted;
}; };
template <>
struct Traceable<IClusterConnectionRecord> : std::true_type {
static std::string toString(IClusterConnectionRecord const& record) { return record.toString(); }
};
struct LeaderInfo { struct LeaderInfo {
constexpr static FileIdentifier file_identifier = 8338794; constexpr static FileIdentifier file_identifier = 8338794;
// The first 7 bits of changeID represent cluster controller process class fitness, the lower the better // The first 7 bits of changeID represent cluster controller process class fitness, the lower the better

View File

@ -20,14 +20,13 @@
#ifndef FDBCLIENT_ICLIENTAPI_H #ifndef FDBCLIENT_ICLIENTAPI_H
#define FDBCLIENT_ICLIENTAPI_H #define FDBCLIENT_ICLIENTAPI_H
#include "flow/ProtocolVersion.h"
#pragma once #pragma once
#include "fdbclient/FDBOptions.g.h" #include "fdbclient/FDBOptions.g.h"
#include "fdbclient/FDBTypes.h" #include "fdbclient/FDBTypes.h"
#include "fdbclient/Tenant.h" #include "fdbclient/Tenant.h"
#include "fdbclient/Tracing.h" #include "fdbclient/Tracing.h"
#include "flow/ProtocolVersion.h"
#include "flow/ThreadHelper.actor.h" #include "flow/ThreadHelper.actor.h"
struct VersionVector; struct VersionVector;
@ -199,6 +198,7 @@ public:
virtual void stopNetwork() = 0; virtual void stopNetwork() = 0;
virtual Reference<IDatabase> createDatabase(const char* clusterFilePath) = 0; virtual Reference<IDatabase> createDatabase(const char* clusterFilePath) = 0;
virtual Reference<IDatabase> createDatabaseFromConnectionString(const char* connectionString) = 0;
virtual void addNetworkThreadCompletionHook(void (*hook)(void*), void* hookParameter) = 0; virtual void addNetworkThreadCompletionHook(void (*hook)(void*), void* hookParameter) = 0;
}; };

View File

@ -20,14 +20,13 @@
#ifndef FDBCLIENT_MULTIVERSIONTRANSACTION_H #ifndef FDBCLIENT_MULTIVERSIONTRANSACTION_H
#define FDBCLIENT_MULTIVERSIONTRANSACTION_H #define FDBCLIENT_MULTIVERSIONTRANSACTION_H
#include "flow/ProtocolVersion.h"
#pragma once #pragma once
#include "fdbclient/fdb_c_options.g.h" #include "fdbclient/fdb_c_options.g.h"
#include "fdbclient/FDBOptions.g.h" #include "fdbclient/FDBOptions.g.h"
#include "fdbclient/FDBTypes.h" #include "fdbclient/FDBTypes.h"
#include "fdbclient/IClientApi.h" #include "fdbclient/IClientApi.h"
#include "flow/ProtocolVersion.h"
#include "flow/ThreadHelper.actor.h" #include "flow/ThreadHelper.actor.h"
// FdbCApi is used as a wrapper around the FoundationDB C API that gets loaded from an external client library. // FdbCApi is used as a wrapper around the FoundationDB C API that gets loaded from an external client library.
@ -128,6 +127,7 @@ struct FdbCApi : public ThreadSafeReferenceCounted<FdbCApi> {
fdb_error_t (*runNetwork)(); fdb_error_t (*runNetwork)();
fdb_error_t (*stopNetwork)(); fdb_error_t (*stopNetwork)();
fdb_error_t (*createDatabase)(const char* clusterFilePath, FDBDatabase** db); fdb_error_t (*createDatabase)(const char* clusterFilePath, FDBDatabase** db);
fdb_error_t (*createDatabaseFromConnectionString)(const char* connectionString, FDBDatabase** db);
// Database // Database
fdb_error_t (*databaseOpenTenant)(FDBDatabase* database, fdb_error_t (*databaseOpenTenant)(FDBDatabase* database,
@ -498,9 +498,11 @@ public:
void runNetwork() override; void runNetwork() override;
void stopNetwork() override; void stopNetwork() override;
Reference<IDatabase> createDatabase(const char* clusterFilePath) override; Reference<IDatabase> createDatabase(const char* clusterFile) override;
Reference<IDatabase> createDatabase609(const char* clusterFilePath); // legacy database creation Reference<IDatabase> createDatabase609(const char* clusterFilePath); // legacy database creation
Reference<IDatabase> createDatabaseFromConnectionString(const char* connectionString) override;
void addNetworkThreadCompletionHook(void (*hook)(void*), void* hookParameter) override; void addNetworkThreadCompletionHook(void (*hook)(void*), void* hookParameter) override;
private: private:
@ -722,6 +724,57 @@ public:
Reference<TenantState> tenantState; Reference<TenantState> tenantState;
}; };
class ClusterConnectionRecord {
private:
enum class Type { FILE, CONNECTION_STRING };
ClusterConnectionRecord(Type type, std::string const& recordStr) : type(type), recordStr(recordStr) {}
Type type;
std::string recordStr;
public:
static ClusterConnectionRecord fromFile(std::string const& clusterFilePath) {
return ClusterConnectionRecord(Type::FILE, clusterFilePath);
}
static ClusterConnectionRecord fromConnectionString(std::string const& connectionString) {
return ClusterConnectionRecord(Type::CONNECTION_STRING, connectionString);
}
Reference<IDatabase> createDatabase(IClientApi* api) const {
switch (type) {
case Type::FILE:
return api->createDatabase(recordStr.c_str());
case Type::CONNECTION_STRING:
return api->createDatabaseFromConnectionString(recordStr.c_str());
default:
ASSERT(false);
throw internal_error();
}
}
std::string toString() const {
switch (type) {
case Type::FILE:
if (recordStr.empty()) {
return "default file";
} else {
return "file: " + recordStr;
}
case Type::CONNECTION_STRING:
return "connection string: " + recordStr;
default:
ASSERT(false);
throw internal_error();
}
}
};
template <>
struct Traceable<ClusterConnectionRecord> : std::true_type {
static std::string toString(ClusterConnectionRecord const& connectionRecord) { return connectionRecord.toString(); }
};
// An implementation of IDatabase that wraps a database created either locally or through a dynamically loaded // An implementation of IDatabase that wraps a database created either locally or through a dynamically loaded
// external client. The MultiVersionDatabase monitors the protocol version of the cluster and automatically // external client. The MultiVersionDatabase monitors the protocol version of the cluster and automatically
// replaces the wrapped database when the protocol version changes. // replaces the wrapped database when the protocol version changes.
@ -729,7 +782,7 @@ class MultiVersionDatabase final : public IDatabase, ThreadSafeReferenceCounted<
public: public:
MultiVersionDatabase(MultiVersionApi* api, MultiVersionDatabase(MultiVersionApi* api,
int threadIdx, int threadIdx,
std::string clusterFilePath, ClusterConnectionRecord const& connectionRecord,
Reference<IDatabase> db, Reference<IDatabase> db,
Reference<IDatabase> versionMonitorDb, Reference<IDatabase> versionMonitorDb,
bool openConnectors = true); bool openConnectors = true);
@ -771,7 +824,7 @@ public:
// A struct that manages the current connection state of the MultiVersionDatabase. This wraps the underlying // A struct that manages the current connection state of the MultiVersionDatabase. This wraps the underlying
// IDatabase object that is currently interacting with the cluster. // IDatabase object that is currently interacting with the cluster.
struct DatabaseState : ThreadSafeReferenceCounted<DatabaseState> { struct DatabaseState : ThreadSafeReferenceCounted<DatabaseState> {
DatabaseState(std::string clusterFilePath, Reference<IDatabase> versionMonitorDb); DatabaseState(ClusterConnectionRecord const& connectionRecord, Reference<IDatabase> versionMonitorDb);
// Replaces the active database connection with a new one. Must be called from the main thread. // Replaces the active database connection with a new one. Must be called from the main thread.
void updateDatabase(Reference<IDatabase> newDb, Reference<ClientInfo> client); void updateDatabase(Reference<IDatabase> newDb, Reference<ClientInfo> client);
@ -796,7 +849,8 @@ public:
Reference<IDatabase> db; Reference<IDatabase> db;
const Reference<ThreadSafeAsyncVar<Reference<IDatabase>>> dbVar; const Reference<ThreadSafeAsyncVar<Reference<IDatabase>>> dbVar;
std::string clusterFilePath; ClusterConnectionRecord connectionRecord;
std::string clusterId;
// Used to monitor the cluster protocol version. Will be the same as db unless we have either not connected // Used to monitor the cluster protocol version. Will be the same as db unless we have either not connected
// yet or if the client version associated with db does not support protocol monitoring. In those cases, // yet or if the client version associated with db does not support protocol monitoring. In those cases,
@ -809,6 +863,8 @@ public:
ThreadFuture<Void> dbReady; ThreadFuture<Void> dbReady;
ThreadFuture<Void> protocolVersionMonitor; ThreadFuture<Void> protocolVersionMonitor;
Future<Void> sharedStateUpdater;
// Versions older than 6.1 do not benefit from having their database connections closed. Additionally, // Versions older than 6.1 do not benefit from having their database connections closed. Additionally,
// there are various issues that result in negative behavior in some cases if the connections are closed. // there are various issues that result in negative behavior in some cases if the connections are closed.
// Therefore, we leave them open. // Therefore, we leave them open.
@ -873,7 +929,10 @@ public:
void addNetworkThreadCompletionHook(void (*hook)(void*), void* hookParameter) override; void addNetworkThreadCompletionHook(void (*hook)(void*), void* hookParameter) override;
// Creates an IDatabase object that represents a connection to the cluster // Creates an IDatabase object that represents a connection to the cluster
Reference<IDatabase> createDatabase(ClusterConnectionRecord const& connectionRecord);
Reference<IDatabase> createDatabase(const char* clusterFilePath) override; Reference<IDatabase> createDatabase(const char* clusterFilePath) override;
Reference<IDatabase> createDatabaseFromConnectionString(const char* connectionString) override;
static MultiVersionApi* api; static MultiVersionApi* api;
Reference<ClientInfo> getLocalClient(); Reference<ClientInfo> getLocalClient();
@ -886,10 +945,18 @@ public:
bool callbackOnMainThread; bool callbackOnMainThread;
bool localClientDisabled; bool localClientDisabled;
ThreadFuture<Void> updateClusterSharedStateMap(std::string clusterFilePath, Future<std::string> updateClusterSharedStateMap(ClusterConnectionRecord const& connectionRecord,
ProtocolVersion dbProtocolVersion, ProtocolVersion dbProtocolVersion,
Reference<IDatabase> db); Reference<IDatabase> db);
void clearClusterSharedStateMapEntry(std::string clusterFilePath, ProtocolVersion dbProtocolVersion); void clearClusterSharedStateMapEntry(std::string clusterId, ProtocolVersion dbProtocolVersion);
// Map of cluster ID -> DatabaseSharedState pointer Future
// Upon cluster version upgrade, clear the map entry for that cluster
struct SharedStateInfo {
ThreadFuture<DatabaseSharedState*> sharedStateFuture;
ProtocolVersion protocolVersion;
};
std::map<std::string, SharedStateInfo> clusterSharedStateMap;
static bool apiVersionAtLeast(int minVersion); static bool apiVersionAtLeast(int minVersion);
@ -913,13 +980,6 @@ private:
Reference<ClientInfo> localClient; Reference<ClientInfo> localClient;
std::map<std::string, ClientDesc> externalClientDescriptions; std::map<std::string, ClientDesc> externalClientDescriptions;
std::map<std::string, std::vector<Reference<ClientInfo>>> externalClients; std::map<std::string, std::vector<Reference<ClientInfo>>> externalClients;
// Map of clusterFilePath -> DatabaseSharedState pointer Future
// Upon cluster version upgrade, clear the map entry for that cluster
struct SharedStateInfo {
ThreadFuture<DatabaseSharedState*> sharedStateFuture;
ProtocolVersion protocolVersion;
};
std::map<std::string, SharedStateInfo> clusterSharedStateMap;
bool networkStartSetup; bool networkStartSetup;
volatile bool networkSetup; volatile bool networkSetup;

View File

@ -166,6 +166,7 @@ public:
ACTORLINEAGE, // Sampling data ACTORLINEAGE, // Sampling data
ACTOR_PROFILER_CONF, // profiler configuration ACTOR_PROFILER_CONF, // profiler configuration
CLUSTERFILEPATH, CLUSTERFILEPATH,
CLUSTERID, // An immutable UID for a cluster
CONFIGURATION, // Configuration of the cluster CONFIGURATION, // Configuration of the cluster
CONNECTIONSTRING, CONNECTIONSTRING,
ERRORMSG, // A single key space contains a json string which describes the last error in special-key-space ERRORMSG, // A single key space contains a json string which describes the last error in special-key-space

View File

@ -72,7 +72,7 @@ private:
DatabaseContext* db; DatabaseContext* db;
public: // Internal use only public: // Internal use only
ThreadSafeDatabase(std::string connFilename, int apiVersion); ThreadSafeDatabase(Reference<IClusterConnectionRecord> connectionRecord, int apiVersion);
ThreadSafeDatabase(DatabaseContext* db) : db(db) {} ThreadSafeDatabase(DatabaseContext* db) : db(db) {}
DatabaseContext* unsafeGetPtr() const { return db; } DatabaseContext* unsafeGetPtr() const { return db; }
}; };
@ -212,6 +212,7 @@ public:
void stopNetwork() override; void stopNetwork() override;
Reference<IDatabase> createDatabase(const char* clusterFilePath) override; Reference<IDatabase> createDatabase(const char* clusterFilePath) override;
Reference<IDatabase> createDatabaseFromConnectionString(const char* connectionString) override;
void addNetworkThreadCompletionHook(void (*hook)(void*), void* hookParameter) override; void addNetworkThreadCompletionHook(void (*hook)(void*), void* hookParameter) override;

View File

@ -250,6 +250,7 @@ ACTOR Future<Void> clusterWatchDatabase(ClusterControllerData* cluster,
dbInfo.myLocality = db->serverInfo->get().myLocality; dbInfo.myLocality = db->serverInfo->get().myLocality;
dbInfo.client = ClientDBInfo(); dbInfo.client = ClientDBInfo();
dbInfo.client.tenantMode = db->config.tenantMode; dbInfo.client.tenantMode = db->config.tenantMode;
dbInfo.client.clusterId = db->serverInfo->get().client.clusterId;
TraceEvent("CCWDB", cluster->id) TraceEvent("CCWDB", cluster->id)
.detail("NewMaster", dbInfo.master.id().toString()) .detail("NewMaster", dbInfo.master.id().toString())
@ -1022,7 +1023,7 @@ void clusterRegisterMaster(ClusterControllerData* self, RegisterMasterRequest co
// Construct the client information // Construct the client information
if (db->clientInfo->get().commitProxies != req.commitProxies || if (db->clientInfo->get().commitProxies != req.commitProxies ||
db->clientInfo->get().grvProxies != req.grvProxies || db->clientInfo->get().grvProxies != req.grvProxies ||
db->clientInfo->get().tenantMode != db->config.tenantMode) { db->clientInfo->get().tenantMode != db->config.tenantMode || db->clientInfo->get().clusterId != req.clusterId) {
TraceEvent("PublishNewClientInfo", self->id) TraceEvent("PublishNewClientInfo", self->id)
.detail("Master", dbInfo.master.id()) .detail("Master", dbInfo.master.id())
.detail("GrvProxies", db->clientInfo->get().grvProxies) .detail("GrvProxies", db->clientInfo->get().grvProxies)
@ -1030,7 +1031,9 @@ void clusterRegisterMaster(ClusterControllerData* self, RegisterMasterRequest co
.detail("CommitProxies", db->clientInfo->get().commitProxies) .detail("CommitProxies", db->clientInfo->get().commitProxies)
.detail("ReqCPs", req.commitProxies) .detail("ReqCPs", req.commitProxies)
.detail("TenantMode", db->clientInfo->get().tenantMode.toString()) .detail("TenantMode", db->clientInfo->get().tenantMode.toString())
.detail("ReqTenantMode", db->config.tenantMode.toString()); .detail("ReqTenantMode", db->config.tenantMode.toString())
.detail("ClusterId", db->clientInfo->get().clusterId)
.detail("ReqClusterId", req.clusterId);
isChanged = true; isChanged = true;
// TODO why construct a new one and not just copy the old one and change proxies + id? // TODO why construct a new one and not just copy the old one and change proxies + id?
ClientDBInfo clientInfo; ClientDBInfo clientInfo;
@ -1038,6 +1041,7 @@ void clusterRegisterMaster(ClusterControllerData* self, RegisterMasterRequest co
clientInfo.commitProxies = req.commitProxies; clientInfo.commitProxies = req.commitProxies;
clientInfo.grvProxies = req.grvProxies; clientInfo.grvProxies = req.grvProxies;
clientInfo.tenantMode = db->config.tenantMode; clientInfo.tenantMode = db->config.tenantMode;
clientInfo.clusterId = req.clusterId;
db->clientInfo->set(clientInfo); db->clientInfo->set(clientInfo);
dbInfo.client = db->clientInfo->get(); dbInfo.client = db->clientInfo->get();
} }
@ -1057,11 +1061,6 @@ void clusterRegisterMaster(ClusterControllerData* self, RegisterMasterRequest co
dbInfo.recoveryCount = req.recoveryCount; dbInfo.recoveryCount = req.recoveryCount;
} }
if (dbInfo.clusterId != req.clusterId) {
isChanged = true;
dbInfo.clusterId = req.clusterId;
}
if (isChanged) { if (isChanged) {
dbInfo.id = deterministicRandom()->randomUniqueID(); dbInfo.id = deterministicRandom()->randomUniqueID();
dbInfo.infoGeneration = ++self->db.dbInfoCount; dbInfo.infoGeneration = ++self->db.dbInfoCount;

View File

@ -2626,7 +2626,7 @@ ACTOR Future<Void> serveTLogInterface(TLogData* self,
} }
// Persist cluster ID once cluster has recovered. // Persist cluster ID once cluster has recovered.
auto ccClusterId = self->dbInfo->get().clusterId; auto ccClusterId = self->dbInfo->get().client.clusterId;
if (self->dbInfo->get().recoveryState == RecoveryState::FULLY_RECOVERED && if (self->dbInfo->get().recoveryState == RecoveryState::FULLY_RECOVERED &&
!self->durableClusterId.isValid()) { !self->durableClusterId.isValid()) {
ASSERT(ccClusterId.isValid()); ASSERT(ccClusterId.isValid());

View File

@ -65,7 +65,6 @@ struct ServerDBInfo {
// which need to stay alive in case this recovery fails // which need to stay alive in case this recovery fails
Optional<LatencyBandConfig> latencyBandConfig; Optional<LatencyBandConfig> latencyBandConfig;
int64_t infoGeneration; int64_t infoGeneration;
UID clusterId;
ServerDBInfo() ServerDBInfo()
: recoveryCount(0), recoveryState(RecoveryState::UNINITIALIZED), logSystemConfig(0), infoGeneration(0) {} : recoveryCount(0), recoveryState(RecoveryState::UNINITIALIZED), logSystemConfig(0), infoGeneration(0) {}
@ -91,8 +90,7 @@ struct ServerDBInfo {
logSystemConfig, logSystemConfig,
priorCommittedLogServers, priorCommittedLogServers,
latencyBandConfig, latencyBandConfig,
infoGeneration, infoGeneration);
clusterId);
} }
}; };