diff --git a/design/global-tag-throttling.md b/design/global-tag-throttling.md index ad7750a3cc..d8a7277f75 100644 --- a/design/global-tag-throttling.md +++ b/design/global-tag-throttling.md @@ -11,16 +11,16 @@ The global tag throttler bases throttling decisions on "quotas" provided by clie The global tag throttler cannot throttle tags to a throughput below the reserved quota, and it cannot allow throughput to exceed the total quota. ### Cost -Internally, the units for these quotas are "page costs", computed as follows. The "page cost" of a read operation is computed as: +Internally, the units for these quotas are bytes. The cost of an operation is rounded up to the nearest page size. The cost of a read operation is computed as: ``` -readCost = ceiling(bytesRead / CLIENT_KNOBS->READ_COST_BYTE_FACTOR); +readCost = ceiling(bytesRead / CLIENT_KNOBS->READ_COST_BYTE_FACTOR) * CLIENT_KNOBS->READ_COST_BYTE_FACTOR; ``` -The "page cost" of a write operation is computed as: +The cost of a write operation is computed as: ``` -writeCost = SERVER_KNOBS->GLOBAL_TAG_THROTTLING_RW_FUNGIBILITY_RATIO * ceiling(bytesWritten / CLIENT_KNOBS->WRITE_COST_BYTE_FACTOR); +writeCost = SERVER_KNOBS->GLOBAL_TAG_THROTTLING_RW_FUNGIBILITY_RATIO * ceiling(bytesWritten / CLIENT_KNOBS->WRITE_COST_BYTE_FACTOR) * CLIENT_KNOBS->WRITE_COST_BYTE_FACTOR; ``` Here `bytesWritten` includes cleared bytes. The size of range clears is estimated at commit time. @@ -41,12 +41,6 @@ To set the quota through `fdbcli`, run: fdbcli> quota set [reserved_throughput|total_throughput] ``` -Note that the quotas are specified in terms of bytes/second, and internally converted to page costs: - -``` -page_cost_quota = ceiling(byte_quota / CLIENT_KNOBS->READ_COST_BYTE_FACTOR) -``` - ### Limit Calculation The transaction budget that ratekeeper calculates and distributes to clients (via GRV proxies) for each tag is calculated based on several intermediate rate calculations, outlined in this section. diff --git a/fdbcli/QuotaCommand.actor.cpp b/fdbcli/QuotaCommand.actor.cpp index e6a86e9b51..4e9517c23c 100644 --- a/fdbcli/QuotaCommand.actor.cpp +++ b/fdbcli/QuotaCommand.actor.cpp @@ -63,9 +63,9 @@ ACTOR Future getQuota(Reference db, TransactionTag tag, LimitTy } else { auto const quota = ThrottleApi::TagQuotaValue::fromValue(v.get()); if (limitType == LimitType::TOTAL) { - fmt::print("{}\n", quota.totalQuota * CLIENT_KNOBS->READ_COST_BYTE_FACTOR); + fmt::print("{}\n", quota.totalQuota); } else if (limitType == LimitType::RESERVED) { - fmt::print("{}\n", quota.reservedQuota * CLIENT_KNOBS->READ_COST_BYTE_FACTOR); + fmt::print("{}\n", quota.reservedQuota); } } return Void(); @@ -90,9 +90,13 @@ ACTOR Future setQuota(Reference db, TransactionTag tag, LimitTy // Internally, costs are stored in terms of pages, but in the API, // costs are specified in terms of bytes if (limitType == LimitType::TOTAL) { - quota.totalQuota = (value - 1) / CLIENT_KNOBS->READ_COST_BYTE_FACTOR + 1; + // Round up to nearest page size + quota.totalQuota = + ((value - 1) / CLIENT_KNOBS->READ_COST_BYTE_FACTOR + 1) * CLIENT_KNOBS->READ_COST_BYTE_FACTOR; } else if (limitType == LimitType::RESERVED) { - quota.reservedQuota = (value - 1) / CLIENT_KNOBS->READ_COST_BYTE_FACTOR + 1; + // Round up to nearest page size + quota.reservedQuota = + ((value - 1) / CLIENT_KNOBS->READ_COST_BYTE_FACTOR + 1) * CLIENT_KNOBS->READ_COST_BYTE_FACTOR; } ThrottleApi::setTagQuota(tr, tag, quota.reservedQuota, quota.totalQuota); wait(safeThreadFutureToFuture(tr->commit())); diff --git a/fdbclient/NativeAPI.actor.cpp b/fdbclient/NativeAPI.actor.cpp index 52b63654d6..02b06d7b54 100644 --- a/fdbclient/NativeAPI.actor.cpp +++ b/fdbclient/NativeAPI.actor.cpp @@ -5819,8 +5819,7 @@ void Transaction::clear(const KeyRangeRef& range, AddConflictRange addConflictRa // NOTE: The throttling cost of each clear is assumed to be one page. // This makes compuation fast, but can be inaccurate and may // underestimate the cost of large clears. - ++trState->totalCost; - + trState->totalCost += CLIENT_KNOBS->WRITE_COST_BYTE_FACTOR; if (addConflictRange) t.write_conflict_ranges.push_back(req.arena, r); } diff --git a/fdbclient/include/fdbclient/NativeAPI.actor.h b/fdbclient/include/fdbclient/NativeAPI.actor.h index 7ac6fa3668..dd2064aec4 100644 --- a/fdbclient/include/fdbclient/NativeAPI.actor.h +++ b/fdbclient/include/fdbclient/NativeAPI.actor.h @@ -568,16 +568,16 @@ ACTOR Future> getCheckpointMetaData(Database cx, // Checks with Data Distributor that it is safe to mark all servers in exclusions as failed ACTOR Future checkSafeExclusions(Database cx, std::vector exclusions); -// Round up to the nearest page size. Multiply by fungibility ratio +// Measured in bytes, rounded up to the nearest page size. Multiply by fungibility ratio // because writes are more expensive than reads. inline uint64_t getWriteOperationCost(uint64_t bytes) { - return CLIENT_KNOBS->GLOBAL_TAG_THROTTLING_RW_FUNGIBILITY_RATIO * + return CLIENT_KNOBS->GLOBAL_TAG_THROTTLING_RW_FUNGIBILITY_RATIO * CLIENT_KNOBS->WRITE_COST_BYTE_FACTOR * ((bytes - 1) / CLIENT_KNOBS->WRITE_COST_BYTE_FACTOR + 1); } -// Round up to the nearest page size. +// Measured in bytes, rounded up to the nearest page size. inline uint64_t getReadOperationCost(uint64_t bytes) { - return (bytes - 1) / CLIENT_KNOBS->READ_COST_BYTE_FACTOR + 1; + return ((bytes - 1) / CLIENT_KNOBS->READ_COST_BYTE_FACTOR + 1) * CLIENT_KNOBS->READ_COST_BYTE_FACTOR; } // Create a transaction to set the value of system key \xff/conf/perpetual_storage_wiggle. If enable == true, the value diff --git a/fdbserver/workloads/TransactionCost.actor.cpp b/fdbserver/workloads/TransactionCost.actor.cpp index 5a0b2b966f..2bef6c1e4a 100644 --- a/fdbserver/workloads/TransactionCost.actor.cpp +++ b/fdbserver/workloads/TransactionCost.actor.cpp @@ -59,7 +59,7 @@ class TransactionCostWorkload : public TestWorkload { return success(tr->get(workload.getKey(testNumber))); } - int64_t expectedFinalCost() const override { return 1; } + int64_t expectedFinalCost() const override { return CLIENT_KNOBS->READ_COST_BYTE_FACTOR; } }; class ReadLargeValueTest : public ITest { @@ -89,7 +89,7 @@ class TransactionCostWorkload : public TestWorkload { return success(tr->get(workload.getKey(testNumber))); } - int64_t expectedFinalCost() const override { return 2; } + int64_t expectedFinalCost() const override { return 2 * CLIENT_KNOBS->READ_COST_BYTE_FACTOR; } }; class WriteTest : public ITest { @@ -101,7 +101,9 @@ class TransactionCostWorkload : public TestWorkload { return Void(); } - int64_t expectedFinalCost() const override { return CLIENT_KNOBS->GLOBAL_TAG_THROTTLING_RW_FUNGIBILITY_RATIO; } + int64_t expectedFinalCost() const override { + return CLIENT_KNOBS->GLOBAL_TAG_THROTTLING_RW_FUNGIBILITY_RATIO * CLIENT_KNOBS->READ_COST_BYTE_FACTOR; + } }; class WriteLargeValueTest : public ITest { @@ -114,7 +116,7 @@ class TransactionCostWorkload : public TestWorkload { } int64_t expectedFinalCost() const override { - return 2 * CLIENT_KNOBS->GLOBAL_TAG_THROTTLING_RW_FUNGIBILITY_RATIO; + return 2 * CLIENT_KNOBS->GLOBAL_TAG_THROTTLING_RW_FUNGIBILITY_RATIO * CLIENT_KNOBS->READ_COST_BYTE_FACTOR; } }; @@ -130,7 +132,7 @@ class TransactionCostWorkload : public TestWorkload { } int64_t expectedFinalCost() const override { - return 10 * CLIENT_KNOBS->GLOBAL_TAG_THROTTLING_RW_FUNGIBILITY_RATIO; + return 10 * CLIENT_KNOBS->GLOBAL_TAG_THROTTLING_RW_FUNGIBILITY_RATIO * CLIENT_KNOBS->READ_COST_BYTE_FACTOR; } }; @@ -143,7 +145,7 @@ class TransactionCostWorkload : public TestWorkload { return Void(); } - int64_t expectedFinalCost() const override { return 1; } + int64_t expectedFinalCost() const override { return CLIENT_KNOBS->READ_COST_BYTE_FACTOR; } }; class ReadRangeTest : public ITest { @@ -174,7 +176,7 @@ class TransactionCostWorkload : public TestWorkload { return success(tr->getRange(keys, 10)); } - int64_t expectedFinalCost() const override { return 1; } + int64_t expectedFinalCost() const override { return CLIENT_KNOBS->READ_COST_BYTE_FACTOR; } }; class ReadMultipleValuesTest : public ITest { @@ -210,7 +212,7 @@ class TransactionCostWorkload : public TestWorkload { return waitForAll(futures); } - int64_t expectedFinalCost() const override { return 10; } + int64_t expectedFinalCost() const override { return 10 * CLIENT_KNOBS->READ_COST_BYTE_FACTOR; } }; class LargeReadRangeTest : public ITest { @@ -244,7 +246,7 @@ class TransactionCostWorkload : public TestWorkload { return success(tr->getRange(keys, 10)); } - int64_t expectedFinalCost() const override { return 11; } + int64_t expectedFinalCost() const override { return 11 * CLIENT_KNOBS->READ_COST_BYTE_FACTOR; } }; static std::unique_ptr createRandomTest(int64_t testNumber) {