diff --git a/documentation/sphinx/source/special-keys.rst b/documentation/sphinx/source/special-keys.rst index 1a278d19b4..1b77b2282e 100644 --- a/documentation/sphinx/source/special-keys.rst +++ b/documentation/sphinx/source/special-keys.rst @@ -209,6 +209,7 @@ that process, and wait for necessary data to be moved away. #. ``\xff\xff/management/options/excluded_locality/force`` Read/write. Setting this key disables safety checks for writes to ``\xff\xff/management/excluded_locality/<locality>``. Setting this key only has an effect in the current transaction and is not persisted on commit. #. ``\xff\xff/management/options/failed_locality/force`` Read/write. Setting this key disables safety checks for writes to ``\xff\xff/management/failed_locality/<locality>``. Setting this key only has an effect in the current transaction and is not persisted on commit. #. ``\xff\xff/management/tenant/map/<tenant>`` Read/write. Setting a key in this range to any value will result in a tenant being created with name ``<tenant>``. Clearing a key in this range will delete the tenant with name ``<tenant>``. Reading all or a portion of this range will return the list of tenants currently present in the cluster, excluding any changes in this transaction. Values read in this range will be JSON objects containing the metadata for the associated tenants. +#. ``\xff\xff/management/tenant/rename/<tenant>`` Read/write. Setting a key in this range to an unused tenant name will result in the tenant with the name ``<tenant>`` to be renamed to the value provided. If the rename operation is a transaction retried in a loop outside of ``fdbcli``, it is possible for the rename to have already occurred, in which case ``tenant_not_found`` or ``tenant_already_exists`` errors may be returned. This can be avoided by checking for the tenant's existence first. An exclusion is syntactically either an ip address (e.g. ``127.0.0.1``), or an ip address and port (e.g. ``127.0.0.1:4500``) or any locality (e.g ``locality_dcid:primary-satellite`` or diff --git a/fdbclient/include/fdbclient/TenantManagement.actor.h b/fdbclient/include/fdbclient/TenantManagement.actor.h index 081763c733..5041036f16 100644 --- a/fdbclient/include/fdbclient/TenantManagement.actor.h +++ b/fdbclient/include/fdbclient/TenantManagement.actor.h @@ -443,7 +443,7 @@ Future<Void> renameTenant(Reference<DB> db, TenantName oldName, TenantName newNa } } wait(renameTenantTransaction(tr, oldName, newName)); - wait(safeThreadFutureToFuture(tr->commit())); + wait(buggifiedCommit(tr, BUGGIFY_WITH_PROB(0.1))); TraceEvent("RenameTenantSuccess").detail("OldName", oldName).detail("NewName", newName); return Void(); } catch (Error& e) { diff --git a/fdbserver/workloads/TenantManagementWorkload.actor.cpp b/fdbserver/workloads/TenantManagementWorkload.actor.cpp index 65f712d067..8d11c9b2c3 100644 --- a/fdbserver/workloads/TenantManagementWorkload.actor.cpp +++ b/fdbserver/workloads/TenantManagementWorkload.actor.cpp @@ -1057,135 +1057,6 @@ struct TenantManagementWorkload : TestWorkload { } } - // Changes the configuration of a tenant - ACTOR static Future<Void> configureImpl(Reference<ReadYourWritesTransaction> tr, - TenantName tenant, - std::map<Standalone<StringRef>, Optional<Value>> configParameters, - OperationType operationType, - bool specialKeysUseInvalidTuple, - TenantManagementWorkload* self) { - if (operationType == OperationType::SPECIAL_KEYS) { - tr->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); - for (auto const& [config, value] : configParameters) { - Tuple t; - if (specialKeysUseInvalidTuple) { - // Wrong number of items - if (deterministicRandom()->coinflip()) { - int numItems = deterministicRandom()->randomInt(0, 3); - if (numItems > 0) { - t.append(tenant); - } - if (numItems > 1) { - t.append(config).append(""_sr); - } - } - // Wrong data types - else { - if (deterministicRandom()->coinflip()) { - t.append(0).append(config); - } else { - t.append(tenant).append(0); - } - } - } else { - t.append(tenant).append(config); - } - if (value.present()) { - tr->set(self->specialKeysTenantConfigPrefix.withSuffix(t.pack()), value.get()); - } else { - tr->clear(self->specialKeysTenantConfigPrefix.withSuffix(t.pack())); - } - } - - wait(tr->commit()); - ASSERT(!specialKeysUseInvalidTuple); - } else { - // We don't have a transaction or database variant of this function - ASSERT(false); - } - - return Void(); - } - - ACTOR static Future<Void> configureTenant(Database cx, TenantManagementWorkload* self) { - state OperationType operationType = OperationType::SPECIAL_KEYS; - - state TenantName tenant = self->chooseTenantName(true); - auto itr = self->createdTenants.find(tenant); - state bool exists = itr != self->createdTenants.end(); - state Reference<ReadYourWritesTransaction> tr = makeReference<ReadYourWritesTransaction>(cx); - - state std::map<Standalone<StringRef>, Optional<Value>> configuration; - state Optional<TenantGroupName> newTenantGroup; - - // If true, the options generated may include an unknown option - state bool hasInvalidOption = deterministicRandom()->random01() < 0.1; - - // True if any tenant group name starts with \xff - state bool hasSystemTenantGroup = false; - - state bool specialKeysUseInvalidTuple = - operationType == OperationType::SPECIAL_KEYS && deterministicRandom()->random01() < 0.1; - - // Generate a tenant group. Sometimes do this at the same time that we include an invalid option to ensure - // that the configure function still fails - if (!hasInvalidOption || deterministicRandom()->coinflip()) { - newTenantGroup = self->chooseTenantGroup(true); - hasSystemTenantGroup = hasSystemTenantGroup || newTenantGroup.orDefault(""_sr).startsWith("\xff"_sr); - configuration["tenant_group"_sr] = newTenantGroup; - } - if (hasInvalidOption) { - configuration["invalid_option"_sr] = ""_sr; - } - - loop { - try { - wait(configureImpl(tr, tenant, configuration, operationType, specialKeysUseInvalidTuple, self)); - - ASSERT(exists); - ASSERT(!hasInvalidOption); - ASSERT(!hasSystemTenantGroup); - ASSERT(!specialKeysUseInvalidTuple); - - auto itr = self->createdTenants.find(tenant); - if (itr->second.tenantGroup.present()) { - auto tenantGroupItr = self->createdTenantGroups.find(itr->second.tenantGroup.get()); - ASSERT(tenantGroupItr != self->createdTenantGroups.end()); - if (--tenantGroupItr->second.tenantCount == 0) { - self->createdTenantGroups.erase(tenantGroupItr); - } - } - if (newTenantGroup.present()) { - self->createdTenantGroups[newTenantGroup.get()].tenantCount++; - } - itr->second.tenantGroup = newTenantGroup; - return Void(); - } catch (Error& e) { - state Error error = e; - if (e.code() == error_code_tenant_not_found) { - ASSERT(!exists); - return Void(); - } else if (e.code() == error_code_special_keys_api_failure) { - ASSERT(hasInvalidOption || specialKeysUseInvalidTuple); - return Void(); - } else if (e.code() == error_code_invalid_tenant_configuration) { - ASSERT(hasInvalidOption); - return Void(); - } else if (e.code() == error_code_invalid_tenant_group_name) { - ASSERT(hasSystemTenantGroup); - return Void(); - } - - try { - wait(tr->onError(e)); - } catch (Error&) { - TraceEvent(SevError, "ConfigureTenantFailure").error(error).detail("TenantName", tenant); - return Void(); - } - } - } - } - Future<Void> start(Database const& cx) override { return _start(cx, this); } ACTOR Future<Void> _start(Database cx, TenantManagementWorkload* self) { state double start = now();