/* * TagThrottle.h * * This source file is part of the FoundationDB open source project * * Copyright 2013-2020 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. */ #ifndef FDBCLIENT_TAG_THROTTLE_H #define FDBCLIENT_TAG_THROTTLE_H #pragma once #include "flow/Error.h" #include "flow/flow.h" #include "flow/network.h" #include "fdbclient/FDBTypes.h" #include class Database; namespace ThrottleApi { } typedef StringRef TransactionTagRef; typedef Standalone TransactionTag; class TagSet { public: typedef std::vector::const_iterator const_iterator; TagSet() : bytes(0) {} void addTag(TransactionTagRef tag); size_t size() const; const_iterator begin() const { return tags.begin(); } const_iterator end() const { return tags.end(); } void clear() { tags.clear(); bytes = 0; } template void save(uint8_t* out, Context& c) const { uint8_t *start = out; for (const auto& tag : *this) { *(out++) = (uint8_t)tag.size(); std::copy(tag.begin(), tag.end(), out); out += tag.size(); } ASSERT((size_t)(out - start) == size() + bytes); } template void load(const uint8_t* data, size_t size, Context& context) { //const uint8_t *start = data; const uint8_t *end = data + size; while(data < end) { uint8_t len = *(data++); // Tags are already deduplicated const auto& tag = tags.emplace_back(context.tryReadZeroCopy(data, len), len); data += len; bytes += tag.size(); } ASSERT(data == end); // Deserialized tag sets share the arena with the request that contained them // For this reason, persisting a TagSet that shares memory with other request // members should be done with caution. arena = context.arena(); } size_t getBytes() const { return bytes; } const Arena& getArena() const { return arena; } private: size_t bytes; Arena arena; // Currently there are never >= 256 tags, so // std::vector is faster than std::set. This may // change if we allow more tags in the future. std::vector tags; }; template <> struct dynamic_size_traits : std::true_type { // May be called multiple times during one serialization template static size_t size(const TagSet& t, Context&) { return t.size() + t.getBytes(); } // Guaranteed to be called only once during serialization template static void save(uint8_t* out, const TagSet& t, Context& c) { t.save(out, c); } // Context is an arbitrary type that is plumbed by reference throughout the // load call tree. template static void load(const uint8_t* data, size_t size, TagSet& t, Context& context) { t.load(data, size, context); } }; enum class TagThrottleType : uint8_t { MANUAL, AUTO }; enum class TagThrottledReason: uint8_t { UNSET = 0, MANUAL, BUSY_READ, BUSY_WRITE }; struct TagThrottleKey { TagSet tags; TagThrottleType throttleType; TransactionPriority priority; TagThrottleKey() : throttleType(TagThrottleType::MANUAL), priority(TransactionPriority::DEFAULT) {} TagThrottleKey(TagSet tags, TagThrottleType throttleType, TransactionPriority priority) : tags(tags), throttleType(throttleType), priority(priority) {} Key toKey() const; static TagThrottleKey fromKey(const KeyRef& key); }; struct TagThrottleValue { double tpsRate; double expirationTime; double initialDuration; TagThrottledReason reason; TagThrottleValue() : tpsRate(0), expirationTime(0), initialDuration(0), reason(TagThrottledReason::UNSET) {} TagThrottleValue(double tpsRate, double expirationTime, double initialDuration, TagThrottledReason 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) { if(ar.protocolVersion().hasTagThrottleValueReason()) { serializer(ar, tpsRate, expirationTime, initialDuration, reason); } else if(ar.protocolVersion().hasTagThrottleValue()) { serializer(ar, tpsRate, expirationTime, initialDuration); if(ar.isDeserializing) { reason = TagThrottledReason::UNSET; } } } }; struct TagThrottleInfo { TransactionTag tag; TagThrottleType throttleType; TransactionPriority priority; double tpsRate; double expirationTime; double initialDuration; TagThrottledReason reason; TagThrottleInfo(TransactionTag tag, TagThrottleType throttleType, TransactionPriority priority, double tpsRate, double expirationTime, double initialDuration, TagThrottledReason 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), reason(value.reason) { ASSERT(key.tags.size() == 1); // Multiple tags per throttle is not currently supported tag = *key.tags.begin(); } }; namespace ThrottleApi { Future> getThrottledTags(Database const& db, int const& limit, bool const& containsRecommend = false); 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(), Optional const& reason = Optional()); Future unthrottleTags(Database const& db, TagSet const& tags, Optional const& throttleType, Optional const& priority); Future unthrottleAll(Database db, Optional throttleType, Optional priority); Future expire(Database db); Future enableAuto(Database const& db, bool const& enabled); }; template using TransactionTagMap = std::unordered_map>; template using PrioritizedTransactionTagMap = std::map>; template using UIDTransactionTagMap = std::unordered_map>; #endif