Merge pull request #6560 from sfc-gh-ajbeamon/fdb-tenant-core-testing

Add core testing support for tenants
This commit is contained in:
Evan Tschannen 2022-03-17 13:18:58 -07:00 committed by GitHub
commit d956b00c9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 1395 additions and 262 deletions

View File

@ -130,8 +130,15 @@ EmptyFuture Database::create_snapshot(FDBDatabase* db,
return EmptyFuture(fdb_database_create_snapshot(db, uid, uid_length, snap_command, snap_command_length)); return EmptyFuture(fdb_database_create_snapshot(db, uid, uid_length, snap_command, snap_command_length));
} }
// Transaction // Tenant
Tenant::Tenant(FDBDatabase* db, const uint8_t* name, int name_length) {
if (fdb_error_t err = fdb_database_open_tenant(db, name, name_length, &tenant)) {
std::cerr << fdb_get_error(err) << std::endl;
std::abort();
}
}
// Transaction
Transaction::Transaction(FDBDatabase* db) { Transaction::Transaction(FDBDatabase* db) {
if (fdb_error_t err = fdb_database_create_transaction(db, &tr_)) { if (fdb_error_t err = fdb_database_create_transaction(db, &tr_)) {
std::cerr << fdb_get_error(err) << std::endl; std::cerr << fdb_get_error(err) << std::endl;
@ -139,6 +146,13 @@ Transaction::Transaction(FDBDatabase* db) {
} }
} }
Transaction::Transaction(Tenant tenant) {
if (fdb_error_t err = fdb_tenant_create_transaction(tenant.tenant, &tr_)) {
std::cerr << fdb_get_error(err) << std::endl;
std::abort();
}
}
Transaction::~Transaction() { Transaction::~Transaction() {
fdb_transaction_destroy(tr_); fdb_transaction_destroy(tr_);
} }

View File

@ -203,6 +203,15 @@ public:
int snap_command_length); int snap_command_length);
}; };
class Tenant final {
public:
Tenant(FDBDatabase* db, const uint8_t* name, int name_length);
private:
friend class Transaction;
FDBTenant* tenant;
};
// Wrapper around FDBTransaction, providing the same set of calls as the C API. // Wrapper around FDBTransaction, providing the same set of calls as the C API.
// Handles cleanup of memory, removing the need to call // Handles cleanup of memory, removing the need to call
// fdb_transaction_destroy. // fdb_transaction_destroy.
@ -210,6 +219,7 @@ class Transaction final {
public: public:
// Given an FDBDatabase, initializes a new transaction. // Given an FDBDatabase, initializes a new transaction.
Transaction(FDBDatabase* db); Transaction(FDBDatabase* db);
Transaction(Tenant tenant);
~Transaction(); ~Transaction();
// Wrapper around fdb_transaction_reset. // Wrapper around fdb_transaction_reset.

View File

@ -2410,6 +2410,101 @@ TEST_CASE("Fast alloc thread cleanup") {
} }
} }
TEST_CASE("Tenant create, access, and delete") {
std::string tenantName = "tenant";
std::string testKey = "foo";
std::string testValue = "bar";
fdb::Transaction tr(db);
while (1) {
fdb_check(tr.set_option(FDB_TR_OPTION_SPECIAL_KEY_SPACE_ENABLE_WRITES, nullptr, 0));
tr.set("\xff\xff/management/tenant_map/" + tenantName, "");
fdb::EmptyFuture commitFuture = tr.commit();
fdb_error_t err = wait_future(commitFuture);
if (err) {
fdb::EmptyFuture f = tr.on_error(err);
fdb_check(wait_future(f));
continue;
}
tr.reset();
break;
}
fdb::Tenant tenant(db, reinterpret_cast<const uint8_t*>(tenantName.c_str()), tenantName.size());
fdb::Transaction tr2(tenant);
while (1) {
tr2.set(testKey, testValue);
fdb::EmptyFuture commitFuture = tr2.commit();
fdb_error_t err = wait_future(commitFuture);
if (err) {
fdb::EmptyFuture f = tr2.on_error(err);
fdb_check(wait_future(f));
continue;
}
tr2.reset();
break;
}
while (1) {
fdb::ValueFuture f1 = tr2.get(testKey, false);
fdb_error_t err = wait_future(f1);
if (err) {
fdb::EmptyFuture f2 = tr.on_error(err);
fdb_check(wait_future(f2));
continue;
}
int out_present;
char* val;
int vallen;
fdb_check(f1.get(&out_present, (const uint8_t**)&val, &vallen));
CHECK(out_present == 1);
CHECK(vallen == testValue.size());
CHECK(testValue == val);
tr2.clear(testKey);
fdb::EmptyFuture commitFuture = tr2.commit();
err = wait_future(commitFuture);
if (err) {
fdb::EmptyFuture f = tr2.on_error(err);
fdb_check(wait_future(f));
continue;
}
tr2.reset();
break;
}
while (1) {
fdb_check(tr.set_option(FDB_TR_OPTION_SPECIAL_KEY_SPACE_ENABLE_WRITES, nullptr, 0));
tr.clear("\xff\xff/management/tenant_map/" + tenantName);
fdb::EmptyFuture commitFuture = tr.commit();
fdb_error_t err = wait_future(commitFuture);
if (err) {
fdb::EmptyFuture f = tr.on_error(err);
fdb_check(wait_future(f));
continue;
}
tr.reset();
break;
}
while (1) {
fdb::ValueFuture f1 = tr2.get(testKey, false);
fdb_error_t err = wait_future(f1);
if (err == error_code_tenant_not_found) {
tr2.reset();
break;
}
if (err) {
fdb::EmptyFuture f2 = tr.on_error(err);
fdb_check(wait_future(f2));
continue;
}
}
}
int main(int argc, char** argv) { int main(int argc, char** argv) {
if (argc < 3) { if (argc < 3) {
std::cout << "Unit tests for the FoundationDB C API.\n" std::cout << "Unit tests for the FoundationDB C API.\n"

View File

@ -139,6 +139,7 @@ set(FDBCLIENT_SRCS
TagThrottle.actor.h TagThrottle.actor.h
TaskBucket.actor.cpp TaskBucket.actor.cpp
TaskBucket.h TaskBucket.h
Tenant.cpp
Tenant.h Tenant.h
TestKnobCollection.cpp TestKnobCollection.cpp
TestKnobCollection.h TestKnobCollection.h

View File

@ -246,7 +246,8 @@ public:
lockAware, lockAware,
internal, internal,
apiVersion, apiVersion,
switchable)); switchable,
defaultTenant));
} }
Optional<KeyRangeLocationInfo> getCachedLocation(const Optional<TenantName>& tenant, Optional<KeyRangeLocationInfo> getCachedLocation(const Optional<TenantName>& tenant,
@ -370,7 +371,8 @@ public:
LockAware, LockAware,
IsInternal = IsInternal::True, IsInternal = IsInternal::True,
int apiVersion = Database::API_VERSION_LATEST, int apiVersion = Database::API_VERSION_LATEST,
IsSwitchable = IsSwitchable::False); IsSwitchable = IsSwitchable::False,
Optional<TenantName> defaultTenant = Optional<TenantName>());
explicit DatabaseContext(const Error& err); explicit DatabaseContext(const Error& err);
@ -392,6 +394,10 @@ public:
QueueModel queueModel; QueueModel queueModel;
EnableLocalityLoadBalance enableLocalityLoadBalance{ EnableLocalityLoadBalance::False }; EnableLocalityLoadBalance enableLocalityLoadBalance{ EnableLocalityLoadBalance::False };
// The tenant used when none is specified for a transaction. Ordinarily this is unspecified, in which case the raw
// key-space is used.
Optional<TenantName> defaultTenant;
struct VersionRequest { struct VersionRequest {
SpanID spanContext; SpanID spanContext;
Promise<GetReadVersionReply> reply; Promise<GetReadVersionReply> reply;

View File

@ -1314,10 +1314,11 @@ DatabaseContext::DatabaseContext(Reference<AsyncVar<Reference<IClusterConnection
LockAware lockAware, LockAware lockAware,
IsInternal internal, IsInternal internal,
int apiVersion, int apiVersion,
IsSwitchable switchable) IsSwitchable switchable,
Optional<TenantName> defaultTenant)
: lockAware(lockAware), switchable(switchable), connectionRecord(connectionRecord), proxyProvisional(false), : lockAware(lockAware), switchable(switchable), connectionRecord(connectionRecord), proxyProvisional(false),
clientLocality(clientLocality), enableLocalityLoadBalance(enableLocalityLoadBalance), internal(internal), clientLocality(clientLocality), enableLocalityLoadBalance(enableLocalityLoadBalance), defaultTenant(defaultTenant),
cc("TransactionMetrics"), transactionReadVersions("ReadVersions", cc), internal(internal), cc("TransactionMetrics"), transactionReadVersions("ReadVersions", cc),
transactionReadVersionsThrottled("ReadVersionsThrottled", cc), transactionReadVersionsThrottled("ReadVersionsThrottled", cc),
transactionReadVersionsCompleted("ReadVersionsCompleted", cc), transactionReadVersionsCompleted("ReadVersionsCompleted", cc),
transactionReadVersionBatches("ReadVersionBatches", cc), transactionReadVersionBatches("ReadVersionBatches", cc),
@ -2795,7 +2796,7 @@ Future<KeyRangeLocationInfo> getKeyLocation(Reference<TransactionState> trState,
UseTenant useTenant, UseTenant useTenant,
Version version) { Version version) {
auto f = getKeyLocation(trState->cx, auto f = getKeyLocation(trState->cx,
useTenant ? trState->tenant : Optional<TenantName>(), useTenant ? trState->tenant() : Optional<TenantName>(),
key, key,
member, member,
trState->spanID, trState->spanID,
@ -2804,7 +2805,7 @@ Future<KeyRangeLocationInfo> getKeyLocation(Reference<TransactionState> trState,
isBackward, isBackward,
version); version);
if (trState->tenant.present() && useTenant) { if (trState->tenant().present() && useTenant) {
return map(f, [trState](const KeyRangeLocationInfo& locationInfo) { return map(f, [trState](const KeyRangeLocationInfo& locationInfo) {
trState->tenantId = locationInfo.tenantEntry.id; trState->tenantId = locationInfo.tenantEntry.id;
return locationInfo; return locationInfo;
@ -2939,7 +2940,7 @@ Future<std::vector<KeyRangeLocationInfo>> getKeyRangeLocations(Reference<Transac
UseTenant useTenant, UseTenant useTenant,
Version version) { Version version) {
auto f = getKeyRangeLocations(trState->cx, auto f = getKeyRangeLocations(trState->cx,
useTenant ? trState->tenant : Optional<TenantName>(), useTenant ? trState->tenant() : Optional<TenantName>(),
keys, keys,
limit, limit,
reverse, reverse,
@ -2949,7 +2950,7 @@ Future<std::vector<KeyRangeLocationInfo>> getKeyRangeLocations(Reference<Transac
trState->useProvisionalProxies, trState->useProvisionalProxies,
version); version);
if (trState->tenant.present() && useTenant) { if (trState->tenant().present() && useTenant) {
return map(f, [trState](const std::vector<KeyRangeLocationInfo>& locationInfo) { return map(f, [trState](const std::vector<KeyRangeLocationInfo>& locationInfo) {
ASSERT(!locationInfo.empty()); ASSERT(!locationInfo.empty());
trState->tenantId = locationInfo[0].tenantEntry.id; trState->tenantId = locationInfo[0].tenantEntry.id;
@ -2969,7 +2970,7 @@ ACTOR Future<Void> warmRange_impl(Reference<TransactionState> trState, KeyRange
loop { loop {
std::vector<KeyRangeLocationInfo> locations = std::vector<KeyRangeLocationInfo> locations =
wait(getKeyRangeLocations_internal(trState->cx, wait(getKeyRangeLocations_internal(trState->cx,
trState->tenant, trState->tenant(),
keys, keys,
CLIENT_KNOBS->WARM_RANGE_SHARD_LIMIT, CLIENT_KNOBS->WARM_RANGE_SHARD_LIMIT,
Reverse::False, Reverse::False,
@ -2987,7 +2988,7 @@ ACTOR Future<Void> warmRange_impl(Reference<TransactionState> trState, KeyRange
if (totalRequests % 20 == 0) { if (totalRequests % 20 == 0) {
// To avoid blocking the proxies from starting other transactions, occasionally get a read version. // To avoid blocking the proxies from starting other transactions, occasionally get a read version.
state Transaction tr(trState->cx, trState->tenant); state Transaction tr(trState->cx, trState->tenant());
loop { loop {
try { try {
tr.setOption(FDBTransactionOptions::LOCK_AWARE); tr.setOption(FDBTransactionOptions::LOCK_AWARE);
@ -3027,14 +3028,15 @@ TransactionState::TransactionState(Database cx,
TaskPriority taskID, TaskPriority taskID,
SpanID spanID, SpanID spanID,
Reference<TransactionLogInfo> trLogInfo) Reference<TransactionLogInfo> trLogInfo)
: cx(cx), tenant(tenant), trLogInfo(trLogInfo), options(cx), taskID(taskID), spanID(spanID) {} : cx(cx), trLogInfo(trLogInfo), options(cx), taskID(taskID), spanID(spanID), tenant_(tenant),
tenantSet(tenant.present()) {}
Reference<TransactionState> TransactionState::cloneAndReset(Reference<TransactionLogInfo> newTrLogInfo, Reference<TransactionState> TransactionState::cloneAndReset(Reference<TransactionLogInfo> newTrLogInfo,
bool generateNewSpan) const { bool generateNewSpan) const {
SpanID newSpanID = generateNewSpan ? generateSpanID(cx->transactionTracingSample) : spanID; SpanID newSpanID = generateNewSpan ? generateSpanID(cx->transactionTracingSample) : spanID;
Reference<TransactionState> newState = Reference<TransactionState> newState =
makeReference<TransactionState>(cx, tenant, cx->taskID, newSpanID, newTrLogInfo); makeReference<TransactionState>(cx, tenant_, cx->taskID, newSpanID, newTrLogInfo);
if (!cx->apiVersionAtLeast(16)) { if (!cx->apiVersionAtLeast(16)) {
newState->options = options; newState->options = options;
@ -3044,22 +3046,42 @@ Reference<TransactionState> TransactionState::cloneAndReset(Reference<Transactio
newState->startTime = startTime; newState->startTime = startTime;
newState->committedVersion = committedVersion; newState->committedVersion = committedVersion;
newState->conflictingKeys = conflictingKeys; newState->conflictingKeys = conflictingKeys;
newState->tenantSet = tenantSet;
return newState; return newState;
} }
TenantInfo TransactionState::getTenantInfo() const { TenantInfo TransactionState::getTenantInfo() {
if (!cx->internal && !options.rawAccess && cx->clientInfo->get().tenantMode == TenantMode::REQUIRED && Optional<TenantName> const& t = tenant();
!tenant.present()) {
throw tenant_name_required(); if (options.rawAccess) {
} else if (options.rawAccess || !tenant.present()) {
return TenantInfo(); return TenantInfo();
} else if (cx->clientInfo->get().tenantMode == TenantMode::DISABLED && tenant.present()) { } else if (!cx->internal && cx->clientInfo->get().tenantMode == TenantMode::REQUIRED && !t.present()) {
throw tenant_name_required();
} else if (!t.present()) {
return TenantInfo();
} else if (cx->clientInfo->get().tenantMode == TenantMode::DISABLED && t.present()) {
throw tenants_disabled(); throw tenants_disabled();
} }
ASSERT(tenantId != TenantInfo::INVALID_TENANT); ASSERT(tenantId != TenantInfo::INVALID_TENANT);
return TenantInfo(tenant.get(), tenantId); return TenantInfo(t.get(), tenantId);
}
Optional<TenantName> const& TransactionState::tenant() {
if (tenantSet) {
return tenant_;
} else {
if (!tenant_.present() && !options.rawAccess) {
tenant_ = cx->defaultTenant;
}
tenantSet = true;
return tenant_;
}
}
bool TransactionState::hasTenant() const {
return tenantSet && tenant_.present();
} }
Future<Void> Transaction::warmRange(KeyRange keys) { Future<Void> Transaction::warmRange(KeyRange keys) {
@ -3073,8 +3095,8 @@ ACTOR Future<Optional<Value>> getValue(Reference<TransactionState> trState,
TransactionRecordLogInfo recordLogInfo) { TransactionRecordLogInfo recordLogInfo) {
state Version ver = wait(version); state Version ver = wait(version);
state Span span("NAPI:getValue"_loc, trState->spanID); state Span span("NAPI:getValue"_loc, trState->spanID);
if (useTenant && trState->tenant.present()) { if (useTenant && trState->tenant().present()) {
span.addTag("tenant"_sr, trState->tenant.get()); span.addTag("tenant"_sr, trState->tenant().get());
} }
span.addTag("key"_sr, key); span.addTag("key"_sr, key);
@ -3142,7 +3164,7 @@ ACTOR Future<Optional<Value>> getValue(Reference<TransactionState> trState,
if (trState->trLogInfo && recordLogInfo) { if (trState->trLogInfo && recordLogInfo) {
int valueSize = reply.value.present() ? reply.value.get().size() : 0; int valueSize = reply.value.present() ? reply.value.get().size() : 0;
trState->trLogInfo->addLog(FdbClientLogEvents::EventGet( trState->trLogInfo->addLog(FdbClientLogEvents::EventGet(
startTimeD, trState->cx->clientLocality.dcId(), latency, valueSize, key, trState->tenant)); startTimeD, trState->cx->clientLocality.dcId(), latency, valueSize, key, trState->tenant()));
} }
trState->cx->getValueCompleted->latency = timer_int() - startTime; trState->cx->getValueCompleted->latency = timer_int() - startTime;
trState->cx->getValueCompleted->log(); trState->cx->getValueCompleted->log();
@ -3177,8 +3199,8 @@ ACTOR Future<Optional<Value>> getValue(Reference<TransactionState> trState,
trState->cx->invalidateCache(locationInfo.tenantEntry.prefix, key); trState->cx->invalidateCache(locationInfo.tenantEntry.prefix, key);
wait(delay(CLIENT_KNOBS->WRONG_SHARD_SERVER_DELAY, trState->taskID)); wait(delay(CLIENT_KNOBS->WRONG_SHARD_SERVER_DELAY, trState->taskID));
} else if (e.code() == error_code_unknown_tenant) { } else if (e.code() == error_code_unknown_tenant) {
ASSERT(useTenant && trState->tenant.present()); ASSERT(useTenant && trState->tenant().present());
trState->cx->invalidateCachedTenant(trState->tenant.get()); trState->cx->invalidateCachedTenant(trState->tenant().get());
wait(delay(CLIENT_KNOBS->UNKNOWN_TENANT_RETRY_DELAY, trState->taskID)); wait(delay(CLIENT_KNOBS->UNKNOWN_TENANT_RETRY_DELAY, trState->taskID));
} else { } else {
if (trState->trLogInfo && recordLogInfo) if (trState->trLogInfo && recordLogInfo)
@ -3186,7 +3208,7 @@ ACTOR Future<Optional<Value>> getValue(Reference<TransactionState> trState,
trState->cx->clientLocality.dcId(), trState->cx->clientLocality.dcId(),
static_cast<int>(e.code()), static_cast<int>(e.code()),
key, key,
trState->tenant)); trState->tenant()));
throw e; throw e;
} }
} }
@ -3284,8 +3306,8 @@ ACTOR Future<Key> getKey(Reference<TransactionState> trState,
wait(delay(CLIENT_KNOBS->WRONG_SHARD_SERVER_DELAY, trState->taskID)); wait(delay(CLIENT_KNOBS->WRONG_SHARD_SERVER_DELAY, trState->taskID));
} else if (e.code() == error_code_unknown_tenant) { } else if (e.code() == error_code_unknown_tenant) {
ASSERT(useTenant && trState->tenant.present()); ASSERT(useTenant && trState->tenant().present());
trState->cx->invalidateCachedTenant(trState->tenant.get()); trState->cx->invalidateCachedTenant(trState->tenant().get());
wait(delay(CLIENT_KNOBS->UNKNOWN_TENANT_RETRY_DELAY, trState->taskID)); wait(delay(CLIENT_KNOBS->UNKNOWN_TENANT_RETRY_DELAY, trState->taskID));
} else { } else {
TraceEvent(SevInfo, "GetKeyError").error(e).detail("AtKey", k.getKey()).detail("Offset", k.offset); TraceEvent(SevInfo, "GetKeyError").error(e).detail("AtKey", k.getKey()).detail("Offset", k.offset);
@ -3630,8 +3652,8 @@ Future<RangeResultFamily> getExactRange(Reference<TransactionState> trState,
state RangeResultFamily output; state RangeResultFamily output;
state Span span("NAPI:getExactRange"_loc, trState->spanID); state Span span("NAPI:getExactRange"_loc, trState->spanID);
if (useTenant && trState->tenant.present()) { if (useTenant && trState->tenant().present()) {
span.addTag("tenant"_sr, trState->tenant.get()); span.addTag("tenant"_sr, trState->tenant().get());
} }
// printf("getExactRange( '%s', '%s' )\n", keys.begin.toString().c_str(), keys.end.toString().c_str()); // printf("getExactRange( '%s', '%s' )\n", keys.begin.toString().c_str(), keys.end.toString().c_str());
@ -3792,14 +3814,14 @@ Future<RangeResultFamily> getExactRange(Reference<TransactionState> trState,
wait(delay(CLIENT_KNOBS->WRONG_SHARD_SERVER_DELAY, trState->taskID)); wait(delay(CLIENT_KNOBS->WRONG_SHARD_SERVER_DELAY, trState->taskID));
break; break;
} else if (e.code() == error_code_unknown_tenant) { } else if (e.code() == error_code_unknown_tenant) {
ASSERT(useTenant && trState->tenant.present()); ASSERT(useTenant && trState->tenant().present());
trState->cx->invalidateCachedTenant(trState->tenant.get()); trState->cx->invalidateCachedTenant(trState->tenant().get());
wait(delay(CLIENT_KNOBS->UNKNOWN_TENANT_RETRY_DELAY, trState->taskID)); wait(delay(CLIENT_KNOBS->UNKNOWN_TENANT_RETRY_DELAY, trState->taskID));
break; break;
} else { } else {
TraceEvent(SevInfo, "GetExactRangeError") TraceEvent(SevInfo, "GetExactRangeError")
.error(e) .error(e)
.detail("Tenant", trState->tenant) .detail("Tenant", trState->tenant())
.detail("ShardBegin", locations[shard].range.begin) .detail("ShardBegin", locations[shard].range.begin)
.detail("ShardEnd", locations[shard].range.end); .detail("ShardEnd", locations[shard].range.end);
throw; throw;
@ -3859,6 +3881,12 @@ Future<RangeResultFamily> getRangeFallback(Reference<TransactionState> trState,
if (b == allKeys.begin && ((reverse && !r.more) || !reverse)) if (b == allKeys.begin && ((reverse && !r.more) || !reverse))
r.readToBegin = true; r.readToBegin = true;
// TODO: this currently causes us to have a conflict range that is too large if our end key resolves to the
// key after the last key in the database. In that case, we don't need a conflict between the last key and
// the end of the database.
//
// If fixed, the ConflictRange test can be updated to stop checking for this condition.
if (e == allKeys.end && ((!reverse && !r.more) || reverse)) if (e == allKeys.end && ((!reverse && !r.more) || reverse))
r.readThroughEnd = true; r.readThroughEnd = true;
@ -3924,7 +3952,7 @@ void getRangeFinished(Reference<TransactionState> trState,
bytes, bytes,
begin.getKey(), begin.getKey(),
end.getKey(), end.getKey(),
trState->tenant)); trState->tenant()));
} }
if (!snapshot) { if (!snapshot) {
@ -3981,8 +4009,8 @@ Future<RangeResultFamily> getRange(Reference<TransactionState> trState,
state KeySelector originalEnd = end; state KeySelector originalEnd = end;
state RangeResultFamily output; state RangeResultFamily output;
state Span span("NAPI:getRange"_loc, trState->spanID); state Span span("NAPI:getRange"_loc, trState->spanID);
if (useTenant && trState->tenant.present()) { if (useTenant && trState->tenant().present()) {
span.addTag("tenant"_sr, trState->tenant.get()); span.addTag("tenant"_sr, trState->tenant().get());
} }
try { try {
@ -4248,8 +4276,8 @@ Future<RangeResultFamily> getRange(Reference<TransactionState> trState,
wait(delay(CLIENT_KNOBS->WRONG_SHARD_SERVER_DELAY, trState->taskID)); wait(delay(CLIENT_KNOBS->WRONG_SHARD_SERVER_DELAY, trState->taskID));
} else if (e.code() == error_code_unknown_tenant) { } else if (e.code() == error_code_unknown_tenant) {
ASSERT(useTenant && trState->tenant.present()); ASSERT(useTenant && trState->tenant().present());
trState->cx->invalidateCachedTenant(trState->tenant.get()); trState->cx->invalidateCachedTenant(trState->tenant().get());
wait(delay(CLIENT_KNOBS->UNKNOWN_TENANT_RETRY_DELAY, trState->taskID)); wait(delay(CLIENT_KNOBS->UNKNOWN_TENANT_RETRY_DELAY, trState->taskID));
} else { } else {
if (trState->trLogInfo) if (trState->trLogInfo)
@ -4259,7 +4287,7 @@ Future<RangeResultFamily> getRange(Reference<TransactionState> trState,
static_cast<int>(e.code()), static_cast<int>(e.code()),
begin.getKey(), begin.getKey(),
end.getKey(), end.getKey(),
trState->tenant)); trState->tenant()));
throw e; throw e;
} }
@ -4704,8 +4732,8 @@ ACTOR Future<Void> getRangeStreamFragment(Reference<TransactionState> trState,
wait(delay(CLIENT_KNOBS->WRONG_SHARD_SERVER_DELAY, trState->taskID)); wait(delay(CLIENT_KNOBS->WRONG_SHARD_SERVER_DELAY, trState->taskID));
break; break;
} else if (e.code() == error_code_unknown_tenant) { } else if (e.code() == error_code_unknown_tenant) {
ASSERT(trState->tenant.present()); ASSERT(trState->tenant().present());
trState->cx->invalidateCachedTenant(trState->tenant.get()); trState->cx->invalidateCachedTenant(trState->tenant().get());
wait(delay(CLIENT_KNOBS->UNKNOWN_TENANT_RETRY_DELAY, trState->taskID)); wait(delay(CLIENT_KNOBS->UNKNOWN_TENANT_RETRY_DELAY, trState->taskID));
break; break;
} else { } else {
@ -4979,7 +5007,7 @@ ACTOR Future<TenantInfo> getTenantMetadata(Reference<TransactionState> trState,
} }
Future<TenantInfo> populateAndGetTenant(Reference<TransactionState> trState, Key const& key, Version version) { Future<TenantInfo> populateAndGetTenant(Reference<TransactionState> trState, Key const& key, Version version) {
if (!trState->tenant.present() || key == metadataVersionKey) { if (!trState->tenant().present() || key == metadataVersionKey) {
return TenantInfo(); return TenantInfo();
} else if (trState->tenantId != TenantInfo::INVALID_TENANT) { } else if (trState->tenantId != TenantInfo::INVALID_TENANT) {
return trState->getTenantInfo(); return trState->getTenantInfo();
@ -5066,7 +5094,7 @@ ACTOR Future<Standalone<VectorRef<const char*>>> getAddressesForKeyActor(Referen
state std::vector<StorageServerInterface> ssi; state std::vector<StorageServerInterface> ssi;
state Key resolvedKey = key; state Key resolvedKey = key;
if (trState->tenant.present()) { if (trState->tenant().present()) {
state Version version = wait(ver); state Version version = wait(ver);
KeyRangeLocationInfo locationInfo = wait(getKeyLocation( KeyRangeLocationInfo locationInfo = wait(getKeyLocation(
trState, ""_sr, &StorageServerInterface::getValue, Reverse::False, UseTenant::True, version)); trState, ""_sr, &StorageServerInterface::getValue, Reverse::False, UseTenant::True, version));
@ -5864,7 +5892,7 @@ ACTOR static Future<Void> tryCommit(Reference<TransactionState> trState,
} }
state Key tenantPrefix; state Key tenantPrefix;
if (trState->tenant.present()) { if (trState->tenant().present()) {
KeyRangeLocationInfo locationInfo = wait(getKeyLocation(trState, KeyRangeLocationInfo locationInfo = wait(getKeyLocation(trState,
""_sr, ""_sr,
&StorageServerInterface::getValue, &StorageServerInterface::getValue,
@ -5951,7 +5979,7 @@ ACTOR static Future<Void> tryCommit(Reference<TransactionState> trState,
req.transaction.mutations.expectedSize(), req.transaction.mutations.expectedSize(),
ci.version, ci.version,
req, req,
trState->tenant)); trState->tenant()));
return Void(); return Void();
} else { } else {
// clear the RYW transaction which contains previous conflicting keys // clear the RYW transaction which contains previous conflicting keys
@ -6009,8 +6037,8 @@ ACTOR static Future<Void> tryCommit(Reference<TransactionState> trState,
// retry it anyway (relying on transaction idempotence) but a client might do something else. // retry it anyway (relying on transaction idempotence) but a client might do something else.
throw commit_unknown_result(); throw commit_unknown_result();
} else if (e.code() == error_code_unknown_tenant) { } else if (e.code() == error_code_unknown_tenant) {
ASSERT(trState->tenant.present()); ASSERT(trState->tenant().present());
trState->cx->invalidateCachedTenant(trState->tenant.get()); trState->cx->invalidateCachedTenant(trState->tenant().get());
throw; throw;
} else { } else {
if (e.code() != error_code_transaction_too_old && e.code() != error_code_not_committed && if (e.code() != error_code_transaction_too_old && e.code() != error_code_not_committed &&
@ -6022,7 +6050,7 @@ ACTOR static Future<Void> tryCommit(Reference<TransactionState> trState,
} }
if (trState->trLogInfo) if (trState->trLogInfo)
trState->trLogInfo->addLog(FdbClientLogEvents::EventCommitError( trState->trLogInfo->addLog(FdbClientLogEvents::EventCommitError(
startTime, trState->cx->clientLocality.dcId(), static_cast<int>(e.code()), req, trState->tenant)); startTime, trState->cx->clientLocality.dcId(), static_cast<int>(e.code()), req, trState->tenant()));
throw; throw;
} }
} }
@ -6368,9 +6396,9 @@ void Transaction::setOption(FDBTransactionOptions::Option option, Optional<Strin
// System key access implies raw access. Native API handles the raw access, // System key access implies raw access. Native API handles the raw access,
// system key access is handled in RYW. // system key access is handled in RYW.
validateOptionValueNotPresent(value); validateOptionValueNotPresent(value);
if (trState->tenant.present()) { if (trState->hasTenant()) {
Error e = invalid_option(); Error e = invalid_option();
TraceEvent(SevWarn, "TenantTransactionRawAccess").error(e).detail("Tenant", trState->tenant); TraceEvent(SevWarn, "TenantTransactionRawAccess").error(e).detail("Tenant", trState->tenant());
throw e; throw e;
} }
trState->options.rawAccess = true; trState->options.rawAccess = true;
@ -6563,7 +6591,7 @@ ACTOR Future<Version> extractReadVersion(Reference<TransactionState> trState,
latency, latency,
trState->options.priority, trState->options.priority,
rep.version, rep.version,
trState->tenant)); trState->tenant()));
if (rep.locked && !trState->options.lockAware) if (rep.locked && !trState->options.lockAware)
throw database_locked(); throw database_locked();
@ -7235,8 +7263,8 @@ ACTOR Future<Standalone<VectorRef<KeyRef>>> getRangeSplitPoints(Reference<Transa
trState->cx->invalidateCache(locations[0].tenantEntry.prefix, keys); trState->cx->invalidateCache(locations[0].tenantEntry.prefix, keys);
wait(delay(CLIENT_KNOBS->WRONG_SHARD_SERVER_DELAY, TaskPriority::DataDistribution)); wait(delay(CLIENT_KNOBS->WRONG_SHARD_SERVER_DELAY, TaskPriority::DataDistribution));
} else if (e.code() == error_code_unknown_tenant) { } else if (e.code() == error_code_unknown_tenant) {
ASSERT(trState->tenant.present()); ASSERT(trState->tenant().present());
trState->cx->invalidateCachedTenant(trState->tenant.get()); trState->cx->invalidateCachedTenant(trState->tenant().get());
wait(delay(CLIENT_KNOBS->UNKNOWN_TENANT_RETRY_DELAY, trState->taskID)); wait(delay(CLIENT_KNOBS->UNKNOWN_TENANT_RETRY_DELAY, trState->taskID));
} else { } else {
TraceEvent(SevError, "GetRangeSplitPoints").error(e); TraceEvent(SevError, "GetRangeSplitPoints").error(e);

View File

@ -237,7 +237,6 @@ struct Watch : public ReferenceCounted<Watch>, NonCopyable {
struct TransactionState : ReferenceCounted<TransactionState> { struct TransactionState : ReferenceCounted<TransactionState> {
Database cx; Database cx;
Optional<TenantName> tenant;
int64_t tenantId = TenantInfo::INVALID_TENANT; int64_t tenantId = TenantInfo::INVALID_TENANT;
Reference<TransactionLogInfo> trLogInfo; Reference<TransactionLogInfo> trLogInfo;
TransactionOptions options; TransactionOptions options;
@ -259,7 +258,7 @@ struct TransactionState : ReferenceCounted<TransactionState> {
std::shared_ptr<CoalescedKeyRangeMap<Value>> conflictingKeys; std::shared_ptr<CoalescedKeyRangeMap<Value>> conflictingKeys;
// Only available so that Transaction can have a default constructor, for use in state variables // Only available so that Transaction can have a default constructor, for use in state variables
TransactionState(TaskPriority taskID, SpanID spanID) : taskID(taskID), spanID(spanID) {} TransactionState(TaskPriority taskID, SpanID spanID) : taskID(taskID), spanID(spanID), tenantSet(false) {}
TransactionState(Database cx, TransactionState(Database cx,
Optional<TenantName> tenant, Optional<TenantName> tenant,
@ -268,7 +267,14 @@ struct TransactionState : ReferenceCounted<TransactionState> {
Reference<TransactionLogInfo> trLogInfo); Reference<TransactionLogInfo> trLogInfo);
Reference<TransactionState> cloneAndReset(Reference<TransactionLogInfo> newTrLogInfo, bool generateNewSpan) const; Reference<TransactionState> cloneAndReset(Reference<TransactionLogInfo> newTrLogInfo, bool generateNewSpan) const;
TenantInfo getTenantInfo() const; TenantInfo getTenantInfo();
Optional<TenantName> const& tenant();
bool hasTenant() const;
private:
Optional<TenantName> tenant_;
bool tenantSet;
}; };
class Transaction : NonCopyable { class Transaction : NonCopyable {
@ -447,7 +453,7 @@ public:
return Standalone<VectorRef<KeyRangeRef>>(tr.transaction.write_conflict_ranges, tr.arena); return Standalone<VectorRef<KeyRangeRef>>(tr.transaction.write_conflict_ranges, tr.arena);
} }
Optional<TenantName> getTenant() { return trState->tenant; } Optional<TenantName> getTenant() { return trState->tenant(); }
Reference<TransactionState> trState; Reference<TransactionState> trState;
std::vector<Reference<Watch>> watches; std::vector<Reference<Watch>> watches;

57
fdbclient/Tenant.cpp Normal file
View File

@ -0,0 +1,57 @@
/*
* Tenant.cpp
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "fdbclient/SystemData.h"
#include "fdbclient/Tenant.h"
#include "flow/UnitTest.h"
TEST_CASE("/fdbclient/TenantMapEntry/Serialization") {
TenantMapEntry entry1(1, ""_sr);
ASSERT(entry1.prefix == "\x00\x00\x00\x00\x00\x00\x00\x01"_sr);
TenantMapEntry entry2 = decodeTenantEntry(encodeTenantEntry(entry1));
ASSERT(entry1.id == entry2.id && entry1.prefix == entry2.prefix);
TenantMapEntry entry3(std::numeric_limits<int64_t>::max(), "foo"_sr);
ASSERT(entry3.prefix == "foo\xfe\xff\xff\xff\xff\xff\xff\xff"_sr);
TenantMapEntry entry4 = decodeTenantEntry(encodeTenantEntry(entry3));
ASSERT(entry3.id == entry4.id && entry3.prefix == entry4.prefix);
for (int i = 0; i < 100; ++i) {
int bits = deterministicRandom()->randomInt(1, 64);
int64_t min = bits == 1 ? 0 : (1 << (bits - 1));
int64_t maxPlusOne = std::min<uint64_t>(1 << bits, std::numeric_limits<int64_t>::max());
int64_t id = deterministicRandom()->randomInt64(min, maxPlusOne);
int subspaceLength = deterministicRandom()->randomInt(0, 20);
Standalone<StringRef> subspace = makeString(subspaceLength);
generateRandomData(mutateString(subspace), subspaceLength);
TenantMapEntry entry(id, subspace);
int64_t bigEndianId = bigEndian64(id);
ASSERT(entry.id == id && entry.prefix.startsWith(subspace) &&
entry.prefix.endsWith(StringRef(reinterpret_cast<uint8_t*>(&bigEndianId), 8)) &&
entry.prefix.size() == subspaceLength + 8);
TenantMapEntry decodedEntry = decodeTenantEntry(encodeTenantEntry(entry));
ASSERT(decodedEntry.id = entry.id && decodedEntry.prefix == entry.prefix);
}
return Void();
}

View File

@ -270,6 +270,7 @@ set(FDBSERVER_SRCS
workloads/TagThrottleApi.actor.cpp workloads/TagThrottleApi.actor.cpp
workloads/TargetedKill.actor.cpp workloads/TargetedKill.actor.cpp
workloads/TaskBucketCorrectness.actor.cpp workloads/TaskBucketCorrectness.actor.cpp
workloads/TenantManagement.actor.cpp
workloads/ThreadSafety.actor.cpp workloads/ThreadSafety.actor.cpp
workloads/Throttling.actor.cpp workloads/Throttling.actor.cpp
workloads/Throughput.actor.cpp workloads/Throughput.actor.cpp

View File

@ -140,6 +140,7 @@ ACTOR Future<Void> fetchCheckpointFile(Database cx,
state StorageServerInterface ssi; state StorageServerInterface ssi;
loop { loop {
try { try {
tr.setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
Optional<Value> ss = wait(tr.get(serverListKeyFor(ssID))); Optional<Value> ss = wait(tr.get(serverListKeyFor(ssID)));
if (!ss.present()) { if (!ss.present()) {
throw checkpoint_not_found(); throw checkpoint_not_found();

View File

@ -300,6 +300,9 @@ public:
stderrSeverity, machineCount, processesPerMachine, coordinators; stderrSeverity, machineCount, processesPerMachine, coordinators;
Optional<std::string> config; Optional<std::string> config;
bool allowDefaultTenant = true;
bool allowDisablingTenants = true;
ConfigDBType getConfigDBType() const { return configDBType; } ConfigDBType getConfigDBType() const { return configDBType; }
bool tomlKeyPresent(const toml::value& data, std::string key) { bool tomlKeyPresent(const toml::value& data, std::string key) {
@ -350,7 +353,9 @@ public:
.add("processesPerMachine", &processesPerMachine) .add("processesPerMachine", &processesPerMachine)
.add("coordinators", &coordinators) .add("coordinators", &coordinators)
.add("configDB", &configDBType) .add("configDB", &configDBType)
.add("extraMachineCountDC", &extraMachineCountDC); .add("extraMachineCountDC", &extraMachineCountDC)
.add("allowDefaultTenant", &allowDefaultTenant)
.add("allowDisablingTenants", &allowDisablingTenants);
try { try {
auto file = toml::parse(testFile); auto file = toml::parse(testFile);
if (file.contains("configuration") && toml::find(file, "configuration").is_table()) { if (file.contains("configuration") && toml::find(file, "configuration").is_table()) {
@ -1786,12 +1791,16 @@ void setupSimulatedSystem(std::vector<Future<Void>>* systemActors,
Standalone<StringRef>* pStartingConfiguration, Standalone<StringRef>* pStartingConfiguration,
std::string whitelistBinPaths, std::string whitelistBinPaths,
TestConfig testConfig, TestConfig testConfig,
ProtocolVersion protocolVersion) { ProtocolVersion protocolVersion,
TenantMode tenantMode) {
// SOMEDAY: this does not test multi-interface configurations // SOMEDAY: this does not test multi-interface configurations
SimulationConfig simconfig(testConfig); SimulationConfig simconfig(testConfig);
if (testConfig.logAntiQuorum != -1) { if (testConfig.logAntiQuorum != -1) {
simconfig.db.tLogWriteAntiQuorum = testConfig.logAntiQuorum; simconfig.db.tLogWriteAntiQuorum = testConfig.logAntiQuorum;
} }
simconfig.db.tenantMode = tenantMode;
StatusObject startingConfigJSON = simconfig.db.toJSON(true); StatusObject startingConfigJSON = simconfig.db.toJSON(true);
std::string startingConfigString = "new"; std::string startingConfigString = "new";
if (testConfig.configureLocked) { if (testConfig.configureLocked) {
@ -1884,7 +1893,7 @@ void setupSimulatedSystem(std::vector<Future<Void>>* systemActors,
ASSERT(g_simulator.storagePolicy && g_simulator.tLogPolicy); ASSERT(g_simulator.storagePolicy && g_simulator.tLogPolicy);
ASSERT(!g_simulator.hasSatelliteReplication || g_simulator.satelliteTLogPolicy); ASSERT(!g_simulator.hasSatelliteReplication || g_simulator.satelliteTLogPolicy);
TraceEvent("SimulatorConfig").detail("ConfigString", StringRef(startingConfigString)); TraceEvent("SimulatorConfig").setMaxFieldLength(10000).detail("ConfigString", StringRef(startingConfigString));
const int dataCenters = simconfig.datacenters; const int dataCenters = simconfig.datacenters;
const int machineCount = simconfig.machine_count; const int machineCount = simconfig.machine_count;
@ -2271,11 +2280,18 @@ ACTOR void setupAndRun(std::string dataFolder,
g_simulator.hasDiffProtocolProcess = testConfig.startIncompatibleProcess; g_simulator.hasDiffProtocolProcess = testConfig.startIncompatibleProcess;
g_simulator.setDiffProtocol = false; g_simulator.setDiffProtocol = false;
state bool allowDefaultTenant = testConfig.allowDefaultTenant;
state bool allowDisablingTenants = testConfig.allowDisablingTenants;
// The RocksDB storage engine does not support the restarting tests because you cannot consistently get a clean // The RocksDB storage engine does not support the restarting tests because you cannot consistently get a clean
// snapshot of the storage engine without a snapshotting file system. // snapshot of the storage engine without a snapshotting file system.
// https://github.com/apple/foundationdb/issues/5155 // https://github.com/apple/foundationdb/issues/5155
if (std::string_view(testFile).find("restarting") != std::string_view::npos) { if (std::string_view(testFile).find("restarting") != std::string_view::npos) {
testConfig.storageEngineExcludeTypes.push_back(4); testConfig.storageEngineExcludeTypes.push_back(4);
// Disable the default tenant in restarting tests for now
// TODO: persist the chosen default tenant in the restartInfo.ini file for the second test
allowDefaultTenant = false;
} }
// TODO: Currently backup and restore related simulation tests are failing when run with rocksDB storage engine // TODO: Currently backup and restore related simulation tests are failing when run with rocksDB storage engine
@ -2285,6 +2301,13 @@ ACTOR void setupAndRun(std::string dataFolder,
testConfig.storageEngineExcludeTypes.push_back(4); testConfig.storageEngineExcludeTypes.push_back(4);
} }
// Disable the default tenant in backup and DR tests for now. This is because backup does not currently duplicate
// the tenant map and related state.
// TODO: reenable when backup/DR supports tenants.
if (std::string_view(testFile).find("Backup") != std::string_view::npos || testConfig.extraDB != 0) {
allowDefaultTenant = false;
}
// The RocksDB engine is not always built with the rest of fdbserver. Don't try to use it if it is not included // The RocksDB engine is not always built with the rest of fdbserver. Don't try to use it if it is not included
// in the build. // in the build.
if (!rocksDBEnabled) { if (!rocksDBEnabled) {
@ -2319,6 +2342,23 @@ ACTOR void setupAndRun(std::string dataFolder,
FlowTransport::createInstance(true, 1, WLTOKEN_RESERVED_COUNT); FlowTransport::createInstance(true, 1, WLTOKEN_RESERVED_COUNT);
TEST(true); // Simulation start TEST(true); // Simulation start
state Optional<TenantName> defaultTenant;
state TenantMode tenantMode = TenantMode::DISABLED;
if (allowDefaultTenant && deterministicRandom()->random01() < 0.5) {
defaultTenant = "SimulatedDefaultTenant"_sr;
if (deterministicRandom()->random01() < 0.9) {
tenantMode = TenantMode::REQUIRED;
} else {
tenantMode = TenantMode::OPTIONAL_TENANT;
}
} else if (!allowDisablingTenants || deterministicRandom()->random01() < 0.5) {
tenantMode = TenantMode::OPTIONAL_TENANT;
}
TraceEvent("SimulatedClusterTenantMode")
.detail("UsingTenant", defaultTenant)
.detail("TenantRequired", tenantMode.toString());
try { try {
// systemActors.push_back( startSystemMonitor(dataFolder) ); // systemActors.push_back( startSystemMonitor(dataFolder) );
if (rebooting) { if (rebooting) {
@ -2344,7 +2384,8 @@ ACTOR void setupAndRun(std::string dataFolder,
&startingConfiguration, &startingConfiguration,
whitelistBinPaths, whitelistBinPaths,
testConfig, testConfig,
protocolVersion); protocolVersion,
tenantMode);
wait(delay(1.0)); // FIXME: WHY!!! //wait for machines to boot wait(delay(1.0)); // FIXME: WHY!!! //wait for machines to boot
} }
std::string clusterFileDir = joinPath(dataFolder, deterministicRandom()->randomUniqueID().toString()); std::string clusterFileDir = joinPath(dataFolder, deterministicRandom()->randomUniqueID().toString());
@ -2355,7 +2396,10 @@ ACTOR void setupAndRun(std::string dataFolder,
TEST_ON_TESTERS, TEST_ON_TESTERS,
testerCount, testerCount,
testFile, testFile,
startingConfiguration), startingConfiguration,
LocalityData(),
UnitTestParameters(),
defaultTenant),
isBuggifyEnabled(BuggifyType::General) ? 36000.0 : 5400.0)); isBuggifyEnabled(BuggifyType::General) ? 36000.0 : 5400.0));
} catch (Error& e) { } catch (Error& e) {
TraceEvent(SevError, "SetupAndRunError").error(e); TraceEvent(SevError, "SetupAndRunError").error(e);

View File

@ -66,6 +66,7 @@ struct WorkloadRequest {
double databasePingDelay; double databasePingDelay;
int64_t sharedRandomNumber; int64_t sharedRandomNumber;
bool useDatabase; bool useDatabase;
Optional<TenantNameRef> defaultTenant;
// The vector of option lists are to construct compound workloads. If there // The vector of option lists are to construct compound workloads. If there
// is only one workload to be run...pass just one list of options! // is only one workload to be run...pass just one list of options!
@ -96,6 +97,7 @@ struct WorkloadRequest {
clientId, clientId,
clientCount, clientCount,
reply, reply,
defaultTenant,
arena); arena);
} }
}; };
@ -127,7 +129,8 @@ ACTOR Future<Void> runTests(Reference<IClusterConnectionRecord> connRecord,
std::string fileName = std::string(), std::string fileName = std::string(),
StringRef startingConfiguration = StringRef(), StringRef startingConfiguration = StringRef(),
LocalityData locality = LocalityData(), LocalityData locality = LocalityData(),
UnitTestParameters testOptions = UnitTestParameters()); UnitTestParameters testOptions = UnitTestParameters(),
Optional<TenantName> defaultTenant = Optional<TenantName>());
#include "flow/unactorcompiler.h" #include "flow/unactorcompiler.h"
#endif #endif

View File

@ -642,6 +642,7 @@ ACTOR Future<Void> testerServerWorkload(WorkloadRequest work,
if (work.useDatabase) { if (work.useDatabase) {
cx = Database::createDatabase(ccr, -1, IsInternal::True, locality); cx = Database::createDatabase(ccr, -1, IsInternal::True, locality);
cx->defaultTenant = work.defaultTenant.castTo<TenantName>();
wait(delay(1.0)); wait(delay(1.0));
} }
@ -779,7 +780,10 @@ void throwIfError(const std::vector<Future<ErrorOr<T>>>& futures, std::string er
} }
} }
ACTOR Future<DistributedTestResults> runWorkload(Database cx, std::vector<TesterInterface> testers, TestSpec spec) { ACTOR Future<DistributedTestResults> runWorkload(Database cx,
std::vector<TesterInterface> testers,
TestSpec spec,
Optional<TenantName> defaultTenant) {
TraceEvent("TestRunning") TraceEvent("TestRunning")
.detail("WorkloadTitle", spec.title) .detail("WorkloadTitle", spec.title)
.detail("TesterCount", testers.size()) .detail("TesterCount", testers.size())
@ -803,6 +807,7 @@ ACTOR Future<DistributedTestResults> runWorkload(Database cx, std::vector<Tester
req.clientId = i; req.clientId = i;
req.clientCount = testers.size(); req.clientCount = testers.size();
req.sharedRandomNumber = sharedRandom; req.sharedRandomNumber = sharedRandom;
req.defaultTenant = defaultTenant.castTo<TenantNameRef>();
workRequests.push_back(testers[i].recruitments.getReply(req)); workRequests.push_back(testers[i].recruitments.getReply(req));
} }
@ -894,7 +899,7 @@ ACTOR Future<Void> changeConfiguration(Database cx, std::vector<TesterInterface>
options.push_back_deep(options.arena(), KeyValueRef(LiteralStringRef("configMode"), configMode)); options.push_back_deep(options.arena(), KeyValueRef(LiteralStringRef("configMode"), configMode));
spec.options.push_back_deep(spec.options.arena(), options); spec.options.push_back_deep(spec.options.arena(), options);
DistributedTestResults testResults = wait(runWorkload(cx, testers, spec)); DistributedTestResults testResults = wait(runWorkload(cx, testers, spec, Optional<TenantName>()));
return Void(); return Void();
} }
@ -949,7 +954,7 @@ ACTOR Future<Void> checkConsistency(Database cx,
state double start = now(); state double start = now();
state bool lastRun = false; state bool lastRun = false;
loop { loop {
DistributedTestResults testResults = wait(runWorkload(cx, testers, spec)); DistributedTestResults testResults = wait(runWorkload(cx, testers, spec, Optional<TenantName>()));
if (testResults.ok() || lastRun) { if (testResults.ok() || lastRun) {
if (g_network->isSimulated()) { if (g_network->isSimulated()) {
g_simulator.connectionFailuresDisableDuration = connectionFailures; g_simulator.connectionFailuresDisableDuration = connectionFailures;
@ -969,11 +974,12 @@ ACTOR Future<Void> checkConsistency(Database cx,
ACTOR Future<bool> runTest(Database cx, ACTOR Future<bool> runTest(Database cx,
std::vector<TesterInterface> testers, std::vector<TesterInterface> testers,
TestSpec spec, TestSpec spec,
Reference<AsyncVar<ServerDBInfo>> dbInfo) { Reference<AsyncVar<ServerDBInfo>> dbInfo,
Optional<TenantName> defaultTenant) {
state DistributedTestResults testResults; state DistributedTestResults testResults;
try { try {
Future<DistributedTestResults> fTestResults = runWorkload(cx, testers, spec); Future<DistributedTestResults> fTestResults = runWorkload(cx, testers, spec, defaultTenant);
if (spec.timeout > 0) { if (spec.timeout > 0) {
fTestResults = timeoutError(fTestResults, spec.timeout); fTestResults = timeoutError(fTestResults, spec.timeout);
} }
@ -1418,7 +1424,8 @@ ACTOR Future<Void> runTests(Reference<AsyncVar<Optional<struct ClusterController
std::vector<TesterInterface> testers, std::vector<TesterInterface> testers,
std::vector<TestSpec> tests, std::vector<TestSpec> tests,
StringRef startingConfiguration, StringRef startingConfiguration,
LocalityData locality) { LocalityData locality,
Optional<TenantName> defaultTenant) {
state Database cx; state Database cx;
state Reference<AsyncVar<ServerDBInfo>> dbInfo(new AsyncVar<ServerDBInfo>); state Reference<AsyncVar<ServerDBInfo>> dbInfo(new AsyncVar<ServerDBInfo>);
state Future<Void> ccMonitor = monitorServerDBInfo(cc, LocalityData(), dbInfo); // FIXME: locality state Future<Void> ccMonitor = monitorServerDBInfo(cc, LocalityData(), dbInfo); // FIXME: locality
@ -1466,6 +1473,7 @@ ACTOR Future<Void> runTests(Reference<AsyncVar<Optional<struct ClusterController
if (useDB) { if (useDB) {
cx = openDBOnServer(dbInfo); cx = openDBOnServer(dbInfo);
cx->defaultTenant = defaultTenant;
} }
state Future<Void> disabler = disableConnectionFailuresAfter(FLOW_KNOBS->SIM_SPEEDUP_AFTER_SECONDS, "Tester"); state Future<Void> disabler = disableConnectionFailuresAfter(FLOW_KNOBS->SIM_SPEEDUP_AFTER_SECONDS, "Tester");
@ -1493,6 +1501,11 @@ ACTOR Future<Void> runTests(Reference<AsyncVar<Optional<struct ClusterController
} }
} }
if (useDB && defaultTenant.present()) {
TraceEvent("CreatingDefaultTenant").detail("Tenant", defaultTenant.get());
wait(ManagementAPI::createTenant(cx.getReference(), defaultTenant.get()));
}
if (useDB && waitForQuiescenceBegin) { if (useDB && waitForQuiescenceBegin) {
TraceEvent("TesterStartingPreTestChecks") TraceEvent("TesterStartingPreTestChecks")
.detail("DatabasePingDelay", databasePingDelay) .detail("DatabasePingDelay", databasePingDelay)
@ -1518,7 +1531,7 @@ ACTOR Future<Void> runTests(Reference<AsyncVar<Optional<struct ClusterController
state int idx = 0; state int idx = 0;
for (; idx < tests.size(); idx++) { for (; idx < tests.size(); idx++) {
printf("Run test:%s start\n", tests[idx].title.toString().c_str()); printf("Run test:%s start\n", tests[idx].title.toString().c_str());
wait(success(runTest(cx, testers, tests[idx], dbInfo))); wait(success(runTest(cx, testers, tests[idx], dbInfo, defaultTenant)));
printf("Run test:%s Done.\n", tests[idx].title.toString().c_str()); printf("Run test:%s Done.\n", tests[idx].title.toString().c_str());
// do we handle a failure here? // do we handle a failure here?
} }
@ -1568,7 +1581,8 @@ ACTOR Future<Void> runTests(Reference<AsyncVar<Optional<struct ClusterController
test_location_t at, test_location_t at,
int minTestersExpected, int minTestersExpected,
StringRef startingConfiguration, StringRef startingConfiguration,
LocalityData locality) { LocalityData locality,
Optional<TenantName> defaultTenant) {
state int flags = (at == TEST_ON_SERVERS ? 0 : GetWorkersRequest::TESTER_CLASS_ONLY) | state int flags = (at == TEST_ON_SERVERS ? 0 : GetWorkersRequest::TESTER_CLASS_ONLY) |
GetWorkersRequest::NON_EXCLUDED_PROCESSES_ONLY; GetWorkersRequest::NON_EXCLUDED_PROCESSES_ONLY;
state Future<Void> testerTimeout = delay(600.0); // wait 600 sec for testers to show up state Future<Void> testerTimeout = delay(600.0); // wait 600 sec for testers to show up
@ -1599,7 +1613,7 @@ ACTOR Future<Void> runTests(Reference<AsyncVar<Optional<struct ClusterController
for (int i = 0; i < workers.size(); i++) for (int i = 0; i < workers.size(); i++)
ts.push_back(workers[i].interf.testerInterface); ts.push_back(workers[i].interf.testerInterface);
wait(runTests(cc, ci, ts, tests, startingConfiguration, locality)); wait(runTests(cc, ci, ts, tests, startingConfiguration, locality, defaultTenant));
return Void(); return Void();
} }
@ -1636,7 +1650,8 @@ ACTOR Future<Void> runTests(Reference<IClusterConnectionRecord> connRecord,
std::string fileName, std::string fileName,
StringRef startingConfiguration, StringRef startingConfiguration,
LocalityData locality, LocalityData locality,
UnitTestParameters testOptions) { UnitTestParameters testOptions,
Optional<TenantName> defaultTenant) {
state std::vector<TestSpec> testSpecs; state std::vector<TestSpec> testSpecs;
auto cc = makeReference<AsyncVar<Optional<ClusterControllerFullInterface>>>(); auto cc = makeReference<AsyncVar<Optional<ClusterControllerFullInterface>>>();
auto ci = makeReference<AsyncVar<Optional<ClusterInterface>>>(); auto ci = makeReference<AsyncVar<Optional<ClusterInterface>>>();
@ -1718,10 +1733,11 @@ ACTOR Future<Void> runTests(Reference<IClusterConnectionRecord> connRecord,
actors.push_back( actors.push_back(
reportErrors(monitorServerDBInfo(cc, LocalityData(), db), "MonitorServerDBInfo")); // FIXME: Locality reportErrors(monitorServerDBInfo(cc, LocalityData(), db), "MonitorServerDBInfo")); // FIXME: Locality
actors.push_back(reportErrors(testerServerCore(iTesters[0], connRecord, db, locality), "TesterServerCore")); actors.push_back(reportErrors(testerServerCore(iTesters[0], connRecord, db, locality), "TesterServerCore"));
tests = runTests(cc, ci, iTesters, testSpecs, startingConfiguration, locality); tests = runTests(cc, ci, iTesters, testSpecs, startingConfiguration, locality, defaultTenant);
} else { } else {
tests = reportErrors(runTests(cc, ci, testSpecs, at, minTestersExpected, startingConfiguration, locality), tests = reportErrors(
"RunTests"); runTests(cc, ci, testSpecs, at, minTestersExpected, startingConfiguration, locality, defaultTenant),
"RunTests");
} }
choose { choose {

View File

@ -93,6 +93,10 @@ struct ConflictRangeWorkload : TestWorkload {
wait(timeKeeperSetDisable(cx)); wait(timeKeeperSetDisable(cx));
} }
// Set one key after the end of the tested range. If this key is included in the result, then
// we may have drifted into the system key-space and cannot evaluate the result.
state Key sentinelKey = StringRef(format("%010d", self->maxKeySpace));
loop { loop {
randomSets = !randomSets; randomSets = !randomSets;
@ -127,6 +131,8 @@ struct ConflictRangeWorkload : TestWorkload {
} }
} }
tr0.set(sentinelKey, deterministicRandom()->randomUniqueID().toString());
wait(tr0.commit()); wait(tr0.commit());
break; break;
} catch (Error& e) { } catch (Error& e) {
@ -177,7 +183,6 @@ struct ConflictRangeWorkload : TestWorkload {
if (self->testReadYourWrites) { if (self->testReadYourWrites) {
trRYOW.setVersion(readVersion); trRYOW.setVersion(readVersion);
trRYOW.setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
} else } else
tr3.setVersion(readVersion); tr3.setVersion(readVersion);
@ -262,7 +267,7 @@ struct ConflictRangeWorkload : TestWorkload {
throw not_committed(); throw not_committed();
} }
if (originalResults[originalResults.size() - 1].key >= LiteralStringRef("\xff")) { if (originalResults[originalResults.size() - 1].key >= sentinelKey) {
// Results go into server keyspace, so if a key selector does not fully resolve offset, a // Results go into server keyspace, so if a key selector does not fully resolve offset, a
// change won't effect results // change won't effect results
throw not_committed(); throw not_committed();
@ -316,7 +321,7 @@ struct ConflictRangeWorkload : TestWorkload {
allKeyEntries += printable(res[i].key) + " "; allKeyEntries += printable(res[i].key) + " ";
} }
TraceEvent("ConflictRangeDump").detail("Keys", allKeyEntries); TraceEvent("ConflictRangeDump").setMaxFieldLength(10000).detail("Keys", allKeyEntries);
} }
throw not_committed(); throw not_committed();
} else { } else {

View File

@ -20,6 +20,7 @@
#include <cstdint> #include <cstdint>
#include <limits> #include <limits>
#include "fdbclient/FDBOptions.g.h"
#include "fdbclient/NativeAPI.actor.h" #include "fdbclient/NativeAPI.actor.h"
#include "fdbclient/ManagementAPI.actor.h" #include "fdbclient/ManagementAPI.actor.h"
#include "fdbserver/MoveKeys.actor.h" #include "fdbserver/MoveKeys.actor.h"
@ -105,6 +106,7 @@ struct DataLossRecoveryWorkload : TestWorkload {
loop { loop {
try { try {
tr.setOption(FDBTransactionOptions::RAW_ACCESS);
state Optional<Value> res = wait(timeoutError(tr.get(key), 30.0)); state Optional<Value> res = wait(timeoutError(tr.get(key), 30.0));
const bool equal = !expectedValue.isError() && res == expectedValue.get(); const bool equal = !expectedValue.isError() && res == expectedValue.get();
if (!equal) { if (!equal) {
@ -126,6 +128,7 @@ struct DataLossRecoveryWorkload : TestWorkload {
state Transaction tr(cx); state Transaction tr(cx);
loop { loop {
try { try {
tr.setOption(FDBTransactionOptions::RAW_ACCESS);
if (value.present()) { if (value.present()) {
tr.set(key, value.get()); tr.set(key, value.get());
} else { } else {
@ -193,6 +196,7 @@ struct DataLossRecoveryWorkload : TestWorkload {
loop { loop {
try { try {
tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
BinaryWriter wrMyOwner(Unversioned()); BinaryWriter wrMyOwner(Unversioned());
wrMyOwner << owner; wrMyOwner << owner;
tr.set(moveKeysLockOwnerKey, wrMyOwner.toValue()); tr.set(moveKeysLockOwnerKey, wrMyOwner.toValue());
@ -228,6 +232,7 @@ struct DataLossRecoveryWorkload : TestWorkload {
state Transaction validateTr(cx); state Transaction validateTr(cx);
loop { loop {
try { try {
validateTr.setOption(FDBTransactionOptions::RAW_ACCESS);
Standalone<VectorRef<const char*>> addresses = wait(validateTr.getAddressesForKey(keys.begin)); Standalone<VectorRef<const char*>> addresses = wait(validateTr.getAddressesForKey(keys.begin));
// The move function is not what we are testing here, crash the test if the move fails. // The move function is not what we are testing here, crash the test if the move fails.
ASSERT(addresses.size() == 1); ASSERT(addresses.size() == 1);

View File

@ -23,7 +23,9 @@
#include <functional> #include <functional>
#include <sstream> #include <sstream>
#include "fdbclient/FDBOptions.g.h"
#include "fdbserver/TesterInterface.actor.h" #include "fdbserver/TesterInterface.actor.h"
#include "fdbclient/GenericManagementAPI.actor.h"
#include "fdbclient/ThreadSafeTransaction.h" #include "fdbclient/ThreadSafeTransaction.h"
#include "flow/ActorCollection.h" #include "flow/ActorCollection.h"
#include "fdbserver/workloads/workloads.actor.h" #include "fdbserver/workloads/workloads.actor.h"
@ -53,7 +55,7 @@ struct ExceptionContract {
static occurance_t requiredIf(bool in) { return in ? Always : Never; } static occurance_t requiredIf(bool in) { return in ? Always : Never; }
static occurance_t possibleIf(bool in) { return in ? Possible : Never; } static occurance_t possibleIf(bool in) { return in ? Possible : Never; }
void handleException(const Error& e) const { void handleException(const Error& e, Reference<ITransaction> tr) const {
// We should always ignore these. // We should always ignore these.
if (e.code() == error_code_used_during_commit || e.code() == error_code_transaction_too_old || if (e.code() == error_code_used_during_commit || e.code() == error_code_transaction_too_old ||
e.code() == error_code_future_version || e.code() == error_code_transaction_cancelled || e.code() == error_code_future_version || e.code() == error_code_transaction_cancelled ||
@ -70,6 +72,7 @@ struct ExceptionContract {
evt.error(e) evt.error(e)
.detail("Thrown", true) .detail("Thrown", true)
.detail("Expected", i->second == Possible ? "possible" : "always") .detail("Expected", i->second == Possible ? "possible" : "always")
.detail("Tenant", tr->getTenant())
.backtrace(); .backtrace();
if (augment) if (augment)
augment(evt); augment(evt);
@ -77,20 +80,21 @@ struct ExceptionContract {
} }
TraceEvent evt(SevError, func.c_str()); TraceEvent evt(SevError, func.c_str());
evt.error(e).detail("Thrown", true).detail("Expected", "never").backtrace(); evt.error(e).detail("Thrown", true).detail("Expected", "never").detail("Tenant", tr->getTenant()).backtrace();
if (augment) if (augment)
augment(evt); augment(evt);
throw e; throw e;
} }
// Return true if we should have thrown, but didn't. // Return true if we should have thrown, but didn't.
void handleNotThrown() const { void handleNotThrown(Reference<ITransaction> tr) const {
for (auto i : expected) { for (auto i : expected) {
if (i.second == Always) { if (i.second == Always) {
TraceEvent evt(SevError, func.c_str()); TraceEvent evt(SevError, func.c_str());
evt.error(Error::fromUnvalidatedCode(i.first)) evt.error(Error::fromUnvalidatedCode(i.first))
.detail("Thrown", false) .detail("Thrown", false)
.detail("Expected", "always") .detail("Expected", "always")
.detail("Tenant", tr->getTenant())
.backtrace(); .backtrace();
if (augment) if (augment)
augment(evt); augment(evt);
@ -113,7 +117,6 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
int maxClearSize; int maxClearSize;
double initialKeyDensity; double initialKeyDensity;
bool useSystemKeys; bool useSystemKeys;
std::string keyPrefix;
KeyRange conflictRange; KeyRange conflictRange;
unsigned int operationId; unsigned int operationId;
int64_t maximumTotalData; int64_t maximumTotalData;
@ -122,6 +125,15 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
bool success; bool success;
Reference<IDatabase> db;
std::vector<Reference<ITenant>> tenants;
std::set<TenantName> createdTenants;
int numTenants;
// Map from tenant number to key prefix
std::map<int, std::string> keyPrefixes;
FuzzApiCorrectnessWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), operationId(0), success(true) { FuzzApiCorrectnessWorkload(WorkloadContext const& wcx) : TestWorkload(wcx), operationId(0), success(true) {
std::call_once(onceFlag, [&]() { addTestCases(); }); std::call_once(onceFlag, [&]() { addTestCases(); });
@ -138,6 +150,9 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
// Only enable special keys writes when allowed to access system keys // Only enable special keys writes when allowed to access system keys
specialKeysWritesEnabled = useSystemKeys && deterministicRandom()->coinflip(); specialKeysWritesEnabled = useSystemKeys && deterministicRandom()->coinflip();
int maxTenants = getOption(options, "numTenants"_sr, 4);
numTenants = deterministicRandom()->randomInt(0, maxTenants + 1);
// See https://github.com/apple/foundationdb/issues/2424 // See https://github.com/apple/foundationdb/issues/2424
if (BUGGIFY) { if (BUGGIFY) {
enableBuggify(true, BuggifyType::Client); enableBuggify(true, BuggifyType::Client);
@ -150,18 +165,20 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
nodes = deterministicRandom()->randomInt(1, 4 << deterministicRandom()->randomInt(0, 20)); nodes = deterministicRandom()->randomInt(1, 4 << deterministicRandom()->randomInt(0, 20));
} }
int newNodes = std::min<int>(nodes, maximumTotalData / (getKeyForIndex(nodes).size() + valueSizeRange.second)); int newNodes =
std::min<int>(nodes, maximumTotalData / (getKeyForIndex(-1, nodes).size() + valueSizeRange.second));
minNode = std::max(minNode, nodes - newNodes); minNode = std::max(minNode, nodes - newNodes);
nodes = newNodes; nodes = newNodes;
if (useSystemKeys && deterministicRandom()->coinflip()) { if (useSystemKeys && deterministicRandom()->coinflip()) {
keyPrefix = "\xff\x01"; keyPrefixes[-1] = "\xff\x01";
} }
maxClearSize = 1 << deterministicRandom()->randomInt(0, 20); maxClearSize = 1 << deterministicRandom()->randomInt(0, 20);
conflictRange = KeyRangeRef(LiteralStringRef("\xfe"), LiteralStringRef("\xfe\x00")); conflictRange = KeyRangeRef(LiteralStringRef("\xfe"), LiteralStringRef("\xfe\x00"));
TraceEvent("FuzzApiCorrectnessConfiguration") TraceEvent("FuzzApiCorrectnessConfiguration")
.detail("Nodes", nodes) .detail("Nodes", nodes)
.detail("NumTenants", numTenants)
.detail("InitialKeyDensity", initialKeyDensity) .detail("InitialKeyDensity", initialKeyDensity)
.detail("AdjacentKeys", adjacentKeys) .detail("AdjacentKeys", adjacentKeys)
.detail("ValueSizeMin", valueSizeRange.first) .detail("ValueSizeMin", valueSizeRange.first)
@ -187,23 +204,59 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
std::string description() const override { return "FuzzApiCorrectness"; } std::string description() const override { return "FuzzApiCorrectness"; }
static TenantName getTenant(int num) { return TenantNameRef(format("tenant_%d", num)); }
bool canUseTenant(Optional<TenantName> tenant) { return !tenant.present() || createdTenants.count(tenant.get()); }
Future<Void> setup(Database const& cx) override {
if (clientId == 0) {
return _setup(cx, this);
}
return Void();
}
ACTOR Future<Void> _setup(Database cx, FuzzApiCorrectnessWorkload* self) {
Reference<IDatabase> db = wait(unsafeThreadFutureToFuture(ThreadSafeDatabase::createFromExistingDatabase(cx)));
self->db = db;
std::vector<Future<Void>> tenantFutures;
for (int i = 0; i < self->numTenants + 1; ++i) {
TenantName tenantName = getTenant(i);
self->tenants.push_back(self->db->openTenant(tenantName));
// The last tenant will not be created
if (i < self->numTenants) {
tenantFutures.push_back(ManagementAPI::createTenant(cx.getReference(), tenantName));
self->createdTenants.insert(tenantName);
}
}
wait(waitForAll(tenantFutures));
return Void();
}
Future<Void> start(Database const& cx) override { Future<Void> start(Database const& cx) override {
if (clientId == 0) { if (clientId == 0) {
return loadAndRun(cx, this); return loadAndRun(this);
} }
return Void(); return Void();
} }
Future<bool> check(Database const& cx) override { return success; } Future<bool> check(Database const& cx) override { return success; }
Key getRandomKey() const { return getKeyForIndex(deterministicRandom()->randomInt(0, nodes)); } Key getKeyForIndex(int tenantNum, int idx) {
Key getKeyForIndex(int idx) const {
idx += minNode; idx += minNode;
if (adjacentKeys) { if (adjacentKeys) {
return Key(keyPrefix + std::string(idx, '\x00')); return Key(keyPrefixes[tenantNum] + std::string(idx, '\x00'));
} else { } else {
return Key(keyPrefix + format("%010d", idx)); return Key(keyPrefixes[tenantNum] + format("%010d", idx));
}
}
KeyRef getMaxKey(Reference<ITransaction> tr) const {
if (useSystemKeys && !tr->getTenant().present()) {
return systemKeys.end;
} else {
return normalKeys.end;
} }
} }
@ -217,65 +270,88 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
// m.push_back( retries.getMetric() ); // m.push_back( retries.getMetric() );
} }
ACTOR Future<Void> loadAndRun(Database db, FuzzApiCorrectnessWorkload* self) { // Prevent a write only transaction whose commit was previously cancelled from being reordered after this
// transaction
ACTOR Future<Void> writeBarrier(Reference<IDatabase> db) {
state Reference<ITransaction> tr = db->createTransaction();
loop {
try {
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
// Write-only transactions have a self-conflict in the system keys
tr->addWriteConflictRange(allKeys);
tr->clear(normalKeys);
wait(unsafeThreadFutureToFuture(tr->commit()));
return Void();
} catch (Error& e) {
wait(unsafeThreadFutureToFuture(tr->onError(e)));
}
}
}
ACTOR Future<Void> loadAndRun(FuzzApiCorrectnessWorkload* self) {
state double startTime = now(); state double startTime = now();
state Reference<IDatabase> cx = state int nodesPerTenant = std::max<int>(1, self->nodes / (self->numTenants + 1));
wait(unsafeThreadFutureToFuture(ThreadSafeDatabase::createFromExistingDatabase(db))); state int keysPerBatch =
std::min<int64_t>(1000,
1 + CLIENT_KNOBS->TRANSACTION_SIZE_LIMIT / 2 /
(self->getKeyForIndex(-1, nodesPerTenant).size() + self->valueSizeRange.second));
try { try {
loop { loop {
state int i = 0; state int tenantNum = -1;
state int keysPerBatch = for (; tenantNum < self->numTenants; ++tenantNum) {
std::min<int64_t>(1000, state int i = 0;
1 + CLIENT_KNOBS->TRANSACTION_SIZE_LIMIT / 2 / wait(self->writeBarrier(self->db));
(self->getKeyForIndex(self->nodes).size() + self->valueSizeRange.second)); for (; i < nodesPerTenant; i += keysPerBatch) {
for (; i < self->nodes; i += keysPerBatch) { state Reference<ITransaction> tr = tenantNum < 0
state Reference<ITransaction> tr = cx->createTransaction(); ? self->db->createTransaction()
loop { : self->tenants[tenantNum]->createTransaction();
if (now() - startTime > self->testDuration) loop {
return Void(); if (now() - startTime > self->testDuration)
try { return Void();
if (i == 0) { try {
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); if (self->useSystemKeys && tenantNum == -1) {
tr->addWriteConflictRange( tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
allKeys); // To prevent a write only transaction whose commit was previously }
// cancelled from being reordered after this transaction if (self->specialKeysRelaxed)
tr->clear(normalKeys); tr->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_RELAXED);
} if (self->specialKeysWritesEnabled)
if (self->useSystemKeys) tr->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
if (self->specialKeysRelaxed)
tr->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_RELAXED);
if (self->specialKeysWritesEnabled)
tr->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
int end = std::min(self->nodes, i + keysPerBatch); if (i == 0) {
tr->clear(KeyRangeRef(self->getKeyForIndex(i), self->getKeyForIndex(end))); tr->clear(normalKeys);
}
for (int j = i; j < end; j++) { int end = std::min(nodesPerTenant, i + keysPerBatch);
if (deterministicRandom()->random01() < self->initialKeyDensity) { tr->clear(KeyRangeRef(self->getKeyForIndex(tenantNum, i),
Key key = self->getKeyForIndex(j); self->getKeyForIndex(tenantNum, end)));
if (key.size() <= (key.startsWith(systemKeys.begin)
? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT for (int j = i; j < end; j++) {
: CLIENT_KNOBS->KEY_SIZE_LIMIT)) { if (deterministicRandom()->random01() < self->initialKeyDensity) {
Value value = self->getRandomValue(); Key key = self->getKeyForIndex(tenantNum, j);
value = value.substr( if (key.size() <= (key.startsWith(systemKeys.begin)
0, std::min<int>(value.size(), CLIENT_KNOBS->VALUE_SIZE_LIMIT)); ? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT
tr->set(key, value); : CLIENT_KNOBS->KEY_SIZE_LIMIT)) {
Value value = self->getRandomValue();
value = value.substr(
0, std::min<int>(value.size(), CLIENT_KNOBS->VALUE_SIZE_LIMIT));
tr->set(key, value);
}
} }
} }
wait(unsafeThreadFutureToFuture(tr->commit()));
//TraceEvent("WDRInitBatch").detail("I", i).detail("CommittedVersion", tr->getCommittedVersion());
break;
} catch (Error& e) {
wait(unsafeThreadFutureToFuture(tr->onError(e)));
} }
wait(unsafeThreadFutureToFuture(tr->commit()));
//TraceEvent("WDRInitBatch").detail("I", i).detail("CommittedVersion", tr->getCommittedVersion());
break;
} catch (Error& e) {
wait(unsafeThreadFutureToFuture(tr->onError(e)));
} }
} }
} }
loop { loop {
try { try {
wait(self->randomTransaction(cx, self) && delay(self->numOps * .001)); wait(self->randomTransaction(self) && delay(self->numOps * .001));
} catch (Error& e) { } catch (Error& e) {
if (e.code() != error_code_not_committed) if (e.code() != error_code_not_committed)
throw e; throw e;
@ -291,20 +367,29 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
} }
} }
ACTOR Future<Void> randomTransaction(Reference<IDatabase> cx, FuzzApiCorrectnessWorkload* self) { ACTOR Future<Void> randomTransaction(FuzzApiCorrectnessWorkload* self) {
state Reference<ITransaction> tr = cx->createTransaction(); state Reference<ITransaction> tr;
state bool readYourWritesDisabled = deterministicRandom()->coinflip(); state bool readYourWritesDisabled = deterministicRandom()->coinflip();
state bool readAheadDisabled = deterministicRandom()->coinflip(); state bool readAheadDisabled = deterministicRandom()->coinflip();
state std::vector<Future<Void>> operations; state std::vector<Future<Void>> operations;
state int waitLocation = 0; state int waitLocation = 0;
state int tenantNum = deterministicRandom()->randomInt(-1, self->tenants.size());
if (tenantNum == -1) {
tr = self->db->createTransaction();
} else {
tr = self->tenants[tenantNum]->createTransaction();
}
state bool rawAccess = tenantNum == -1 && deterministicRandom()->coinflip();
loop { loop {
state bool cancelled = false; state bool cancelled = false;
if (readYourWritesDisabled) if (readYourWritesDisabled)
tr->setOption(FDBTransactionOptions::READ_YOUR_WRITES_DISABLE); tr->setOption(FDBTransactionOptions::READ_YOUR_WRITES_DISABLE);
if (readAheadDisabled) if (readAheadDisabled)
tr->setOption(FDBTransactionOptions::READ_AHEAD_DISABLE); tr->setOption(FDBTransactionOptions::READ_AHEAD_DISABLE);
if (self->useSystemKeys) { if (self->useSystemKeys && tenantNum == -1) {
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
} }
if (self->specialKeysRelaxed) { if (self->specialKeysRelaxed) {
@ -313,6 +398,9 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
if (self->specialKeysWritesEnabled) { if (self->specialKeysWritesEnabled) {
tr->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); tr->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
} }
if (rawAccess) {
tr->setOption(FDBTransactionOptions::RAW_ACCESS);
}
tr->addWriteConflictRange(self->conflictRange); tr->addWriteConflictRange(self->conflictRange);
try { try {
@ -350,7 +438,8 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
wait(timeoutError(unsafeThreadFutureToFuture(tr->commit()), 30)); wait(timeoutError(unsafeThreadFutureToFuture(tr->commit()), 30));
} catch (Error& e) { } catch (Error& e) {
if (e.code() == error_code_client_invalid_operation || if (e.code() == error_code_client_invalid_operation ||
e.code() == error_code_transaction_too_large) { e.code() == error_code_transaction_too_large || e.code() == error_code_unknown_tenant ||
e.code() == error_code_invalid_option) {
throw not_committed(); throw not_committed();
} }
} }
@ -380,14 +469,14 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
typedef T value_type; typedef T value_type;
ACTOR static Future<Void> runTest(unsigned int id, FuzzApiCorrectnessWorkload* wl, Reference<ITransaction> tr) { ACTOR static Future<Void> runTest(unsigned int id, FuzzApiCorrectnessWorkload* wl, Reference<ITransaction> tr) {
state Subclass self(id, wl); state Subclass self(id, wl, tr);
try { try {
value_type result = wait(timeoutError(BaseTest::runTest2(tr, &self), 1000)); value_type result = wait(timeoutError(BaseTest::runTest2(tr, &self), 1000));
self.contract.handleNotThrown(); self.contract.handleNotThrown(tr);
return self.errorCheck(tr, result); return self.errorCheck(tr, result);
} catch (Error& e) { } catch (Error& e) {
self.contract.handleException(e); self.contract.handleException(e, tr);
} }
return Void(); return Void();
} }
@ -404,7 +493,7 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
value_type result = wait(future); value_type result = wait(future);
if (future.isError()) { if (future.isError()) {
self->contract.handleException(future.getError()); self->contract.handleException(future.getError(), tr);
} else { } else {
ASSERT(future.isValid()); ASSERT(future.isValid());
} }
@ -588,7 +677,7 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
typedef BaseTest<TestSetVersion, Version> base_type; typedef BaseTest<TestSetVersion, Version> base_type;
Version v; Version v;
TestSetVersion(unsigned int id, FuzzApiCorrectnessWorkload* workload) TestSetVersion(unsigned int id, FuzzApiCorrectnessWorkload* workload, Reference<ITransaction> tr)
: BaseTest(id, workload, "TestSetVersion") { : BaseTest(id, workload, "TestSetVersion") {
if (deterministicRandom()->coinflip()) if (deterministicRandom()->coinflip())
v = deterministicRandom()->randomInt64(INT64_MIN, 0); v = deterministicRandom()->randomInt64(INT64_MIN, 0);
@ -620,13 +709,13 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
typedef BaseTest<TestGet, Optional<Value>> base_type; typedef BaseTest<TestGet, Optional<Value>> base_type;
Key key; Key key;
TestGet(unsigned int id, FuzzApiCorrectnessWorkload* workload) : BaseTest(id, workload, "TestGet") { TestGet(unsigned int id, FuzzApiCorrectnessWorkload* workload, Reference<ITransaction> tr)
: BaseTest(id, workload, "TestGet") {
key = makeKey(); key = makeKey();
contract = { contract = {
std::make_pair(error_code_key_outside_legal_range, std::make_pair(
ExceptionContract::requiredIf( error_code_key_outside_legal_range,
(key >= (workload->useSystemKeys ? systemKeys.end : normalKeys.end)) && ExceptionContract::requiredIf((key >= workload->getMaxKey(tr)) && !specialKeys.contains(key))),
!specialKeys.contains(key))),
std::make_pair(error_code_client_invalid_operation, ExceptionContract::Possible), std::make_pair(error_code_client_invalid_operation, ExceptionContract::Possible),
std::make_pair(error_code_accessed_unreadable, ExceptionContract::Possible), std::make_pair(error_code_accessed_unreadable, ExceptionContract::Possible),
std::make_pair( std::make_pair(
@ -641,7 +730,11 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
ExceptionContract::possibleIf( ExceptionContract::possibleIf(
key == key ==
LiteralStringRef("auto_coordinators") LiteralStringRef("auto_coordinators")
.withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin))) .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin))),
std::make_pair(error_code_tenant_not_found,
ExceptionContract::possibleIf(!workload->canUseTenant(tr->getTenant()))),
std::make_pair(error_code_invalid_option,
ExceptionContract::possibleIf(tr->getTenant().present() && specialKeys.contains(key)))
}; };
} }
@ -651,7 +744,8 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
void augmentTrace(TraceEvent& e) const override { void augmentTrace(TraceEvent& e) const override {
base_type::augmentTrace(e); base_type::augmentTrace(e);
e.detail("Key", printable(key)); e.detail("Key", key);
e.detail("Size", key.size());
} }
}; };
@ -659,14 +753,15 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
typedef BaseTest<TestGetKey, Key> base_type; typedef BaseTest<TestGetKey, Key> base_type;
KeySelector keysel; KeySelector keysel;
TestGetKey(unsigned int id, FuzzApiCorrectnessWorkload* workload) : BaseTest(id, workload, "TestGetKey") { TestGetKey(unsigned int id, FuzzApiCorrectnessWorkload* workload, Reference<ITransaction> tr)
: BaseTest(id, workload, "TestGetKey") {
keysel = makeKeySel(); keysel = makeKeySel();
contract = { std::make_pair( contract = { std::make_pair(error_code_key_outside_legal_range,
error_code_key_outside_legal_range, ExceptionContract::requiredIf((keysel.getKey() > workload->getMaxKey(tr)))),
ExceptionContract::requiredIf(
(keysel.getKey() > (workload->useSystemKeys ? systemKeys.end : normalKeys.end)))),
std::make_pair(error_code_client_invalid_operation, ExceptionContract::Possible), std::make_pair(error_code_client_invalid_operation, ExceptionContract::Possible),
std::make_pair(error_code_accessed_unreadable, ExceptionContract::Possible) }; std::make_pair(error_code_accessed_unreadable, ExceptionContract::Possible),
std::make_pair(error_code_tenant_not_found,
ExceptionContract::possibleIf(!workload->canUseTenant(tr->getTenant()))) };
} }
ThreadFuture<value_type> createFuture(Reference<ITransaction> tr) override { ThreadFuture<value_type> createFuture(Reference<ITransaction> tr) override {
@ -684,7 +779,8 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
KeySelector keysel1, keysel2; KeySelector keysel1, keysel2;
int limit; int limit;
TestGetRange0(unsigned int id, FuzzApiCorrectnessWorkload* workload) : BaseTest(id, workload, "TestGetRange0") { TestGetRange0(unsigned int id, FuzzApiCorrectnessWorkload* workload, Reference<ITransaction> tr)
: BaseTest(id, workload, "TestGetRange0") {
keysel1 = makeKeySel(); keysel1 = makeKeySel();
keysel2 = makeKeySel(); keysel2 = makeKeySel();
limit = 0; limit = 0;
@ -704,10 +800,9 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
std::make_pair(error_code_range_limits_invalid, ExceptionContract::possibleButRequiredIf(limit < 0)), std::make_pair(error_code_range_limits_invalid, ExceptionContract::possibleButRequiredIf(limit < 0)),
std::make_pair(error_code_client_invalid_operation, ExceptionContract::Possible), std::make_pair(error_code_client_invalid_operation, ExceptionContract::Possible),
std::make_pair(error_code_key_outside_legal_range, std::make_pair(error_code_key_outside_legal_range,
ExceptionContract::requiredIf( ExceptionContract::requiredIf(((keysel1.getKey() > workload->getMaxKey(tr)) ||
((keysel1.getKey() > (workload->useSystemKeys ? systemKeys.end : normalKeys.end)) || (keysel2.getKey() > workload->getMaxKey(tr))) &&
(keysel2.getKey() > (workload->useSystemKeys ? systemKeys.end : normalKeys.end))) && !isSpecialKeyRange)),
!isSpecialKeyRange)),
std::make_pair(error_code_special_keys_cross_module_read, std::make_pair(error_code_special_keys_cross_module_read,
ExceptionContract::possibleIf(isSpecialKeyRange && !workload->specialKeysRelaxed)), ExceptionContract::possibleIf(isSpecialKeyRange && !workload->specialKeysRelaxed)),
std::make_pair(error_code_special_keys_no_module_found, std::make_pair(error_code_special_keys_no_module_found,
@ -715,7 +810,11 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
// Read some special keys, e.g. status/json, can throw timed_out // Read some special keys, e.g. status/json, can throw timed_out
std::make_pair(error_code_timed_out, ExceptionContract::possibleIf(isSpecialKeyRange)), std::make_pair(error_code_timed_out, ExceptionContract::possibleIf(isSpecialKeyRange)),
std::make_pair(error_code_special_keys_api_failure, ExceptionContract::possibleIf(isSpecialKeyRange)), std::make_pair(error_code_special_keys_api_failure, ExceptionContract::possibleIf(isSpecialKeyRange)),
std::make_pair(error_code_accessed_unreadable, ExceptionContract::Possible) std::make_pair(error_code_accessed_unreadable, ExceptionContract::Possible),
std::make_pair(error_code_tenant_not_found,
ExceptionContract::possibleIf(!workload->canUseTenant(tr->getTenant()))),
std::make_pair(error_code_invalid_option,
ExceptionContract::possibleIf(tr->getTenant().present() && isSpecialKeyRange))
}; };
} }
@ -735,7 +834,8 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
KeySelector keysel1, keysel2; KeySelector keysel1, keysel2;
GetRangeLimits limits; GetRangeLimits limits;
TestGetRange1(unsigned int id, FuzzApiCorrectnessWorkload* workload) : BaseTest(id, workload, "TestGetRange1") { TestGetRange1(unsigned int id, FuzzApiCorrectnessWorkload* workload, Reference<ITransaction> tr)
: BaseTest(id, workload, "TestGetRange1") {
keysel1 = makeKeySel(); keysel1 = makeKeySel();
keysel2 = makeKeySel(); keysel2 = makeKeySel();
limits = makeRangeLimits(); limits = makeRangeLimits();
@ -748,17 +848,20 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
ExceptionContract::possibleButRequiredIf(!limits.isReached() && !limits.isValid())), ExceptionContract::possibleButRequiredIf(!limits.isReached() && !limits.isValid())),
std::make_pair(error_code_client_invalid_operation, ExceptionContract::Possible), std::make_pair(error_code_client_invalid_operation, ExceptionContract::Possible),
std::make_pair(error_code_key_outside_legal_range, std::make_pair(error_code_key_outside_legal_range,
ExceptionContract::requiredIf( ExceptionContract::requiredIf(((keysel1.getKey() > workload->getMaxKey(tr)) ||
((keysel1.getKey() > (workload->useSystemKeys ? systemKeys.end : normalKeys.end)) || (keysel2.getKey() > workload->getMaxKey(tr))) &&
(keysel2.getKey() > (workload->useSystemKeys ? systemKeys.end : normalKeys.end))) && !isSpecialKeyRange)),
!isSpecialKeyRange)),
std::make_pair(error_code_special_keys_cross_module_read, std::make_pair(error_code_special_keys_cross_module_read,
ExceptionContract::possibleIf(isSpecialKeyRange && !workload->specialKeysRelaxed)), ExceptionContract::possibleIf(isSpecialKeyRange && !workload->specialKeysRelaxed)),
std::make_pair(error_code_special_keys_no_module_found, std::make_pair(error_code_special_keys_no_module_found,
ExceptionContract::possibleIf(isSpecialKeyRange && !workload->specialKeysRelaxed)), ExceptionContract::possibleIf(isSpecialKeyRange && !workload->specialKeysRelaxed)),
std::make_pair(error_code_timed_out, ExceptionContract::possibleIf(isSpecialKeyRange)), std::make_pair(error_code_timed_out, ExceptionContract::possibleIf(isSpecialKeyRange)),
std::make_pair(error_code_special_keys_api_failure, ExceptionContract::possibleIf(isSpecialKeyRange)), std::make_pair(error_code_special_keys_api_failure, ExceptionContract::possibleIf(isSpecialKeyRange)),
std::make_pair(error_code_accessed_unreadable, ExceptionContract::Possible) std::make_pair(error_code_accessed_unreadable, ExceptionContract::Possible),
std::make_pair(error_code_tenant_not_found,
ExceptionContract::possibleIf(!workload->canUseTenant(tr->getTenant()))),
std::make_pair(error_code_invalid_option,
ExceptionContract::possibleIf(tr->getTenant().present() && isSpecialKeyRange))
}; };
} }
@ -781,7 +884,8 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
Key key1, key2; Key key1, key2;
int limit; int limit;
TestGetRange2(unsigned int id, FuzzApiCorrectnessWorkload* workload) : BaseTest(id, workload, "TestGetRange2") { TestGetRange2(unsigned int id, FuzzApiCorrectnessWorkload* workload, Reference<ITransaction> tr)
: BaseTest(id, workload, "TestGetRange2") {
key1 = makeKey(); key1 = makeKey();
key2 = makeKey(); key2 = makeKey();
limit = 0; limit = 0;
@ -806,11 +910,10 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
std::make_pair(error_code_inverted_range, ExceptionContract::requiredIf(key1 > key2)), std::make_pair(error_code_inverted_range, ExceptionContract::requiredIf(key1 > key2)),
std::make_pair(error_code_range_limits_invalid, ExceptionContract::possibleButRequiredIf(limit < 0)), std::make_pair(error_code_range_limits_invalid, ExceptionContract::possibleButRequiredIf(limit < 0)),
std::make_pair(error_code_client_invalid_operation, ExceptionContract::Possible), std::make_pair(error_code_client_invalid_operation, ExceptionContract::Possible),
std::make_pair(error_code_key_outside_legal_range, std::make_pair(
ExceptionContract::requiredIf( error_code_key_outside_legal_range,
((key1 > (workload->useSystemKeys ? systemKeys.end : normalKeys.end)) || ExceptionContract::requiredIf(
(key2 > (workload->useSystemKeys ? systemKeys.end : normalKeys.end))) && ((key1 > workload->getMaxKey(tr)) || (key2 > workload->getMaxKey(tr))) && !isSpecialKeyRange)),
!isSpecialKeyRange)),
std::make_pair(error_code_special_keys_cross_module_read, std::make_pair(error_code_special_keys_cross_module_read,
ExceptionContract::possibleIf(isSpecialKeyRange && !workload->specialKeysRelaxed)), ExceptionContract::possibleIf(isSpecialKeyRange && !workload->specialKeysRelaxed)),
std::make_pair(error_code_special_keys_no_module_found, std::make_pair(error_code_special_keys_no_module_found,
@ -821,7 +924,11 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
std::make_pair(error_code_special_keys_api_failure, std::make_pair(error_code_special_keys_api_failure,
ExceptionContract::possibleIf(key1 <= autoCoordinatorSpecialKey && ExceptionContract::possibleIf(key1 <= autoCoordinatorSpecialKey &&
autoCoordinatorSpecialKey < key2)), autoCoordinatorSpecialKey < key2)),
std::make_pair(error_code_accessed_unreadable, ExceptionContract::Possible) std::make_pair(error_code_accessed_unreadable, ExceptionContract::Possible),
std::make_pair(error_code_tenant_not_found,
ExceptionContract::possibleIf(!workload->canUseTenant(tr->getTenant()))),
std::make_pair(error_code_invalid_option,
ExceptionContract::possibleIf(tr->getTenant().present() && isSpecialKeyRange))
}; };
} }
@ -832,7 +939,7 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
void augmentTrace(TraceEvent& e) const override { void augmentTrace(TraceEvent& e) const override {
base_type::augmentTrace(e); base_type::augmentTrace(e);
e.detail("Key1", printable(key1)).detail("Key2", printable(key2)).detail("Limit", limit); e.detail("Key1", key1).detail("Key2", key2).detail("Limit", limit);
} }
}; };
@ -841,7 +948,8 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
Key key1, key2; Key key1, key2;
GetRangeLimits limits; GetRangeLimits limits;
TestGetRange3(unsigned int id, FuzzApiCorrectnessWorkload* workload) : BaseTest(id, workload, "TestGetRange3") { TestGetRange3(unsigned int id, FuzzApiCorrectnessWorkload* workload, Reference<ITransaction> tr)
: BaseTest(id, workload, "TestGetRange3") {
key1 = makeKey(); key1 = makeKey();
key2 = makeKey(); key2 = makeKey();
limits = makeRangeLimits(); limits = makeRangeLimits();
@ -857,11 +965,10 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
std::make_pair(error_code_range_limits_invalid, std::make_pair(error_code_range_limits_invalid,
ExceptionContract::possibleButRequiredIf(!limits.isReached() && !limits.isValid())), ExceptionContract::possibleButRequiredIf(!limits.isReached() && !limits.isValid())),
std::make_pair(error_code_client_invalid_operation, ExceptionContract::Possible), std::make_pair(error_code_client_invalid_operation, ExceptionContract::Possible),
std::make_pair(error_code_key_outside_legal_range, std::make_pair(
ExceptionContract::requiredIf( error_code_key_outside_legal_range,
((key1 > (workload->useSystemKeys ? systemKeys.end : normalKeys.end)) || ExceptionContract::requiredIf(
(key2 > (workload->useSystemKeys ? systemKeys.end : normalKeys.end))) && ((key1 > workload->getMaxKey(tr)) || (key2 > workload->getMaxKey(tr))) && !isSpecialKeyRange)),
!isSpecialKeyRange)),
std::make_pair(error_code_special_keys_cross_module_read, std::make_pair(error_code_special_keys_cross_module_read,
ExceptionContract::possibleIf(isSpecialKeyRange && !workload->specialKeysRelaxed)), ExceptionContract::possibleIf(isSpecialKeyRange && !workload->specialKeysRelaxed)),
std::make_pair(error_code_special_keys_no_module_found, std::make_pair(error_code_special_keys_no_module_found,
@ -872,7 +979,11 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
std::make_pair(error_code_special_keys_api_failure, std::make_pair(error_code_special_keys_api_failure,
ExceptionContract::possibleIf((key1 <= autoCoordinatorSpecialKey) && ExceptionContract::possibleIf((key1 <= autoCoordinatorSpecialKey) &&
(autoCoordinatorSpecialKey < key2))), (autoCoordinatorSpecialKey < key2))),
std::make_pair(error_code_accessed_unreadable, ExceptionContract::Possible) std::make_pair(error_code_accessed_unreadable, ExceptionContract::Possible),
std::make_pair(error_code_tenant_not_found,
ExceptionContract::possibleIf(!workload->canUseTenant(tr->getTenant()))),
std::make_pair(error_code_invalid_option,
ExceptionContract::possibleIf(tr->getTenant().present() && isSpecialKeyRange))
}; };
} }
@ -883,7 +994,7 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
void augmentTrace(TraceEvent& e) const override { void augmentTrace(TraceEvent& e) const override {
base_type::augmentTrace(e); base_type::augmentTrace(e);
e.detail("Key1", printable(key1)).detail("Key2", printable(key2)); e.detail("Key1", key1).detail("Key2", key2);
std::stringstream ss; std::stringstream ss;
ss << "(" << limits.rows << ", " << limits.minRows << ", " << limits.bytes << ")"; ss << "(" << limits.rows << ", " << limits.minRows << ", " << limits.bytes << ")";
e.detail("Limits", ss.str()); e.detail("Limits", ss.str());
@ -894,10 +1005,12 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
typedef BaseTest<TestGetAddressesForKey, Standalone<VectorRef<const char*>>> base_type; typedef BaseTest<TestGetAddressesForKey, Standalone<VectorRef<const char*>>> base_type;
Key key; Key key;
TestGetAddressesForKey(unsigned int id, FuzzApiCorrectnessWorkload* workload) TestGetAddressesForKey(unsigned int id, FuzzApiCorrectnessWorkload* workload, Reference<ITransaction> tr)
: BaseTest(id, workload, "TestGetAddressesForKey") { : BaseTest(id, workload, "TestGetAddressesForKey") {
key = makeKey(); key = makeKey();
contract = { std::make_pair(error_code_client_invalid_operation, ExceptionContract::Possible) }; contract = { std::make_pair(error_code_client_invalid_operation, ExceptionContract::Possible),
std::make_pair(error_code_tenant_not_found,
ExceptionContract::requiredIf(!workload->canUseTenant(tr->getTenant()))) };
} }
ThreadFuture<value_type> createFuture(Reference<ITransaction> tr) override { ThreadFuture<value_type> createFuture(Reference<ITransaction> tr) override {
@ -906,7 +1019,7 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
void augmentTrace(TraceEvent& e) const override { void augmentTrace(TraceEvent& e) const override {
base_type::augmentTrace(e); base_type::augmentTrace(e);
e.detail("Key", printable(key)); e.detail("Key", key);
} }
}; };
@ -914,22 +1027,21 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
typedef BaseTest<TestAddReadConflictRange, Void> base_type; typedef BaseTest<TestAddReadConflictRange, Void> base_type;
Key key1, key2; Key key1, key2;
TestAddReadConflictRange(unsigned int id, FuzzApiCorrectnessWorkload* workload) TestAddReadConflictRange(unsigned int id, FuzzApiCorrectnessWorkload* workload, Reference<ITransaction> tr)
: BaseTestCallback(id, workload, "TestAddReadConflictRange") { : BaseTestCallback(id, workload, "TestAddReadConflictRange") {
key1 = makeKey(); key1 = makeKey();
key2 = makeKey(); key2 = makeKey();
contract = { std::make_pair(error_code_inverted_range, ExceptionContract::requiredIf(key1 > key2)), contract = { std::make_pair(error_code_inverted_range, ExceptionContract::requiredIf(key1 > key2)),
std::make_pair(error_code_key_outside_legal_range, std::make_pair(error_code_key_outside_legal_range,
ExceptionContract::requiredIf( ExceptionContract::requiredIf((key1 > workload->getMaxKey(tr)) ||
(key1 > (workload->useSystemKeys ? systemKeys.end : normalKeys.end)) || (key2 > workload->getMaxKey(tr)))) };
(key2 > (workload->useSystemKeys ? systemKeys.end : normalKeys.end)))) };
} }
void callback(Reference<ITransaction> tr) override { tr->addReadConflictRange(KeyRangeRef(key1, key2)); } void callback(Reference<ITransaction> tr) override { tr->addReadConflictRange(KeyRangeRef(key1, key2)); }
void augmentTrace(TraceEvent& e) const override { void augmentTrace(TraceEvent& e) const override {
base_type::augmentTrace(e); base_type::augmentTrace(e);
e.detail("Key1", printable(key1)).detail("Key2", printable(key2)); e.detail("Key1", key1).detail("Key2", key2);
} }
}; };
@ -940,7 +1052,7 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
uint8_t op; uint8_t op;
int32_t pos; int32_t pos;
TestAtomicOp(unsigned int id, FuzzApiCorrectnessWorkload* workload) TestAtomicOp(unsigned int id, FuzzApiCorrectnessWorkload* workload, Reference<ITransaction> tr)
: BaseTestCallback(id, workload, "TestAtomicOp") { : BaseTestCallback(id, workload, "TestAtomicOp") {
key = makeKey(); key = makeKey();
while (isProtectedKey(key)) { while (isProtectedKey(key)) {
@ -990,8 +1102,7 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
error_code_invalid_mutation_type, error_code_invalid_mutation_type,
ExceptionContract::requiredIf(!isValidMutationType(op) || !isAtomicOp((MutationRef::Type)op))), ExceptionContract::requiredIf(!isValidMutationType(op) || !isAtomicOp((MutationRef::Type)op))),
std::make_pair(error_code_key_outside_legal_range, std::make_pair(error_code_key_outside_legal_range,
ExceptionContract::requiredIf( ExceptionContract::requiredIf((key >= workload->getMaxKey(tr)))),
(key >= (workload->useSystemKeys ? systemKeys.end : normalKeys.end)))),
std::make_pair( std::make_pair(
error_code_client_invalid_operation, error_code_client_invalid_operation,
ExceptionContract::requiredIf( ExceptionContract::requiredIf(
@ -1004,7 +1115,7 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
void augmentTrace(TraceEvent& e) const override { void augmentTrace(TraceEvent& e) const override {
base_type::augmentTrace(e); base_type::augmentTrace(e);
e.detail("Key", printable(key)).detail("Value", printable(value)).detail("Op", op).detail("Pos", pos); e.detail("Key", key).detail("Value", value).detail("Op", op).detail("Pos", pos);
} }
}; };
@ -1013,7 +1124,8 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
Key key; Key key;
Value value; Value value;
TestSet(unsigned int id, FuzzApiCorrectnessWorkload* workload) : BaseTestCallback(id, workload, "TestSet") { TestSet(unsigned int id, FuzzApiCorrectnessWorkload* workload, Reference<ITransaction> tr)
: BaseTestCallback(id, workload, "TestSet") {
key = makeKey(); key = makeKey();
while (isProtectedKey(key)) { while (isProtectedKey(key)) {
key = makeKey(); key = makeKey();
@ -1027,9 +1139,8 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
std::make_pair(error_code_value_too_large, std::make_pair(error_code_value_too_large,
ExceptionContract::requiredIf(value.size() > CLIENT_KNOBS->VALUE_SIZE_LIMIT)), ExceptionContract::requiredIf(value.size() > CLIENT_KNOBS->VALUE_SIZE_LIMIT)),
std::make_pair(error_code_key_outside_legal_range, std::make_pair(error_code_key_outside_legal_range,
ExceptionContract::requiredIf( ExceptionContract::requiredIf((key >= workload->getMaxKey(tr)) &&
(key >= (workload->useSystemKeys ? systemKeys.end : normalKeys.end)) && !specialKeys.contains(key))),
!specialKeys.contains(key))),
std::make_pair(error_code_special_keys_write_disabled, std::make_pair(error_code_special_keys_write_disabled,
ExceptionContract::requiredIf(specialKeys.contains(key) && ExceptionContract::requiredIf(specialKeys.contains(key) &&
!workload->specialKeysWritesEnabled)), !workload->specialKeysWritesEnabled)),
@ -1042,7 +1153,7 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
void augmentTrace(TraceEvent& e) const override { void augmentTrace(TraceEvent& e) const override {
base_type::augmentTrace(e); base_type::augmentTrace(e);
e.detail("Key", printable(key)).detail("Value", printable(value)); e.detail("Key", key).detail("Value", value);
} }
}; };
@ -1050,7 +1161,7 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
typedef BaseTest<TestClear0, Void> base_type; typedef BaseTest<TestClear0, Void> base_type;
Key key1, key2; Key key1, key2;
TestClear0(unsigned int id, FuzzApiCorrectnessWorkload* workload) TestClear0(unsigned int id, FuzzApiCorrectnessWorkload* workload, Reference<ITransaction> tr)
: BaseTestCallback(id, workload, "TestClear0") { : BaseTestCallback(id, workload, "TestClear0") {
key1 = makeKey(); key1 = makeKey();
key2 = makeKey(); key2 = makeKey();
@ -1063,11 +1174,10 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
contract = { contract = {
std::make_pair(error_code_inverted_range, ExceptionContract::requiredIf(key1 > key2)), std::make_pair(error_code_inverted_range, ExceptionContract::requiredIf(key1 > key2)),
std::make_pair(error_code_key_outside_legal_range, std::make_pair(
ExceptionContract::requiredIf( error_code_key_outside_legal_range,
((key1 > (workload->useSystemKeys ? systemKeys.end : normalKeys.end)) || ExceptionContract::requiredIf(
(key2 > (workload->useSystemKeys ? systemKeys.end : normalKeys.end))) && ((key1 > workload->getMaxKey(tr)) || (key2 > workload->getMaxKey(tr))) && !isSpecialKeyRange)),
!isSpecialKeyRange)),
std::make_pair(error_code_special_keys_write_disabled, std::make_pair(error_code_special_keys_write_disabled,
ExceptionContract::requiredIf(isSpecialKeyRange && !workload->specialKeysWritesEnabled)), ExceptionContract::requiredIf(isSpecialKeyRange && !workload->specialKeysWritesEnabled)),
std::make_pair(error_code_special_keys_cross_module_clear, std::make_pair(error_code_special_keys_cross_module_clear,
@ -1081,7 +1191,7 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
void augmentTrace(TraceEvent& e) const override { void augmentTrace(TraceEvent& e) const override {
base_type::augmentTrace(e); base_type::augmentTrace(e);
e.detail("Key1", printable(key1)).detail("Key2", printable(key2)); e.detail("Key1", key1).detail("Key2", key2);
} }
}; };
@ -1089,7 +1199,7 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
typedef BaseTest<TestClear1, Void> base_type; typedef BaseTest<TestClear1, Void> base_type;
Key key1, key2; Key key1, key2;
TestClear1(unsigned int id, FuzzApiCorrectnessWorkload* workload) TestClear1(unsigned int id, FuzzApiCorrectnessWorkload* workload, Reference<ITransaction> tr)
: BaseTestCallback(id, workload, "TestClear1") { : BaseTestCallback(id, workload, "TestClear1") {
key1 = makeKey(); key1 = makeKey();
key2 = makeKey(); key2 = makeKey();
@ -1102,11 +1212,10 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
contract = { contract = {
std::make_pair(error_code_inverted_range, ExceptionContract::requiredIf(key1 > key2)), std::make_pair(error_code_inverted_range, ExceptionContract::requiredIf(key1 > key2)),
std::make_pair(error_code_key_outside_legal_range, std::make_pair(
ExceptionContract::requiredIf( error_code_key_outside_legal_range,
((key1 > (workload->useSystemKeys ? systemKeys.end : normalKeys.end)) || ExceptionContract::requiredIf(
(key2 > (workload->useSystemKeys ? systemKeys.end : normalKeys.end))) && ((key1 > workload->getMaxKey(tr)) || (key2 > workload->getMaxKey(tr))) && !isSpecialKeyRange)),
!isSpecialKeyRange)),
std::make_pair(error_code_special_keys_write_disabled, std::make_pair(error_code_special_keys_write_disabled,
ExceptionContract::requiredIf(isSpecialKeyRange && !workload->specialKeysWritesEnabled)), ExceptionContract::requiredIf(isSpecialKeyRange && !workload->specialKeysWritesEnabled)),
std::make_pair(error_code_special_keys_cross_module_clear, std::make_pair(error_code_special_keys_cross_module_clear,
@ -1120,7 +1229,7 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
void augmentTrace(TraceEvent& e) const override { void augmentTrace(TraceEvent& e) const override {
base_type::augmentTrace(e); base_type::augmentTrace(e);
e.detail("Key1", printable(key1)).detail("Key2", printable(key2)); e.detail("Key1", key1).detail("Key2", key2);
} }
}; };
@ -1128,15 +1237,14 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
typedef BaseTest<TestClear2, Void> base_type; typedef BaseTest<TestClear2, Void> base_type;
Key key; Key key;
TestClear2(unsigned int id, FuzzApiCorrectnessWorkload* workload) TestClear2(unsigned int id, FuzzApiCorrectnessWorkload* workload, Reference<ITransaction> tr)
: BaseTestCallback(id, workload, "TestClear2") { : BaseTestCallback(id, workload, "TestClear2") {
key = makeKey(); key = makeKey();
while (isProtectedKey(key)) { while (isProtectedKey(key)) {
key = makeKey(); key = makeKey();
} }
contract = { std::make_pair(error_code_key_outside_legal_range, contract = { std::make_pair(error_code_key_outside_legal_range,
ExceptionContract::requiredIf( ExceptionContract::requiredIf(key >= workload->getMaxKey(tr))),
key >= (workload->useSystemKeys ? systemKeys.end : normalKeys.end))),
std::make_pair(error_code_special_keys_write_disabled, std::make_pair(error_code_special_keys_write_disabled,
ExceptionContract::requiredIf(specialKeys.contains(key) && ExceptionContract::requiredIf(specialKeys.contains(key) &&
!workload->specialKeysWritesEnabled)), !workload->specialKeysWritesEnabled)),
@ -1149,7 +1257,7 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
void augmentTrace(TraceEvent& e) const override { void augmentTrace(TraceEvent& e) const override {
base_type::augmentTrace(e); base_type::augmentTrace(e);
e.detail("Key", printable(key)); e.detail("Key", key);
} }
}; };
@ -1157,7 +1265,8 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
typedef BaseTest<TestWatch, Void> base_type; typedef BaseTest<TestWatch, Void> base_type;
Key key; Key key;
TestWatch(unsigned int id, FuzzApiCorrectnessWorkload* workload) : BaseTest(id, workload, "TestWatch") { TestWatch(unsigned int id, FuzzApiCorrectnessWorkload* workload, Reference<ITransaction> tr)
: BaseTest(id, workload, "TestWatch") {
key = makeKey(); key = makeKey();
contract = { std::make_pair( contract = { std::make_pair(
error_code_key_too_large, error_code_key_too_large,
@ -1166,18 +1275,19 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
: CLIENT_KNOBS->KEY_SIZE_LIMIT))), : CLIENT_KNOBS->KEY_SIZE_LIMIT))),
std::make_pair(error_code_watches_disabled, ExceptionContract::Possible), std::make_pair(error_code_watches_disabled, ExceptionContract::Possible),
std::make_pair(error_code_key_outside_legal_range, std::make_pair(error_code_key_outside_legal_range,
ExceptionContract::requiredIf( ExceptionContract::requiredIf((key >= workload->getMaxKey(tr)))),
(key >= (workload->useSystemKeys ? systemKeys.end : normalKeys.end)))),
std::make_pair(error_code_client_invalid_operation, ExceptionContract::Possible), std::make_pair(error_code_client_invalid_operation, ExceptionContract::Possible),
std::make_pair(error_code_timed_out, ExceptionContract::Possible), std::make_pair(error_code_timed_out, ExceptionContract::Possible),
std::make_pair(error_code_accessed_unreadable, ExceptionContract::Possible) }; std::make_pair(error_code_accessed_unreadable, ExceptionContract::Possible),
std::make_pair(error_code_tenant_not_found,
ExceptionContract::possibleIf(!workload->canUseTenant(tr->getTenant()))) };
} }
ThreadFuture<value_type> createFuture(Reference<ITransaction> tr) override { return tr->watch(key); } ThreadFuture<value_type> createFuture(Reference<ITransaction> tr) override { return tr->watch(key); }
void augmentTrace(TraceEvent& e) const override { void augmentTrace(TraceEvent& e) const override {
base_type::augmentTrace(e); base_type::augmentTrace(e);
e.detail("Key", printable(key)); e.detail("Key", key);
} }
}; };
@ -1185,22 +1295,21 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
typedef BaseTest<TestAddWriteConflictRange, Void> base_type; typedef BaseTest<TestAddWriteConflictRange, Void> base_type;
Key key1, key2; Key key1, key2;
TestAddWriteConflictRange(unsigned int id, FuzzApiCorrectnessWorkload* workload) TestAddWriteConflictRange(unsigned int id, FuzzApiCorrectnessWorkload* workload, Reference<ITransaction> tr)
: BaseTestCallback(id, workload, "TestAddWriteConflictRange") { : BaseTestCallback(id, workload, "TestAddWriteConflictRange") {
key1 = makeKey(); key1 = makeKey();
key2 = makeKey(); key2 = makeKey();
contract = { std::make_pair(error_code_inverted_range, ExceptionContract::requiredIf(key1 > key2)), contract = { std::make_pair(error_code_inverted_range, ExceptionContract::requiredIf(key1 > key2)),
std::make_pair(error_code_key_outside_legal_range, std::make_pair(error_code_key_outside_legal_range,
ExceptionContract::requiredIf( ExceptionContract::requiredIf((key1 > workload->getMaxKey(tr)) ||
(key1 > (workload->useSystemKeys ? systemKeys.end : normalKeys.end)) || (key2 > workload->getMaxKey(tr)))) };
(key2 > (workload->useSystemKeys ? systemKeys.end : normalKeys.end)))) };
} }
void callback(Reference<ITransaction> tr) override { tr->addWriteConflictRange(KeyRangeRef(key1, key2)); } void callback(Reference<ITransaction> tr) override { tr->addWriteConflictRange(KeyRangeRef(key1, key2)); }
void augmentTrace(TraceEvent& e) const override { void augmentTrace(TraceEvent& e) const override {
base_type::augmentTrace(e); base_type::augmentTrace(e);
e.detail("Key1", printable(key1)).detail("Key2", printable(key2)); e.detail("Key1", key1).detail("Key2", key2);
} }
}; };
@ -1209,7 +1318,7 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
int op; int op;
Optional<Standalone<StringRef>> val; Optional<Standalone<StringRef>> val;
TestSetOption(unsigned int id, FuzzApiCorrectnessWorkload* workload) TestSetOption(unsigned int id, FuzzApiCorrectnessWorkload* workload, Reference<ITransaction> tr)
: BaseTestCallback(id, workload, "TestSetOption") { : BaseTestCallback(id, workload, "TestSetOption") {
double arv = deterministicRandom()->random01(); double arv = deterministicRandom()->random01();
if (arv < 0.25) { if (arv < 0.25) {
@ -1240,7 +1349,8 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
// do not test the following options since they are actually used by the workload // do not test the following options since they are actually used by the workload
if (op == FDBTransactionOptions::ACCESS_SYSTEM_KEYS || op == FDBTransactionOptions::READ_SYSTEM_KEYS || if (op == FDBTransactionOptions::ACCESS_SYSTEM_KEYS || op == FDBTransactionOptions::READ_SYSTEM_KEYS ||
op == FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES) { op == FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES ||
op == FDBTransactionOptions::RAW_ACCESS) {
op = -1; op = -1;
} }
@ -1285,7 +1395,7 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
void augmentTrace(TraceEvent& e) const override { void augmentTrace(TraceEvent& e) const override {
base_type::augmentTrace(e); base_type::augmentTrace(e);
e.detail("Op", op).detail("Val", printable(val)); e.detail("Op", op).detail("Val", val);
} }
}; };
@ -1293,7 +1403,7 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
typedef BaseTest<TestOnError, Void> base_type; typedef BaseTest<TestOnError, Void> base_type;
int errorcode; int errorcode;
TestOnError(unsigned int id, FuzzApiCorrectnessWorkload* workload) TestOnError(unsigned int id, FuzzApiCorrectnessWorkload* workload, Reference<ITransaction> tr)
: BaseTestCallback(id, workload, "TestOnError") { : BaseTestCallback(id, workload, "TestOnError") {
errorcode = 0; errorcode = 0;
double erv = deterministicRandom()->random01(); double erv = deterministicRandom()->random01();
@ -1309,7 +1419,7 @@ struct FuzzApiCorrectnessWorkload : TestWorkload {
tr->onError(Error::fromUnvalidatedCode(errorcode)); tr->onError(Error::fromUnvalidatedCode(errorcode));
// This is necessary here, as onError will have reset this // This is necessary here, as onError will have reset this
// value, we will be looking at the wrong thing. // value, we will be looking at the wrong thing.
if (workload->useSystemKeys) if (workload->useSystemKeys && !tr->getTenant().present())
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
} }

View File

@ -18,6 +18,7 @@
* limitations under the License. * limitations under the License.
*/ */
#include "fdbclient/FDBOptions.g.h"
#include "fdbclient/NativeAPI.actor.h" #include "fdbclient/NativeAPI.actor.h"
#include "fdbserver/TesterInterface.actor.h" #include "fdbserver/TesterInterface.actor.h"
#include "fdbserver/workloads/workloads.actor.h" #include "fdbserver/workloads/workloads.actor.h"
@ -54,6 +55,7 @@ struct LockDatabaseWorkload : TestWorkload {
state Transaction tr(cx); state Transaction tr(cx);
loop { loop {
try { try {
tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
wait(lockDatabase(&tr, lockID)); wait(lockDatabase(&tr, lockID));
state RangeResult data = wait(tr.getRange(normalKeys, 50000)); state RangeResult data = wait(tr.getRange(normalKeys, 50000));
ASSERT(!data.more); ASSERT(!data.more);
@ -70,6 +72,7 @@ struct LockDatabaseWorkload : TestWorkload {
loop { loop {
try { try {
tr.setOption(FDBTransactionOptions::LOCK_AWARE); tr.setOption(FDBTransactionOptions::LOCK_AWARE);
tr.setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
Optional<Value> val = wait(tr.get(databaseLockedKey)); Optional<Value> val = wait(tr.get(databaseLockedKey));
if (!val.present()) if (!val.present())
return Void(); return Void();

View File

@ -18,6 +18,7 @@
* limitations under the License. * limitations under the License.
*/ */
#include "fdbclient/FDBOptions.g.h"
#include "fdbclient/NativeAPI.actor.h" #include "fdbclient/NativeAPI.actor.h"
#include "fdbclient/CoordinationInterface.h" #include "fdbclient/CoordinationInterface.h"
#include "fdbserver/TesterInterface.actor.h" #include "fdbserver/TesterInterface.actor.h"
@ -48,6 +49,7 @@ ACTOR Future<bool> ignoreSSFailuresForDuration(Database cx, double duration) {
loop { loop {
try { try {
tr.setOption(FDBTransactionOptions::LOCK_AWARE); tr.setOption(FDBTransactionOptions::LOCK_AWARE);
tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
tr.clear(healthyZoneKey); tr.clear(healthyZoneKey);
wait(tr.commit()); wait(tr.commit());
TraceEvent("IgnoreSSFailureComplete").log(); TraceEvent("IgnoreSSFailureComplete").log();

View File

@ -137,7 +137,7 @@ struct PerformanceWorkload : TestWorkload {
TestSpec spec(LiteralStringRef("PerformanceSetup"), false, false); TestSpec spec(LiteralStringRef("PerformanceSetup"), false, false);
spec.options = options; spec.options = options;
spec.phases = TestWorkload::SETUP; spec.phases = TestWorkload::SETUP;
DistributedTestResults results = wait(runWorkload(cx, testers, spec)); DistributedTestResults results = wait(runWorkload(cx, testers, spec, Optional<TenantName>()));
return Void(); return Void();
} }
@ -172,7 +172,7 @@ struct PerformanceWorkload : TestWorkload {
TestSpec spec(LiteralStringRef("PerformanceRun"), false, false); TestSpec spec(LiteralStringRef("PerformanceRun"), false, false);
spec.phases = TestWorkload::EXECUTION | TestWorkload::METRICS; spec.phases = TestWorkload::EXECUTION | TestWorkload::METRICS;
spec.options = options; spec.options = options;
DistributedTestResults r = wait(runWorkload(cx, self->testers, spec)); DistributedTestResults r = wait(runWorkload(cx, self->testers, spec, Optional<TenantName>()));
results = r; results = r;
} catch (Error& e) { } catch (Error& e) {
TraceEvent("PerformanceRunError") TraceEvent("PerformanceRunError")

View File

@ -77,11 +77,11 @@ struct SSCheckpointWorkload : TestWorkload {
// Create checkpoint. // Create checkpoint.
state Transaction tr(cx); state Transaction tr(cx);
tr.setOption(FDBTransactionOptions::LOCK_AWARE);
tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
state CheckpointFormat format = RocksDBColumnFamily; state CheckpointFormat format = RocksDBColumnFamily;
loop { loop {
try { try {
tr.setOption(FDBTransactionOptions::LOCK_AWARE);
tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
wait(createCheckpoint(&tr, KeyRangeRef(key, endKey), format)); wait(createCheckpoint(&tr, KeyRangeRef(key, endKey), format));
wait(tr.commit()); wait(tr.commit());
version = tr.getCommittedVersion(); version = tr.getCommittedVersion();
@ -157,9 +157,10 @@ struct SSCheckpointWorkload : TestWorkload {
// Compare the keyrange between the original database and the one restored from checkpoint. // Compare the keyrange between the original database and the one restored from checkpoint.
// For now, it should have been a single key. // For now, it should have been a single key.
tr.reset(); tr.reset();
tr.setOption(FDBTransactionOptions::LOCK_AWARE);
loop { loop {
try { try {
tr.setOption(FDBTransactionOptions::LOCK_AWARE);
tr.setOption(FDBTransactionOptions::RAW_ACCESS);
state RangeResult res = wait(tr.getRange(KeyRangeRef(key, endKey), CLIENT_KNOBS->TOO_MANY)); state RangeResult res = wait(tr.getRange(KeyRangeRef(key, endKey), CLIENT_KNOBS->TOO_MANY));
break; break;
} catch (Error& e) { } catch (Error& e) {
@ -182,10 +183,10 @@ struct SSCheckpointWorkload : TestWorkload {
Key key, Key key,
ErrorOr<Optional<Value>> expectedValue) { ErrorOr<Optional<Value>> expectedValue) {
state Transaction tr(cx); state Transaction tr(cx);
tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
loop { loop {
try { try {
tr.setOption(FDBTransactionOptions::RAW_ACCESS);
state Optional<Value> res = wait(timeoutError(tr.get(key), 30.0)); state Optional<Value> res = wait(timeoutError(tr.get(key), 30.0));
const bool equal = !expectedValue.isError() && res == expectedValue.get(); const bool equal = !expectedValue.isError() && res == expectedValue.get();
if (!equal) { if (!equal) {
@ -208,6 +209,7 @@ struct SSCheckpointWorkload : TestWorkload {
state Version version; state Version version;
loop { loop {
try { try {
tr.setOption(FDBTransactionOptions::RAW_ACCESS);
if (value.present()) { if (value.present()) {
tr.set(key, value.get()); tr.set(key, value.get());
} else { } else {

View File

@ -118,8 +118,6 @@ struct SelectorCorrectnessWorkload : TestWorkload {
state Transaction tr(cx); state Transaction tr(cx);
state ReadYourWritesTransaction trRYOW(cx); state ReadYourWritesTransaction trRYOW(cx);
trRYOW.setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
if (self->testReadYourWrites) { if (self->testReadYourWrites) {
myValue = StringRef(format("%010d", deterministicRandom()->randomInt(0, 10000000))); myValue = StringRef(format("%010d", deterministicRandom()->randomInt(0, 10000000)));
for (int i = 2; i < self->maxKeySpace; i += 4) for (int i = 2; i < self->maxKeySpace; i += 4)

View File

@ -70,6 +70,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
Future<Void> _setup(Database cx, SpecialKeySpaceCorrectnessWorkload* self) { Future<Void> _setup(Database cx, SpecialKeySpaceCorrectnessWorkload* self) {
cx->specialKeySpace = std::make_unique<SpecialKeySpace>(); cx->specialKeySpace = std::make_unique<SpecialKeySpace>();
self->ryw = makeReference<ReadYourWritesTransaction>(cx); self->ryw = makeReference<ReadYourWritesTransaction>(cx);
self->ryw->setOption(FDBTransactionOptions::RAW_ACCESS);
self->ryw->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_RELAXED); self->ryw->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_RELAXED);
self->ryw->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); self->ryw->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
self->ryw->setVersion(100); self->ryw->setVersion(100);
@ -291,6 +292,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
state Reference<ReadYourWritesTransaction> tx = makeReference<ReadYourWritesTransaction>(cx); state Reference<ReadYourWritesTransaction> tx = makeReference<ReadYourWritesTransaction>(cx);
// begin key outside module range // begin key outside module range
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
wait(success(tx->getRange( wait(success(tx->getRange(
KeyRangeRef(LiteralStringRef("\xff\xff/transactio"), LiteralStringRef("\xff\xff/transaction0")), KeyRangeRef(LiteralStringRef("\xff\xff/transactio"), LiteralStringRef("\xff\xff/transaction0")),
CLIENT_KNOBS->TOO_MANY))); CLIENT_KNOBS->TOO_MANY)));
@ -303,6 +305,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
} }
// end key outside module range // end key outside module range
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
wait(success(tx->getRange( wait(success(tx->getRange(
KeyRangeRef(LiteralStringRef("\xff\xff/transaction/"), LiteralStringRef("\xff\xff/transaction1")), KeyRangeRef(LiteralStringRef("\xff\xff/transaction/"), LiteralStringRef("\xff\xff/transaction1")),
CLIENT_KNOBS->TOO_MANY))); CLIENT_KNOBS->TOO_MANY)));
@ -315,6 +318,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
} }
// both begin and end outside module range // both begin and end outside module range
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
wait(success(tx->getRange( wait(success(tx->getRange(
KeyRangeRef(LiteralStringRef("\xff\xff/transaction"), LiteralStringRef("\xff\xff/transaction1")), KeyRangeRef(LiteralStringRef("\xff\xff/transaction"), LiteralStringRef("\xff\xff/transaction1")),
CLIENT_KNOBS->TOO_MANY))); CLIENT_KNOBS->TOO_MANY)));
@ -327,6 +331,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
} }
// legal range read using the module range // legal range read using the module range
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
wait(success(tx->getRange( wait(success(tx->getRange(
KeyRangeRef(LiteralStringRef("\xff\xff/transaction/"), LiteralStringRef("\xff\xff/transaction0")), KeyRangeRef(LiteralStringRef("\xff\xff/transaction/"), LiteralStringRef("\xff\xff/transaction0")),
CLIENT_KNOBS->TOO_MANY))); CLIENT_KNOBS->TOO_MANY)));
@ -337,6 +342,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
} }
// cross module read with option turned on // cross module read with option turned on
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_RELAXED); tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_RELAXED);
const KeyRef startKey = LiteralStringRef("\xff\xff/transactio"); const KeyRef startKey = LiteralStringRef("\xff\xff/transactio");
const KeyRef endKey = LiteralStringRef("\xff\xff/transaction1"); const KeyRef endKey = LiteralStringRef("\xff\xff/transaction1");
@ -350,6 +356,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
} }
// end keySelector inside module range, *** a tricky corner case *** // end keySelector inside module range, *** a tricky corner case ***
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
tx->addReadConflictRange(singleKeyRange(LiteralStringRef("testKey"))); tx->addReadConflictRange(singleKeyRange(LiteralStringRef("testKey")));
KeySelector begin = KeySelectorRef(readConflictRangeKeysRange.begin, false, 1); KeySelector begin = KeySelectorRef(readConflictRangeKeysRange.begin, false, 1);
KeySelector end = KeySelectorRef(LiteralStringRef("\xff\xff/transaction0"), false, 0); KeySelector end = KeySelectorRef(LiteralStringRef("\xff\xff/transaction0"), false, 0);
@ -361,6 +368,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
} }
// No module found error case with keys // No module found error case with keys
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
wait(success(tx->getRange(KeyRangeRef(LiteralStringRef("\xff\xff/A_no_module_related_prefix"), wait(success(tx->getRange(KeyRangeRef(LiteralStringRef("\xff\xff/A_no_module_related_prefix"),
LiteralStringRef("\xff\xff/I_am_also_not_in_any_module")), LiteralStringRef("\xff\xff/I_am_also_not_in_any_module")),
CLIENT_KNOBS->TOO_MANY))); CLIENT_KNOBS->TOO_MANY)));
@ -373,6 +381,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
} }
// No module found error with KeySelectors, *** a tricky corner case *** // No module found error with KeySelectors, *** a tricky corner case ***
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
KeySelector begin = KeySelectorRef(LiteralStringRef("\xff\xff/zzz_i_am_not_a_module"), false, 1); KeySelector begin = KeySelectorRef(LiteralStringRef("\xff\xff/zzz_i_am_not_a_module"), false, 1);
KeySelector end = KeySelectorRef(LiteralStringRef("\xff\xff/zzz_to_be_the_final_one"), false, 2); KeySelector end = KeySelectorRef(LiteralStringRef("\xff\xff/zzz_to_be_the_final_one"), false, 2);
wait(success(tx->getRange(begin, end, CLIENT_KNOBS->TOO_MANY))); wait(success(tx->getRange(begin, end, CLIENT_KNOBS->TOO_MANY)));
@ -385,6 +394,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
} }
// begin and end keySelectors clamp up to the boundary of the module // begin and end keySelectors clamp up to the boundary of the module
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
const KeyRef key = LiteralStringRef("\xff\xff/cluster_file_path"); const KeyRef key = LiteralStringRef("\xff\xff/cluster_file_path");
KeySelector begin = KeySelectorRef(key, false, 0); KeySelector begin = KeySelectorRef(key, false, 0);
KeySelector end = KeySelectorRef(keyAfter(key), false, 2); KeySelector end = KeySelectorRef(keyAfter(key), false, 2);
@ -395,6 +405,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
throw; throw;
} }
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
tx->addReadConflictRange(singleKeyRange(LiteralStringRef("readKey"))); tx->addReadConflictRange(singleKeyRange(LiteralStringRef("readKey")));
const KeyRef key = LiteralStringRef("\xff\xff/transaction/a_to_be_the_first"); const KeyRef key = LiteralStringRef("\xff\xff/transaction/a_to_be_the_first");
KeySelector begin = KeySelectorRef(key, false, 0); KeySelector begin = KeySelectorRef(key, false, 0);
@ -408,6 +419,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
// Errors introduced by SpecialKeyRangeRWImpl // Errors introduced by SpecialKeyRangeRWImpl
// Writes are disabled by default // Writes are disabled by default
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
tx->set(LiteralStringRef("\xff\xff/I_am_not_a_range_can_be_written"), ValueRef()); tx->set(LiteralStringRef("\xff\xff/I_am_not_a_range_can_be_written"), ValueRef());
} catch (Error& e) { } catch (Error& e) {
if (e.code() == error_code_actor_cancelled) if (e.code() == error_code_actor_cancelled)
@ -417,6 +429,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
} }
// The special key is not in a range that can be called with set // The special key is not in a range that can be called with set
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
tx->set(LiteralStringRef("\xff\xff/I_am_not_a_range_can_be_written"), ValueRef()); tx->set(LiteralStringRef("\xff\xff/I_am_not_a_range_can_be_written"), ValueRef());
ASSERT(false); ASSERT(false);
@ -428,6 +441,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
} }
// A clear cross two ranges are forbidden // A clear cross two ranges are forbidden
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
tx->clear(KeyRangeRef(SpecialKeySpace::getManagementApiCommandRange("exclude").begin, tx->clear(KeyRangeRef(SpecialKeySpace::getManagementApiCommandRange("exclude").begin,
SpecialKeySpace::getManagementApiCommandRange("failed").end)); SpecialKeySpace::getManagementApiCommandRange("failed").end));
@ -440,6 +454,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
} }
// base key of the end key selector not in (\xff\xff, \xff\xff\xff), throw key_outside_legal_range() // base key of the end key selector not in (\xff\xff, \xff\xff\xff), throw key_outside_legal_range()
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
const KeySelector startKeySelector = KeySelectorRef(LiteralStringRef("\xff\xff/test"), true, -200); const KeySelector startKeySelector = KeySelectorRef(LiteralStringRef("\xff\xff/test"), true, -200);
const KeySelector endKeySelector = KeySelectorRef(LiteralStringRef("test"), true, -10); const KeySelector endKeySelector = KeySelectorRef(LiteralStringRef("test"), true, -10);
RangeResult result = RangeResult result =
@ -453,6 +468,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
} }
// test case when registered range is the same as the underlying module // test case when registered range is the same as the underlying module
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
state RangeResult result = wait(tx->getRange(KeyRangeRef(LiteralStringRef("\xff\xff/worker_interfaces/"), state RangeResult result = wait(tx->getRange(KeyRangeRef(LiteralStringRef("\xff\xff/worker_interfaces/"),
LiteralStringRef("\xff\xff/worker_interfaces0")), LiteralStringRef("\xff\xff/worker_interfaces0")),
CLIENT_KNOBS->TOO_MANY)); CLIENT_KNOBS->TOO_MANY));
@ -480,12 +496,13 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
state Reference<ReadYourWritesTransaction> tx = makeReference<ReadYourWritesTransaction>(cx); state Reference<ReadYourWritesTransaction> tx = makeReference<ReadYourWritesTransaction>(cx);
state Reference<ReadYourWritesTransaction> referenceTx = makeReference<ReadYourWritesTransaction>(cx); state Reference<ReadYourWritesTransaction> referenceTx = makeReference<ReadYourWritesTransaction>(cx);
state bool ryw = deterministicRandom()->coinflip(); state bool ryw = deterministicRandom()->coinflip();
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
if (!ryw) { if (!ryw) {
tx->setOption(FDBTransactionOptions::READ_YOUR_WRITES_DISABLE); tx->setOption(FDBTransactionOptions::READ_YOUR_WRITES_DISABLE);
} }
referenceTx->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
referenceTx->setVersion(100); // Prevent this from doing a GRV or committing referenceTx->setVersion(100); // Prevent this from doing a GRV or committing
referenceTx->clear(normalKeys); referenceTx->clear(normalKeys);
referenceTx->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
int numKeys = deterministicRandom()->randomInt(1, self->conflictRangeSizeFactor) * 4; int numKeys = deterministicRandom()->randomInt(1, self->conflictRangeSizeFactor) * 4;
state std::vector<std::string> keys; // Must all be distinct state std::vector<std::string> keys; // Must all be distinct
keys.resize(numKeys); keys.resize(numKeys);
@ -630,6 +647,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
state Reference<ReadYourWritesTransaction> tx = makeReference<ReadYourWritesTransaction>(cx); state Reference<ReadYourWritesTransaction> tx = makeReference<ReadYourWritesTransaction>(cx);
// test ordered option keys // test ordered option keys
{ {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
for (const std::string& option : SpecialKeySpace::getManagementApiOptionsSet()) { for (const std::string& option : SpecialKeySpace::getManagementApiOptionsSet()) {
tx->set( tx->set(
@ -648,6 +666,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
} }
// "exclude" error message shema check // "exclude" error message shema check
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
tx->set(LiteralStringRef("Invalid_Network_Address") tx->set(LiteralStringRef("Invalid_Network_Address")
.withPrefix(SpecialKeySpace::getManagementApiCommandPrefix("exclude")), .withPrefix(SpecialKeySpace::getManagementApiCommandPrefix("exclude")),
@ -676,6 +695,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
// "setclass" // "setclass"
{ {
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
// test getRange // test getRange
state RangeResult result = wait(tx->getRange( state RangeResult result = wait(tx->getRange(
@ -747,6 +767,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
{ {
try { try {
// test getRange // test getRange
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
state RangeResult class_source_result = wait(tx->getRange( state RangeResult class_source_result = wait(tx->getRange(
KeyRangeRef(LiteralStringRef("process/class_source/"), LiteralStringRef("process/class_source0")) KeyRangeRef(LiteralStringRef("process/class_source/"), LiteralStringRef("process/class_source0"))
.withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::CONFIGURATION).begin), .withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::CONFIGURATION).begin),
@ -784,6 +805,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
// class source will be changed // class source will be changed
wait(tx->commit()); wait(tx->commit());
tx->reset(); tx->reset();
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
Optional<Value> class_source = wait(tx->get( Optional<Value> class_source = wait(tx->get(
Key("process/class_source/" + address) Key("process/class_source/" + address)
.withPrefix( .withPrefix(
@ -807,6 +829,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
// maske sure we lock the database // maske sure we lock the database
loop { loop {
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
// lock the database // lock the database
UID uid = deterministicRandom()->randomUniqueID(); UID uid = deterministicRandom()->randomUniqueID();
@ -842,6 +865,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
// if database locked, fdb read should get database_locked error // if database locked, fdb read should get database_locked error
try { try {
tx->reset(); tx->reset();
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
RangeResult res = wait(tx->getRange(normalKeys, 1)); RangeResult res = wait(tx->getRange(normalKeys, 1));
} catch (Error& e) { } catch (Error& e) {
if (e.code() == error_code_actor_cancelled) if (e.code() == error_code_actor_cancelled)
@ -853,6 +877,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
loop { loop {
try { try {
tx->reset(); tx->reset();
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
// unlock the database // unlock the database
tx->clear(SpecialKeySpace::getManagementApiCommandPrefix("lock")); tx->clear(SpecialKeySpace::getManagementApiCommandPrefix("lock"));
@ -860,6 +885,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
TraceEvent(SevDebug, "DatabaseUnlocked").log(); TraceEvent(SevDebug, "DatabaseUnlocked").log();
tx->reset(); tx->reset();
// read should be successful // read should be successful
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
RangeResult res = wait(tx->getRange(normalKeys, 1)); RangeResult res = wait(tx->getRange(normalKeys, 1));
tx->reset(); tx->reset();
break; break;
@ -904,6 +930,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
{ {
loop { loop {
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
tx->clear(SpecialKeySpace::getManagementApiCommandPrefix("consistencycheck")); tx->clear(SpecialKeySpace::getManagementApiCommandPrefix("consistencycheck"));
wait(tx->commit()); wait(tx->commit());
@ -1001,6 +1028,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
loop { loop {
try { try {
std::string new_processes_key(new_coordinator_process); std::string new_processes_key(new_coordinator_process);
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
for (const auto& address : old_coordinators_processes) { for (const auto& address : old_coordinators_processes) {
new_processes_key += "," + address; new_processes_key += "," + address;
@ -1071,6 +1099,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
loop { loop {
try { try {
std::string new_processes_key; std::string new_processes_key;
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
for (const auto& address : old_coordinators_processes) { for (const auto& address : old_coordinators_processes) {
new_processes_key += new_processes_key.size() ? "," : ""; new_processes_key += new_processes_key.size() ? "," : "";
@ -1116,6 +1145,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
} }
// advanceversion // advanceversion
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
Version v1 = wait(tx->getReadVersion()); Version v1 = wait(tx->getReadVersion());
TraceEvent(SevDebug, "InitialReadVersion").detail("Version", v1); TraceEvent(SevDebug, "InitialReadVersion").detail("Version", v1);
state Version v2 = 2 * v1; state Version v2 = 2 * v1;
@ -1127,6 +1157,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
TraceEvent(SevDebug, "AdvanceVersionSuccess").detail("Version", v3); TraceEvent(SevDebug, "AdvanceVersionSuccess").detail("Version", v3);
break; break;
} }
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
// force the cluster to recover at v2 // force the cluster to recover at v2
tx->set(SpecialKeySpace::getManagementApiCommandPrefix("advanceversion"), std::to_string(v2)); tx->set(SpecialKeySpace::getManagementApiCommandPrefix("advanceversion"), std::to_string(v2));
@ -1192,6 +1223,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
// update the sample rate and size limit // update the sample rate and size limit
loop { loop {
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
tx->set(LiteralStringRef("client_txn_sample_rate") tx->set(LiteralStringRef("client_txn_sample_rate")
.withPrefix(SpecialKeySpace::getManagementApiCommandPrefix("profile")), .withPrefix(SpecialKeySpace::getManagementApiCommandPrefix("profile")),
@ -1225,6 +1257,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
// Change back to default // Change back to default
loop { loop {
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
tx->set(LiteralStringRef("client_txn_sample_rate") tx->set(LiteralStringRef("client_txn_sample_rate")
.withPrefix(SpecialKeySpace::getManagementApiCommandPrefix("profile")), .withPrefix(SpecialKeySpace::getManagementApiCommandPrefix("profile")),
@ -1242,6 +1275,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
// Test invalid values // Test invalid values
loop { loop {
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
tx->set((deterministicRandom()->coinflip() ? LiteralStringRef("client_txn_sample_rate") tx->set((deterministicRandom()->coinflip() ? LiteralStringRef("client_txn_sample_rate")
: LiteralStringRef("client_txn_size_limit")) : LiteralStringRef("client_txn_size_limit"))
@ -1297,6 +1331,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
// Make sure setting more than one zone as maintenance will fail // Make sure setting more than one zone as maintenance will fail
loop { loop {
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
tx->set(Key(deterministicRandom()->randomAlphaNumeric(8)) tx->set(Key(deterministicRandom()->randomAlphaNumeric(8))
.withPrefix(SpecialKeySpace::getManagementApiCommandPrefix("maintenance")), .withPrefix(SpecialKeySpace::getManagementApiCommandPrefix("maintenance")),
@ -1333,6 +1368,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
state int ignoreSSFailuresRetry = 0; state int ignoreSSFailuresRetry = 0;
loop { loop {
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
tx->set(ignoreSSFailuresZoneString.withPrefix( tx->set(ignoreSSFailuresZoneString.withPrefix(
SpecialKeySpace::getManagementApiCommandPrefix("maintenance")), SpecialKeySpace::getManagementApiCommandPrefix("maintenance")),
@ -1371,6 +1407,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
// set dd mode to 0 and disable DD for rebalance // set dd mode to 0 and disable DD for rebalance
loop { loop {
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
KeyRef ddPrefix = SpecialKeySpace::getManagementApiCommandPrefix("datadistribution"); KeyRef ddPrefix = SpecialKeySpace::getManagementApiCommandPrefix("datadistribution");
tx->set(LiteralStringRef("mode").withPrefix(ddPrefix), LiteralStringRef("0")); tx->set(LiteralStringRef("mode").withPrefix(ddPrefix), LiteralStringRef("0"));
@ -1410,6 +1447,7 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
// then, clear all changes // then, clear all changes
loop { loop {
try { try {
tx->setOption(FDBTransactionOptions::RAW_ACCESS);
tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
tx->clear(ignoreSSFailuresZoneString.withPrefix( tx->clear(ignoreSSFailuresZoneString.withPrefix(
SpecialKeySpace::getManagementApiCommandPrefix("maintenance"))); SpecialKeySpace::getManagementApiCommandPrefix("maintenance")));
@ -1450,10 +1488,12 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
state Reference<ReadYourWritesTransaction> tr2(new ReadYourWritesTransaction(cx)); state Reference<ReadYourWritesTransaction> tr2(new ReadYourWritesTransaction(cx));
loop { loop {
try { try {
Version readVersion = wait(tr1->getReadVersion()); tr1->setOption(FDBTransactionOptions::RAW_ACCESS);
tr2->setVersion(readVersion);
tr1->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES); tr1->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
tr2->setOption(FDBTransactionOptions::READ_SYSTEM_KEYS); tr2->setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
Version readVersion = wait(tr1->getReadVersion());
tr2->setVersion(readVersion);
KeyRef ddPrefix = SpecialKeySpace::getManagementApiCommandPrefix("datadistribution"); KeyRef ddPrefix = SpecialKeySpace::getManagementApiCommandPrefix("datadistribution");
tr1->set(LiteralStringRef("mode").withPrefix(ddPrefix), LiteralStringRef("1")); tr1->set(LiteralStringRef("mode").withPrefix(ddPrefix), LiteralStringRef("1"));
wait(tr1->commit()); wait(tr1->commit());

View File

@ -0,0 +1,583 @@
/*
* TenantManagement.actor.cpp
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cstdint>
#include <limits>
#include "fdbclient/FDBOptions.g.h"
#include "fdbclient/GenericManagementAPI.actor.h"
#include "fdbrpc/simulator.h"
#include "fdbserver/workloads/workloads.actor.h"
#include "fdbserver/Knobs.h"
#include "flow/Error.h"
#include "flow/IRandom.h"
#include "flow/flow.h"
#include "flow/actorcompiler.h" // This must be the last #include.
struct TenantManagementWorkload : TestWorkload {
struct TenantState {
int64_t id;
bool empty;
TenantState() : id(-1), empty(true) {}
TenantState(int64_t id, bool empty) : id(id), empty(empty) {}
};
std::map<TenantName, TenantState> createdTenants;
int64_t maxId = -1;
Key tenantSubspace;
const Key keyName = "key"_sr;
const Key tenantSubspaceKey = "tenant_subspace"_sr;
const Value noTenantValue = "no_tenant"_sr;
const TenantName tenantNamePrefix = "tenant_management_workload_"_sr;
TenantName localTenantNamePrefix;
const Key specialKeysTenantMapPrefix = TenantMapRangeImpl::submoduleRange.begin.withPrefix(
SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::MANAGEMENT).begin);
int maxTenants;
double testDuration;
enum class OperationType { SPECIAL_KEYS, MANAGEMENT_DATABASE, MANAGEMENT_TRANSACTION };
static OperationType randomOperationType() {
int randomNum = deterministicRandom()->randomInt(0, 3);
if (randomNum == 0) {
return OperationType::SPECIAL_KEYS;
} else if (randomNum == 1) {
return OperationType::MANAGEMENT_DATABASE;
} else {
return OperationType::MANAGEMENT_TRANSACTION;
}
}
TenantManagementWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) {
maxTenants = std::min<int>(1e8 - 1, getOption(options, "maxTenants"_sr, 1000));
testDuration = getOption(options, "testDuration"_sr, 60.0);
localTenantNamePrefix = format("%stenant_%d_", tenantNamePrefix.toString().c_str(), clientId);
}
std::string description() const override { return "TenantManagement"; }
Future<Void> setup(Database const& cx) override { return _setup(cx, this); }
ACTOR Future<Void> _setup(Database cx, TenantManagementWorkload* self) {
state Transaction tr(cx);
if (self->clientId == 0) {
self->tenantSubspace = makeString(deterministicRandom()->randomInt(0, 10));
generateRandomData(mutateString(self->tenantSubspace), self->tenantSubspace.size());
loop {
try {
tr.setOption(FDBTransactionOptions::RAW_ACCESS);
tr.set(self->keyName, self->noTenantValue);
tr.set(self->tenantSubspaceKey, self->tenantSubspace);
tr.set(tenantDataPrefixKey, self->tenantSubspace);
wait(tr.commit());
break;
} catch (Error& e) {
wait(tr.onError(e));
}
}
} else {
loop {
try {
tr.setOption(FDBTransactionOptions::RAW_ACCESS);
Optional<Value> val = wait(tr.get(self->tenantSubspaceKey));
if (val.present()) {
self->tenantSubspace = val.get();
break;
}
wait(delay(1.0));
} catch (Error& e) {
wait(tr.onError(e));
}
}
}
return Void();
}
TenantName chooseTenantName(bool allowSystemTenant) {
TenantName tenant(format(
"%s%08d", localTenantNamePrefix.toString().c_str(), deterministicRandom()->randomInt(0, maxTenants)));
if (allowSystemTenant && deterministicRandom()->random01() < 0.02) {
tenant = tenant.withPrefix("\xff"_sr);
}
return tenant;
}
ACTOR Future<Void> createTenant(Database cx, TenantManagementWorkload* self) {
state TenantName tenant = self->chooseTenantName(true);
state bool alreadyExists = self->createdTenants.count(tenant);
state OperationType operationType = TenantManagementWorkload::randomOperationType();
state Reference<ReadYourWritesTransaction> tr = makeReference<ReadYourWritesTransaction>(cx);
loop {
try {
if (operationType == OperationType::SPECIAL_KEYS) {
tr->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
Key key = self->specialKeysTenantMapPrefix.withSuffix(tenant);
tr->set(key, ""_sr);
wait(tr->commit());
} else if (operationType == OperationType::MANAGEMENT_DATABASE) {
wait(ManagementAPI::createTenant(cx.getReference(), tenant));
} else {
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
Optional<TenantMapEntry> _ = wait(ManagementAPI::createTenantTransaction(tr, tenant));
wait(tr->commit());
}
if (operationType != OperationType::MANAGEMENT_DATABASE && alreadyExists) {
return Void();
}
ASSERT(!alreadyExists);
ASSERT(!tenant.startsWith("\xff"_sr));
state Optional<TenantMapEntry> entry = wait(ManagementAPI::tryGetTenant(cx.getReference(), tenant));
ASSERT(entry.present());
ASSERT(entry.get().id > self->maxId);
ASSERT(entry.get().prefix.startsWith(self->tenantSubspace));
self->maxId = entry.get().id;
self->createdTenants[tenant] = TenantState(entry.get().id, true);
state bool insertData = deterministicRandom()->random01() < 0.5;
if (insertData) {
state Transaction insertTr(cx, tenant);
loop {
try {
insertTr.set(self->keyName, tenant);
wait(insertTr.commit());
break;
} catch (Error& e) {
wait(insertTr.onError(e));
}
}
self->createdTenants[tenant].empty = false;
state Transaction checkTr(cx);
loop {
try {
checkTr.setOption(FDBTransactionOptions::RAW_ACCESS);
Optional<Value> val = wait(checkTr.get(self->keyName.withPrefix(entry.get().prefix)));
ASSERT(val.present());
ASSERT(val.get() == tenant);
break;
} catch (Error& e) {
wait(checkTr.onError(e));
}
}
}
wait(self->checkTenant(cx, self, tenant, self->createdTenants[tenant]));
return Void();
} catch (Error& e) {
if (e.code() == error_code_invalid_tenant_name) {
ASSERT(tenant.startsWith("\xff"_sr));
return Void();
} else if (operationType == OperationType::MANAGEMENT_DATABASE) {
if (e.code() == error_code_tenant_already_exists) {
ASSERT(alreadyExists && operationType == OperationType::MANAGEMENT_DATABASE);
} else {
TraceEvent(SevError, "CreateTenantFailure").error(e).detail("TenantName", tenant);
}
return Void();
} else {
try {
wait(tr->onError(e));
} catch (Error& e) {
TraceEvent(SevError, "CreateTenantFailure").error(e).detail("TenantName", tenant);
return Void();
}
}
}
}
}
ACTOR Future<Void> deleteTenant(Database cx, TenantManagementWorkload* self) {
state TenantName tenant = self->chooseTenantName(true);
state OperationType operationType = TenantManagementWorkload::randomOperationType();
state Reference<ReadYourWritesTransaction> tr = makeReference<ReadYourWritesTransaction>(cx);
state Optional<TenantName> endTenant = operationType != OperationType::MANAGEMENT_DATABASE &&
!tenant.startsWith("\xff"_sr) &&
deterministicRandom()->random01() < 0.2
? Optional<TenantName>(self->chooseTenantName(false))
: Optional<TenantName>();
if (endTenant.present() && endTenant < tenant) {
TenantName temp = tenant;
tenant = endTenant.get();
endTenant = temp;
}
auto itr = self->createdTenants.find(tenant);
state bool alreadyExists = itr != self->createdTenants.end();
state bool isEmpty = true;
state std::vector<TenantName> tenants;
if (!endTenant.present()) {
tenants.push_back(tenant);
} else if (endTenant.present()) {
for (auto itr = self->createdTenants.lower_bound(tenant);
itr != self->createdTenants.end() && itr->first < endTenant.get();
++itr) {
tenants.push_back(itr->first);
}
}
try {
if (alreadyExists || endTenant.present()) {
state int tenantIndex = 0;
for (; tenantIndex < tenants.size(); ++tenantIndex) {
if (deterministicRandom()->random01() < 0.9) {
state Transaction clearTr(cx, tenants[tenantIndex]);
loop {
try {
clearTr.clear(self->keyName);
wait(clearTr.commit());
auto itr = self->createdTenants.find(tenants[tenantIndex]);
ASSERT(itr != self->createdTenants.end());
itr->second.empty = true;
break;
} catch (Error& e) {
wait(clearTr.onError(e));
}
}
} else {
auto itr = self->createdTenants.find(tenants[tenantIndex]);
ASSERT(itr != self->createdTenants.end());
isEmpty = isEmpty && itr->second.empty;
}
}
}
} catch (Error& e) {
TraceEvent(SevError, "DeleteTenantFailure")
.error(e)
.detail("TenantName", tenant)
.detail("EndTenant", endTenant);
return Void();
}
loop {
try {
if (operationType == OperationType::SPECIAL_KEYS) {
tr->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_ENABLE_WRITES);
Key key = self->specialKeysTenantMapPrefix.withSuffix(tenant);
if (endTenant.present()) {
tr->clear(KeyRangeRef(key, self->specialKeysTenantMapPrefix.withSuffix(endTenant.get())));
} else {
tr->clear(key);
}
wait(tr->commit());
} else if (operationType == OperationType::MANAGEMENT_DATABASE) {
ASSERT(tenants.size() == 1);
for (auto tenant : tenants) {
wait(ManagementAPI::deleteTenant(cx.getReference(), tenant));
}
} else {
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
for (auto tenant : tenants) {
wait(ManagementAPI::deleteTenantTransaction(tr, tenant));
}
wait(tr->commit());
}
if (!alreadyExists && !endTenant.present() && operationType != OperationType::MANAGEMENT_DATABASE) {
return Void();
}
ASSERT(alreadyExists || endTenant.present());
ASSERT(isEmpty);
for (auto tenant : tenants) {
self->createdTenants.erase(tenant);
}
return Void();
} catch (Error& e) {
if (e.code() == error_code_tenant_not_empty) {
ASSERT(!isEmpty);
return Void();
} else if (operationType == OperationType::MANAGEMENT_DATABASE) {
if (e.code() == error_code_tenant_not_found) {
ASSERT(!alreadyExists && !endTenant.present());
} else {
TraceEvent(SevError, "DeleteTenantFailure")
.error(e)
.detail("TenantName", tenant)
.detail("EndTenant", endTenant);
}
return Void();
} else {
try {
wait(tr->onError(e));
} catch (Error& e) {
TraceEvent(SevError, "DeleteTenantFailure")
.error(e)
.detail("TenantName", tenant)
.detail("EndTenant", endTenant);
return Void();
}
}
}
}
}
ACTOR Future<Void> checkTenant(Database cx,
TenantManagementWorkload* self,
TenantName tenant,
TenantState tenantState) {
state Transaction tr(cx, tenant);
loop {
try {
state RangeResult result = wait(tr.getRange(KeyRangeRef(""_sr, "\xff"_sr), 2));
if (tenantState.empty) {
ASSERT(result.size() == 0);
} else {
ASSERT(result.size() == 1);
ASSERT(result[0].key == self->keyName);
ASSERT(result[0].value == tenant);
}
break;
} catch (Error& e) {
wait(tr.onError(e));
}
}
return Void();
}
static TenantMapEntry jsonToTenantMapEntry(ValueRef tenantJson) {
json_spirit::mValue jsonObject;
json_spirit::read_string(tenantJson.toString(), jsonObject);
JSONDoc jsonDoc(jsonObject);
int64_t id;
std::string prefix;
jsonDoc.get("id", id);
jsonDoc.get("prefix", prefix);
Key prefixKey = KeyRef(prefix);
TenantMapEntry entry(id, prefixKey.substr(0, prefixKey.size() - 8));
ASSERT(entry.prefix == prefixKey);
return entry;
}
ACTOR Future<Void> getTenant(Database cx, TenantManagementWorkload* self) {
state TenantName tenant = self->chooseTenantName(true);
auto itr = self->createdTenants.find(tenant);
state bool alreadyExists = itr != self->createdTenants.end();
state TenantState tenantState = itr->second;
state OperationType operationType = TenantManagementWorkload::randomOperationType();
state Reference<ReadYourWritesTransaction> tr = makeReference<ReadYourWritesTransaction>(cx);
loop {
try {
state TenantMapEntry entry;
if (operationType == OperationType::SPECIAL_KEYS) {
Key key = self->specialKeysTenantMapPrefix.withSuffix(tenant);
Optional<Value> value = wait(tr->get(key));
if (!value.present()) {
throw tenant_not_found();
}
entry = TenantManagementWorkload::jsonToTenantMapEntry(value.get());
} else if (operationType == OperationType::MANAGEMENT_DATABASE) {
TenantMapEntry _entry = wait(ManagementAPI::getTenant(cx.getReference(), tenant));
entry = _entry;
} else {
tr->setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
TenantMapEntry _entry = wait(ManagementAPI::getTenantTransaction(tr, tenant));
entry = _entry;
}
ASSERT(alreadyExists);
ASSERT(entry.id == tenantState.id);
wait(self->checkTenant(cx, self, tenant, tenantState));
return Void();
} catch (Error& e) {
state bool retry = true;
state Error error = e;
if (e.code() == error_code_tenant_not_found) {
ASSERT(!alreadyExists);
return Void();
} else if (operationType != OperationType::MANAGEMENT_DATABASE) {
try {
wait(tr->onError(e));
} catch (Error& e) {
error = e;
retry = false;
}
}
if (!retry) {
TraceEvent(SevError, "GetTenantFailure").error(error).detail("TenantName", tenant);
return Void();
}
}
}
}
ACTOR Future<Void> listTenants(Database cx, TenantManagementWorkload* self) {
state TenantName beginTenant = self->chooseTenantName(false);
state TenantName endTenant = self->chooseTenantName(false);
state int limit = std::min(CLIENT_KNOBS->TOO_MANY, deterministicRandom()->randomInt(1, self->maxTenants * 2));
state OperationType operationType = TenantManagementWorkload::randomOperationType();
state Reference<ReadYourWritesTransaction> tr = makeReference<ReadYourWritesTransaction>(cx);
if (beginTenant > endTenant) {
std::swap(beginTenant, endTenant);
}
loop {
try {
state std::map<TenantName, TenantMapEntry> tenants;
if (operationType == OperationType::SPECIAL_KEYS) {
KeyRange range = KeyRangeRef(beginTenant, endTenant).withPrefix(self->specialKeysTenantMapPrefix);
RangeResult results = wait(tr->getRange(range, limit));
for (auto result : results) {
tenants[result.key.removePrefix(self->specialKeysTenantMapPrefix)] =
TenantManagementWorkload::jsonToTenantMapEntry(result.value);
}
} else if (operationType == OperationType::MANAGEMENT_DATABASE) {
std::map<TenantName, TenantMapEntry> _tenants =
wait(ManagementAPI::listTenants(cx.getReference(), beginTenant, endTenant, limit));
tenants = _tenants;
} else {
tr->setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
std::map<TenantName, TenantMapEntry> _tenants =
wait(ManagementAPI::listTenantsTransaction(tr, beginTenant, endTenant, limit));
tenants = _tenants;
}
ASSERT(tenants.size() <= limit);
auto localItr = self->createdTenants.lower_bound(beginTenant);
auto tenantMapItr = tenants.begin();
for (; tenantMapItr != tenants.end(); ++tenantMapItr, ++localItr) {
ASSERT(localItr != self->createdTenants.end());
ASSERT(localItr->first == tenantMapItr->first);
}
if (!(tenants.size() == limit || localItr == self->createdTenants.end())) {
for (auto tenant : self->createdTenants) {
TraceEvent("ExistingTenant").detail("Tenant", tenant.first);
}
}
ASSERT(tenants.size() == limit || localItr == self->createdTenants.end() ||
localItr->first >= endTenant);
return Void();
} catch (Error& e) {
state bool retry = true;
state Error error = e;
if (operationType != OperationType::MANAGEMENT_DATABASE) {
try {
wait(tr->onError(e));
} catch (Error& e) {
error = e;
retry = false;
}
}
if (!retry) {
TraceEvent(SevError, "ListTenantFailure")
.error(error)
.detail("BeginTenant", beginTenant)
.detail("EndTenant", endTenant);
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();
while (now() < start + self->testDuration) {
state int operation = deterministicRandom()->randomInt(0, 4);
if (operation == 0) {
wait(self->createTenant(cx, self));
} else if (operation == 1) {
wait(self->deleteTenant(cx, self));
} else if (operation == 2) {
wait(self->getTenant(cx, self));
} else {
wait(self->listTenants(cx, self));
}
}
return Void();
}
Future<bool> check(Database const& cx) override { return _check(cx, this); }
ACTOR Future<bool> _check(Database cx, TenantManagementWorkload* self) {
state Transaction tr(cx);
loop {
try {
tr.setOption(FDBTransactionOptions::RAW_ACCESS);
Optional<Value> val = wait(tr.get(self->keyName));
ASSERT(val.present() && val.get() == self->noTenantValue);
break;
} catch (Error& e) {
wait(tr.onError(e));
}
}
state std::map<TenantName, TenantState>::iterator itr = self->createdTenants.begin();
state std::vector<Future<Void>> checkTenants;
state TenantName beginTenant = ""_sr.withPrefix(self->localTenantNamePrefix);
state TenantName endTenant = "\xff\xff"_sr.withPrefix(self->localTenantNamePrefix);
loop {
std::map<TenantName, TenantMapEntry> tenants =
wait(ManagementAPI::listTenants(cx.getReference(), beginTenant, endTenant, 1000));
TenantNameRef lastTenant;
for (auto tenant : tenants) {
ASSERT(itr != self->createdTenants.end());
ASSERT(tenant.first == itr->first);
checkTenants.push_back(self->checkTenant(cx, self, tenant.first, itr->second));
lastTenant = tenant.first;
++itr;
}
if (tenants.size() < 1000) {
break;
} else {
beginTenant = keyAfter(lastTenant);
}
}
ASSERT(itr == self->createdTenants.end());
wait(waitForAll(checkTenants));
return true;
}
void getMetrics(std::vector<PerfMetric>& m) override {}
};
WorkloadFactory<TenantManagementWorkload> TenantManagementWorkload("TenantManagement");

View File

@ -42,6 +42,7 @@ struct VersionStampWorkload : TestWorkload {
std::map<Key, std::vector<std::pair<Version, Standalone<StringRef>>>> versionStampKey_commit; std::map<Key, std::vector<std::pair<Version, Standalone<StringRef>>>> versionStampKey_commit;
int apiVersion; int apiVersion;
bool soleOwnerOfMetadataVersionKey; bool soleOwnerOfMetadataVersionKey;
bool allowMetadataVersionKey;
VersionStampWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { VersionStampWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) {
testDuration = getOption(options, LiteralStringRef("testDuration"), 60.0); testDuration = getOption(options, LiteralStringRef("testDuration"), 60.0);
@ -74,6 +75,9 @@ struct VersionStampWorkload : TestWorkload {
apiVersion = Database::API_VERSION_LATEST; apiVersion = Database::API_VERSION_LATEST;
} }
TraceEvent("VersionStampApiVersion").detail("ApiVersion", apiVersion); TraceEvent("VersionStampApiVersion").detail("ApiVersion", apiVersion);
allowMetadataVersionKey = apiVersion >= 610 || apiVersion == Database::API_VERSION_LATEST;
cx->apiVersion = apiVersion; cx->apiVersion = apiVersion;
if (clientId == 0) if (clientId == 0)
return _start(cx, this, 1 / transactionsPerSecond); return _start(cx, this, 1 / transactionsPerSecond);
@ -81,7 +85,7 @@ struct VersionStampWorkload : TestWorkload {
} }
Key keyForIndex(uint64_t index) { Key keyForIndex(uint64_t index) {
if ((apiVersion >= 610 || apiVersion == Database::API_VERSION_LATEST) && index == 0) { if (allowMetadataVersionKey && index == 0) {
return metadataVersionKey; return metadataVersionKey;
} }
@ -191,8 +195,7 @@ struct VersionStampWorkload : TestWorkload {
RangeResult result_ = wait(tr.getRange( RangeResult result_ = wait(tr.getRange(
KeyRangeRef(self->vsValuePrefix, endOfRange(self->vsValuePrefix)), self->nodeCount + 1)); KeyRangeRef(self->vsValuePrefix, endOfRange(self->vsValuePrefix)), self->nodeCount + 1));
result = result_; result = result_;
if ((self->apiVersion >= 610 || self->apiVersion == Database::API_VERSION_LATEST) && if (self->allowMetadataVersionKey && self->key_commit.count(metadataVersionKey)) {
self->key_commit.count(metadataVersionKey)) {
Optional<Value> mVal = wait(tr.get(metadataVersionKey)); Optional<Value> mVal = wait(tr.get(metadataVersionKey));
if (mVal.present()) { if (mVal.present()) {
result.push_back_deep(result.arena(), KeyValueRef(metadataVersionKey, mVal.get())); result.push_back_deep(result.arena(), KeyValueRef(metadataVersionKey, mVal.get()));
@ -314,6 +317,7 @@ struct VersionStampWorkload : TestWorkload {
extraDB = Database::createDatabase(extraFile, -1); extraDB = Database::createDatabase(extraFile, -1);
} }
state Future<Void> metadataWatch = Void();
loop { loop {
wait(poisson(&lastTime, delay)); wait(poisson(&lastTime, delay));
bool oldVSFormat = !cx->apiVersionAtLeast(520); bool oldVSFormat = !cx->apiVersionAtLeast(520);
@ -350,6 +354,7 @@ struct VersionStampWorkload : TestWorkload {
state Error err; state Error err;
//TraceEvent("VST_CommitBegin").detail("Key", printable(key)).detail("VsKey", printable(versionStampKey)).detail("Clear", printable(range)); //TraceEvent("VST_CommitBegin").detail("Key", printable(key)).detail("VsKey", printable(versionStampKey)).detail("Clear", printable(range));
state Key testKey; state Key testKey;
state Future<Void> nextMetadataWatch;
try { try {
tr.atomicOp(key, versionStampValue, MutationRef::SetVersionstampedValue); tr.atomicOp(key, versionStampValue, MutationRef::SetVersionstampedValue);
if (key == metadataVersionKey) { if (key == metadataVersionKey) {
@ -358,12 +363,21 @@ struct VersionStampWorkload : TestWorkload {
} }
tr.clear(range); tr.clear(range);
tr.atomicOp(versionStampKey, value, MutationRef::SetVersionstampedKey); tr.atomicOp(versionStampKey, value, MutationRef::SetVersionstampedKey);
if (key == metadataVersionKey) {
nextMetadataWatch = tr.watch(versionStampKey);
}
state Future<Standalone<StringRef>> fTrVs = tr.getVersionstamp(); state Future<Standalone<StringRef>> fTrVs = tr.getVersionstamp();
wait(tr.commit()); wait(tr.commit());
committedVersion = tr.getCommittedVersion(); committedVersion = tr.getCommittedVersion();
Standalone<StringRef> committedVersionStamp_ = wait(fTrVs); Standalone<StringRef> committedVersionStamp_ = wait(fTrVs);
committedVersionStamp = committedVersionStamp_; committedVersionStamp = committedVersionStamp_;
if (key == metadataVersionKey) {
wait(timeoutError(metadataWatch, 30));
nextMetadataWatch = metadataWatch;
}
} catch (Error& e) { } catch (Error& e) {
err = e; err = e;
if (err.code() == error_code_database_locked && g_simulator.extraDB != nullptr) { if (err.code() == error_code_database_locked && g_simulator.extraDB != nullptr) {

View File

@ -583,9 +583,29 @@ struct WriteDuringReadWorkload : TestWorkload {
std::string(deterministicRandom()->randomInt(valueSizeRange.first, valueSizeRange.second + 1), 'x')); std::string(deterministicRandom()->randomInt(valueSizeRange.first, valueSizeRange.second + 1), 'x'));
} }
// Prevent a write only transaction whose commit was previously cancelled from being reordered after this
// transaction
ACTOR Future<Void> writeBarrier(Database cx) {
state Transaction tr(cx);
loop {
try {
tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
// Write-only transactions have a self-conflict in the system keys
tr.addWriteConflictRange(allKeys);
wait(tr.commit());
return Void();
} catch (Error& e) {
wait(tr.onError(e));
}
}
}
ACTOR Future<Void> loadAndRun(Database cx, WriteDuringReadWorkload* self) { ACTOR Future<Void> loadAndRun(Database cx, WriteDuringReadWorkload* self) {
state double startTime = now(); state double startTime = now();
loop { loop {
wait(self->writeBarrier(cx));
state int i = 0; state int i = 0;
state int keysPerBatch = state int keysPerBatch =
std::min<int64_t>(1000, std::min<int64_t>(1000,
@ -595,19 +615,16 @@ struct WriteDuringReadWorkload : TestWorkload {
for (; i < self->nodes; i += keysPerBatch) { for (; i < self->nodes; i += keysPerBatch) {
state Transaction tr(cx); state Transaction tr(cx);
loop { loop {
if (now() - startTime > self->testDuration)
return Void();
try { try {
if (i == 0) { if (now() - startTime > self->testDuration)
tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); return Void();
tr.addWriteConflictRange(
allKeys); // To prevent a write only transaction whose commit was previously cancelled
// from being reordered after this transaction
tr.clear(normalKeys);
}
if (self->useSystemKeys) if (self->useSystemKeys)
tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
if (i == 0) {
tr.clear(normalKeys);
}
int end = std::min(self->nodes, i + keysPerBatch); int end = std::min(self->nodes, i + keysPerBatch);
tr.clear(KeyRangeRef(self->getKeyForIndex(i), self->getKeyForIndex(end))); tr.clear(KeyRangeRef(self->getKeyForIndex(i), self->getKeyForIndex(end)));
self->memoryDatabase.erase(self->memoryDatabase.lower_bound(self->getKeyForIndex(i)), self->memoryDatabase.erase(self->memoryDatabase.lower_bound(self->getKeyForIndex(i)),

View File

@ -207,7 +207,10 @@ public:
ISimulator::BackupAgentType simDrAgents; ISimulator::BackupAgentType simDrAgents;
}; };
ACTOR Future<DistributedTestResults> runWorkload(Database cx, std::vector<TesterInterface> testers, TestSpec spec); ACTOR Future<DistributedTestResults> runWorkload(Database cx,
std::vector<TesterInterface> testers,
TestSpec spec,
Optional<TenantName> defaultTenant);
void logMetrics(std::vector<PerfMetric> metrics); void logMetrics(std::vector<PerfMetric> metrics);

View File

@ -300,6 +300,8 @@ if(WITH_PYTHON)
add_fdb_test(TEST_FILES slow/SwizzledDdBalance.toml) add_fdb_test(TEST_FILES slow/SwizzledDdBalance.toml)
add_fdb_test(TEST_FILES slow/SwizzledRollbackTimeLapse.toml) add_fdb_test(TEST_FILES slow/SwizzledRollbackTimeLapse.toml)
add_fdb_test(TEST_FILES slow/SwizzledRollbackTimeLapseIncrement.toml) add_fdb_test(TEST_FILES slow/SwizzledRollbackTimeLapseIncrement.toml)
add_fdb_test(TEST_FILES slow/SwizzledTenantManagement.toml)
add_fdb_test(TEST_FILES slow/TenantManagement.toml)
add_fdb_test(TEST_FILES slow/VersionStampBackupToDB.toml) add_fdb_test(TEST_FILES slow/VersionStampBackupToDB.toml)
add_fdb_test(TEST_FILES slow/VersionStampSwitchover.toml) add_fdb_test(TEST_FILES slow/VersionStampSwitchover.toml)
add_fdb_test(TEST_FILES slow/WriteDuringReadAtomicRestore.toml) add_fdb_test(TEST_FILES slow/WriteDuringReadAtomicRestore.toml)

View File

@ -136,5 +136,5 @@ logdir = {logdir}
def create_database(self, storage='ssd'): def create_database(self, storage='ssd'):
args = [self.fdbcli_binary, '-C', self.etc.joinpath('fdb.cluster'), '--exec', args = [self.fdbcli_binary, '-C', self.etc.joinpath('fdb.cluster'), '--exec',
'configure new single {}'.format(storage)] 'configure new single {} tenant_mode=optional_experimental'.format(storage)]
subprocess.run(args) subprocess.run(args)

View File

@ -1,3 +1,6 @@
[configuration]
allowDefaultTenant = false
[[test]] [[test]]
testTitle = 'ChangeFeed' testTitle = 'ChangeFeed'

View File

@ -1,5 +1,7 @@
[configuration] [configuration]
StderrSeverity = 30 StderrSeverity = 30
allowDisablingTenants = false
allowDefaultTenant = false
[[test]] [[test]]
testTitle = 'FuzzApiCorrectness' testTitle = 'FuzzApiCorrectness'

View File

@ -1,5 +1,7 @@
[configuration] [configuration]
StderrSeverity = 30 StderrSeverity = 30
allowDisablingTenants = false
allowDefaultTenant = false
[[test]] [[test]]
testTitle = 'FuzzApiCorrectness' testTitle = 'FuzzApiCorrectness'

View File

@ -0,0 +1,46 @@
[configuration]
allowDisablingTenants = false
[[test]]
testTitle = 'TenantManagementTest'
clearAfterTest = true
timeout = 2100
runSetup = true
[[test.workload]]
testName = 'TenantManagement'
maxTenants = 1000
testDuration = 60
[[test.workload]]
testName = 'RandomClogging'
testDuration = 120.0
swizzle = 1
[[test.workload]]
testName = 'Rollback'
testDuration = 120.0
meanDelay = 10.0
[[test.workload]]
testName = 'Attrition'
testDuration = 120.0
[[test.workload]]
testName = 'Attrition'
machinesToKill = 10
machinesToLeave = 3
reboot = true
testDuration = 120.0
[[test.workload]]
testName = 'Attrition'
machinesToKill = 10
machinesToLeave = 3
reboot = true
testDuration = 120.0
[[test.workload]]
testName = 'ChangeConfig'
maxDelayBeforeChange = 120.0
coordinators = 'auto'

View File

@ -0,0 +1,13 @@
[configuration]
allowDisablingTenants = false
[[test]]
testTitle = 'TenantManagementTest'
clearAfterTest = true
timeout = 2100
runSetup = true
[[test.workload]]
testName = 'TenantManagement'
maxTenants = 1000
testDuration = 60

View File

@ -1,5 +1,6 @@
[configuration] [configuration]
StderrSeverity = 30 StderrSeverity = 30
allowDefaultTenant = false
[[test]] [[test]]
testTitle = 'WriteDuringReadTest' testTitle = 'WriteDuringReadTest'