Merge pull request #6560 from sfc-gh-ajbeamon/fdb-tenant-core-testing
Add core testing support for tenants
This commit is contained in:
commit
d956b00c9d
|
@ -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_);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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");
|
|
@ -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) {
|
||||||
|
|
|
@ -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)),
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
[configuration]
|
||||||
|
allowDefaultTenant = false
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
testTitle = 'ChangeFeed'
|
testTitle = 'ChangeFeed'
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
[configuration]
|
[configuration]
|
||||||
StderrSeverity = 30
|
StderrSeverity = 30
|
||||||
|
allowDisablingTenants = false
|
||||||
|
allowDefaultTenant = false
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
testTitle = 'FuzzApiCorrectness'
|
testTitle = 'FuzzApiCorrectness'
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
[configuration]
|
[configuration]
|
||||||
StderrSeverity = 30
|
StderrSeverity = 30
|
||||||
|
allowDisablingTenants = false
|
||||||
|
allowDefaultTenant = false
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
testTitle = 'FuzzApiCorrectness'
|
testTitle = 'FuzzApiCorrectness'
|
||||||
|
|
|
@ -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'
|
|
@ -0,0 +1,13 @@
|
||||||
|
[configuration]
|
||||||
|
allowDisablingTenants = false
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
testTitle = 'TenantManagementTest'
|
||||||
|
clearAfterTest = true
|
||||||
|
timeout = 2100
|
||||||
|
runSetup = true
|
||||||
|
|
||||||
|
[[test.workload]]
|
||||||
|
testName = 'TenantManagement'
|
||||||
|
maxTenants = 1000
|
||||||
|
testDuration = 60
|
|
@ -1,5 +1,6 @@
|
||||||
[configuration]
|
[configuration]
|
||||||
StderrSeverity = 30
|
StderrSeverity = 30
|
||||||
|
allowDefaultTenant = false
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
testTitle = 'WriteDuringReadTest'
|
testTitle = 'WriteDuringReadTest'
|
||||||
|
|
Loading…
Reference in New Issue