Merge pull request #8803 from sfc-gh-akejriwal/commitproxies

Add a transaction option to bypass storage quota enforcement
This commit is contained in:
Ankita Kejriwal 2022-11-15 15:22:11 -08:00 committed by GitHub
commit 3036f2458f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 35 additions and 7 deletions

View File

@ -497,6 +497,11 @@ func (o TransactionOptions) SetRawAccess() error {
return o.setOpt(303, nil)
}
// Allows this transaction to bypass storage quota enforcement. Should only be used for transactions that directly or indirectly decrease the size of the tenant group's data.
func (o TransactionOptions) SetBypassStorageQuota() error {
return o.setOpt(304, nil)
}
// Not yet implemented.
func (o TransactionOptions) SetDebugRetryLogging(param string) error {
return o.setOpt(401, []byte(param))

View File

@ -5953,6 +5953,7 @@ void TransactionOptions::clear() {
useGrvCache = false;
skipGrvCache = false;
rawAccess = false;
bypassStorageQuota = false;
}
TransactionOptions::TransactionOptions() {
@ -6694,6 +6695,9 @@ Future<Void> Transaction::commitMutations() {
if (trState->options.firstInBatch) {
tr.flags = tr.flags | CommitTransactionRequest::FLAG_FIRST_IN_BATCH;
}
if (trState->options.bypassStorageQuota) {
tr.flags = tr.flags | CommitTransactionRequest::FLAG_BYPASS_STORAGE_QUOTA;
}
if (trState->options.reportConflictingKeys) {
tr.transaction.report_conflicting_keys = true;
}
@ -6972,6 +6976,10 @@ void Transaction::setOption(FDBTransactionOptions::Option option, Optional<Strin
trState->options.rawAccess = true;
break;
case FDBTransactionOptions::BYPASS_STORAGE_QUOTA:
trState->options.bypassStorageQuota = true;
break;
case FDBTransactionOptions::AUTHORIZATION_TOKEN:
if (value.present())
trState->authToken = Standalone<StringRef>(value.get());

View File

@ -196,10 +196,11 @@ struct CommitID {
struct CommitTransactionRequest : TimedRequest {
constexpr static FileIdentifier file_identifier = 93948;
enum { FLAG_IS_LOCK_AWARE = 0x1, FLAG_FIRST_IN_BATCH = 0x2 };
enum { FLAG_IS_LOCK_AWARE = 0x1, FLAG_FIRST_IN_BATCH = 0x2, FLAG_BYPASS_STORAGE_QUOTA = 0x4 };
bool isLockAware() const { return (flags & FLAG_IS_LOCK_AWARE) != 0; }
bool firstInBatch() const { return (flags & FLAG_FIRST_IN_BATCH) != 0; }
bool bypassStorageQuota() const { return (flags & FLAG_BYPASS_STORAGE_QUOTA) != 0; }
Arena arena;
SpanContext spanContext;

View File

@ -161,6 +161,7 @@ struct TransactionOptions {
bool useGrvCache : 1;
bool skipGrvCache : 1;
bool rawAccess : 1;
bool bypassStorageQuota : 1;
TransactionPriority priority;

View File

@ -253,6 +253,8 @@ description is not currently required but encouraged.
description="Allows this transaction to read system keys (those that start with the byte 0xFF). Implies raw_access."/>
<Option name="raw_access" code="303"
description="Allows this transaction to access the raw key-space when tenant mode is on."/>
<Option name="bypass_storage_quota" code="304"
description="Allows this transaction to bypass storage quota enforcement. Should only be used for transactions that directly or indirectly decrease the size of the tenant group's data."/>
<Option name="debug_dump" code="400"
hidden="true" />
<Option name="debug_retry_logging" code="401" paramType="String" paramDescription="Optional transaction name" />

View File

@ -414,7 +414,7 @@ ACTOR Future<Void> commitBatcher(ProxyCommitData* commitData,
}
Optional<TenantNameRef> const& tenantName = req.tenantInfo.name;
if (SERVER_KNOBS->STORAGE_QUOTA_ENABLED && tenantName.present() &&
if (SERVER_KNOBS->STORAGE_QUOTA_ENABLED && !req.bypassStorageQuota() && tenantName.present() &&
commitData->tenantsOverStorageQuota.count(tenantName.get()) > 0) {
req.reply.sendError(storage_quota_exceeded());
continue;

View File

@ -92,11 +92,15 @@ struct StorageQuotaWorkload : TestWorkload {
}
// Check that writes to both the tenants are rejected when the group is over quota.
state bool rejected1 = wait(tryWrite(self, cx, self->tenant, /*expectOk=*/false));
state bool rejected1 = wait(tryWrite(self, cx, self->tenant, /*bypassQuota=*/false, /*expectOk=*/false));
ASSERT(rejected1);
state bool rejected2 = wait(tryWrite(self, cx, self->emptyTenant, /*expectOk=*/false));
state bool rejected2 = wait(tryWrite(self, cx, self->emptyTenant, /*bypassQuota=*/false, /*expectOk=*/false));
ASSERT(rejected2);
// Check that transaction is able to commit if we use the FDBTransactionOptions to bypass quota.
state bool bypassed = wait(tryWrite(self, cx, self->tenant, /*bypassQuota=*/true, /*expectOk=*/true));
ASSERT(bypassed);
// Increase the quota or clear the quota. Check that writes to both the tenants are now able to commit.
if (deterministicRandom()->coinflip()) {
quota = size * 2;
@ -104,9 +108,9 @@ struct StorageQuotaWorkload : TestWorkload {
} else {
wait(clearStorageQuotaHelper(cx, self->group));
}
state bool committed1 = wait(tryWrite(self, cx, self->tenant, /*expectOk=*/true));
state bool committed1 = wait(tryWrite(self, cx, self->tenant, /*bypassQuota=*/false, /*expectOk=*/true));
ASSERT(committed1);
state bool committed2 = wait(tryWrite(self, cx, self->emptyTenant, /*expectOk=*/true));
state bool committed2 = wait(tryWrite(self, cx, self->emptyTenant, /*bypassQuota=*/false, /*expectOk=*/true));
ASSERT(committed2);
return Void();
@ -173,13 +177,20 @@ struct StorageQuotaWorkload : TestWorkload {
}
}
ACTOR static Future<bool> tryWrite(StorageQuotaWorkload* self, Database cx, TenantName tenant, bool expectOk) {
ACTOR static Future<bool> tryWrite(StorageQuotaWorkload* self,
Database cx,
TenantName tenant,
bool bypassQuota,
bool expectOk) {
state int i;
// Retry the transaction a few times if needed; this allows us wait for a while for all
// the storage usage and quota related monitors to fetch and propagate the latest information
// about the tenants that are over storage quota.
for (i = 0; i < 10; i++) {
state Transaction tr(cx, tenant);
if (bypassQuota) {
tr.setOption(FDBTransactionOptions::BYPASS_STORAGE_QUOTA);
}
loop {
try {
Standalone<KeyValueRef> kv =