diff --git a/fdbcli/fdbcli.actor.cpp b/fdbcli/fdbcli.actor.cpp index 47a380c251..7e6b819035 100644 --- a/fdbcli/fdbcli.actor.cpp +++ b/fdbcli/fdbcli.actor.cpp @@ -2541,6 +2541,16 @@ void throttleGenerator(const char* text, const char *line, std::vector= 2 && tokencmp(tokens[1], "list")) { + if(tokens.size() == 2) { + const char* opts[] = { "throttled", "recommended", "all", nullptr }; + arrayGenerator(text, line, opts, lc); + } + else if(tokens.size() == 3) { + const char* opts[] = {"LIMITS", nullptr}; + arrayGenerator(text, line, opts, lc); + } + } } void fdbcliCompCmd(std::string const& text, std::vector& lc) { @@ -2661,6 +2671,9 @@ std::vector throttleHintGenerator(std::vector const& tok else if((tokencmp(tokens[1], "enable") || tokencmp(tokens[1], "disable")) && tokens.size() == 2) { return { "auto" }; } + else if(tokens.size() == 2 && tokencmp(tokens[1], "list")) { + return {"[throttled|recommended|all]", "[LIMITS]"}; + } else if(tokens.size() == 2 && inArgument) { return { "[ARGS]" }; } @@ -4077,8 +4090,8 @@ ACTOR Future cli(CLIOptions opt, LineNoise* plinenoise) { continue; } else if(tokencmp(tokens[1], "list")) { - if(tokens.size() > 3) { - printf("Usage: throttle list [LIMIT]\n"); + if(tokens.size() > 4) { + printf("Usage: throttle list [throttled|recommended|all] [LIMIT]\n"); printf("\n"); printf("Lists tags that are currently throttled.\n"); printf("The default LIMIT is 100 tags.\n"); @@ -4086,36 +4099,72 @@ ACTOR Future cli(CLIOptions opt, LineNoise* plinenoise) { continue; } - state int throttleListLimit = 100; + state bool reportThrottled = true; + state bool reportRecommended = false; if(tokens.size() >= 3) { + if(tokencmp(tokens[2], "recommended")) { + reportThrottled = false; reportRecommended = true; + } + else if(tokencmp(tokens[2], "all")){ + reportThrottled = true; reportRecommended = true; + } + else if(!tokencmp(tokens[2], "throttled")){ + printf("ERROR: failed to parse `%s'.\n", printable(tokens[2]).c_str()); + is_error = true; + continue; + } + } + + state int throttleListLimit = 100; + if(tokens.size() >= 4) { char *end; throttleListLimit = std::strtol((const char*)tokens[2].begin(), &end, 10); - if ((tokens.size() > 3 && !std::isspace(*end)) || (tokens.size() == 3 && *end != '\0')) { - printf("ERROR: failed to parse limit `%s'.\n", printable(tokens[2]).c_str()); + if ((tokens.size() > 4 && !std::isspace(*end)) || (tokens.size() == 3 && *end != '\0')) { + printf("ERROR: failed to parse limit `%s'.\n", printable(tokens[3]).c_str()); is_error = true; continue; } } - std::vector tags = wait(ThrottleApi::getThrottledTags(db, throttleListLimit)); + state std::vector tags; // = wait(ThrottleApi::getThrottledTags(db, throttleListLimit)); + if(reportThrottled && reportRecommended) { + wait(store(tags, ThrottleApi::getThrottledTags(db, throttleListLimit, true))); + } + else if(reportThrottled) { + wait(store(tags, ThrottleApi::getThrottledTags(db, throttleListLimit))); + } + else if(reportRecommended) { + wait(store(tags, ThrottleApi::getRecommendedTags(db, throttleListLimit))); + } bool anyLogged = false; for(auto itr = tags.begin(); itr != tags.end(); ++itr) { if(itr->expirationTime > now()) { if(!anyLogged) { printf("Throttled tags:\n\n"); - printf(" Rate (txn/s) | Expiration (s) | Priority | Type | Tag\n"); - printf(" --------------+----------------+-----------+--------+------------------\n"); + printf(" Rate (txn/s) | Expiration (s) | Priority | Type | Reason |Tag\n"); + printf(" --------------+----------------+-----------+--------+------------+------\n"); anyLogged = true; } - printf(" %12d | %13ds | %9s | %6s | %s\n", - (int)(itr->tpsRate), - std::min((int)(itr->expirationTime-now()), (int)(itr->initialDuration)), - transactionPriorityToString(itr->priority, false), - itr->throttleType == TagThrottleType::AUTO ? "auto" : "manual", - itr->tag.toString().c_str()); + std::string reasonStr = "unset"; + if(itr->reason == TagThrottledReason::MANUAL){ + reasonStr = "manual"; + } + else if(itr->reason == TagThrottledReason::BUSY_WRITE) { + reasonStr = "busy write"; + } + else if(itr->reason == TagThrottledReason::BUSY_READ) { + reasonStr = "busy read"; + } + + printf(" %12d | %13ds | %9s | %6s | %10s |%s\n", (int)(itr->tpsRate), + std::min((int)(itr->expirationTime - now()), (int)(itr->initialDuration)), + transactionPriorityToString(itr->priority, false), + itr->throttleType == TagThrottleType::AUTO ? "auto" : "manual", + reasonStr.c_str(), + itr->tag.toString().c_str()); } } @@ -4124,7 +4173,7 @@ ACTOR Future cli(CLIOptions opt, LineNoise* plinenoise) { printf("Usage: throttle list [LIMIT]\n"); } if(!anyLogged) { - printf("There are no throttled tags\n"); + printf("There are no %s tags\n", reportThrottled ? "throttled" : "recommended"); } } else if(tokencmp(tokens[1], "on")) { diff --git a/fdbclient/TagThrottle.actor.cpp b/fdbclient/TagThrottle.actor.cpp index 86fabf9bde..64a6d8e3b1 100644 --- a/fdbclient/TagThrottle.actor.cpp +++ b/fdbclient/TagThrottle.actor.cpp @@ -216,13 +216,18 @@ namespace ThrottleApi { } ACTOR Future throttleTags(Database db, TagSet tags, double tpsRate, double initialDuration, - TagThrottleType throttleType, TransactionPriority priority, Optional expirationTime) { + TagThrottleType throttleType, TransactionPriority priority, Optional expirationTime, + Optional reason) { state Transaction tr(db); state Key key = TagThrottleKey(tags, throttleType, priority).toKey(); ASSERT(initialDuration > 0); - TagThrottleValue throttle(tpsRate, expirationTime.present() ? expirationTime.get() : 0, initialDuration); + if(throttleType == TagThrottleType::MANUAL) { + reason = TagThrottledReason::MANUAL; + } + TagThrottleValue throttle(tpsRate, expirationTime.present() ? expirationTime.get() : 0, initialDuration, + reason.present() ? reason.get() : TagThrottledReason::UNSET); BinaryWriter wr(IncludeVersion(ProtocolVersion::withTagThrottleValue())); wr << throttle; state Value value = wr.toValue(); diff --git a/fdbclient/TagThrottle.h b/fdbclient/TagThrottle.h index 061fdb663b..93db19011a 100644 --- a/fdbclient/TagThrottle.h +++ b/fdbclient/TagThrottle.h @@ -115,6 +115,10 @@ enum class TagThrottleType : uint8_t { AUTO }; +struct TagThrottledReason { + static constexpr uint8_t UNSET = 0, MANUAL = 1, BUSY_READ = 2, BUSY_WRITE = 3; +}; + struct TagThrottleKey { TagSet tags; TagThrottleType throttleType; @@ -132,17 +136,18 @@ struct TagThrottleValue { double tpsRate; double expirationTime; double initialDuration; + uint8_t reason; - TagThrottleValue() : tpsRate(0), expirationTime(0), initialDuration(0) {} - TagThrottleValue(double tpsRate, double expirationTime, double initialDuration) - : tpsRate(tpsRate), expirationTime(expirationTime), initialDuration(initialDuration) {} + TagThrottleValue() : tpsRate(0), expirationTime(0), initialDuration(0), reason(TagThrottledReason::UNSET) {} + TagThrottleValue(double tpsRate, double expirationTime, double initialDuration, uint8_t reason) + : tpsRate(tpsRate), expirationTime(expirationTime), initialDuration(initialDuration), reason(reason) {} static TagThrottleValue fromValue(const ValueRef& value); //To change this serialization, ProtocolVersion::TagThrottleValue must be updated, and downgrades need to be considered template void serialize(Ar& ar) { - serializer(ar, tpsRate, expirationTime, initialDuration); + serializer(ar, tpsRate, expirationTime, initialDuration, reason); } }; @@ -153,12 +158,13 @@ struct TagThrottleInfo { double tpsRate; double expirationTime; double initialDuration; + uint8_t reason; - TagThrottleInfo(TransactionTag tag, TagThrottleType throttleType, TransactionPriority priority, double tpsRate, double expirationTime, double initialDuration) - : tag(tag), throttleType(throttleType), priority(priority), tpsRate(tpsRate), expirationTime(expirationTime), initialDuration(initialDuration) {} + TagThrottleInfo(TransactionTag tag, TagThrottleType throttleType, TransactionPriority priority, double tpsRate, double expirationTime, double initialDuration, uint8_t reason = TagThrottledReason::UNSET) + : tag(tag), throttleType(throttleType), priority(priority), tpsRate(tpsRate), expirationTime(expirationTime), initialDuration(initialDuration), reason(reason) {} - TagThrottleInfo(TagThrottleKey key, TagThrottleValue value) - : throttleType(key.throttleType), priority(key.priority), tpsRate(value.tpsRate), expirationTime(value.expirationTime), initialDuration(value.initialDuration) + TagThrottleInfo(TagThrottleKey key, TagThrottleValue value) + : throttleType(key.throttleType), priority(key.priority), tpsRate(value.tpsRate), expirationTime(value.expirationTime), initialDuration(value.initialDuration), reason(value.reason) { ASSERT(key.tags.size() == 1); // Multiple tags per throttle is not currently supported tag = *key.tags.begin(); @@ -167,10 +173,11 @@ struct TagThrottleInfo { namespace ThrottleApi { Future> getThrottledTags(Database const& db, int const& limit, bool const& containsRecommend = false); - Future> getRecommendedTags(Database const& db, int limit); + Future> getRecommendedTags(Database const& db, int const& limit); Future throttleTags(Database const& db, TagSet const& tags, double const& tpsRate, double const& initialDuration, - TagThrottleType const& throttleType, TransactionPriority const& priority, Optional const& expirationTime = Optional()); + TagThrottleType const& throttleType, TransactionPriority const& priority, Optional const& expirationTime = Optional(), + Optional const& reason = Optional()); Future unthrottleTags(Database const& db, TagSet const& tags, Optional const& throttleType, Optional const& priority);