Update the tenant maps to be keyed by ID

This commit is contained in:
A.J. Beamon 2023-01-23 14:09:12 -08:00
parent 1a7203d27b
commit fd13bc04c8
25 changed files with 636 additions and 664 deletions

View File

@ -186,10 +186,15 @@ ACTOR Future<bool> tenantCreateCommand(Reference<IDatabase> db, std::vector<Stri
state ClusterType clusterType = wait(TenantAPI::getClusterType(tr));
if (clusterType == ClusterType::METACLUSTER_MANAGEMENT) {
TenantMapEntry tenantEntry;
AssignClusterAutomatically assignClusterAutomatically = AssignClusterAutomatically::True;
for (auto const& [name, value] : configuration.get()) {
if (name == "assigned_cluster"_sr) {
assignClusterAutomatically = AssignClusterAutomatically::False;
}
tenantEntry.configure(name, value);
}
wait(MetaclusterAPI::createTenant(db, tokens[2], tenantEntry));
tenantEntry.tenantName = tokens[2];
wait(MetaclusterAPI::createTenant(db, tenantEntry, assignClusterAutomatically));
} else {
if (!doneExistenceCheck) {
// Hold the reference to the standalone's memory

View File

@ -271,7 +271,7 @@ ACTOR static Future<Void> decodeBackupLogValue(Arena* arena,
Version version,
Reference<KeyRangeMap<Version>> key_version,
Database cx,
std::unordered_map<int64_t, TenantName>* tenantMap,
std::map<int64_t, TenantName>* tenantMap,
bool provisionalProxy) {
try {
state uint64_t offset(0);
@ -657,7 +657,7 @@ ACTOR Future<int> kvMutationLogToTransactions(Database cx,
PromiseStream<Future<Void>> addActor,
FlowLock* commitLock,
Reference<KeyRangeMap<Version>> keyVersion,
std::unordered_map<int64_t, TenantName>* tenantMap,
std::map<int64_t, TenantName>* tenantMap,
bool provisionalProxy) {
state Version lastVersion = invalidVersion;
state bool endOfStream = false;
@ -789,7 +789,7 @@ ACTOR Future<Void> applyMutations(Database cx,
PublicRequestStream<CommitTransactionRequest> commit,
NotifiedVersion* committedVersion,
Reference<KeyRangeMap<Version>> keyVersion,
std::unordered_map<int64_t, TenantName>* tenantMap,
std::map<int64_t, TenantName>* tenantMap,
bool provisionalProxy) {
state FlowLock commitLock(CLIENT_KNOBS->BACKUP_LOCK_BYTES);
state PromiseStream<Future<Void>> addActor;

View File

@ -23,6 +23,7 @@
FDB_DEFINE_BOOLEAN_PARAM(AddNewTenants);
FDB_DEFINE_BOOLEAN_PARAM(RemoveMissingTenants);
FDB_DEFINE_BOOLEAN_PARAM(AssignClusterAutomatically);
std::string clusterTypeToString(const ClusterType& clusterType) {
switch (clusterType) {

View File

@ -60,10 +60,8 @@ std::string TenantMapEntry::tenantStateToString(TenantState tenantState) {
return "removing";
case TenantState::UPDATING_CONFIGURATION:
return "updating configuration";
case TenantState::RENAMING_FROM:
return "renaming from";
case TenantState::RENAMING_TO:
return "renaming to";
case TenantState::RENAMING:
return "renaming";
case TenantState::ERROR:
return "error";
default:
@ -81,10 +79,8 @@ TenantState TenantMapEntry::stringToTenantState(std::string stateStr) {
return TenantState::REMOVING;
} else if (stateStr == "updating configuration") {
return TenantState::UPDATING_CONFIGURATION;
} else if (stateStr == "renaming from") {
return TenantState::RENAMING_FROM;
} else if (stateStr == "renaming to") {
return TenantState::RENAMING_TO;
} else if (stateStr == "renaming") {
return TenantState::RENAMING;
} else if (stateStr == "error") {
return TenantState::ERROR;
}

View File

@ -575,7 +575,7 @@ ACTOR Future<Void> applyMutations(Database cx,
PublicRequestStream<CommitTransactionRequest> commit,
NotifiedVersion* committedVersion,
Reference<KeyRangeMap<Version>> keyVersion,
std::unordered_map<int64_t, TenantName>* tenantMap,
std::map<int64_t, TenantName>* tenantMap,
bool provisionalProxy);
ACTOR Future<Void> cleanupBackup(Database cx, DeleteData deleteData);

View File

@ -20,7 +20,9 @@
#pragma once
#include "fdbclient/FDBOptions.g.h"
#include "fdbclient/Tenant.h"
#include "flow/IRandom.h"
#include "flow/Platform.h"
#include "flow/ThreadHelper.actor.h"
#if defined(NO_INTELLISENSE) && !defined(FDBCLIENT_METACLUSTER_MANAGEMENT_ACTOR_G_H)
#define FDBCLIENT_METACLUSTER_MANAGEMENT_ACTOR_G_H
@ -80,6 +82,7 @@ struct DataClusterMetadata {
FDB_DECLARE_BOOLEAN_PARAM(AddNewTenants);
FDB_DECLARE_BOOLEAN_PARAM(RemoveMissingTenants);
FDB_DECLARE_BOOLEAN_PARAM(AssignClusterAutomatically);
namespace MetaclusterAPI {
@ -373,20 +376,33 @@ struct MetaclusterOperationContext {
};
template <class Transaction>
Future<Optional<TenantMapEntry>> tryGetTenantTransaction(Transaction tr, TenantName name) {
Future<Optional<TenantMapEntry>> tryGetTenantTransaction(Transaction tr, int64_t tenantId) {
tr->setOption(FDBTransactionOptions::RAW_ACCESS);
return ManagementClusterMetadata::tenantMetadata().tenantMap.get(tr, name);
return ManagementClusterMetadata::tenantMetadata().tenantMap.get(tr, tenantId);
}
ACTOR template <class DB>
Future<Optional<TenantMapEntry>> tryGetTenant(Reference<DB> db, TenantName name) {
ACTOR template <class Transaction>
Future<Optional<TenantMapEntry>> tryGetTenantTransaction(Transaction tr, TenantName name) {
tr->setOption(FDBTransactionOptions::RAW_ACCESS);
Optional<int64_t> tenantId = wait(ManagementClusterMetadata::tenantMetadata().tenantNameIndex.get(tr, name));
if (tenantId.present()) {
Optional<TenantMapEntry> entry =
wait(ManagementClusterMetadata::tenantMetadata().tenantMap.get(tr, tenantId.get()));
return entry;
} else {
return Optional<TenantMapEntry>();
}
}
ACTOR template <class DB, class Tenant>
Future<Optional<TenantMapEntry>> tryGetTenant(Reference<DB> db, Tenant tenant) {
state Reference<typename DB::TransactionT> tr = db->createTransaction();
loop {
try {
tr->setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
tr->setOption(FDBTransactionOptions::READ_LOCK_AWARE);
Optional<TenantMapEntry> entry = wait(tryGetTenantTransaction(tr, name));
Optional<TenantMapEntry> entry = wait(tryGetTenantTransaction(tr, tenant));
return entry;
} catch (Error& e) {
wait(safeThreadFutureToFuture(tr->onError(e)));
@ -394,9 +410,9 @@ Future<Optional<TenantMapEntry>> tryGetTenant(Reference<DB> db, TenantName name)
}
}
ACTOR template <class Transaction>
Future<TenantMapEntry> getTenantTransaction(Transaction tr, TenantName name) {
Optional<TenantMapEntry> entry = wait(tryGetTenantTransaction(tr, name));
ACTOR template <class Transaction, class Tenant>
Future<TenantMapEntry> getTenantTransaction(Transaction tr, Tenant tenant) {
Optional<TenantMapEntry> entry = wait(tryGetTenantTransaction(tr, tenant));
if (!entry.present()) {
throw tenant_not_found();
}
@ -404,9 +420,9 @@ Future<TenantMapEntry> getTenantTransaction(Transaction tr, TenantName name) {
return entry.get();
}
ACTOR template <class DB>
Future<TenantMapEntry> getTenant(Reference<DB> db, TenantName name) {
Optional<TenantMapEntry> entry = wait(tryGetTenant(db, name));
ACTOR template <class DB, class Tenant>
Future<TenantMapEntry> getTenant(Reference<DB> db, Tenant tenant) {
Optional<TenantMapEntry> entry = wait(tryGetTenant(db, tenant));
if (!entry.present()) {
throw tenant_not_found();
}
@ -416,12 +432,12 @@ Future<TenantMapEntry> getTenant(Reference<DB> db, TenantName name) {
ACTOR template <class Transaction>
Future<Void> managementClusterCheckEmpty(Transaction tr) {
state Future<KeyBackedRangeResult<std::pair<TenantName, TenantMapEntry>>> tenantsFuture =
state Future<KeyBackedRangeResult<std::pair<int64_t, TenantMapEntry>>> tenantsFuture =
TenantMetadata::tenantMap().getRange(tr, {}, {}, 1);
state typename transaction_future_type<Transaction, RangeResult>::type dbContentsFuture =
tr->getRange(normalKeys, 1);
KeyBackedRangeResult<std::pair<TenantName, TenantMapEntry>> tenants = wait(tenantsFuture);
KeyBackedRangeResult<std::pair<int64_t, TenantMapEntry>> tenants = wait(tenantsFuture);
if (!tenants.results.empty()) {
throw cluster_not_empty();
}
@ -796,8 +812,8 @@ struct RemoveClusterImpl {
// Erase each tenant from the tenant map on the management cluster
for (Tuple entry : tenantEntries.results) {
ASSERT(entry.getString(0) == self->ctx.clusterName.get());
ManagementClusterMetadata::tenantMetadata().tenantMap.erase(tr, entry.getString(1));
ManagementClusterMetadata::tenantMetadata().tenantIdIndex.erase(tr, entry.getInt(2));
ManagementClusterMetadata::tenantMetadata().tenantMap.erase(tr, entry.getInt(2));
ManagementClusterMetadata::tenantMetadata().tenantNameIndex.erase(tr, entry.getString(1));
ManagementClusterMetadata::tenantMetadata().lastTenantModification.setVersionstamp(tr, Versionstamp(), 0);
}
@ -994,7 +1010,6 @@ Future<std::map<ClusterName, DataClusterMetadata>> listClusters(Reference<DB> db
template <class Transaction>
void managementClusterAddTenantToGroup(Transaction tr,
TenantName tenantName,
TenantMapEntry tenantEntry,
DataClusterMetadata* clusterMetadata,
bool groupAlreadyExists) {
@ -1010,7 +1025,7 @@ void managementClusterAddTenantToGroup(Transaction tr,
tr, Tuple::makeTuple(tenantEntry.assignedCluster.get(), tenantEntry.tenantGroup.get()));
}
ManagementClusterMetadata::tenantMetadata().tenantGroupTenantIndex.insert(
tr, Tuple::makeTuple(tenantEntry.tenantGroup.get(), tenantName));
tr, Tuple::makeTuple(tenantEntry.tenantGroup.get(), tenantEntry.id));
}
if (!groupAlreadyExists) {
@ -1028,14 +1043,12 @@ void managementClusterAddTenantToGroup(Transaction tr,
ACTOR template <class Transaction>
Future<Void> managementClusterRemoveTenantFromGroup(Transaction tr,
TenantName tenantName,
TenantMapEntry tenantEntry,
DataClusterMetadata* clusterMetadata,
bool isRenamePair = false) {
state bool updateClusterCapacity = !tenantEntry.tenantGroup.present() && !isRenamePair;
DataClusterMetadata* clusterMetadata) {
state bool updateClusterCapacity = !tenantEntry.tenantGroup.present();
if (tenantEntry.tenantGroup.present()) {
ManagementClusterMetadata::tenantMetadata().tenantGroupTenantIndex.erase(
tr, Tuple::makeTuple(tenantEntry.tenantGroup.get(), tenantName));
tr, Tuple::makeTuple(tenantEntry.tenantGroup.get(), tenantEntry.id));
state KeyBackedSet<Tuple>::RangeResultType result =
wait(ManagementClusterMetadata::tenantMetadata().tenantGroupTenantIndex.getRange(
@ -1070,21 +1083,18 @@ Future<Void> managementClusterRemoveTenantFromGroup(Transaction tr,
template <class DB>
struct CreateTenantImpl {
MetaclusterOperationContext<DB> ctx;
bool preferAssignedCluster;
AssignClusterAutomatically assignClusterAutomatically;
// Initialization parameters
TenantName tenantName;
TenantMapEntry tenantEntry;
// Parameter set if tenant creation permanently fails on the data cluster
Optional<int64_t> replaceExistingTenantId;
CreateTenantImpl(Reference<DB> managementDb,
bool preferAssignedCluster,
TenantName tenantName,
TenantMapEntry tenantEntry)
: ctx(managementDb), preferAssignedCluster(preferAssignedCluster), tenantName(tenantName),
tenantEntry(tenantEntry) {}
TenantMapEntry tenantEntry,
AssignClusterAutomatically assignClusterAutomatically)
: ctx(managementDb), tenantEntry(tenantEntry), assignClusterAutomatically(assignClusterAutomatically) {}
ACTOR static Future<ClusterName> checkClusterAvailability(Reference<IDatabase> dataClusterDb,
ClusterName clusterName) {
@ -1107,7 +1117,7 @@ struct CreateTenantImpl {
ACTOR static Future<bool> checkForExistingTenant(CreateTenantImpl* self, Reference<typename DB::TransactionT> tr) {
// Check if the tenant already exists. If it's partially created and matches the parameters we
// specified, continue creating it. Otherwise, fail with an error.
state Optional<TenantMapEntry> existingEntry = wait(tryGetTenantTransaction(tr, self->tenantName));
state Optional<TenantMapEntry> existingEntry = wait(tryGetTenantTransaction(tr, self->tenantEntry.tenantName));
if (existingEntry.present()) {
if (!existingEntry.get().matchesConfiguration(self->tenantEntry) ||
existingEntry.get().tenantState != TenantState::REGISTERING) {
@ -1117,12 +1127,11 @@ struct CreateTenantImpl {
} else if (!self->replaceExistingTenantId.present() ||
self->replaceExistingTenantId.get() != existingEntry.get().id) {
// The tenant creation has already started, so resume where we left off
ASSERT(existingEntry.get().assignedCluster.present());
if (self->preferAssignedCluster &&
existingEntry.get().assignedCluster.get() != self->tenantEntry.assignedCluster.get()) {
if (!self->assignClusterAutomatically &&
existingEntry.get().assignedCluster != self->tenantEntry.assignedCluster) {
TraceEvent("MetaclusterCreateTenantClusterMismatch")
.detail("Preferred", self->tenantEntry.assignedCluster.get())
.detail("Actual", existingEntry.get().assignedCluster.get());
.detail("Preferred", self->tenantEntry.assignedCluster)
.detail("Actual", existingEntry.get().assignedCluster);
throw invalid_tenant_configuration();
}
self->tenantEntry = existingEntry.get();
@ -1130,23 +1139,23 @@ struct CreateTenantImpl {
return true;
} else {
// The previous creation is permanently failed, so cleanup the tenant and create it again from scratch
// We don't need to remove it from the tenant map because we will overwrite the existing entry later in
// this transaction.
ManagementClusterMetadata::tenantMetadata().tenantIdIndex.erase(tr, existingEntry.get().id);
// We don't need to remove it from the tenantNameIndex because we will overwrite the existing entry
// later in this transaction.
ManagementClusterMetadata::tenantMetadata().tenantMap.erase(tr, existingEntry.get().id);
ManagementClusterMetadata::tenantMetadata().tenantCount.atomicOp(tr, -1, MutationRef::AddValue);
ManagementClusterMetadata::clusterTenantCount.atomicOp(
tr, existingEntry.get().assignedCluster.get(), -1, MutationRef::AddValue);
ManagementClusterMetadata::clusterTenantIndex.erase(
tr,
Tuple::makeTuple(
existingEntry.get().assignedCluster.get(), self->tenantName, existingEntry.get().id));
Tuple::makeTuple(existingEntry.get().assignedCluster.get(),
self->tenantEntry.tenantName,
existingEntry.get().id));
state DataClusterMetadata previousAssignedClusterMetadata =
wait(getClusterTransaction(tr, existingEntry.get().assignedCluster.get()));
wait(managementClusterRemoveTenantFromGroup(
tr, self->tenantName, existingEntry.get(), &previousAssignedClusterMetadata));
wait(managementClusterRemoveTenantFromGroup(tr, existingEntry.get(), &previousAssignedClusterMetadata));
}
} else if (self->replaceExistingTenantId.present()) {
throw tenant_removed();
@ -1168,7 +1177,7 @@ struct CreateTenantImpl {
if (groupEntry.present()) {
ASSERT(groupEntry.get().assignedCluster.present());
if (self->preferAssignedCluster &&
if (!self->assignClusterAutomatically &&
groupEntry.get().assignedCluster.get() != self->tenantEntry.assignedCluster.get()) {
TraceEvent("MetaclusterCreateTenantGroupClusterMismatch")
.detail("TenantGroupCluster", groupEntry.get().assignedCluster.get())
@ -1184,7 +1193,7 @@ struct CreateTenantImpl {
state std::vector<Future<ClusterName>> clusterAvailabilityChecks;
// Get a set of the most full clusters that still have capacity
// If preferred cluster is specified, look for that one.
if (self->preferAssignedCluster) {
if (!self->assignClusterAutomatically) {
DataClusterMetadata dataClusterMetadata =
wait(getClusterTransaction(tr, self->tenantEntry.assignedCluster.get()));
if (!dataClusterMetadata.entry.hasCapacity()) {
@ -1262,8 +1271,9 @@ struct CreateTenantImpl {
ManagementClusterMetadata::tenantMetadata().lastTenantId.set(tr, self->tenantEntry.id);
self->tenantEntry.tenantState = TenantState::REGISTERING;
ManagementClusterMetadata::tenantMetadata().tenantMap.set(tr, self->tenantName, self->tenantEntry);
ManagementClusterMetadata::tenantMetadata().tenantIdIndex.set(tr, self->tenantEntry.id, self->tenantName);
ManagementClusterMetadata::tenantMetadata().tenantMap.set(tr, self->tenantEntry.id, self->tenantEntry);
ManagementClusterMetadata::tenantMetadata().tenantNameIndex.set(
tr, self->tenantEntry.tenantName, self->tenantEntry.id);
ManagementClusterMetadata::tenantMetadata().lastTenantModification.setVersionstamp(tr, Versionstamp(), 0);
ManagementClusterMetadata::tenantMetadata().tenantCount.atomicOp(tr, 1, MutationRef::AddValue);
@ -1278,8 +1288,10 @@ struct CreateTenantImpl {
}
// Updated indexes to include the new tenant
ManagementClusterMetadata::clusterTenantIndex.insert(
tr, Tuple::makeTuple(self->tenantEntry.assignedCluster.get(), self->tenantName, self->tenantEntry.id));
ManagementClusterMetadata::clusterTenantIndex.insert(tr,
Tuple::makeTuple(self->tenantEntry.assignedCluster.get(),
self->tenantEntry.tenantName,
self->tenantEntry.id));
wait(setClusterFuture);
@ -1290,14 +1302,14 @@ struct CreateTenantImpl {
}
managementClusterAddTenantToGroup(
tr, self->tenantName, self->tenantEntry, &self->ctx.dataClusterMetadata.get(), assignment.second);
tr, self->tenantEntry, &self->ctx.dataClusterMetadata.get(), assignment.second);
return Void();
}
ACTOR static Future<Void> storeTenantInDataCluster(CreateTenantImpl* self, Reference<ITransaction> tr) {
std::pair<Optional<TenantMapEntry>, bool> dataClusterTenant = wait(
TenantAPI::createTenantTransaction(tr, self->tenantName, self->tenantEntry, ClusterType::METACLUSTER_DATA));
std::pair<Optional<TenantMapEntry>, bool> dataClusterTenant =
wait(TenantAPI::createTenantTransaction(tr, self->tenantEntry, ClusterType::METACLUSTER_DATA));
// If the tenant map entry is empty, then we encountered a tombstone indicating that the tenant was
// simultaneously removed.
@ -1309,17 +1321,15 @@ struct CreateTenantImpl {
}
ACTOR static Future<Void> markTenantReady(CreateTenantImpl* self, Reference<typename DB::TransactionT> tr) {
state Optional<TenantMapEntry> managementEntry = wait(tryGetTenantTransaction(tr, self->tenantName));
state Optional<TenantMapEntry> managementEntry = wait(tryGetTenantTransaction(tr, self->tenantEntry.id));
if (!managementEntry.present()) {
throw tenant_removed();
} else if (managementEntry.get().id != self->tenantEntry.id) {
throw tenant_already_exists();
}
if (managementEntry.get().tenantState == TenantState::REGISTERING) {
TenantMapEntry updatedEntry = managementEntry.get();
updatedEntry.tenantState = TenantState::READY;
ManagementClusterMetadata::tenantMetadata().tenantMap.set(tr, self->tenantName, updatedEntry);
ManagementClusterMetadata::tenantMetadata().tenantMap.set(tr, updatedEntry.id, updatedEntry);
ManagementClusterMetadata::tenantMetadata().lastTenantModification.setVersionstamp(tr, Versionstamp(), 0);
}
@ -1327,7 +1337,7 @@ struct CreateTenantImpl {
}
ACTOR static Future<Void> run(CreateTenantImpl* self) {
if (self->tenantName.startsWith("\xff"_sr)) {
if (self->tenantEntry.tenantName.startsWith("\xff"_sr)) {
throw invalid_tenant_name();
}
@ -1361,8 +1371,10 @@ struct CreateTenantImpl {
};
ACTOR template <class DB>
Future<Void> createTenant(Reference<DB> db, TenantName name, TenantMapEntry tenantEntry) {
state CreateTenantImpl<DB> impl(db, tenantEntry.assignedCluster.present(), name, tenantEntry);
Future<Void> createTenant(Reference<DB> db,
TenantMapEntry tenantEntry,
AssignClusterAutomatically assignClusterAutomatically) {
state CreateTenantImpl<DB> impl(db, tenantEntry, assignClusterAutomatically);
wait(impl.run());
return Void();
}
@ -1374,44 +1386,35 @@ struct DeleteTenantImpl {
// Initialization parameters
// Either one can be specified, and the other will be looked up
// and filled in by reading the metacluster metadata
TenantName tenantName;
Optional<TenantName> tenantName;
int64_t tenantId = -1;
// Parameters set in markTenantInRemovingState
Optional<TenantName> pairName;
DeleteTenantImpl(Reference<DB> managementDb, TenantName tenantName) : ctx(managementDb), tenantName(tenantName) {}
DeleteTenantImpl(Reference<DB> managementDb, int64_t tenantId) : ctx(managementDb), tenantId(tenantId) {}
// Loads the cluster details for the cluster where the tenant is assigned.
// Returns true if the deletion is already in progress
ACTOR static Future<bool> getAssignedLocation(DeleteTenantImpl* self, Reference<typename DB::TransactionT> tr) {
// Look at tenantIdIndex if given ID, then fill out the corresponding name
if (self->tenantId != -1) {
TenantName indexName =
wait(ManagementClusterMetadata::tenantMetadata().tenantIdIndex.getD(tr, self->tenantId));
self->tenantName = indexName;
ACTOR static Future<std::pair<int64_t, bool>> getAssignedLocation(DeleteTenantImpl* self,
Reference<typename DB::TransactionT> tr) {
state int64_t resolvedId = self->tenantId;
if (self->tenantId == -1) {
ASSERT(self->tenantName.present());
wait(store(resolvedId,
ManagementClusterMetadata::tenantMetadata().tenantNameIndex.getD(
tr, self->tenantName.get(), Snapshot::False, TenantInfo::INVALID_TENANT)));
}
state Optional<TenantMapEntry> tenantEntry = wait(tryGetTenantTransaction(tr, self->tenantName));
if (!tenantEntry.present()) {
throw tenant_not_found();
}
state TenantMapEntry tenantEntry = wait(getTenantTransaction(tr, resolvedId));
// Disallow removing the "new" name of a renamed tenant before it completes
if (tenantEntry.get().tenantState == TenantState::RENAMING_TO) {
if (self->tenantName.present() && tenantEntry.tenantName != self->tenantName.get()) {
ASSERT(tenantEntry.tenantState == TenantState::RENAMING ||
tenantEntry.tenantState == TenantState::REMOVING);
throw tenant_not_found();
}
if (tenantEntry.get().tenantState == TenantState::REMOVING) {
if (tenantEntry.get().renamePair.present()) {
self->pairName = tenantEntry.get().renamePair.get();
}
}
ASSERT(self->tenantId == -1 || self->tenantId == tenantEntry.get().id);
self->tenantId = tenantEntry.get().id;
wait(self->ctx.setCluster(tr, tenantEntry.get().assignedCluster.get()));
return tenantEntry.get().tenantState == TenantState::REMOVING;
wait(self->ctx.setCluster(tr, tenantEntry.assignedCluster.get()));
return std::make_pair(resolvedId, tenantEntry.tenantState == TenantState::REMOVING);
}
// Does an initial check if the tenant is empty. This is an optimization to prevent us marking a tenant
@ -1420,8 +1423,8 @@ struct DeleteTenantImpl {
//
// SOMEDAY: should this also lock the tenant when locking is supported?
ACTOR static Future<Void> checkTenantEmpty(DeleteTenantImpl* self, Reference<ITransaction> tr) {
state Optional<TenantMapEntry> tenantEntry = wait(TenantAPI::tryGetTenantTransaction(tr, self->tenantName));
if (!tenantEntry.present() || tenantEntry.get().id != self->tenantId) {
state Optional<TenantMapEntry> tenantEntry = wait(TenantAPI::tryGetTenantTransaction(tr, self->tenantId));
if (!tenantEntry.present()) {
// The tenant must have been removed simultaneously
return Void();
}
@ -1438,41 +1441,13 @@ struct DeleteTenantImpl {
// Mark the tenant as being in a removing state on the management cluster
ACTOR static Future<Void> markTenantInRemovingState(DeleteTenantImpl* self,
Reference<typename DB::TransactionT> tr) {
state Optional<TenantMapEntry> tenantEntry = wait(tryGetTenantTransaction(tr, self->tenantName));
state TenantMapEntry tenantEntry = wait(getTenantTransaction(tr, self->tenantId));
if (!tenantEntry.present() || tenantEntry.get().id != self->tenantId ||
tenantEntry.get().tenantState == TenantState::RENAMING_TO) {
throw tenant_not_found();
}
if (tenantEntry.tenantState != TenantState::REMOVING) {
tenantEntry.tenantState = TenantState::REMOVING;
if (tenantEntry.get().renamePair.present()) {
ASSERT(tenantEntry.get().tenantState == TenantState::RENAMING_FROM ||
tenantEntry.get().tenantState == TenantState::REMOVING);
self->pairName = tenantEntry.get().renamePair.get();
}
if (tenantEntry.get().tenantState != TenantState::REMOVING) {
state TenantMapEntry updatedEntry = tenantEntry.get();
// Check if we are deleting a tenant in the middle of a rename
updatedEntry.tenantState = TenantState::REMOVING;
ManagementClusterMetadata::tenantMetadata().tenantMap.set(tr, self->tenantName, updatedEntry);
ManagementClusterMetadata::tenantMetadata().tenantMap.set(tr, tenantEntry.id, tenantEntry);
ManagementClusterMetadata::tenantMetadata().lastTenantModification.setVersionstamp(tr, Versionstamp(), 0);
// If this has a rename pair, also mark the other entry for deletion
if (self->pairName.present()) {
state Optional<TenantMapEntry> pairEntry = wait(tryGetTenantTransaction(tr, self->pairName.get()));
TenantMapEntry updatedPairEntry = pairEntry.get();
// Sanity check that our pair has us named as their partner
ASSERT(updatedPairEntry.renamePair.present());
ASSERT(updatedPairEntry.renamePair.get() == self->tenantName);
ASSERT(updatedPairEntry.id == self->tenantId);
CODE_PROBE(true, "marking pair tenant in removing state");
updatedPairEntry.tenantState = TenantState::REMOVING;
ManagementClusterMetadata::tenantMetadata().tenantMap.set(tr, self->pairName.get(), updatedPairEntry);
ManagementClusterMetadata::tenantMetadata().lastTenantModification.setVersionstamp(
tr, Versionstamp(), 0);
}
}
return Void();
@ -1480,27 +1455,18 @@ struct DeleteTenantImpl {
// Delete the tenant and related metadata on the management cluster
ACTOR static Future<Void> deleteTenantFromManagementCluster(DeleteTenantImpl* self,
Reference<typename DB::TransactionT> tr,
bool pairDelete = false) {
// If pair is present, and this is not already a pair delete, call this function recursively
state Future<Void> pairFuture = Void();
if (!pairDelete && self->pairName.present()) {
CODE_PROBE(true, "deleting pair tenant from management cluster");
pairFuture = deleteTenantFromManagementCluster(self, tr, true);
}
state TenantName tenantName = pairDelete ? self->pairName.get() : self->tenantName;
state Optional<TenantMapEntry> tenantEntry = wait(tryGetTenantTransaction(tr, tenantName));
Reference<typename DB::TransactionT> tr) {
state Optional<TenantMapEntry> tenantEntry = wait(tryGetTenantTransaction(tr, self->tenantId));
if (!tenantEntry.present() || tenantEntry.get().id != self->tenantId) {
if (!tenantEntry.present()) {
return Void();
}
ASSERT(tenantEntry.get().tenantState == TenantState::REMOVING &&
(pairDelete || tenantEntry.get().renamePair == self->pairName));
ASSERT(tenantEntry.get().tenantState == TenantState::REMOVING);
// Erase the tenant entry itself
ManagementClusterMetadata::tenantMetadata().tenantMap.erase(tr, tenantName);
ManagementClusterMetadata::tenantMetadata().tenantIdIndex.erase(tr, tenantEntry.get().id);
ManagementClusterMetadata::tenantMetadata().tenantMap.erase(tr, tenantEntry.get().id);
ManagementClusterMetadata::tenantMetadata().tenantNameIndex.erase(tr, tenantEntry.get().tenantName);
ManagementClusterMetadata::tenantMetadata().lastTenantModification.setVersionstamp(tr, Versionstamp(), 0);
// This is idempotent because this function is only called if the tenant is in the map
@ -1510,22 +1476,39 @@ struct DeleteTenantImpl {
// Remove the tenant from the cluster -> tenant index
ManagementClusterMetadata::clusterTenantIndex.erase(
tr, Tuple::makeTuple(tenantEntry.get().assignedCluster.get(), tenantName, self->tenantId));
tr,
Tuple::makeTuple(tenantEntry.get().assignedCluster.get(), tenantEntry.get().tenantName, self->tenantId));
if (tenantEntry.get().renameDestination.present()) {
// If renaming, remove the metadata associated with the tenant destination
ManagementClusterMetadata::tenantMetadata().tenantNameIndex.erase(
tr, tenantEntry.get().renameDestination.get());
ManagementClusterMetadata::clusterTenantIndex.erase(
tr,
Tuple::makeTuple(tenantEntry.get().assignedCluster.get(),
tenantEntry.get().renameDestination.get(),
self->tenantId));
}
// Remove the tenant from its tenant group
wait(managementClusterRemoveTenantFromGroup(
tr, tenantName, tenantEntry.get(), &self->ctx.dataClusterMetadata.get(), pairDelete));
wait(managementClusterRemoveTenantFromGroup(tr, tenantEntry.get(), &self->ctx.dataClusterMetadata.get()));
wait(pairFuture);
return Void();
}
ACTOR static Future<Void> run(DeleteTenantImpl* self) {
// Get information about the tenant and where it is assigned
bool deletionInProgress = wait(self->ctx.runManagementTransaction(
std::pair<int64_t, bool> result = wait(self->ctx.runManagementTransaction(
[self = self](Reference<typename DB::TransactionT> tr) { return getAssignedLocation(self, tr); }));
if (!deletionInProgress) {
if (self->tenantId == -1) {
self->tenantId = result.first;
} else {
ASSERT(result.first == self->tenantId);
}
if (!result.second) {
wait(self->ctx.runDataClusterTransaction(
[self = self](Reference<ITransaction> tr) { return checkTenantEmpty(self, tr); }));
@ -1536,16 +1519,7 @@ struct DeleteTenantImpl {
// Delete tenant on the data cluster
wait(self->ctx.runDataClusterTransaction([self = self](Reference<ITransaction> tr) {
// If the removed tenant is being renamed, attempt to delete both the old and new names.
// At most one should be present with the given ID, and the other will be a no-op.
Future<Void> pairDelete = Void();
if (self->pairName.present()) {
CODE_PROBE(true, "deleting pair tenant from data cluster");
pairDelete = TenantAPI::deleteTenantTransaction(
tr, self->pairName.get(), self->tenantId, ClusterType::METACLUSTER_DATA);
}
return pairDelete && TenantAPI::deleteTenantTransaction(
tr, self->tenantName, self->tenantId, ClusterType::METACLUSTER_DATA);
return TenantAPI::deleteTenantTransaction(tr, self->tenantId, ClusterType::METACLUSTER_DATA);
}));
wait(self->ctx.runManagementTransaction([self = self](Reference<typename DB::TransactionT> tr) {
return deleteTenantFromManagementCluster(self, tr);
@ -1577,10 +1551,23 @@ Future<std::vector<std::pair<TenantName, TenantMapEntry>>> listTenantsTransactio
int limit) {
tr->setOption(FDBTransactionOptions::RAW_ACCESS);
state KeyBackedRangeResult<std::pair<TenantName, TenantMapEntry>> results =
wait(ManagementClusterMetadata::tenantMetadata().tenantMap.getRange(tr, begin, end, limit));
state KeyBackedRangeResult<std::pair<TenantName, int64_t>> matchingTenants =
wait(ManagementClusterMetadata::tenantMetadata().tenantNameIndex.getRange(tr, begin, end, limit));
return results.results;
state std::vector<Future<TenantMapEntry>> tenantEntryFutures;
for (auto const& [name, id] : matchingTenants.results) {
tenantEntryFutures.push_back(getTenantTransaction(tr, id));
}
wait(waitForAll(tenantEntryFutures));
std::vector<std::pair<TenantName, TenantMapEntry>> results;
for (int i = 0; i < matchingTenants.results.size(); ++i) {
// Tenants being renamed will show up twice; once under each name
results.emplace_back(matchingTenants.results[i].first, tenantEntryFutures[i].get());
}
return results;
}
ACTOR template <class DB>
@ -1592,48 +1579,49 @@ Future<std::vector<std::pair<TenantName, TenantMapEntry>>> listTenants(
int offset = 0,
std::vector<TenantState> filters = std::vector<TenantState>()) {
state Reference<typename DB::TransactionT> tr = db->createTransaction();
state std::vector<std::pair<TenantName, TenantMapEntry>> results;
loop {
try {
tr->setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
tr->setOption(FDBTransactionOptions::READ_LOCK_AWARE);
if (filters.empty()) {
state std::vector<std::pair<TenantName, TenantMapEntry>> tenants;
wait(store(tenants, listTenantsTransaction(tr, begin, end, limit + offset)));
if (offset >= tenants.size()) {
tenants.clear();
wait(store(results, listTenantsTransaction(tr, begin, end, limit + offset)));
if (offset >= results.size()) {
results.clear();
} else if (offset > 0) {
tenants.erase(tenants.begin(), tenants.begin() + offset);
results.erase(results.begin(), results.begin() + offset);
}
return tenants;
return results;
}
tr->setOption(FDBTransactionOptions::RAW_ACCESS);
state KeyBackedRangeResult<std::pair<TenantName, TenantMapEntry>> results =
wait(ManagementClusterMetadata::tenantMetadata().tenantMap.getRange(
tr, begin, end, std::max(limit + offset, 100)));
state std::vector<std::pair<TenantName, TenantMapEntry>> filterResults;
state int count = 0;
loop {
for (auto pair : results.results) {
if (filters.empty() || std::count(filters.begin(), filters.end(), pair.second.tenantState)) {
std::vector<std::pair<TenantName, TenantMapEntry>> tenantBatch =
wait(listTenantsTransaction(tr, begin, end, std::max(limit + offset, 1000)));
if (tenantBatch.empty()) {
return results;
}
for (auto const& [name, entry] : tenantBatch) {
if (filters.empty() || std::count(filters.begin(), filters.end(), entry.tenantState)) {
++count;
if (count > offset) {
filterResults.push_back(pair);
results.push_back(std::make_pair(name, entry));
if (count - offset == limit) {
ASSERT(count - offset == filterResults.size());
return filterResults;
ASSERT(count - offset == results.size());
return results;
}
}
}
}
if (!results.more) {
return filterResults;
}
begin = keyAfter(results.results.back().first);
wait(store(results,
ManagementClusterMetadata::tenantMetadata().tenantMap.getRange(
tr, begin, end, std::max(limit + offset, 100))));
begin = keyAfter(tenantBatch.back().first);
}
} catch (Error& e) {
wait(safeThreadFutureToFuture(tr->onError(e)));
@ -1677,10 +1665,8 @@ struct ConfigureTenantImpl {
throw cluster_no_capacity();
}
wait(managementClusterRemoveTenantFromGroup(
tr, self->tenantName, tenantEntry, &self->ctx.dataClusterMetadata.get()));
managementClusterAddTenantToGroup(
tr, self->tenantName, entryWithUpdatedGroup, &self->ctx.dataClusterMetadata.get(), false);
wait(managementClusterRemoveTenantFromGroup(tr, tenantEntry, &self->ctx.dataClusterMetadata.get()));
managementClusterAddTenantToGroup(tr, entryWithUpdatedGroup, &self->ctx.dataClusterMetadata.get(), false);
return Void();
}
@ -1692,19 +1678,15 @@ struct ConfigureTenantImpl {
if (!self->ctx.dataClusterMetadata.get().entry.hasCapacity()) {
throw cluster_no_capacity();
}
wait(managementClusterRemoveTenantFromGroup(
tr, self->tenantName, tenantEntry, &self->ctx.dataClusterMetadata.get()));
managementClusterAddTenantToGroup(
tr, self->tenantName, entryWithUpdatedGroup, &self->ctx.dataClusterMetadata.get(), false);
wait(managementClusterRemoveTenantFromGroup(tr, tenantEntry, &self->ctx.dataClusterMetadata.get()));
managementClusterAddTenantToGroup(tr, entryWithUpdatedGroup, &self->ctx.dataClusterMetadata.get(), false);
return Void();
}
// Moves between groups in the same cluster are freely allowed
else if (tenantGroupEntry.get().assignedCluster == tenantEntry.assignedCluster) {
wait(managementClusterRemoveTenantFromGroup(
tr, self->tenantName, tenantEntry, &self->ctx.dataClusterMetadata.get()));
managementClusterAddTenantToGroup(
tr, self->tenantName, entryWithUpdatedGroup, &self->ctx.dataClusterMetadata.get(), true);
wait(managementClusterRemoveTenantFromGroup(tr, tenantEntry, &self->ctx.dataClusterMetadata.get()));
managementClusterAddTenantToGroup(tr, entryWithUpdatedGroup, &self->ctx.dataClusterMetadata.get(), true);
return Void();
}
@ -1750,7 +1732,7 @@ struct ConfigureTenantImpl {
}
++self->updatedEntry.configurationSequenceNum;
ManagementClusterMetadata::tenantMetadata().tenantMap.set(tr, self->tenantName, self->updatedEntry);
ManagementClusterMetadata::tenantMetadata().tenantMap.set(tr, self->updatedEntry.id, self->updatedEntry);
ManagementClusterMetadata::tenantMetadata().lastTenantModification.setVersionstamp(tr, Versionstamp(), 0);
return Void();
@ -1758,9 +1740,10 @@ struct ConfigureTenantImpl {
// Updates the configuration in the data cluster
ACTOR static Future<Void> updateDataCluster(ConfigureTenantImpl* self, Reference<ITransaction> tr) {
state Optional<TenantMapEntry> tenantEntry = wait(TenantAPI::tryGetTenantTransaction(tr, self->tenantName));
state Optional<TenantMapEntry> tenantEntry =
wait(TenantAPI::tryGetTenantTransaction(tr, self->updatedEntry.id));
if (!tenantEntry.present() || tenantEntry.get().id != self->updatedEntry.id ||
if (!tenantEntry.present() ||
tenantEntry.get().configurationSequenceNum >= self->updatedEntry.configurationSequenceNum) {
// If the tenant isn't in the metacluster, it must have been concurrently removed
return Void();
@ -1770,23 +1753,22 @@ struct ConfigureTenantImpl {
dataClusterEntry.tenantState = TenantState::READY;
dataClusterEntry.assignedCluster = {};
wait(TenantAPI::configureTenantTransaction(tr, self->tenantName, tenantEntry.get(), dataClusterEntry));
wait(TenantAPI::configureTenantTransaction(tr, tenantEntry.get(), dataClusterEntry));
return Void();
}
// Updates the tenant state in the management cluster to READY
ACTOR static Future<Void> markManagementTenantAsReady(ConfigureTenantImpl* self,
Reference<typename DB::TransactionT> tr) {
state Optional<TenantMapEntry> tenantEntry = wait(tryGetTenantTransaction(tr, self->tenantName));
state Optional<TenantMapEntry> tenantEntry = wait(tryGetTenantTransaction(tr, self->updatedEntry.id));
if (!tenantEntry.present() || tenantEntry.get().id != self->updatedEntry.id ||
tenantEntry.get().tenantState != TenantState::UPDATING_CONFIGURATION ||
if (!tenantEntry.present() || tenantEntry.get().tenantState != TenantState::UPDATING_CONFIGURATION ||
tenantEntry.get().configurationSequenceNum > self->updatedEntry.configurationSequenceNum) {
return Void();
}
tenantEntry.get().tenantState = TenantState::READY;
ManagementClusterMetadata::tenantMetadata().tenantMap.set(tr, self->tenantName, tenantEntry.get());
ManagementClusterMetadata::tenantMetadata().tenantMap.set(tr, tenantEntry.get().id, tenantEntry.get());
ManagementClusterMetadata::tenantMetadata().lastTenantModification.setVersionstamp(tr, Versionstamp(), 0);
return Void();
}
@ -1828,114 +1810,77 @@ struct RenameTenantImpl {
RenameTenantImpl(Reference<DB> managementDb, TenantName oldName, TenantName newName)
: ctx(managementDb), oldName(oldName), newName(newName) {}
// Delete the tenant and related metadata on the management cluster
ACTOR static Future<Void> deleteTenantFromManagementCluster(RenameTenantImpl* self,
Reference<typename DB::TransactionT> tr,
TenantMapEntry tenantEntry) {
// Erase the tenant entry itself
ManagementClusterMetadata::tenantMetadata().tenantMap.erase(tr, self->oldName);
ManagementClusterMetadata::tenantMetadata().lastTenantModification.setVersionstamp(tr, Versionstamp(), 0);
// Remove old tenant from tenant count
ManagementClusterMetadata::tenantMetadata().tenantCount.atomicOp(tr, -1, MutationRef::AddValue);
ManagementClusterMetadata::clusterTenantCount.atomicOp(
tr, tenantEntry.assignedCluster.get(), -1, MutationRef::AddValue);
// Clean up cluster based tenant indices and remove the old entry from its tenant group
// Remove the tenant from the cluster -> tenant index
ManagementClusterMetadata::clusterTenantIndex.erase(
tr, Tuple::makeTuple(tenantEntry.assignedCluster.get(), self->oldName, self->tenantId));
// Remove the tenant from its tenant group
wait(managementClusterRemoveTenantFromGroup(
tr, self->oldName, tenantEntry, &self->ctx.dataClusterMetadata.get(), true));
return Void();
}
ACTOR static Future<Void> markTenantsInRenamingState(RenameTenantImpl* self,
Reference<typename DB::TransactionT> tr) {
state TenantMapEntry oldTenantEntry;
state Optional<TenantMapEntry> newTenantEntry;
wait(store(oldTenantEntry, getTenantTransaction(tr, self->oldName)) &&
store(newTenantEntry, tryGetTenantTransaction(tr, self->newName)));
state TenantMapEntry tenantEntry;
state Optional<int64_t> newNameId;
wait(store(tenantEntry, getTenantTransaction(tr, self->oldName)) &&
store(newNameId, ManagementClusterMetadata::tenantMetadata().tenantNameIndex.get(tr, self->newName)));
if (self->tenantId != -1 && oldTenantEntry.id != self->tenantId) {
if (self->tenantId != -1 && tenantEntry.id != self->tenantId) {
// The tenant must have been removed simultaneously
CODE_PROBE(true, "Metacluster rename old tenant ID mismatch");
throw tenant_removed();
}
self->tenantId = tenantEntry.id;
// If marked for deletion, abort the rename
if (oldTenantEntry.tenantState == TenantState::REMOVING) {
if (tenantEntry.tenantState == TenantState::REMOVING) {
CODE_PROBE(true, "Metacluster rename candidates marked for deletion");
throw tenant_removed();
}
// If the new entry is present, we can only continue if this is a retry of the same rename
// To check this, verify both entries are in the correct state
// and have each other as pairs
if (newTenantEntry.present()) {
if (newTenantEntry.get().tenantState == TenantState::RENAMING_TO &&
oldTenantEntry.tenantState == TenantState::RENAMING_FROM && newTenantEntry.get().renamePair.present() &&
newTenantEntry.get().renamePair.get() == self->oldName && oldTenantEntry.renamePair.present() &&
oldTenantEntry.renamePair.get() == self->newName) {
wait(self->ctx.setCluster(tr, oldTenantEntry.assignedCluster.get()));
self->tenantId = newTenantEntry.get().id;
self->configurationSequenceNum = newTenantEntry.get().configurationSequenceNum;
CODE_PROBE(true, "Metacluster rename retry in progress");
return Void();
} else {
CODE_PROBE(true, "Metacluster rename new name already exists");
throw tenant_already_exists();
};
} else {
if (self->tenantId == -1) {
self->tenantId = oldTenantEntry.id;
if (newNameId.present() && (newNameId.get() != self->tenantId || self->oldName == self->newName)) {
CODE_PROBE(true, "Metacluster rename new name already exists");
throw tenant_already_exists();
}
wait(self->ctx.setCluster(tr, tenantEntry.assignedCluster.get()));
if (tenantEntry.tenantState == TenantState::RENAMING) {
if (tenantEntry.tenantName != self->oldName) {
CODE_PROBE(true, "Renaming a tenant that is currently the destination of another rename");
throw tenant_not_found();
}
++oldTenantEntry.configurationSequenceNum;
self->configurationSequenceNum = oldTenantEntry.configurationSequenceNum;
wait(self->ctx.setCluster(tr, oldTenantEntry.assignedCluster.get()));
if (oldTenantEntry.tenantState != TenantState::READY) {
CODE_PROBE(true, "Metacluster unable to proceed with rename operation");
throw invalid_tenant_state();
if (tenantEntry.renameDestination.get() != self->newName) {
CODE_PROBE(true, "Metacluster concurrent rename with different name");
throw tenant_already_exists();
} else {
CODE_PROBE(true, "Metacluster rename retry in progress");
self->configurationSequenceNum = tenantEntry.configurationSequenceNum;
return Void();
}
}
if (tenantEntry.tenantState != TenantState::READY) {
CODE_PROBE(true, "Metacluster unable to proceed with rename operation");
throw invalid_tenant_state();
}
self->configurationSequenceNum = tenantEntry.configurationSequenceNum + 1;
// Check cluster capacity. If we would exceed the amount due to temporary extra tenants
// then we deny the rename request altogether.
int64_t clusterTenantCount = wait(ManagementClusterMetadata::clusterTenantCount.getD(
tr, oldTenantEntry.assignedCluster.get(), Snapshot::False, 0));
tr, tenantEntry.assignedCluster.get(), Snapshot::False, 0));
if (clusterTenantCount + 1 > CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER) {
throw cluster_no_capacity();
}
TenantMapEntry updatedOldEntry = oldTenantEntry;
TenantMapEntry updatedNewEntry(updatedOldEntry);
ASSERT(updatedOldEntry.configurationSequenceNum == self->configurationSequenceNum);
ASSERT(updatedNewEntry.configurationSequenceNum == self->configurationSequenceNum);
updatedOldEntry.tenantState = TenantState::RENAMING_FROM;
updatedNewEntry.tenantState = TenantState::RENAMING_TO;
updatedOldEntry.renamePair = self->newName;
updatedNewEntry.renamePair = self->oldName;
TenantMapEntry updatedEntry = tenantEntry;
updatedEntry.tenantState = TenantState::RENAMING;
updatedEntry.renameDestination = self->newName;
updatedEntry.configurationSequenceNum = self->configurationSequenceNum;
ManagementClusterMetadata::tenantMetadata().tenantMap.set(tr, self->oldName, updatedOldEntry);
ManagementClusterMetadata::tenantMetadata().tenantMap.set(tr, self->newName, updatedNewEntry);
ManagementClusterMetadata::tenantMetadata().tenantMap.set(tr, self->tenantId, updatedEntry);
ManagementClusterMetadata::tenantMetadata().tenantNameIndex.set(tr, self->newName, self->tenantId);
ManagementClusterMetadata::tenantMetadata().lastTenantModification.setVersionstamp(tr, Versionstamp(), 0);
// Add temporary tenant to tenantCount to prevent exceeding capacity during a rename
ManagementClusterMetadata::tenantMetadata().tenantCount.atomicOp(tr, 1, MutationRef::AddValue);
ManagementClusterMetadata::clusterTenantCount.atomicOp(
tr, updatedNewEntry.assignedCluster.get(), 1, MutationRef::AddValue);
// Updated indexes to include the new tenant
ManagementClusterMetadata::clusterTenantIndex.insert(
tr, Tuple::makeTuple(updatedNewEntry.assignedCluster.get(), self->newName, self->tenantId));
tr, Tuple::makeTuple(updatedEntry.assignedCluster.get(), self->newName, self->tenantId));
// Add new name to tenant group. It should already exist since the old name was part of it.
managementClusterAddTenantToGroup(
tr, self->newName, updatedNewEntry, &self->ctx.dataClusterMetadata.get(), true);
return Void();
}
@ -1953,44 +1898,40 @@ struct RenameTenantImpl {
ACTOR static Future<Void> finishRenameFromManagementCluster(RenameTenantImpl* self,
Reference<typename DB::TransactionT> tr) {
state Optional<TenantMapEntry> oldTenantEntry;
state Optional<TenantMapEntry> newTenantEntry;
wait(store(oldTenantEntry, tryGetTenantTransaction(tr, self->oldName)) &&
store(newTenantEntry, tryGetTenantTransaction(tr, self->newName)));
Optional<TenantMapEntry> tenantEntry = wait(tryGetTenantTransaction(tr, self->tenantId));
// Another (or several other) operations have already removed/changed the old entry
// Possible for the new entry to also have been tampered with,
// so it may or may not be present with or without the same id, which are all
// legal states. Assume the rename completed properly in this case
if (!oldTenantEntry.present() || oldTenantEntry.get().id != self->tenantId ||
oldTenantEntry.get().configurationSequenceNum > self->configurationSequenceNum) {
if (!tenantEntry.present() || tenantEntry.get().tenantName != self->oldName ||
tenantEntry.get().configurationSequenceNum > self->configurationSequenceNum) {
CODE_PROBE(true,
"Metacluster finished rename with missing entries, mismatched id, and/or mismatched "
"configuration sequence.");
return Void();
}
if (oldTenantEntry.get().tenantState == TenantState::REMOVING) {
ASSERT(newTenantEntry.get().tenantState == TenantState::REMOVING);
if (tenantEntry.get().tenantState == TenantState::REMOVING) {
throw tenant_removed();
}
ASSERT(newTenantEntry.present());
ASSERT(newTenantEntry.get().id == self->tenantId);
TenantMapEntry updatedOldEntry = oldTenantEntry.get();
TenantMapEntry updatedNewEntry = newTenantEntry.get();
TenantMapEntry updatedEntry = tenantEntry.get();
// Only update if in the expected state
if (updatedNewEntry.tenantState == TenantState::RENAMING_TO) {
updatedNewEntry.tenantState = TenantState::READY;
updatedNewEntry.renamePair.reset();
ManagementClusterMetadata::tenantMetadata().tenantMap.set(tr, self->newName, updatedNewEntry);
ManagementClusterMetadata::tenantMetadata().tenantIdIndex.set(tr, self->tenantId, self->newName);
if (updatedEntry.tenantState == TenantState::RENAMING) {
updatedEntry.tenantName = self->newName;
updatedEntry.tenantState = TenantState::READY;
updatedEntry.renameDestination.reset();
ManagementClusterMetadata::tenantMetadata().tenantMap.set(tr, self->tenantId, updatedEntry);
ManagementClusterMetadata::tenantMetadata().lastTenantModification.setVersionstamp(tr, Versionstamp(), 0);
ManagementClusterMetadata::tenantMetadata().tenantNameIndex.erase(tr, self->oldName);
// Remove the tenant from the cluster -> tenant index
ManagementClusterMetadata::clusterTenantIndex.erase(
tr, Tuple::makeTuple(updatedEntry.assignedCluster.get(), self->oldName, self->tenantId));
}
// We will remove the old entry from the management cluster
// This should still be the same old entry since the tenantId matches from the check above.
wait(deleteTenantFromManagementCluster(self, tr, updatedOldEntry));
return Void();
}

View File

@ -49,19 +49,17 @@ constexpr static int PREFIX_SIZE = sizeof(int64_t);
// REMOVING - the tenant has been marked for removal and is being removed on the data cluster
// UPDATING_CONFIGURATION - the tenant configuration has changed on the management cluster and is being applied to the
// data cluster
// RENAMING_FROM - the tenant is being renamed to a new name and is awaiting the rename to complete on the data cluster
// RENAMING_TO - the tenant is being created as a rename from an existing tenant and is awaiting the rename to complete
// on the data cluster
// RENAMING - the tenant is in the process of being renamed
// ERROR - the tenant is in an error state
//
// A tenant in any configuration is allowed to be removed. Only tenants in the READY or UPDATING_CONFIGURATION phases
// can have their configuration updated. A tenant must not exist or be in the REGISTERING phase to be created. To be
// renamed, a tenant must be in the READY or RENAMING_FROM state. In the latter case, the rename destination must match
// renamed, a tenant must be in the READY or RENAMING state. In the latter case, the rename destination must match
// the original rename attempt.
//
// If an operation fails and the tenant is left in a non-ready state, re-running the same operation is legal. If
// successful, the tenant will return to the READY state.
enum class TenantState { REGISTERING, READY, REMOVING, UPDATING_CONFIGURATION, RENAMING_FROM, RENAMING_TO, ERROR };
enum class TenantState { REGISTERING, READY, REMOVING, UPDATING_CONFIGURATION, RENAMING, ERROR };
// Represents the lock state the tenant could be in.
// Can be used in conjunction with the other tenant states above.
@ -84,7 +82,7 @@ struct TenantMapEntry {
Optional<TenantGroupName> tenantGroup;
Optional<ClusterName> assignedCluster;
int64_t configurationSequenceNum = 0;
Optional<TenantName> renamePair;
Optional<TenantName> renameDestination;
// Can be set to an error string if the tenant is in the ERROR state
std::string error;
@ -114,7 +112,7 @@ struct TenantMapEntry {
tenantGroup,
assignedCluster,
configurationSequenceNum,
renamePair,
renameDestination,
error);
if constexpr (Ar::isDeserializing) {
if (id >= 0) {
@ -165,11 +163,37 @@ struct TenantTombstoneCleanupData {
}
};
// This is used so that tenant IDs will be ordered and so that we can easily map arbitrary ranges in the tenant map to
// the affected tenant IDs.
struct TenantIdCodec {
static Standalone<StringRef> pack(int64_t val) {
int64_t swapped = bigEndian64(val);
return StringRef((uint8_t*)&swapped, sizeof(swapped));
}
static int64_t unpack(Standalone<StringRef> val) { return bigEndian64(*(int64_t*)val.begin()); }
static Optional<int64_t> lowerBound(Standalone<StringRef> val) {
if (val.size() == 8) {
return unpack(val);
} else if (val.size() > 8) {
int64_t result = unpack(val);
if (result == std::numeric_limits<int64_t>::max()) {
return {};
}
return result + 1;
} else {
int64_t result = 0;
memcpy(&result, val.begin(), val.size());
return bigEndian64(result);
}
}
};
struct TenantMetadataSpecification {
Key subspace;
KeyBackedObjectMap<TenantName, TenantMapEntry, decltype(IncludeVersion()), NullCodec> tenantMap;
KeyBackedMap<int64_t, TenantName> tenantIdIndex;
KeyBackedObjectMap<int64_t, TenantMapEntry, decltype(IncludeVersion()), TenantIdCodec> tenantMap;
KeyBackedMap<TenantName, int64_t> tenantNameIndex;
KeyBackedProperty<int64_t> lastTenantId;
KeyBackedBinaryValue<int64_t> tenantCount;
KeyBackedSet<int64_t> tenantTombstones;
@ -180,7 +204,7 @@ struct TenantMetadataSpecification {
TenantMetadataSpecification(KeyRef prefix)
: subspace(prefix.withSuffix("tenant/"_sr)), tenantMap(subspace.withSuffix("map/"_sr), IncludeVersion()),
tenantIdIndex(subspace.withSuffix("idIndex/"_sr)), lastTenantId(subspace.withSuffix("lastId"_sr)),
tenantNameIndex(subspace.withSuffix("nameIndex/"_sr)), lastTenantId(subspace.withSuffix("lastId"_sr)),
tenantCount(subspace.withSuffix("count"_sr)), tenantTombstones(subspace.withSuffix("tombstones/"_sr)),
tombstoneCleanupData(subspace.withSuffix("tombstoneCleanup"_sr), IncludeVersion()),
tenantGroupTenantIndex(subspace.withSuffix("tenantGroup/tenantIndex/"_sr)),
@ -193,7 +217,7 @@ struct TenantMetadata {
static inline auto& subspace() { return instance().subspace; }
static inline auto& tenantMap() { return instance().tenantMap; }
static inline auto& tenantIdIndex() { return instance().tenantIdIndex; }
static inline auto& tenantNameIndex() { return instance().tenantNameIndex; }
static inline auto& lastTenantId() { return instance().lastTenantId; }
static inline auto& tenantCount() { return instance().tenantCount; }
static inline auto& tenantTombstones() { return instance().tenantTombstones; }

View File

@ -55,14 +55,13 @@ enum class TenantEntryCacheRefreshMode { PERIODIC_TASK = 1, WATCH = 2, NONE = 3
template <class T>
struct TenantEntryCachePayload {
TenantName name;
TenantMapEntry entry;
// Custom client payload
T payload;
};
template <class T>
using TenantEntryCachePayloadFunc = std::function<TenantEntryCachePayload<T>(const TenantName&, const TenantMapEntry&)>;
using TenantEntryCachePayloadFunc = std::function<TenantEntryCachePayload<T>(const TenantMapEntry&)>;
// In-memory cache for TenantEntryMap objects. It supports three indices:
// 1. Lookup by 'TenantId'
@ -97,13 +96,13 @@ private:
Counter numRefreshes;
Counter refreshByWatchTrigger;
ACTOR static Future<TenantNameEntryPairVec> getTenantList(Reference<ReadYourWritesTransaction> tr) {
ACTOR static Future<std::vector<std::pair<int64_t, TenantMapEntry>>> getTenantList(
Reference<ReadYourWritesTransaction> tr) {
tr->setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
tr->setOption(FDBTransactionOptions::READ_LOCK_AWARE);
KeyBackedRangeResult<std::pair<TenantName, TenantMapEntry>> tenantList =
wait(TenantMetadata::tenantMap().getRange(
tr, Optional<TenantName>(), Optional<TenantName>(), CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER + 1));
KeyBackedRangeResult<std::pair<int64_t, TenantMapEntry>> tenantList =
wait(TenantMetadata::tenantMap().getRange(tr, {}, {}, CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER + 1));
ASSERT(tenantList.results.size() <= CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER && !tenantList.more);
TraceEvent(SevDebug, "TenantEntryCacheGetTenantList").detail("Count", tenantList.results.size());
@ -120,13 +119,10 @@ private:
try {
tr->setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
tr->setOption(FDBTransactionOptions::READ_LOCK_AWARE);
state Optional<TenantName> name = wait(TenantMetadata::tenantIdIndex().get(tr, tenantId));
if (name.present()) {
Optional<TenantMapEntry> entry = wait(TenantMetadata::tenantMap().get(tr, name.get()));
if (entry.present()) {
cache->put(std::make_pair(name.get(), entry.get()));
updateCacheRefreshMetrics(cache, reason);
}
state Optional<TenantMapEntry> entry = wait(TenantMetadata::tenantMap().get(tr, tenantId));
if (entry.present()) {
cache->put(entry.get());
updateCacheRefreshMetrics(cache, reason);
}
break;
} catch (Error& e) {
@ -147,10 +143,13 @@ private:
try {
tr->setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
tr->setOption(FDBTransactionOptions::READ_LOCK_AWARE);
Optional<TenantMapEntry> entry = wait(TenantMetadata::tenantMap().get(tr, name));
if (entry.present()) {
cache->put(std::make_pair(name, entry.get()));
updateCacheRefreshMetrics(cache, reason);
state Optional<int64_t> tenantId = wait(TenantMetadata::tenantNameIndex().get(tr, name));
if (tenantId.present()) {
Optional<TenantMapEntry> entry = wait(TenantMetadata::tenantMap().get(tr, tenantId.get()));
if (entry.present()) {
cache->put(entry.get());
updateCacheRefreshMetrics(cache, reason);
}
}
break;
} catch (Error& e) {
@ -277,12 +276,12 @@ private:
state Reference<ReadYourWritesTransaction> tr = cache->getDatabase()->createTransaction();
loop {
try {
state TenantNameEntryPairVec tenantList = wait(getTenantList(tr));
state std::vector<std::pair<int64_t, TenantMapEntry>> tenantList = wait(getTenantList(tr));
// Refresh cache entries reflecting the latest database state
cache->clear();
for (auto& tenant : tenantList) {
cache->put(tenant);
cache->put(tenant.second);
}
updateCacheRefreshMetrics(cache, reason);
@ -378,9 +377,8 @@ private:
Future<Void> refresh(TenantEntryCacheRefreshReason reason) { return refreshImpl(this, reason); }
static TenantEntryCachePayload<Void> defaultCreatePayload(const TenantName& name, const TenantMapEntry& entry) {
static TenantEntryCachePayload<Void> defaultCreatePayload(const TenantMapEntry& entry) {
TenantEntryCachePayload<Void> payload;
payload.name = name;
payload.entry = entry;
return payload;
@ -405,7 +403,7 @@ private:
return Void();
}
// Ensure byId and byName cache are in-sync
itrName = mapByTenantName.find(itrId->value.name);
itrName = mapByTenantName.find(itrId->value.entry.tenantName);
ASSERT(itrName != mapByTenantName.end());
} else if (tenantName.present()) {
ASSERT(!tenantId.present() && !tenantPrefix.present());
@ -538,11 +536,10 @@ public:
return removeEntryInt(Optional<int64_t>(), Optional<KeyRef>(), tenantName, refreshCache);
}
void put(const TenantNameEntryPair& pair) {
const auto& [name, entry] = pair;
TenantEntryCachePayload<T> payload = createPayloadFunc(name, entry);
void put(const TenantMapEntry& entry) {
TenantEntryCachePayload<T> payload = createPayloadFunc(entry);
auto idItr = mapByTenantId.find(entry.id);
auto nameItr = mapByTenantName.find(name);
auto nameItr = mapByTenantName.find(entry.tenantName);
Optional<TenantName> existingName;
Optional<int64_t> existingId;
@ -550,7 +547,7 @@ public:
existingId = nameItr->value.entry.id;
}
if (idItr != mapByTenantId.end()) {
existingName = idItr->value.name;
existingName = idItr->value.entry.tenantName;
}
if (existingId.present()) {
mapByTenantId.erase(existingId.get());
@ -560,14 +557,14 @@ public:
}
mapByTenantId[entry.id] = payload;
mapByTenantName[name] = payload;
mapByTenantName[entry.tenantName] = payload;
TraceEvent("TenantEntryCachePut")
.detail("TenantName", name)
.detail("TenantName", entry.tenantName)
.detail("TenantNameExisting", existingName)
.detail("TenantID", entry.id)
.detail("TenantIDExisting", existingId)
.detail("TenantPrefix", pair.second.prefix);
.detail("TenantPrefix", entry.prefix);
CODE_PROBE(idItr == mapByTenantId.end() && nameItr == mapByTenantName.end(), "TenantCache new entry");
CODE_PROBE(idItr != mapByTenantId.end() && nameItr == mapByTenantName.end(), "TenantCache entry name updated");
@ -591,4 +588,4 @@ public:
};
#include "flow/unactorcompiler.h"
#endif // FDBCLIENT_TENANTENTRYCACHE_ACTOR_H
#endif // FDBCLIENT_TENANTENTRYCACHE_ACTOR_H

View File

@ -38,20 +38,32 @@
namespace TenantAPI {
template <class Transaction>
Future<Optional<TenantMapEntry>> tryGetTenantTransaction(Transaction tr, TenantName name) {
Future<Optional<TenantMapEntry>> tryGetTenantTransaction(Transaction tr, int64_t tenantId) {
tr->setOption(FDBTransactionOptions::RAW_ACCESS);
return TenantMetadata::tenantMap().get(tr, name);
return TenantMetadata::tenantMap().get(tr, tenantId);
}
ACTOR template <class DB>
Future<Optional<TenantMapEntry>> tryGetTenant(Reference<DB> db, TenantName name) {
ACTOR template <class Transaction>
Future<Optional<TenantMapEntry>> tryGetTenantTransaction(Transaction tr, TenantName name) {
tr->setOption(FDBTransactionOptions::RAW_ACCESS);
Optional<int64_t> tenantId = wait(TenantMetadata::tenantNameIndex().get(tr, name));
if (tenantId.present()) {
Optional<TenantMapEntry> entry = wait(TenantMetadata::tenantMap().get(tr, tenantId.get()));
return entry;
} else {
return Optional<TenantMapEntry>();
}
}
ACTOR template <class DB, class Tenant>
Future<Optional<TenantMapEntry>> tryGetTenant(Reference<DB> db, Tenant tenant) {
state Reference<typename DB::TransactionT> tr = db->createTransaction();
loop {
try {
tr->setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
tr->setOption(FDBTransactionOptions::READ_LOCK_AWARE);
Optional<TenantMapEntry> entry = wait(tryGetTenantTransaction(tr, name));
Optional<TenantMapEntry> entry = wait(tryGetTenantTransaction(tr, tenant));
return entry;
} catch (Error& e) {
wait(safeThreadFutureToFuture(tr->onError(e)));
@ -59,9 +71,9 @@ Future<Optional<TenantMapEntry>> tryGetTenant(Reference<DB> db, TenantName name)
}
}
ACTOR template <class Transaction>
Future<TenantMapEntry> getTenantTransaction(Transaction tr, TenantName name) {
Optional<TenantMapEntry> entry = wait(tryGetTenantTransaction(tr, name));
ACTOR template <class Transaction, class Tenant>
Future<TenantMapEntry> getTenantTransaction(Transaction tr, Tenant tenant) {
Optional<TenantMapEntry> entry = wait(tryGetTenantTransaction(tr, tenant));
if (!entry.present()) {
throw tenant_not_found();
}
@ -69,9 +81,9 @@ Future<TenantMapEntry> getTenantTransaction(Transaction tr, TenantName name) {
return entry.get();
}
ACTOR template <class DB>
Future<TenantMapEntry> getTenant(Reference<DB> db, TenantName name) {
Optional<TenantMapEntry> entry = wait(tryGetTenant(db, name));
ACTOR template <class DB, class Tenant>
Future<TenantMapEntry> getTenant(Reference<DB> db, Tenant tenant) {
Optional<TenantMapEntry> entry = wait(tryGetTenant(db, tenant));
if (!entry.present()) {
throw tenant_not_found();
}
@ -126,29 +138,24 @@ Future<bool> checkTombstone(Transaction tr, int64_t id) {
return hasTombstone;
}
// Creates a tenant with the given name. If the tenant already exists, the boolean return parameter will be false
// Creates a tenant. If the tenant already exists, the boolean return parameter will be false
// and the existing entry will be returned. If the tenant cannot be created, then the optional will be empty.
ACTOR template <class Transaction>
Future<std::pair<Optional<TenantMapEntry>, bool>> createTenantTransaction(
Transaction tr,
TenantNameRef name,
TenantMapEntry tenantEntry,
ClusterType clusterType = ClusterType::STANDALONE) {
Future<std::pair<Optional<TenantMapEntry>, bool>>
createTenantTransaction(Transaction tr, TenantMapEntry tenantEntry, ClusterType clusterType = ClusterType::STANDALONE) {
ASSERT(clusterType != ClusterType::METACLUSTER_MANAGEMENT);
ASSERT(tenantEntry.id >= 0);
if (name.startsWith("\xff"_sr)) {
if (tenantEntry.tenantName.startsWith("\xff"_sr)) {
throw invalid_tenant_name();
}
if (tenantEntry.tenantGroup.present() && tenantEntry.tenantGroup.get().startsWith("\xff"_sr)) {
throw invalid_tenant_group_name();
}
tenantEntry.tenantName = name;
tr->setOption(FDBTransactionOptions::RAW_ACCESS);
state Future<Optional<TenantMapEntry>> existingEntryFuture = tryGetTenantTransaction(tr, name);
state Future<Optional<TenantMapEntry>> existingEntryFuture = tryGetTenantTransaction(tr, tenantEntry.tenantName);
state Future<Void> tenantModeCheck = checkTenantMode(tr, clusterType);
state Future<bool> tombstoneFuture =
(clusterType == ClusterType::STANDALONE) ? false : checkTombstone(tr, tenantEntry.id);
@ -179,12 +186,13 @@ Future<std::pair<Optional<TenantMapEntry>, bool>> createTenantTransaction(
tenantEntry.tenantState = TenantState::READY;
tenantEntry.assignedCluster = Optional<ClusterName>();
TenantMetadata::tenantMap().set(tr, name, tenantEntry);
TenantMetadata::tenantIdIndex().set(tr, tenantEntry.id, name);
TenantMetadata::tenantMap().set(tr, tenantEntry.id, tenantEntry);
TenantMetadata::tenantNameIndex().set(tr, tenantEntry.tenantName, tenantEntry.id);
TenantMetadata::lastTenantModification().setVersionstamp(tr, Versionstamp(), 0);
if (tenantEntry.tenantGroup.present()) {
TenantMetadata::tenantGroupTenantIndex().insert(tr, Tuple::makeTuple(tenantEntry.tenantGroup.get(), name));
TenantMetadata::tenantGroupTenantIndex().insert(
tr, Tuple::makeTuple(tenantEntry.tenantGroup.get(), tenantEntry.id));
// Create the tenant group associated with this tenant if it doesn't already exist
Optional<TenantGroupEntry> existingTenantGroup = wait(existingTenantGroupEntryFuture);
@ -228,6 +236,8 @@ Future<Optional<TenantMapEntry>> createTenant(Reference<DB> db,
ASSERT(clusterType == ClusterType::STANDALONE || !generateTenantId);
tenantEntry.tenantName = name;
loop {
try {
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
@ -239,8 +249,8 @@ Future<Optional<TenantMapEntry>> createTenant(Reference<DB> db,
}
if (checkExistence) {
Optional<TenantMapEntry> entry = wait(tryGetTenantTransaction(tr, name));
if (entry.present()) {
Optional<int64_t> existingId = wait(TenantMetadata::tenantNameIndex().get(tr, name));
if (existingId.present()) {
throw tenant_already_exists();
}
@ -254,7 +264,7 @@ Future<Optional<TenantMapEntry>> createTenant(Reference<DB> db,
}
state std::pair<Optional<TenantMapEntry>, bool> newTenant =
wait(createTenantTransaction(tr, name, tenantEntry, clusterType));
wait(createTenantTransaction(tr, tenantEntry, clusterType));
if (newTenant.second) {
ASSERT(newTenant.first.present());
@ -319,25 +329,23 @@ Future<Void> markTenantTombstones(Transaction tr, int64_t tenantId) {
return Void();
}
// Deletes the tenant with the given name. If tenantId is specified, the tenant being deleted must also have the same
// ID. If no matching tenant is found, this function returns without deleting anything. This behavior allows the
// function to be used idempotently: if the transaction is retried after having succeeded, it will see that the tenant
// is absent (or optionally created with a new ID) and do nothing.
// Deletes a tenant with the given ID. If no matching tenant is found, this function returns without deleting anything.
// This behavior allows the function to be used idempotently: if the transaction is retried after having succeeded, it
// will see that the tenant is absent and do nothing.
ACTOR template <class Transaction>
Future<Void> deleteTenantTransaction(Transaction tr,
TenantNameRef name,
Optional<int64_t> tenantId = Optional<int64_t>(),
int64_t tenantId,
ClusterType clusterType = ClusterType::STANDALONE) {
ASSERT(clusterType == ClusterType::STANDALONE || tenantId.present());
ASSERT(tenantId != TenantInfo::INVALID_TENANT);
ASSERT(clusterType != ClusterType::METACLUSTER_MANAGEMENT);
tr->setOption(FDBTransactionOptions::RAW_ACCESS);
state Future<Optional<TenantMapEntry>> tenantEntryFuture = tryGetTenantTransaction(tr, name);
state Future<Optional<TenantMapEntry>> tenantEntryFuture = tryGetTenantTransaction(tr, tenantId);
wait(checkTenantMode(tr, clusterType));
state Optional<TenantMapEntry> tenantEntry = wait(tenantEntryFuture);
if (tenantEntry.present() && (!tenantId.present() || tenantEntry.get().id == tenantId.get())) {
if (tenantEntry.present()) {
state typename transaction_future_type<Transaction, RangeResult>::type prefixRangeFuture =
tr->getRange(prefixRange(tenantEntry.get().prefix), 1);
@ -347,14 +355,14 @@ Future<Void> deleteTenantTransaction(Transaction tr,
}
// This is idempotent because we only erase an entry from the tenant map if it is present
TenantMetadata::tenantMap().erase(tr, name);
TenantMetadata::tenantIdIndex().erase(tr, tenantEntry.get().id);
TenantMetadata::tenantMap().erase(tr, tenantId);
TenantMetadata::tenantNameIndex().erase(tr, tenantEntry.get().tenantName);
TenantMetadata::tenantCount().atomicOp(tr, -1, MutationRef::AddValue);
TenantMetadata::lastTenantModification().setVersionstamp(tr, Versionstamp(), 0);
if (tenantEntry.get().tenantGroup.present()) {
TenantMetadata::tenantGroupTenantIndex().erase(tr,
Tuple::makeTuple(tenantEntry.get().tenantGroup.get(), name));
TenantMetadata::tenantGroupTenantIndex().erase(
tr, Tuple::makeTuple(tenantEntry.get().tenantGroup.get(), tenantId));
KeyBackedSet<Tuple>::RangeResultType tenantsInGroup =
wait(TenantMetadata::tenantGroupTenantIndex().getRange(
tr,
@ -362,14 +370,14 @@ Future<Void> deleteTenantTransaction(Transaction tr,
Tuple::makeTuple(keyAfter(tenantEntry.get().tenantGroup.get())),
2));
if (tenantsInGroup.results.empty() ||
(tenantsInGroup.results.size() == 1 && tenantsInGroup.results[0].getString(1) == name)) {
(tenantsInGroup.results.size() == 1 && tenantsInGroup.results[0].getInt(1) == tenantId)) {
TenantMetadata::tenantGroupMap().erase(tr, tenantEntry.get().tenantGroup.get());
}
}
}
if (clusterType == ClusterType::METACLUSTER_DATA) {
wait(markTenantTombstones(tr, tenantId.get()));
wait(markTenantTombstones(tr, tenantId));
}
return Void();
@ -391,21 +399,22 @@ Future<Void> deleteTenant(Reference<DB> db,
tr->setOption(FDBTransactionOptions::LOCK_AWARE);
if (checkExistence) {
TenantMapEntry entry = wait(getTenantTransaction(tr, name));
// If an ID wasn't specified, use the current ID. This way we cannot inadvertently delete
// multiple tenants if this transaction retries.
if (!tenantId.present()) {
tenantId = entry.id;
Optional<int64_t> actualId = wait(TenantMetadata::tenantNameIndex().get(tr, name));
if (!actualId.present() || (tenantId.present() && tenantId != actualId)) {
throw tenant_not_found();
}
tenantId = actualId;
checkExistence = false;
}
wait(deleteTenantTransaction(tr, name, tenantId, clusterType));
wait(deleteTenantTransaction(tr, tenantId.get(), clusterType));
wait(buggifiedCommit(tr, BUGGIFY_WITH_PROB(0.1)));
TraceEvent("DeletedTenant").detail("Tenant", name).detail("Version", tr->getCommittedVersion());
TraceEvent("DeletedTenant")
.detail("Tenant", name)
.detail("TenantId", tenantId)
.detail("Version", tr->getCommittedVersion());
return Void();
} catch (Error& e) {
wait(safeThreadFutureToFuture(tr->onError(e)));
@ -418,13 +427,12 @@ Future<Void> deleteTenant(Reference<DB> db,
// to be changed. This must only be called on a non-management cluster.
ACTOR template <class Transaction>
Future<Void> configureTenantTransaction(Transaction tr,
TenantNameRef tenantName,
TenantMapEntry originalEntry,
TenantMapEntry updatedTenantEntry) {
ASSERT(updatedTenantEntry.id == originalEntry.id);
tr->setOption(FDBTransactionOptions::RAW_ACCESS);
TenantMetadata::tenantMap().set(tr, tenantName, updatedTenantEntry);
TenantMetadata::tenantMap().set(tr, updatedTenantEntry.id, updatedTenantEntry);
TenantMetadata::lastTenantModification().setVersionstamp(tr, Versionstamp(), 0);
// If the tenant group was changed, we need to update the tenant group metadata structures
@ -435,7 +443,7 @@ Future<Void> configureTenantTransaction(Transaction tr,
if (originalEntry.tenantGroup.present()) {
// Remove this tenant from the original tenant group index
TenantMetadata::tenantGroupTenantIndex().erase(
tr, Tuple::makeTuple(originalEntry.tenantGroup.get(), tenantName));
tr, Tuple::makeTuple(originalEntry.tenantGroup.get(), updatedTenantEntry.id));
// Check if the original tenant group is now empty. If so, remove the tenant group.
KeyBackedSet<Tuple>::RangeResultType tenants = wait(TenantMetadata::tenantGroupTenantIndex().getRange(
@ -445,7 +453,7 @@ Future<Void> configureTenantTransaction(Transaction tr,
2));
if (tenants.results.empty() ||
(tenants.results.size() == 1 && tenants.results[0].getString(1) == tenantName)) {
(tenants.results.size() == 1 && tenants.results[0].getInt(1) == updatedTenantEntry.id)) {
TenantMetadata::tenantGroupMap().erase(tr, originalEntry.tenantGroup.get());
}
}
@ -459,7 +467,7 @@ Future<Void> configureTenantTransaction(Transaction tr,
// Insert this tenant in the tenant group index
TenantMetadata::tenantGroupTenantIndex().insert(
tr, Tuple::makeTuple(updatedTenantEntry.tenantGroup.get(), tenantName));
tr, Tuple::makeTuple(updatedTenantEntry.tenantGroup.get(), updatedTenantEntry.id));
}
}
@ -473,10 +481,22 @@ Future<std::vector<std::pair<TenantName, TenantMapEntry>>> listTenantsTransactio
int limit) {
tr->setOption(FDBTransactionOptions::RAW_ACCESS);
KeyBackedRangeResult<std::pair<TenantName, TenantMapEntry>> results =
wait(TenantMetadata::tenantMap().getRange(tr, begin, end, limit));
KeyBackedRangeResult<std::pair<TenantName, int64_t>> matchingTenants =
wait(TenantMetadata::tenantNameIndex().getRange(tr, begin, end, limit));
return results.results;
state std::vector<Future<TenantMapEntry>> tenantEntryFutures;
for (auto const& [name, id] : matchingTenants.results) {
tenantEntryFutures.push_back(getTenantTransaction(tr, id));
}
wait(waitForAll(tenantEntryFutures));
std::vector<std::pair<TenantName, TenantMapEntry>> results;
for (auto const& f : tenantEntryFutures) {
results.emplace_back(f.get().tenantName, f.get());
}
return results;
}
ACTOR template <class DB>
@ -508,35 +528,45 @@ Future<Void> renameTenantTransaction(Transaction tr,
Optional<int64_t> configureSequenceNum = Optional<int64_t>()) {
ASSERT(clusterType == ClusterType::STANDALONE || (tenantId.present() && configureSequenceNum.present()));
ASSERT(clusterType != ClusterType::METACLUSTER_MANAGEMENT);
wait(checkTenantMode(tr, clusterType));
tr->setOption(FDBTransactionOptions::RAW_ACCESS);
state Optional<TenantMapEntry> oldEntry;
state Optional<TenantMapEntry> newEntry;
wait(store(oldEntry, tryGetTenantTransaction(tr, oldName)) &&
store(newEntry, tryGetTenantTransaction(tr, newName)));
if (!oldEntry.present() || (tenantId.present() && tenantId.get() != oldEntry.get().id)) {
state Future<Void> tenantModeCheck = checkTenantMode(tr, clusterType);
state Future<Optional<int64_t>> oldNameIdFuture =
tenantId.present() ? Future<Optional<int64_t>>() : TenantMetadata::tenantNameIndex().get(tr, oldName);
state Future<Optional<int64_t>> newNameIdFuture = TenantMetadata::tenantNameIndex().get(tr, newName);
wait(tenantModeCheck);
if (!tenantId.present()) {
wait(store(tenantId, oldNameIdFuture));
if (!tenantId.present()) {
throw tenant_not_found();
}
}
state TenantMapEntry entry = wait(getTenantTransaction(tr, tenantId.get()));
Optional<int64_t> newNameId = wait(newNameIdFuture);
if (entry.tenantName != oldName) {
throw tenant_not_found();
}
if (newEntry.present()) {
if (newNameId.present()) {
throw tenant_already_exists();
}
if (configureSequenceNum.present()) {
if (oldEntry.get().configurationSequenceNum >= configureSequenceNum.get()) {
if (entry.configurationSequenceNum >= configureSequenceNum.get()) {
return Void();
}
oldEntry.get().configurationSequenceNum = configureSequenceNum.get();
entry.configurationSequenceNum = configureSequenceNum.get();
}
TenantMetadata::tenantMap().erase(tr, oldName);
TenantMetadata::tenantMap().set(tr, newName, oldEntry.get());
TenantMetadata::tenantIdIndex().set(tr, oldEntry.get().id, newName);
TenantMetadata::lastTenantModification().setVersionstamp(tr, Versionstamp(), 0);
// Update the tenant group index to reflect the new tenant name
if (oldEntry.get().tenantGroup.present()) {
TenantMetadata::tenantGroupTenantIndex().erase(tr, Tuple::makeTuple(oldEntry.get().tenantGroup.get(), oldName));
TenantMetadata::tenantGroupTenantIndex().insert(tr,
Tuple::makeTuple(oldEntry.get().tenantGroup.get(), newName));
}
entry.tenantName = newName;
TenantMetadata::tenantMap().set(tr, tenantId.get(), entry);
TenantMetadata::tenantNameIndex().set(tr, newName, tenantId.get());
TenantMetadata::tenantNameIndex().erase(tr, oldName);
TenantMetadata::lastTenantModification().setVersionstamp(tr, Versionstamp(), 0);
if (clusterType == ClusterType::METACLUSTER_DATA) {
wait(markTenantTombstones(tr, tenantId.get()));
@ -555,50 +585,38 @@ Future<Void> renameTenant(Reference<DB> db,
ASSERT(clusterType == ClusterType::STANDALONE || tenantId.present());
state bool firstTry = true;
state int64_t id;
loop {
try {
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
state Optional<TenantMapEntry> oldEntry;
state Optional<TenantMapEntry> newEntry;
wait(store(oldEntry, tryGetTenantTransaction(tr, oldName)) &&
store(newEntry, tryGetTenantTransaction(tr, newName)));
if (firstTry) {
if (!oldEntry.present()) {
throw tenant_not_found();
}
if (newEntry.present()) {
throw tenant_already_exists();
}
// Store the id we see when first reading this key
id = oldEntry.get().id;
firstTry = false;
} else {
// If we got commit_unknown_result, the rename may have already occurred.
if (newEntry.present()) {
int64_t checkId = newEntry.get().id;
if (id == checkId) {
ASSERT(!oldEntry.present() || oldEntry.get().id != id);
return Void();
}
// If the new entry is present but does not match, then
// the rename should fail, so we throw an error.
throw tenant_already_exists();
}
if (!oldEntry.present()) {
throw tenant_not_found();
}
int64_t checkId = oldEntry.get().id;
// If the id has changed since we made our first attempt,
// then it's possible we've already moved the tenant. Don't move it again.
if (id != checkId) {
if (!tenantId.present()) {
wait(store(tenantId, TenantMetadata::tenantNameIndex().get(tr, oldName)));
if (!tenantId.present()) {
throw tenant_not_found();
}
}
state Future<Optional<int64_t>> newNameIdFuture = TenantMetadata::tenantNameIndex().get(tr, newName);
state TenantMapEntry entry = wait(getTenantTransaction(tr, tenantId.get()));
Optional<int64_t> newNameId = wait(newNameIdFuture);
if (!firstTry && entry.tenantName == newName) {
// On a retry, the rename may have already occurred
return Void();
} else if (entry.tenantName != oldName) {
throw tenant_not_found();
} else if (newNameId.present() && newNameId.get() != tenantId.get()) {
throw tenant_already_exists();
}
firstTry = false;
wait(renameTenantTransaction(tr, oldName, newName, tenantId, clusterType));
wait(buggifiedCommit(tr, BUGGIFY_WITH_PROB(0.1)));
TraceEvent("RenameTenantSuccess").detail("OldName", oldName).detail("NewName", newName);
TraceEvent("TenantRenamed")
.detail("OldName", oldName)
.detail("NewName", newName)
.detail("TenantId", tenantId.get());
return Void();
} catch (Error& e) {
wait(safeThreadFutureToFuture(tr->onError(e)));
@ -664,4 +682,4 @@ Future<std::vector<std::pair<TenantGroupName, TenantGroupEntry>>> listTenantGrou
} // namespace TenantAPI
#include "flow/unactorcompiler.h"
#endif
#endif

View File

@ -110,8 +110,7 @@ private:
std::vector<std::pair<Standalone<StringRef>, Optional<Value>>> configMutations,
int64_t tenantId,
std::map<TenantGroupName, int>* tenantGroupNetTenantDelta) {
state TenantMapEntry tenantEntry;
tenantEntry.setId(tenantId);
state TenantMapEntry tenantEntry(tenantId, tenantName, TenantState::READY);
for (auto const& [name, value] : configMutations) {
tenantEntry.configure(name, value);
@ -122,7 +121,7 @@ private:
}
std::pair<Optional<TenantMapEntry>, bool> entry =
wait(TenantAPI::createTenantTransaction(&ryw->getTransaction(), tenantName, tenantEntry));
wait(TenantAPI::createTenantTransaction(&ryw->getTransaction(), tenantEntry));
return entry.second;
}
@ -180,7 +179,7 @@ private:
}
}
wait(TenantAPI::configureTenantTransaction(&ryw->getTransaction(), tenantName, originalEntry, updatedEntry));
wait(TenantAPI::configureTenantTransaction(&ryw->getTransaction(), originalEntry, updatedEntry));
return Void();
}
@ -190,7 +189,7 @@ private:
state Optional<TenantMapEntry> tenantEntry =
wait(TenantAPI::tryGetTenantTransaction(&ryw->getTransaction(), tenantName));
if (tenantEntry.present()) {
wait(TenantAPI::deleteTenantTransaction(&ryw->getTransaction(), tenantName));
wait(TenantAPI::deleteTenantTransaction(&ryw->getTransaction(), tenantEntry.get().id));
if (tenantEntry.get().tenantGroup.present()) {
(*tenantGroupNetTenantDelta)[tenantEntry.get().tenantGroup.get()]--;
}
@ -217,10 +216,7 @@ private:
std::vector<Future<Void>> deleteFutures;
for (auto tenant : tenants) {
deleteFutures.push_back(TenantAPI::deleteTenantTransaction(&ryw->getTransaction(), tenant.first));
if (tenant.second.tenantGroup.present()) {
(*tenantGroupNetTenantDelta)[tenant.second.tenantGroup.get()]--;
}
deleteFutures.push_back(deleteSingleTenant(ryw, tenant.first, tenantGroupNetTenantDelta));
}
wait(waitForAll(deleteFutures));

View File

@ -137,8 +137,8 @@ private:
std::map<Tag, Version>* tag_popped = nullptr;
std::unordered_map<UID, StorageServerInterface>* tssMapping = nullptr;
std::unordered_map<int64_t, TenantName>* tenantMap = nullptr;
std::map<TenantName, int64_t>* tenantNameIndex = nullptr;
std::map<int64_t, TenantName>* tenantMap = nullptr;
std::unordered_map<TenantName, int64_t>* tenantNameIndex = nullptr;
EncryptionAtRestMode encryptMode;
// true if the mutations were already written to the txnStateStore as part of recovery
@ -672,20 +672,19 @@ private:
void checkSetTenantMapPrefix(MutationRef m) {
KeyRef prefix = TenantMetadata::tenantMap().subspace.begin;
if (m.param1.startsWith(prefix)) {
TenantName tenantName = m.param1.removePrefix(prefix);
TenantMapEntry tenantEntry = TenantMapEntry::decode(m.param2);
if (tenantMap) {
ASSERT(version != invalidVersion);
TraceEvent("CommitProxyInsertTenant", dbgid)
.detail("Tenant", tenantName)
.detail("Tenant", tenantEntry.tenantName)
.detail("Id", tenantEntry.id)
.detail("Version", version);
(*tenantMap)[tenantEntry.id] = tenantName;
(*tenantMap)[tenantEntry.id] = tenantEntry.tenantName;
if (tenantNameIndex) {
(*tenantNameIndex)[tenantName] = tenantEntry.id;
(*tenantNameIndex)[tenantEntry.tenantName] = tenantEntry.id;
}
}
@ -1095,25 +1094,31 @@ private:
if (tenantMap && tenantNameIndex) {
ASSERT(version != invalidVersion);
StringRef startTenant = std::max(range.begin, subspace.begin).removePrefix(subspace.begin);
StringRef endTenant =
range.end.startsWith(subspace.begin) ? range.end.removePrefix(subspace.begin) : "\xff\xff"_sr;
Optional<int64_t> startId = 0;
Optional<int64_t> endId;
if (range.begin > subspace.begin) {
startId = TenantIdCodec::lowerBound(range.begin.removePrefix(subspace.begin));
}
if (range.end.startsWith(subspace.begin)) {
endId = TenantIdCodec::lowerBound(range.end.removePrefix(subspace.begin));
}
TraceEvent("CommitProxyEraseTenants", dbgid)
.detail("BeginTenant", startTenant)
.detail("EndTenant", endTenant)
.detail("BeginTenant", startId)
.detail("EndTenant", endId)
.detail("Version", version);
auto startItr = tenantNameIndex->lower_bound(startTenant);
auto endItr = tenantNameIndex->lower_bound(endTenant);
auto startItr = startId.present() ? tenantMap->lower_bound(startId.get()) : tenantMap->end();
auto endItr = endId.present() ? tenantMap->lower_bound(endId.get()) : tenantMap->end();
auto itr = startItr;
while (itr != endItr) {
tenantMap->erase(itr->second);
tenantNameIndex->erase(itr->second);
itr++;
}
tenantNameIndex->erase(startItr, endItr);
tenantMap->erase(startItr, endItr);
}
if (!initialCommit) {

View File

@ -499,11 +499,11 @@ Future<Void> loadBlobMetadataForTenant(BGTenantMap* self, BlobMetadataDomainId d
}
// list of tenants that may or may not already exist
void BGTenantMap::addTenants(std::vector<std::pair<TenantName, TenantMapEntry>> tenants) {
void BGTenantMap::addTenants(std::vector<std::pair<int64_t, TenantMapEntry>> tenants) {
std::vector<BlobMetadataDomainId> tenantsToLoad;
for (auto entry : tenants) {
if (tenantInfoById.insert({ entry.second.id, entry.second }).second) {
auto r = makeReference<GranuleTenantData>(entry.first, entry.second);
if (tenantInfoById.insert({ entry.first, entry.second }).second) {
auto r = makeReference<GranuleTenantData>(entry.second);
tenantData.insert(KeyRangeRef(entry.second.prefix, entry.second.prefix.withSuffix(normalKeys.end)), r);
if (SERVER_KNOBS->BG_METADATA_SOURCE != "tenant") {
r->bstoreLoaded.send(Void());

View File

@ -1249,10 +1249,9 @@ ACTOR Future<Void> writeInitialGranuleMapping(Reference<BlobManagerData> bmData,
}
ACTOR Future<Void> loadTenantMap(Reference<ReadYourWritesTransaction> tr, Reference<BlobManagerData> bmData) {
state KeyBackedRangeResult<std::pair<TenantName, TenantMapEntry>> tenantResults;
state KeyBackedRangeResult<std::pair<int64_t, TenantMapEntry>> tenantResults;
wait(store(tenantResults,
TenantMetadata::tenantMap().getRange(
tr, Optional<TenantName>(), Optional<TenantName>(), CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER + 1)));
TenantMetadata::tenantMap().getRange(tr, {}, {}, CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER + 1)));
ASSERT(tenantResults.results.size() <= CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER && !tenantResults.more);
bmData->tenantData.addTenants(tenantResults.results);

View File

@ -4831,15 +4831,13 @@ ACTOR Future<Void> monitorTenants(Reference<BlobWorkerData> bwData) {
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
tr->setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE);
tr->setOption(FDBTransactionOptions::LOCK_AWARE);
state KeyBackedRangeResult<std::pair<TenantName, TenantMapEntry>> tenantResults;
wait(store(tenantResults,
TenantMetadata::tenantMap().getRange(tr,
Optional<TenantName>(),
Optional<TenantName>(),
CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER + 1)));
state KeyBackedRangeResult<std::pair<int64_t, TenantMapEntry>> tenantResults;
wait(
store(tenantResults,
TenantMetadata::tenantMap().getRange(tr, {}, {}, CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER + 1)));
ASSERT(tenantResults.results.size() <= CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER && !tenantResults.more);
std::vector<std::pair<TenantName, TenantMapEntry>> tenants;
std::vector<std::pair<int64_t, TenantMapEntry>> tenants;
for (auto& it : tenantResults.results) {
// FIXME: handle removing/moving tenants!
tenants.push_back(std::pair(it.first, it.second));

View File

@ -37,16 +37,11 @@ class TenantCacheImpl {
tr->setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
tr->setOption(FDBTransactionOptions::READ_LOCK_AWARE);
KeyBackedRangeResult<std::pair<TenantName, TenantMapEntry>> tenantList =
KeyBackedRangeResult<std::pair<int64_t, TenantMapEntry>> tenantList =
wait(TenantMetadata::tenantMap().getRange(tr, {}, {}, CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER + 1));
ASSERT(tenantList.results.size() <= CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER && !tenantList.more);
std::vector<std::pair<int64_t, TenantMapEntry>> results;
for (auto [_, entry] : tenantList.results) {
results.push_back(std::make_pair(entry.id, entry));
}
return results;
return tenantList.results;
}
public:

View File

@ -97,13 +97,12 @@ ACTOR Future<ForcedPurgeState> getForcePurgedState(Transaction* tr, KeyRange key
// TODO: versioned like SS has?
struct GranuleTenantData : NonCopyable, ReferenceCounted<GranuleTenantData> {
TenantName name;
TenantMapEntry entry;
Reference<BlobConnectionProvider> bstore;
Promise<Void> bstoreLoaded;
GranuleTenantData() {}
GranuleTenantData(TenantName name, TenantMapEntry entry) : name(name), entry(entry) {}
GranuleTenantData(TenantMapEntry entry) : entry(entry) {}
void updateBStore(const BlobMetadataDetailsRef& metadata) {
if (bstoreLoaded.canBeSet()) {
@ -120,7 +119,7 @@ struct GranuleTenantData : NonCopyable, ReferenceCounted<GranuleTenantData> {
// TODO: add refreshing
struct BGTenantMap {
public:
void addTenants(std::vector<std::pair<TenantName, TenantMapEntry>>);
void addTenants(std::vector<std::pair<int64_t, TenantMapEntry>>);
void removeTenants(std::vector<int64_t> tenantIds);
Optional<TenantMapEntry> getTenantById(int64_t id);

View File

@ -110,7 +110,7 @@ struct ProxyStats {
NotifiedVersion* pVersion,
NotifiedVersion* pCommittedVersion,
int64_t* commitBatchesMemBytesCountPtr,
std::unordered_map<int64_t, TenantName>* pTenantMap)
std::map<int64_t, TenantName>* pTenantMap)
: cc("ProxyStats", id.toString()), txnCommitIn("TxnCommitIn", cc),
txnCommitVersionAssigned("TxnCommitVersionAssigned", cc), txnCommitResolving("TxnCommitResolving", cc),
txnCommitResolved("TxnCommitResolved", cc), txnCommitOut("TxnCommitOut", cc),
@ -177,8 +177,8 @@ struct ExpectedIdempotencyIdCountForKey {
struct ProxyCommitData {
UID dbgid;
int64_t commitBatchesMemBytesCount;
std::map<TenantName, int64_t> tenantNameIndex;
std::unordered_map<int64_t, TenantName> tenantMap;
std::unordered_map<TenantName, int64_t> tenantNameIndex;
std::map<int64_t, TenantName> tenantMap;
std::unordered_set<int64_t> tenantsOverStorageQuota;
ProxyStats stats;
MasterInterface master;

View File

@ -52,10 +52,10 @@ private:
KeyBackedRangeResult<Tuple> clusterTenantTuples;
KeyBackedRangeResult<Tuple> clusterTenantGroupTuples;
std::map<TenantName, TenantMapEntry> tenantMap;
std::map<int64_t, TenantMapEntry> tenantMap;
KeyBackedRangeResult<std::pair<TenantGroupName, TenantGroupEntry>> tenantGroups;
std::map<ClusterName, std::set<TenantName>> clusterTenantMap;
std::map<ClusterName, std::set<int64_t>> clusterTenantMap;
std::map<ClusterName, std::set<TenantGroupName>> clusterTenantGroupMap;
int64_t tenantCount;
@ -70,7 +70,7 @@ private:
ACTOR static Future<Void> loadManagementClusterMetadata(MetaclusterConsistencyCheck* self) {
state Reference<typename DB::TransactionT> managementTr = self->managementDb->createTransaction();
state std::vector<std::pair<TenantName, TenantMapEntry>> tenantList;
state KeyBackedRangeResult<std::pair<int64_t, TenantMapEntry>> tenantList;
loop {
try {
@ -99,8 +99,8 @@ private:
MetaclusterAPI::ManagementClusterMetadata::tenantMetadata().tenantCount.getD(
managementTr, Snapshot::False, 0)) &&
store(tenantList,
MetaclusterAPI::listTenantsTransaction(
managementTr, ""_sr, "\xff\xff"_sr, metaclusterMaxTenants)) &&
MetaclusterAPI::ManagementClusterMetadata::tenantMetadata().tenantMap.getRange(
managementTr, {}, {}, metaclusterMaxTenants)) &&
store(self->managementMetadata.tenantGroups,
MetaclusterAPI::ManagementClusterMetadata::tenantMetadata().tenantGroupMap.getRange(
managementTr, {}, {}, metaclusterMaxTenants)) &&
@ -113,14 +113,15 @@ private:
}
}
self->managementMetadata.tenantMap = std::map<TenantName, TenantMapEntry>(tenantList.begin(), tenantList.end());
self->managementMetadata.tenantMap =
std::map<int64_t, TenantMapEntry>(tenantList.results.begin(), tenantList.results.end());
for (auto t : self->managementMetadata.clusterTenantTuples.results) {
ASSERT_EQ(t.size(), 3);
TenantName tenantName = t.getString(1);
int64_t tenantId = t.getInt(2);
ASSERT_EQ(tenantId, self->managementMetadata.tenantMap[tenantName].id);
self->managementMetadata.clusterTenantMap[t.getString(0)].insert(tenantName);
ASSERT(tenantName == self->managementMetadata.tenantMap[tenantId].tenantName);
self->managementMetadata.clusterTenantMap[t.getString(0)].insert(tenantId);
}
for (auto t : self->managementMetadata.clusterTenantGroupTuples.results) {
@ -200,13 +201,13 @@ private:
// Iterate through all tenants and verify related metadata
std::map<ClusterName, int> clusterAllocated;
std::set<TenantGroupName> processedTenantGroups;
for (auto [name, entry] : managementMetadata.tenantMap) {
for (auto [tenantId, entry] : managementMetadata.tenantMap) {
ASSERT(entry.assignedCluster.present());
// Each tenant should be assigned to the same cluster where it is stored in the cluster tenant index
auto clusterItr = managementMetadata.clusterTenantMap.find(entry.assignedCluster.get());
ASSERT(clusterItr != managementMetadata.clusterTenantMap.end());
ASSERT(clusterItr->second.count(name));
ASSERT(clusterItr->second.count(tenantId));
if (entry.tenantGroup.present()) {
// Count the number of tenant groups allocated in each cluster
@ -216,7 +217,7 @@ private:
// The tenant group should be stored in the same cluster where it is stored in the cluster tenant
// group index
auto clusterTenantGroupItr = managementMetadata.clusterTenantGroupMap.find(entry.assignedCluster.get());
ASSERT(clusterTenantGroupItr != managementMetadata.clusterTenantMap.end());
ASSERT(clusterTenantGroupItr != managementMetadata.clusterTenantGroupMap.end());
ASSERT(clusterTenantGroupItr->second.count(entry.tenantGroup.get()));
} else {
// Track the actual tenant group allocation per cluster (a tenant with no group counts against the
@ -251,7 +252,7 @@ private:
state Reference<ITransaction> dataTr = dataDb->createTransaction();
state Optional<MetaclusterRegistrationEntry> dataClusterRegistration;
state std::vector<std::pair<TenantName, TenantMapEntry>> dataClusterTenantList;
state KeyBackedRangeResult<std::pair<int64_t, TenantMapEntry>> dataClusterTenantList;
state KeyBackedRangeResult<std::pair<TenantGroupName, TenantGroupEntry>> dataClusterTenantGroupList;
state TenantConsistencyCheck<IDatabase> tenantConsistencyCheck(dataDb);
@ -262,8 +263,8 @@ private:
dataTr->setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
wait(store(dataClusterRegistration, MetaclusterMetadata::metaclusterRegistration().get(dataTr)) &&
store(dataClusterTenantList,
TenantAPI::listTenantsTransaction(
dataTr, ""_sr, "\xff\xff"_sr, CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER + 1)) &&
TenantMetadata::tenantMap().getRange(
dataTr, {}, {}, CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER + 1)) &&
store(dataClusterTenantGroupList,
TenantMetadata::tenantGroupMap().getRange(
dataTr, {}, {}, CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER + 1)));
@ -274,8 +275,8 @@ private:
}
}
state std::map<TenantName, TenantMapEntry> dataClusterTenantMap(dataClusterTenantList.begin(),
dataClusterTenantList.end());
state std::map<int64_t, TenantMapEntry> dataClusterTenantMap(dataClusterTenantList.results.begin(),
dataClusterTenantList.results.end());
state std::map<TenantGroupName, TenantGroupEntry> dataClusterTenantGroupMap(
dataClusterTenantGroupList.results.begin(), dataClusterTenantGroupList.results.end());
@ -298,25 +299,20 @@ private:
if (metaclusterEntry.tenantGroup.present()) {
groupExpectedTenantCounts.try_emplace(metaclusterEntry.tenantGroup.get(), 0);
}
if (metaclusterEntry.renamePair.present() &&
(metaclusterEntry.tenantState == TenantState::RENAMING_FROM ||
metaclusterEntry.tenantState == TenantState::RENAMING_TO)) {
ASSERT(dataClusterTenantMap.count(metaclusterEntry.renamePair.get()));
} else {
ASSERT(metaclusterEntry.tenantState == TenantState::REGISTERING ||
metaclusterEntry.tenantState == TenantState::REMOVING);
}
ASSERT(metaclusterEntry.tenantState == TenantState::REGISTERING ||
metaclusterEntry.tenantState == TenantState::REMOVING);
} else if (metaclusterEntry.tenantGroup.present()) {
++groupExpectedTenantCounts[metaclusterEntry.tenantGroup.get()];
}
}
}
for (auto [name, entry] : dataClusterTenantMap) {
ASSERT(expectedTenants.count(name));
TenantMapEntry const& metaclusterEntry = self->managementMetadata.tenantMap[name];
for (auto [tenantId, entry] : dataClusterTenantMap) {
ASSERT(expectedTenants.count(tenantId));
TenantMapEntry const& metaclusterEntry = self->managementMetadata.tenantMap[tenantId];
ASSERT(!entry.assignedCluster.present());
ASSERT_EQ(entry.id, metaclusterEntry.id);
ASSERT(entry.tenantName == metaclusterEntry.tenantName);
ASSERT_EQ(entry.tenantState, TenantState::READY);
if (!self->allowPartialMetaclusterOperations) {

View File

@ -45,16 +45,16 @@ private:
struct TenantData {
Optional<MetaclusterRegistrationEntry> metaclusterRegistration;
std::map<TenantName, TenantMapEntry> tenantMap;
std::map<int64_t, TenantName> tenantIdIndex;
std::map<int64_t, TenantMapEntry> tenantMap;
std::map<TenantName, int64_t> tenantNameIndex;
int64_t lastTenantId;
int64_t tenantCount;
std::set<int64_t> tenantTombstones;
Optional<TenantTombstoneCleanupData> tombstoneCleanupData;
std::map<TenantGroupName, TenantGroupEntry> tenantGroupMap;
std::map<TenantGroupName, std::set<TenantName>> tenantGroupIndex;
std::map<TenantGroupName, std::set<int64_t>> tenantGroupIndex;
std::set<TenantName> tenantsInTenantGroupIndex;
std::set<int64_t> tenantsInTenantGroupIndex;
ClusterType clusterType;
};
@ -67,8 +67,8 @@ private:
ACTOR static Future<Void> loadTenantMetadata(TenantConsistencyCheck* self) {
state Reference<typename DB::TransactionT> tr = self->db->createTransaction();
state KeyBackedRangeResult<std::pair<TenantName, TenantMapEntry>> tenantList;
state KeyBackedRangeResult<std::pair<int64_t, TenantName>> tenantIdIndexList;
state KeyBackedRangeResult<std::pair<int64_t, TenantMapEntry>> tenantList;
state KeyBackedRangeResult<std::pair<TenantName, int64_t>> tenantNameIndexList;
state KeyBackedRangeResult<int64_t> tenantTombstoneList;
state KeyBackedRangeResult<std::pair<TenantGroupName, TenantGroupEntry>> tenantGroupList;
state KeyBackedRangeResult<Tuple> tenantGroupTenantTuples;
@ -92,8 +92,8 @@ private:
wait(
store(tenantList, tenantMetadata->tenantMap.getRange(tr, {}, {}, metaclusterMaxTenants)) &&
store(tenantIdIndexList,
tenantMetadata->tenantIdIndex.getRange(tr, {}, {}, metaclusterMaxTenants)) &&
store(tenantNameIndexList,
tenantMetadata->tenantNameIndex.getRange(tr, {}, {}, metaclusterMaxTenants)) &&
store(self->metadata.lastTenantId, tenantMetadata->lastTenantId.getD(tr, Snapshot::False, -1)) &&
store(self->metadata.tenantCount, tenantMetadata->tenantCount.getD(tr, Snapshot::False, 0)) &&
store(tenantTombstoneList,
@ -111,11 +111,11 @@ private:
ASSERT(!tenantList.more);
self->metadata.tenantMap =
std::map<TenantName, TenantMapEntry>(tenantList.results.begin(), tenantList.results.end());
std::map<int64_t, TenantMapEntry>(tenantList.results.begin(), tenantList.results.end());
ASSERT(!tenantIdIndexList.more);
self->metadata.tenantIdIndex =
std::map<int64_t, TenantName>(tenantIdIndexList.results.begin(), tenantIdIndexList.results.end());
ASSERT(!tenantNameIndexList.more);
self->metadata.tenantNameIndex =
std::map<TenantName, int64_t>(tenantNameIndexList.results.begin(), tenantNameIndexList.results.end());
ASSERT(!tenantTombstoneList.more);
self->metadata.tenantTombstones =
@ -128,11 +128,11 @@ private:
for (auto t : tenantGroupTenantTuples.results) {
ASSERT_EQ(t.size(), 2);
TenantGroupName tenantGroupName = t.getString(0);
TenantName tenantName = t.getString(1);
int64_t tenantId = t.getInt(1);
ASSERT(self->metadata.tenantGroupMap.count(tenantGroupName));
ASSERT(self->metadata.tenantMap.count(tenantName));
self->metadata.tenantGroupIndex[tenantGroupName].insert(tenantName);
ASSERT(self->metadata.tenantsInTenantGroupIndex.insert(tenantName).second);
ASSERT(self->metadata.tenantMap.count(tenantId));
self->metadata.tenantGroupIndex[tenantGroupName].insert(tenantId);
ASSERT(self->metadata.tenantsInTenantGroupIndex.insert(tenantId).second);
}
ASSERT_EQ(self->metadata.tenantGroupIndex.size(), self->metadata.tenantGroupMap.size());
@ -147,51 +147,46 @@ private:
}
ASSERT_EQ(metadata.tenantMap.size(), metadata.tenantCount);
ASSERT_EQ(metadata.tenantIdIndex.size(), metadata.tenantCount);
ASSERT_EQ(metadata.tenantNameIndex.size(), metadata.tenantCount);
for (auto [tenantName, tenantMapEntry] : metadata.tenantMap) {
int renameCount = 0;
for (auto [tenantId, tenantMapEntry] : metadata.tenantMap) {
ASSERT_EQ(tenantId, tenantMapEntry.id);
if (metadata.clusterType != ClusterType::METACLUSTER_DATA) {
ASSERT_LE(tenantMapEntry.id, metadata.lastTenantId);
ASSERT_LE(tenantId, metadata.lastTenantId);
}
ASSERT(metadata.tenantIdIndex[tenantMapEntry.id] == tenantName);
ASSERT_EQ(metadata.tenantNameIndex[tenantMapEntry.tenantName], tenantId);
if (tenantMapEntry.tenantGroup.present()) {
auto tenantGroupMapItr = metadata.tenantGroupMap.find(tenantMapEntry.tenantGroup.get());
ASSERT(tenantGroupMapItr != metadata.tenantGroupMap.end());
ASSERT(tenantMapEntry.assignedCluster == tenantGroupMapItr->second.assignedCluster);
ASSERT(metadata.tenantGroupIndex[tenantMapEntry.tenantGroup.get()].count(tenantName));
ASSERT(metadata.tenantGroupIndex[tenantMapEntry.tenantGroup.get()].count(tenantId));
} else {
ASSERT(!metadata.tenantsInTenantGroupIndex.count(tenantName));
ASSERT(!metadata.tenantsInTenantGroupIndex.count(tenantId));
}
if (metadata.clusterType == ClusterType::METACLUSTER_MANAGEMENT) {
ASSERT(tenantMapEntry.assignedCluster.present());
// If the rename pair is present, it should be in the map and match our current entry
if (tenantMapEntry.renamePair.present()) {
auto pairMapEntry = metadata.tenantMap[tenantMapEntry.renamePair.get()];
ASSERT_EQ(pairMapEntry.id, tenantMapEntry.id);
ASSERT(pairMapEntry.prefix == tenantMapEntry.prefix);
ASSERT_EQ(pairMapEntry.configurationSequenceNum, tenantMapEntry.configurationSequenceNum);
ASSERT(pairMapEntry.assignedCluster.present());
ASSERT(pairMapEntry.assignedCluster.get() == tenantMapEntry.assignedCluster.get());
ASSERT(pairMapEntry.renamePair.present());
ASSERT(pairMapEntry.renamePair.get() == tenantName);
if (tenantMapEntry.tenantState == TenantState::RENAMING_FROM) {
ASSERT_EQ(pairMapEntry.tenantState, TenantState::RENAMING_TO);
} else if (tenantMapEntry.tenantState == TenantState::RENAMING_TO) {
ASSERT_EQ(pairMapEntry.tenantState, TenantState::RENAMING_FROM);
} else if (tenantMapEntry.tenantState == TenantState::REMOVING) {
ASSERT_EQ(pairMapEntry.tenantState, TenantState::REMOVING);
} else {
ASSERT(false); // Entry in an invalid state if we have a rename pair
}
if (tenantMapEntry.renameDestination.present()) {
ASSERT(tenantMapEntry.tenantState == TenantState::RENAMING ||
tenantMapEntry.tenantState == TenantState::REMOVING);
auto nameIndexItr = metadata.tenantNameIndex.find(tenantMapEntry.renameDestination.get());
ASSERT(nameIndexItr != metadata.tenantNameIndex.end());
ASSERT_EQ(nameIndexItr->second, tenantMapEntry.id);
++renameCount;
} else {
ASSERT_NE(tenantMapEntry.tenantState, TenantState::RENAMING);
}
} else {
ASSERT_EQ(tenantMapEntry.tenantState, TenantState::READY);
ASSERT(!tenantMapEntry.assignedCluster.present());
ASSERT(!tenantMapEntry.renamePair.present());
ASSERT(!tenantMapEntry.renameDestination.present());
}
}
ASSERT_EQ(metadata.tenantMap.size() + renameCount, metadata.tenantNameIndex.size());
}
// Check that the tenant tombstones are properly cleaned up and only present on a metacluster data cluster

View File

@ -11233,7 +11233,7 @@ ACTOR Future<Void> initTenantMap(StorageServer* self) {
state Version version = wait(tr->getReadVersion());
// This limits the number of tenants, but eventually we shouldn't need to do this at all
// when SSs store only the local tenants
KeyBackedRangeResult<std::pair<TenantName, TenantMapEntry>> entries =
KeyBackedRangeResult<std::pair<int64_t, TenantMapEntry>> entries =
wait(TenantMetadata::tenantMap().getRange(tr, {}, {}, CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER + 1));
ASSERT(entries.results.size() <= CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER && !entries.more);
@ -11241,8 +11241,8 @@ ACTOR Future<Void> initTenantMap(StorageServer* self) {
.detail("Version", version)
.detail("NumTenants", entries.results.size());
for (auto entry : entries.results) {
self->insertTenant(entry.second.prefix, entry.first, version, false);
for (auto const& [_, entry] : entries.results) {
self->insertTenant(entry.prefix, entry.tenantName, version, false);
}
break;
} catch (Error& e) {

View File

@ -128,7 +128,10 @@ struct ThreadData : ReferenceCounted<ThreadData>, NonCopyable {
}
}
void openTenant(Database const& cx) { tenant = makeReference<Tenant>(cx, tenantName); }
Future<Void> openTenant(Database const& cx) {
tenant = makeReference<Tenant>(cx, tenantName);
return tenant->ready();
}
// TODO could make keys variable length?
Key getKey(uint32_t key, uint32_t id) {
@ -282,17 +285,17 @@ struct BlobGranuleCorrectnessWorkload : TestWorkload {
}
state int directoryIdx = 0;
state std::vector<std::pair<TenantName, TenantMapEntry>> tenants;
state std::vector<std::pair<int64_t, TenantMapEntry>> tenants;
state BGTenantMap tenantData(self->dbInfo);
state Reference<GranuleTenantData> data;
for (; directoryIdx < self->directories.size(); directoryIdx++) {
// Set up the blob range first
TenantMapEntry tenantEntry = wait(self->setUpTenant(cx, self->directories[directoryIdx]->tenantName));
self->directories[directoryIdx]->openTenant(cx);
state TenantMapEntry tenantEntry = wait(self->setUpTenant(cx, self->directories[directoryIdx]->tenantName));
wait(self->directories[directoryIdx]->openTenant(cx));
self->directories[directoryIdx]->tenantEntry = tenantEntry;
self->directories[directoryIdx]->directoryRange =
KeyRangeRef(tenantEntry.prefix, tenantEntry.prefix.withSuffix(normalKeys.end));
tenants.push_back({ self->directories[directoryIdx]->tenant->name.get(), tenantEntry });
tenants.push_back({ self->directories[directoryIdx]->tenant->id(), tenantEntry });
bool _success = wait(cx->blobbifyRange(self->directories[directoryIdx]->directoryRange));
ASSERT(_success);
}

View File

@ -424,6 +424,7 @@ struct MetaclusterManagementWorkload : TestWorkload {
ACTOR static Future<Void> createTenant(MetaclusterManagementWorkload* self) {
state TenantName tenant = self->chooseTenantName();
state Optional<TenantGroupName> tenantGroup = self->chooseTenantGroup();
state AssignClusterAutomatically assignClusterAutomatically(deterministicRandom()->coinflip());
auto itr = self->createdTenants.find(tenant);
state bool exists = itr != self->createdTenants.end();
@ -431,24 +432,24 @@ struct MetaclusterManagementWorkload : TestWorkload {
state bool hasCapacity = tenantGroupExists || self->ungroupedTenants.size() + self->tenantGroups.size() <
self->totalTenantGroupCapacity;
state bool retried = false;
state bool preferAssignedCluster = deterministicRandom()->coinflip();
// Choose between two preferred clusters because if we get a partial completion and
// retry, we want the operation to eventually succeed instead of having a chance of
// never re-visiting the original preferred cluster.
state std::pair<ClusterName, ClusterName> preferredClusters;
state Optional<ClusterName> originalPreferredCluster;
if (preferAssignedCluster) {
if (!assignClusterAutomatically) {
preferredClusters.first = self->chooseClusterName();
preferredClusters.second = self->chooseClusterName();
}
state TenantMapEntry tenantMapEntry;
tenantMapEntry.tenantName = tenant;
tenantMapEntry.tenantGroup = tenantGroup;
try {
loop {
try {
if (preferAssignedCluster && (!retried || deterministicRandom()->coinflip())) {
if (!assignClusterAutomatically && (!retried || deterministicRandom()->coinflip())) {
tenantMapEntry.assignedCluster =
deterministicRandom()->coinflip() ? preferredClusters.first : preferredClusters.second;
if (!originalPreferredCluster.present()) {
@ -456,7 +457,7 @@ struct MetaclusterManagementWorkload : TestWorkload {
}
}
Future<Void> createFuture =
MetaclusterAPI::createTenant(self->managementDb, tenant, tenantMapEntry);
MetaclusterAPI::createTenant(self->managementDb, tenantMapEntry, assignClusterAutomatically);
Optional<Void> result = wait(timeout(createFuture, deterministicRandom()->randomInt(1, 30)));
if (result.present()) {
break;
@ -470,7 +471,7 @@ struct MetaclusterManagementWorkload : TestWorkload {
ASSERT(entry.present());
tenantMapEntry = entry.get();
break;
} else if (preferAssignedCluster && retried &&
} else if (!assignClusterAutomatically && retried &&
originalPreferredCluster.get() != tenantMapEntry.assignedCluster.get() &&
(e.code() == error_code_cluster_no_capacity ||
e.code() == error_code_cluster_not_found ||
@ -503,7 +504,7 @@ struct MetaclusterManagementWorkload : TestWorkload {
}
auto assignedCluster = self->dataDbs.find(entry.assignedCluster.get());
ASSERT(!preferAssignedCluster || tenantMapEntry.assignedCluster.get() == assignedCluster->first);
ASSERT(assignClusterAutomatically || tenantMapEntry.assignedCluster.get() == assignedCluster->first);
ASSERT(assignedCluster != self->dataDbs.end());
ASSERT(assignedCluster->second.tenants.insert(tenant).second);
@ -526,10 +527,10 @@ struct MetaclusterManagementWorkload : TestWorkload {
ASSERT(!hasCapacity && !exists);
return Void();
} else if (e.code() == error_code_cluster_no_capacity) {
ASSERT(preferAssignedCluster);
ASSERT(!assignClusterAutomatically);
return Void();
} else if (e.code() == error_code_cluster_not_found) {
ASSERT(preferAssignedCluster);
ASSERT(!assignClusterAutomatically);
return Void();
} else if (e.code() == error_code_invalid_tenant_configuration) {
ASSERT(tenantGroup.present());

View File

@ -34,9 +34,8 @@
#include "flow/actorcompiler.h" // This must be the last #include.
namespace {
TenantEntryCachePayload<int64_t> createPayload(const TenantName& name, const TenantMapEntry& entry) {
TenantEntryCachePayload<int64_t> createPayload(const TenantMapEntry& entry) {
TenantEntryCachePayload<int64_t> payload;
payload.name = name;
payload.entry = entry;
payload.payload = entry.id;
@ -66,16 +65,16 @@ struct TenantEntryCacheWorkload : TestWorkload {
ASSERT_EQ(left.get().payload, right.id);
}
ACTOR static Future<Void> compareContents(std::vector<std::pair<TenantName, TenantMapEntry>>* tenants,
ACTOR static Future<Void> compareContents(std::vector<TenantMapEntry>* tenants,
Reference<TenantEntryCache<int64_t>> cache) {
state int i;
for (i = 0; i < tenants->size(); i++) {
if (deterministicRandom()->coinflip()) {
Optional<TenantEntryCachePayload<int64_t>> e = wait(cache->getById(tenants->at(i).second.id));
compareTenants(e, tenants->at(i).second);
Optional<TenantEntryCachePayload<int64_t>> e = wait(cache->getById(tenants->at(i).id));
compareTenants(e, tenants->at(i));
} else {
Optional<TenantEntryCachePayload<int64_t>> e = wait(cache->getByName(tenants->at(i).first));
compareTenants(e, tenants->at(i).second);
Optional<TenantEntryCachePayload<int64_t>> e = wait(cache->getByName(tenants->at(i).tenantName));
compareTenants(e, tenants->at(i));
}
}
@ -104,7 +103,7 @@ struct TenantEntryCacheWorkload : TestWorkload {
ACTOR static Future<Void> testCreateTenantsAndLookup(Database cx,
TenantEntryCacheWorkload* self,
std::vector<std::pair<TenantName, TenantMapEntry>>* tenantList,
std::vector<TenantMapEntry>* tenantList,
TenantEntryCacheRefreshMode refreshMode) {
state Reference<TenantEntryCache<int64_t>> cache = makeReference<TenantEntryCache<int64_t>>(
cx, deterministicRandom()->randomUniqueID(), createPayload, refreshMode);
@ -128,9 +127,9 @@ struct TenantEntryCacheWorkload : TestWorkload {
continue;
}
Optional<TenantMapEntry> entry = wait(TenantAPI::createTenant(cx.getReference(), StringRef(name)));
Optional<TenantMapEntry> entry = wait(TenantAPI::createTenant(cx.getReference(), name));
ASSERT(entry.present());
tenantList->emplace_back(std::make_pair(name, entry.get()));
tenantList->emplace_back(entry.get());
tenantNames.emplace(name);
i++;
}
@ -143,7 +142,7 @@ struct TenantEntryCacheWorkload : TestWorkload {
ACTOR static Future<Void> testTenantInsert(Database cx,
TenantEntryCacheWorkload* self,
std::vector<std::pair<TenantName, TenantMapEntry>>* tenantList,
std::vector<TenantMapEntry>* tenantList,
TenantEntryCacheRefreshMode refreshMode) {
state Reference<TenantEntryCache<int64_t>> cache = makeReference<TenantEntryCache<int64_t>>(
cx, deterministicRandom()->randomUniqueID(), createPayload, refreshMode);
@ -157,42 +156,44 @@ struct TenantEntryCacheWorkload : TestWorkload {
ASSERT_EQ(cache->numRefreshByInit(), 1);
ASSERT_GE(cache->numCacheRefreshes(), 1);
state std::pair<TenantName, TenantMapEntry> p = tenantList->at(0);
state TenantMapEntry p = tenantList->at(0);
state Optional<TenantEntryCachePayload<int64_t>> entry;
// Tenant rename
p.first = TenantName(format("%s%08d",
self->localTenantNamePrefix.toString().c_str(),
deterministicRandom()->randomInt(self->maxTenants + 100, self->maxTenants + 200)));
p.tenantName =
TenantName(format("%s%08d",
self->localTenantNamePrefix.toString().c_str(),
deterministicRandom()->randomInt(self->maxTenants + 100, self->maxTenants + 200)));
cache->put(p);
Optional<TenantEntryCachePayload<int64_t>> e = wait(cache->getByName(p.first));
Optional<TenantEntryCachePayload<int64_t>> e = wait(cache->getByName(p.tenantName));
entry = e;
compareTenants(entry, p.second);
compareTenants(entry, p);
// Tenant delete & recreate
p.second.id = p.second.id + deterministicRandom()->randomInt(self->maxTenants + 500, self->maxTenants + 700);
p.id = p.id + deterministicRandom()->randomInt(self->maxTenants + 500, self->maxTenants + 700);
cache->put(p);
Optional<TenantEntryCachePayload<int64_t>> e1 = wait(cache->getById(p.second.id));
Optional<TenantEntryCachePayload<int64_t>> e1 = wait(cache->getById(p.id));
entry = e1;
compareTenants(entry, p.second);
ASSERT_EQ(p.first.contents().toString().compare(entry.get().name.contents().toString()), 0);
compareTenants(entry, p);
ASSERT_EQ(p.tenantName.compare(entry.get().entry.tenantName), 0);
// Delete a tenant and rename an existing TenantEntry to reuse the name of deleted tenant
state std::pair<TenantName, TenantMapEntry> p1 = tenantList->back();
state TenantMapEntry p1 = tenantList->back();
tenantList->pop_back();
wait(TenantAPI::deleteTenant(cx.getReference(), p1.first));
cache->put(std::make_pair(p1.first, p.second));
Optional<TenantEntryCachePayload<int64_t>> e2 = wait(cache->getById(p.second.id));
wait(TenantAPI::deleteTenant(cx.getReference(), p1.tenantName));
p.tenantName = p1.tenantName;
cache->put(p);
Optional<TenantEntryCachePayload<int64_t>> e2 = wait(cache->getById(p.id));
entry = e2;
compareTenants(entry, p.second);
ASSERT_EQ(p1.first.contents().toString().compare(entry.get().name.contents().toString()), 0);
compareTenants(entry, p);
ASSERT_EQ(p1.tenantName.compare(entry.get().entry.tenantName), 0);
TraceEvent("TestTenantInsertEnd");
return Void();
}
ACTOR static Future<Void> testCacheReload(Database cx,
std::vector<std::pair<TenantName, TenantMapEntry>>* tenantList,
std::vector<TenantMapEntry>* tenantList,
TenantEntryCacheRefreshMode refreshMode) {
state Reference<TenantEntryCache<int64_t>> cache = makeReference<TenantEntryCache<int64_t>>(
cx, deterministicRandom()->randomUniqueID(), createPayload, refreshMode);
@ -270,8 +271,7 @@ struct TenantEntryCacheWorkload : TestWorkload {
return Void();
}
ACTOR static Future<Void> tenantEntryRemove(Database cx,
std::vector<std::pair<TenantName, TenantMapEntry>>* tenantList) {
ACTOR static Future<Void> tenantEntryRemove(Database cx, std::vector<TenantMapEntry>* tenantList) {
state Reference<TenantEntryCache<int64_t>> cache = makeReference<TenantEntryCache<int64_t>>(
cx, deterministicRandom()->randomUniqueID(), createPayload, TenantEntryCacheRefreshMode::NONE);
@ -281,35 +281,34 @@ struct TenantEntryCacheWorkload : TestWorkload {
// Remove an entry from the cache
state int idx = deterministicRandom()->randomInt(0, tenantList->size());
Optional<TenantEntryCachePayload<int64_t>> entry = wait(cache->getByName(tenantList->at(idx).first));
Optional<TenantEntryCachePayload<int64_t>> entry = wait(cache->getByName(tenantList->at(idx).tenantName));
ASSERT(entry.present());
TraceEvent("TestTenantEntryRemoveStart")
.detail("Id", tenantList->at(idx).second.id)
.detail("Name", tenantList->at(idx).first)
.detail("Prefix", tenantList->at(idx).second.prefix);
.detail("Id", tenantList->at(idx).id)
.detail("Name", tenantList->at(idx).tenantName)
.detail("Prefix", tenantList->at(idx).prefix);
wait(TenantAPI::deleteTenant(cx.getReference(), tenantList->at(idx).first));
wait(TenantAPI::deleteTenant(cx.getReference(), tenantList->at(idx).tenantName));
if (deterministicRandom()->coinflip()) {
wait(cache->removeEntryById(tenantList->at(idx).second.id));
wait(cache->removeEntryById(tenantList->at(idx).id));
} else if (deterministicRandom()->coinflip()) {
wait(cache->removeEntryByPrefix(tenantList->at(idx).second.prefix));
wait(cache->removeEntryByPrefix(tenantList->at(idx).prefix));
} else {
wait(cache->removeEntryByName(tenantList->at(idx).first));
wait(cache->removeEntryByName(tenantList->at(idx).tenantName));
}
state Optional<TenantEntryCachePayload<int64_t>> e = wait(cache->getById(tenantList->at(idx).second.id));
state Optional<TenantEntryCachePayload<int64_t>> e = wait(cache->getById(tenantList->at(idx).id));
ASSERT(!e.present());
state Optional<TenantEntryCachePayload<int64_t>> e1 =
wait(cache->getByPrefix(tenantList->at(idx).second.prefix));
state Optional<TenantEntryCachePayload<int64_t>> e1 = wait(cache->getByPrefix(tenantList->at(idx).prefix));
ASSERT(!e1.present());
state Optional<TenantEntryCachePayload<int64_t>> e2 = wait(cache->getByName(tenantList->at(idx).first));
state Optional<TenantEntryCachePayload<int64_t>> e2 = wait(cache->getByName(tenantList->at(idx).tenantName));
ASSERT(!e2.present());
// Ensure remove-entry is an idempotent operation
cache->removeEntryByName(tenantList->at(idx).first);
Optional<TenantEntryCachePayload<int64_t>> e3 = wait(cache->getById(tenantList->at(idx).second.id));
cache->removeEntryByName(tenantList->at(idx).tenantName);
Optional<TenantEntryCachePayload<int64_t>> e3 = wait(cache->getById(tenantList->at(idx).id));
ASSERT(!e3.present());
return Void();
@ -365,7 +364,7 @@ struct TenantEntryCacheWorkload : TestWorkload {
}
ACTOR Future<Void> _start(Database cx, TenantEntryCacheWorkload* self) {
state std::vector<std::pair<TenantName, TenantMapEntry>> tenantList;
state std::vector<TenantMapEntry> tenantList;
state TenantEntryCacheRefreshMode refreshMode;
if (deterministicRandom()->coinflip()) {
refreshMode = TenantEntryCacheRefreshMode::PERIODIC_TASK;

View File

@ -171,13 +171,15 @@ struct TenantManagementConcurrencyWorkload : TestWorkload {
ACTOR static Future<Void> createTenant(TenantManagementConcurrencyWorkload* self) {
state TenantName tenant = self->chooseTenantName();
state TenantMapEntry entry;
entry.tenantName = tenant;
entry.tenantGroup = self->chooseTenantGroup();
try {
loop {
Future<Void> createFuture =
self->useMetacluster ? MetaclusterAPI::createTenant(self->mvDb, tenant, entry)
: success(TenantAPI::createTenant(self->dataDb.getReference(), tenant, entry));
self->useMetacluster
? MetaclusterAPI::createTenant(self->mvDb, entry, AssignClusterAutomatically::True)
: success(TenantAPI::createTenant(self->dataDb.getReference(), tenant, entry));
Optional<Void> result = wait(timeout(createFuture, 30));
if (result.present()) {
break;
@ -236,7 +238,7 @@ struct TenantManagementConcurrencyWorkload : TestWorkload {
for (auto param : configParams) {
updatedEntry.configure(param.first, param.second);
}
wait(TenantAPI::configureTenantTransaction(tr, tenant, entry, updatedEntry));
wait(TenantAPI::configureTenantTransaction(tr, entry, updatedEntry));
wait(buggifiedCommit(tr, BUGGIFY_WITH_PROB(0.1)));
break;
} catch (Error& e) {

View File

@ -26,6 +26,7 @@
#include "fdbclient/FDBTypes.h"
#include "fdbclient/GenericManagementAPI.actor.h"
#include "fdbclient/KeyBackedTypes.h"
#include "fdbclient/KeyRangeMap.h"
#include "fdbclient/MetaclusterManagement.actor.h"
#include "fdbclient/ReadYourWrites.h"
#include "fdbclient/RunRYWTransaction.actor.h"
@ -386,7 +387,7 @@ struct TenantManagementWorkload : TestWorkload {
std::vector<Future<Void>> createFutures;
for (auto [tenant, entry] : tenantsToCreate) {
entry.setId(nextId++);
createFutures.push_back(success(TenantAPI::createTenantTransaction(tr, tenant, entry)));
createFutures.push_back(success(TenantAPI::createTenantTransaction(tr, entry)));
}
TenantMetadata::lastTenantId().set(tr, nextId - 1);
wait(waitForAll(createFutures));
@ -397,7 +398,7 @@ struct TenantManagementWorkload : TestWorkload {
tenantsToCreate.begin()->second.assignedCluster = self->dataClusterName;
}
wait(MetaclusterAPI::createTenant(
self->mvDb, tenantsToCreate.begin()->first, tenantsToCreate.begin()->second));
self->mvDb, tenantsToCreate.begin()->second, AssignClusterAutomatically::True));
}
return Void();
@ -431,6 +432,7 @@ struct TenantManagementWorkload : TestWorkload {
}
TenantMapEntry entry;
entry.tenantName = tenant;
entry.tenantGroup = self->chooseTenantGroup(true);
if (self->createdTenants.count(tenant)) {
@ -704,10 +706,9 @@ struct TenantManagementWorkload : TestWorkload {
ACTOR static Future<Void> deleteTenantImpl(Reference<ReadYourWritesTransaction> tr,
TenantName beginTenant,
Optional<TenantName> endTenant,
std::vector<TenantName> tenants,
std::map<TenantName, int64_t> tenants,
OperationType operationType,
TenantManagementWorkload* self) {
state int tenantIndex;
if (operationType == OperationType::SPECIAL_KEYS) {
tr->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
Key key = self->specialKeysTenantMapPrefix.withSuffix(beginTenant);
@ -723,8 +724,10 @@ struct TenantManagementWorkload : TestWorkload {
} else if (operationType == OperationType::MANAGEMENT_TRANSACTION) {
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
std::vector<Future<Void>> deleteFutures;
for (tenantIndex = 0; tenantIndex != tenants.size(); ++tenantIndex) {
deleteFutures.push_back(TenantAPI::deleteTenantTransaction(tr, tenants[tenantIndex]));
for (auto const& [name, id] : tenants) {
if (id != TenantInfo::INVALID_TENANT) {
deleteFutures.push_back(TenantAPI::deleteTenantTransaction(tr, id));
}
}
wait(waitForAll(deleteFutures));
@ -782,38 +785,38 @@ struct TenantManagementWorkload : TestWorkload {
state bool anyExists = alreadyExists;
// Collect a list of all tenants that we expect should be deleted by this operation
state std::vector<TenantName> tenants;
state std::map<TenantName, int64_t> tenants;
if (!endTenant.present()) {
tenants.push_back(beginTenant);
tenants[beginTenant] = anyExists ? itr->second.tenant->id() : TenantInfo::INVALID_TENANT;
} else if (endTenant.present()) {
for (auto itr = self->createdTenants.lower_bound(beginTenant);
itr != self->createdTenants.end() && itr->first < endTenant.get();
++itr) {
tenants.push_back(itr->first);
tenants[itr->first] = itr->second.tenant->id();
anyExists = true;
}
}
// Check whether each tenant is empty.
state int tenantIndex;
state std::map<TenantName, int64_t>::iterator tenantItr;
state std::vector<std::pair<TenantName, Future<ErrorOr<Void>>>> watchFutures;
try {
if (alreadyExists || endTenant.present()) {
for (tenantIndex = 0; tenantIndex < tenants.size(); ++tenantIndex) {
for (tenantItr = tenants.begin(); tenantItr != tenants.end(); ++tenantItr) {
// For most tenants, we will delete the contents and make them empty
if (deterministicRandom()->random01() < 0.9) {
wait(clearTenantData(self, tenants[tenantIndex]));
wait(clearTenantData(self, tenantItr->first));
// watch the tenant to be deleted
if (watchTenantCheck) {
watchFutures.emplace_back(
tenants[tenantIndex],
errorOr(watchTenant(self, self->createdTenants[tenants[tenantIndex]].tenant)));
tenantItr->first,
errorOr(watchTenant(self, self->createdTenants[tenantItr->first].tenant)));
}
}
// Otherwise, we will just report the current emptiness of the tenant
else {
auto itr = self->createdTenants.find(tenants[tenantIndex]);
auto itr = self->createdTenants.find(tenantItr->first);
ASSERT(itr != self->createdTenants.end());
isEmpty = isEmpty && itr->second.empty;
}
@ -881,7 +884,7 @@ struct TenantManagementWorkload : TestWorkload {
if (!tenants.empty()) {
// Check the state of the first deleted tenant
Optional<TenantMapEntry> resultEntry =
wait(self->tryGetTenant(*tenants.begin(), operationType));
wait(self->tryGetTenant(tenants.begin()->first, operationType));
if (!resultEntry.present()) {
alreadyExists = false;
@ -893,9 +896,8 @@ struct TenantManagementWorkload : TestWorkload {
}
}
// The management transaction operation is a no-op if there are no tenants to delete in a range
// delete
if (tenants.size() == 0 && operationType == OperationType::MANAGEMENT_TRANSACTION) {
// The management transaction operation is a no-op if there are no tenants to delete
if (!anyExists && operationType == OperationType::MANAGEMENT_TRANSACTION) {
return Void();
}
@ -928,8 +930,8 @@ struct TenantManagementWorkload : TestWorkload {
}
// Update our local state to remove the deleted tenants
for (auto tenant : tenants) {
auto itr = self->createdTenants.find(tenant);
for (auto const& [name, id] : tenants) {
auto itr = self->createdTenants.find(name);
ASSERT(itr != self->createdTenants.end());
// If the tenant group has no tenants remaining, stop tracking it
@ -941,7 +943,7 @@ struct TenantManagementWorkload : TestWorkload {
}
}
self->createdTenants.erase(tenant);
self->createdTenants.erase(name);
}
// check for watch result
@ -1677,7 +1679,7 @@ struct TenantManagementWorkload : TestWorkload {
ASSERT(tenantGroups.size() <= limit);
// Compare the resulting tenant list to the list we expected to get
// Compare the resulting tenant group list to the list we expected to get
auto localItr = self->createdTenantGroups.lower_bound(beginTenantGroup);
auto tenantMapItr = tenantGroups.begin();
for (; tenantMapItr != tenantGroups.end(); ++tenantMapItr, ++localItr) {