fix merge conflict

This commit is contained in:
Nim Wijetunga 2022-07-27 10:58:41 -07:00
commit 2cd73242de
11 changed files with 115 additions and 34 deletions

View File

@ -22,6 +22,7 @@
#include "fdbclient/FDBTypes.h"
#include "fdbclient/SystemData.h"
#include "fdbclient/Tenant.h"
#include "flow/IRandom.h"
#include "flow/UnitTest.h"
#define init(...) KNOB_FN(__VA_ARGS__, INIT_ATOMIC_KNOB, INIT_KNOB)(__VA_ARGS__)
@ -289,6 +290,9 @@ void ClientKnobs::initialize(Randomize randomize) {
init( CHANGE_QUORUM_BAD_STATE_RETRY_TIMES, 3 );
init( CHANGE_QUORUM_BAD_STATE_RETRY_DELAY, 2.0 );
// Tenants and Metacluster
init( MAX_TENANTS_PER_CLUSTER, 1e6 ); if ( randomize && BUGGIFY ) MAX_TENANTS_PER_CLUSTER = deterministicRandom()->randomInt(20, 100);
// clang-format on
}

View File

@ -284,6 +284,9 @@ public:
int CHANGE_QUORUM_BAD_STATE_RETRY_TIMES;
double CHANGE_QUORUM_BAD_STATE_RETRY_DELAY;
// Tenants and Metacluster
int MAX_TENANTS_PER_CLUSTER;
ClientKnobs(Randomize randomize);
void initialize(Randomize randomize);
};

View File

@ -100,12 +100,13 @@ struct TenantMetadataSpecification {
KeyBackedObjectMap<TenantName, TenantMapEntry, decltype(IncludeVersion()), NullCodec> tenantMap;
KeyBackedProperty<int64_t> lastTenantId;
KeyBackedBinaryValue<int64_t> tenantCount;
KeyBackedSet<Tuple> tenantGroupTenantIndex;
KeyBackedObjectMap<TenantGroupName, TenantGroupEntry, decltype(IncludeVersion()), NullCodec> tenantGroupMap;
TenantMetadataSpecification(KeyRef subspace)
: tenantMap(subspace.withSuffix("tenant/map/"_sr), IncludeVersion()),
lastTenantId(subspace.withSuffix("tenant/lastId"_sr)),
lastTenantId(subspace.withSuffix("tenant/lastId"_sr)), tenantCount(subspace.withSuffix("tenant/count"_sr)),
tenantGroupTenantIndex(subspace.withSuffix("tenant/tenantGroup/tenantIndex/"_sr)),
tenantGroupMap(subspace.withSuffix("tenant/tenantGroup/map/"_sr), IncludeVersion()) {}
};
@ -117,6 +118,7 @@ private:
public:
static inline auto& tenantMap = instance.tenantMap;
static inline auto& lastTenantId = instance.lastTenantId;
static inline auto& tenantCount = instance.tenantCount;
static inline auto& tenantGroupTenantIndex = instance.tenantGroupTenantIndex;
static inline auto& tenantGroupMap = instance.tenantGroupMap;

View File

@ -19,6 +19,7 @@
*/
#pragma once
#include "fdbclient/ClientBooleanParams.h"
#include "flow/IRandom.h"
#if defined(NO_INTELLISENSE) && !defined(FDBCLIENT_TENANT_MANAGEMENT_ACTOR_G_H)
#define FDBCLIENT_TENANT_MANAGEMENT_ACTOR_G_H
@ -140,6 +141,16 @@ Future<std::pair<Optional<TenantMapEntry>, bool>> createTenantTransaction(Transa
}
}
// This is idempotent because we only add an entry to the tenant map if it isn't already there
TenantMetadata::tenantCount.atomicOp(tr, 1, MutationRef::AddValue);
// Read the tenant count after incrementing the counter so that simultaneous attempts to create
// tenants in the same transaction are properly reflected.
int64_t tenantCount = wait(TenantMetadata::tenantCount.getD(tr, Snapshot::False, 0));
if (tenantCount > CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER) {
throw cluster_no_capacity();
}
return std::make_pair(tenantEntry, true);
}
@ -232,7 +243,10 @@ Future<Void> deleteTenantTransaction(Transaction tr,
throw tenant_not_empty();
}
// This is idempotent because we only erase an entry from the tenant map if it is present
TenantMetadata::tenantMap.erase(tr, name);
TenantMetadata::tenantCount.atomicOp(tr, -1, MutationRef::AddValue);
if (tenantEntry.get().tenantGroup.present()) {
TenantMetadata::tenantGroupTenantIndex.erase(tr,
Tuple::makeTuple(tenantEntry.get().tenantGroup.get(), name));

View File

@ -107,7 +107,8 @@ private:
return results;
}
ACTOR static Future<Void> createTenant(
// Returns true if the tenant was created, false if it already existed
ACTOR static Future<bool> createTenant(
ReadYourWritesTransaction* ryw,
TenantNameRef tenantName,
std::vector<std::pair<Standalone<StringRef>, Optional<Value>>> configMutations,
@ -128,23 +129,39 @@ private:
std::pair<Optional<TenantMapEntry>, bool> entry =
wait(TenantAPI::createTenantTransaction(&ryw->getTransaction(), tenantName, tenantEntry));
return Void();
return entry.second;
}
ACTOR static Future<Void> createTenants(
ReadYourWritesTransaction* ryw,
std::map<TenantName, std::vector<std::pair<Standalone<StringRef>, Optional<Value>>>> tenants,
std::map<TenantGroupName, int>* tenantGroupNetTenantDelta) {
state Future<int64_t> tenantCountFuture =
TenantMetadata::tenantCount.getD(&ryw->getTransaction(), Snapshot::False, 0);
int64_t _nextId = wait(TenantAPI::getNextTenantId(&ryw->getTransaction()));
int64_t nextId = _nextId;
state int64_t nextId = _nextId;
std::vector<Future<Void>> createFutures;
state std::vector<Future<bool>> createFutures;
for (auto const& [tenant, config] : tenants) {
createFutures.push_back(createTenant(ryw, tenant, config, nextId++, tenantGroupNetTenantDelta));
}
TenantMetadata::lastTenantId.set(&ryw->getTransaction(), nextId - 1);
wait(waitForAll(createFutures));
state int numCreatedTenants = 0;
for (auto f : createFutures) {
if (f.get()) {
++numCreatedTenants;
}
}
// Check the tenant count here rather than rely on the createTenantTransaction check because we don't have RYW
int64_t tenantCount = wait(tenantCountFuture);
if (tenantCount + numCreatedTenants > CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER) {
throw cluster_no_capacity();
}
return Void();
}

View File

@ -967,19 +967,12 @@ ACTOR Future<Void> monitorClientRanges(Reference<BlobManagerData> bmData) {
} else {
state KeyBackedRangeResult<std::pair<TenantName, TenantMapEntry>> tenantResults;
wait(store(tenantResults,
TenantMetadata::tenantMap.getRange(
tr, Optional<TenantName>(), Optional<TenantName>(), CLIENT_KNOBS->TOO_MANY)));
ASSERT_WE_THINK(!tenantResults.more && tenantResults.results.size() < CLIENT_KNOBS->TOO_MANY);
if (tenantResults.more || tenantResults.results.size() >= CLIENT_KNOBS->TOO_MANY) {
TraceEvent(SevError, "BlobManagerTooManyTenants", bmData->id)
.detail("Epoch", bmData->epoch)
.detail("TenantCount", tenantResults.results.size());
wait(delay(600));
if (bmData->iAmReplaced.canBeSet()) {
bmData->iAmReplaced.sendError(internal_error());
}
throw internal_error();
}
TenantMetadata::tenantMap.getRange(tr,
Optional<TenantName>(),
Optional<TenantName>(),
CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER + 1)));
ASSERT(tenantResults.results.size() <= CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER &&
!tenantResults.more);
std::vector<Key> prefixes;
for (auto& it : tenantResults.results) {

View File

@ -3999,18 +3999,11 @@ ACTOR Future<Void> monitorTenants(Reference<BlobWorkerData> bwData) {
tr->setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE);
state KeyBackedRangeResult<std::pair<TenantName, TenantMapEntry>> tenantResults;
wait(store(tenantResults,
TenantMetadata::tenantMap.getRange(
tr, Optional<TenantName>(), Optional<TenantName>(), CLIENT_KNOBS->TOO_MANY)));
ASSERT_WE_THINK(!tenantResults.more && tenantResults.results.size() < CLIENT_KNOBS->TOO_MANY);
if (tenantResults.more || tenantResults.results.size() >= CLIENT_KNOBS->TOO_MANY) {
TraceEvent(SevError, "BlobWorkerTooManyTenants", bwData->id)
.detail("TenantCount", tenantResults.results.size());
wait(delay(600));
if (bwData->fatalError.canBeSet()) {
bwData->fatalError.sendError(internal_error());
}
throw internal_error();
}
TenantMetadata::tenantMap.getRange(tr,
Optional<TenantName>(),
Optional<TenantName>(),
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;
for (auto& it : tenantResults.results) {

View File

@ -33,8 +33,8 @@ class TenantCacheImpl {
KeyBackedRangeResult<std::pair<TenantName, TenantMapEntry>> tenantList =
wait(TenantMetadata::tenantMap.getRange(
tr, Optional<TenantName>(), Optional<TenantName>(), CLIENT_KNOBS->TOO_MANY));
ASSERT(!tenantList.more && tenantList.results.size() < CLIENT_KNOBS->TOO_MANY);
tr, Optional<TenantName>(), Optional<TenantName>(), CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER + 1));
ASSERT(tenantList.results.size() <= CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER && !tenantList.more);
return tenantList.results;
}

View File

@ -10127,7 +10127,8 @@ ACTOR Future<Void> initTenantMap(StorageServer* self) {
// when SSs store only the local tenants
KeyBackedRangeResult<std::pair<TenantName, TenantMapEntry>> entries =
wait(TenantMetadata::tenantMap.getRange(
tr, Optional<TenantName>(), Optional<TenantName>(), CLIENT_KNOBS->TOO_MANY));
tr, Optional<TenantName>(), Optional<TenantName>(), CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER + 1));
ASSERT(entries.results.size() <= CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER && !entries.more);
TraceEvent("InitTenantMap", self->thisServerID)
.detail("Version", version)

View File

@ -20,7 +20,9 @@
#include <cstdint>
#include <limits>
#include "fdbclient/ClientBooleanParams.h"
#include "fdbclient/FDBOptions.g.h"
#include "fdbclient/ReadYourWrites.h"
#include "fdbclient/RunTransaction.actor.h"
#include "fdbclient/TenantManagement.actor.h"
#include "fdbclient/TenantSpecialKeys.actor.h"
@ -195,6 +197,7 @@ struct TenantManagementWorkload : TestWorkload {
// True if any tenant group name starts with \xff
state bool hasSystemTenantGroup = false;
state int newTenants = 0;
state std::map<TenantName, TenantMapEntry> tenantsToCreate;
for (int i = 0; i < numTenants; ++i) {
TenantName tenant = self->chooseTenantName(true);
@ -211,19 +214,38 @@ struct TenantManagementWorkload : TestWorkload {
}
tenantsToCreate[tenant] = entry;
alreadyExists = alreadyExists || self->createdTenants.count(tenant);
if (self->createdTenants.count(tenant)) {
alreadyExists = true;
} else if (!tenantsToCreate.count(tenant)) {
++newTenants;
}
tenantsToCreate[tenant] = entry;
hasSystemTenant = hasSystemTenant || tenant.startsWith("\xff"_sr);
hasSystemTenantGroup = hasSystemTenantGroup || entry.tenantGroup.orDefault(""_sr).startsWith("\xff"_sr);
}
state Reference<ReadYourWritesTransaction> tr = makeReference<ReadYourWritesTransaction>(cx);
state int64_t minTenantCount = std::numeric_limits<int64_t>::max();
state int64_t finalTenantCount = 0;
loop {
try {
if (operationType != OperationType::MANAGEMENT_DATABASE) {
tr->setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
wait(store(finalTenantCount, TenantMetadata::tenantCount.getD(tr, Snapshot::False, 0)));
minTenantCount = std::min(finalTenantCount, minTenantCount);
}
wait(createImpl(cx, tr, tenantsToCreate, operationType, self));
if (operationType == OperationType::MANAGEMENT_DATABASE) {
ASSERT(!alreadyExists);
} else {
// Make sure that we had capacity to create the tenants. This cannot be validated for database
// operations because we cannot determine the tenant count in the same transaction that the tenant
// is created
ASSERT(minTenantCount + newTenants <= CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER);
}
// It is not legal to create a tenant or tenant group starting with \xff
@ -300,6 +322,13 @@ struct TenantManagementWorkload : TestWorkload {
} else if (e.code() == error_code_invalid_tenant_group_name) {
ASSERT(hasSystemTenantGroup);
return Void();
} else if (e.code() == error_code_cluster_no_capacity) {
// Confirm that we overshot our capacity. This check cannot be done for database operations
// because we cannot transactionally get the tenant count with the creation.
if (operationType != OperationType::MANAGEMENT_DATABASE) {
ASSERT(finalTenantCount + newTenants > CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER);
}
return Void();
}
// Database-based operations should not need to be retried
@ -980,6 +1009,26 @@ struct TenantManagementWorkload : TestWorkload {
return Void();
}
// Verify that the tenant count matches the actual number of tenants in the cluster and that we haven't created too
// many
ACTOR static Future<Void> checkTenantCount(Database cx) {
state Reference<ReadYourWritesTransaction> tr = cx->createTransaction();
loop {
try {
tr->setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
state int64_t tenantCount = wait(TenantMetadata::tenantCount.getD(tr, Snapshot::False, 0));
KeyBackedRangeResult<std::pair<TenantName, TenantMapEntry>> tenants =
wait(TenantMetadata::tenantMap.getRange(tr, {}, {}, CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER + 1));
ASSERT(tenants.results.size() == tenantCount && !tenants.more);
ASSERT(tenantCount <= CLIENT_KNOBS->MAX_TENANTS_PER_CLUSTER);
return Void();
} catch (Error& e) {
wait(tr->onError(e));
}
}
}
// Verify that the set of tenants in the database matches our local state
ACTOR static Future<Void> compareTenants(Database cx, TenantManagementWorkload* self) {
state std::map<TenantName, TenantData>::iterator localItr = self->createdTenants.begin();
@ -1103,6 +1152,10 @@ struct TenantManagementWorkload : TestWorkload {
}
}
if (self->clientId == 0) {
wait(checkTenantCount(cx));
}
wait(compareTenants(cx, self) && compareTenantGroups(cx, self));
return true;
}

View File

@ -235,6 +235,7 @@ ERROR( unknown_tenant, 2137, "Tenant is not available from this server" )
ERROR( illegal_tenant_access, 2138, "Illegal tenant access" )
ERROR( invalid_tenant_group_name, 2139, "Tenant group name cannot begin with \\xff" )
ERROR( invalid_tenant_configuration, 2140, "Tenant configuration is invalid" )
ERROR( cluster_no_capacity, 2141, "Cluster does not have capacity to perform the specified operation" )
// 2200 - errors from bindings and official APIs
ERROR( api_version_unset, 2200, "API version is not set" )