mkql_key_payload_value_lru_cache.h 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. #pragma once
  2. #include <yql/essentials/public/udf/udf_value.h>
  3. #include <yql/essentials/minikql/mkql_node.h>
  4. #include <yql/essentials/minikql/computation/mkql_computation_node_holders.h>
  5. #include <util/generic/string.h>
  6. #include <unordered_map>
  7. #include <list>
  8. #include <chrono>
  9. namespace NKikimr::NMiniKQL {
  10. //Design notes:
  11. // Commodity LRUCache with HashMap and DoubleLinkedList. Key(that is TUnboxedValue) is stored in both HashMap and List
  12. // Lazy(postponed) TTL implementation. An entry is not deleted immediately when its TTL is expired, the deletion is postponed until:
  13. // - when it is accessed via Get()
  14. // - when it is not accessed for a long period of time and becomes the LRU. Then it is garbage collected in Tick()
  15. // Not thread safe
  16. // Never requests system time, expects monotonically increased time points in methods argument
  17. class TUnboxedKeyValueLruCacheWithTtl {
  18. struct TEntry {
  19. TEntry(NUdf::TUnboxedValue key, NUdf::TUnboxedValue value, std::chrono::time_point<std::chrono::steady_clock> expiration)
  20. : Key(std::move(key))
  21. , Value(std::move(value))
  22. , Expiration(std::move(expiration))
  23. {}
  24. NUdf::TUnboxedValue Key;
  25. NUdf::TUnboxedValue Value;
  26. std::chrono::time_point<std::chrono::steady_clock> Expiration;
  27. };
  28. using TUsageList = std::list<TEntry>;
  29. public:
  30. TUnboxedKeyValueLruCacheWithTtl(size_t maxSize, const NKikimr::NMiniKQL::TType* keyType)
  31. : MaxSize(maxSize)
  32. , KeyTypeHelper(keyType)
  33. , Map(
  34. 1000,
  35. KeyTypeHelper.GetValueHash(),
  36. KeyTypeHelper.GetValueEqual()
  37. )
  38. {
  39. Y_ABORT_UNLESS(MaxSize > 0);
  40. }
  41. TUnboxedKeyValueLruCacheWithTtl(const TUnboxedKeyValueLruCacheWithTtl&) = delete; //to prevent unintentional copy of a large object
  42. void Update(NUdf::TUnboxedValue&& key, NUdf::TUnboxedValue&& value, std::chrono::time_point<std::chrono::steady_clock>&& expiration) {
  43. if (auto it = Map.find(key); it != Map.end()) {
  44. Touch(it->second);
  45. auto& entry = *it->second;
  46. entry.Value = std::move(value);
  47. entry.Expiration = std::move(expiration);
  48. } else {
  49. if (Map.size() == MaxSize) {
  50. RemoveLeastRecentlyUsedEntry();
  51. }
  52. UsageList.emplace_back(key, std::move(value), std::move(expiration));
  53. Map.emplace_hint(it, std::move(key), --UsageList.end());
  54. }
  55. }
  56. std::optional<NUdf::TUnboxedValue> Get(const NUdf::TUnboxedValue key, const std::chrono::time_point<std::chrono::steady_clock>& now) {
  57. if (auto it = Map.find(key); it != Map.end()) {
  58. if (now < it->second->Expiration) {
  59. Touch(it->second);
  60. return it->second->Value;
  61. } else {
  62. UsageList.erase(it->second);
  63. Map.erase(it);
  64. return std::nullopt;
  65. }
  66. }
  67. return std::nullopt;
  68. }
  69. // Perform garbage collection, single step, O(1) time.
  70. // Must be called periodically
  71. bool Tick(const std::chrono::time_point<std::chrono::steady_clock>& now) {
  72. if (UsageList.empty()) {
  73. return false;
  74. }
  75. if (now < UsageList.front().Expiration) {
  76. return false;
  77. }
  78. RemoveLeastRecentlyUsedEntry();
  79. return true;
  80. }
  81. // Perform garbage collection, O(1) amortized, but O(n) one-time
  82. void Prune(const std::chrono::time_point<std::chrono::steady_clock>& now) {
  83. while (Tick(now)) {
  84. }
  85. }
  86. size_t Size() const {
  87. Y_ABORT_UNLESS(Map.size() == UsageList.size());
  88. return Map.size();
  89. }
  90. private:
  91. struct TKeyTypeHelpers {
  92. TKeyTypes KeyTypes;
  93. bool IsTuple;
  94. NUdf::IHash::TPtr Hash;
  95. NUdf::IEquate::TPtr Equate;
  96. };
  97. void Touch(TUsageList::iterator it) {
  98. UsageList.splice(UsageList.end(), UsageList, it); //move accessed element to the end of Usage list
  99. }
  100. void RemoveLeastRecentlyUsedEntry() {
  101. Map.erase(UsageList.front().Key);
  102. UsageList.pop_front();
  103. }
  104. private:
  105. const size_t MaxSize;
  106. TUsageList UsageList;
  107. const TKeyTypeContanerHelper<true, true, false> KeyTypeHelper;
  108. std::unordered_map<
  109. NUdf::TUnboxedValue,
  110. TUsageList::iterator,
  111. TValueHasher,
  112. TValueEqual,
  113. NKikimr::NMiniKQL::TMKQLAllocator<std::pair<const NUdf::TUnboxedValue, TUsageList::iterator>>
  114. > Map;
  115. };
  116. } //namespace NKikimr::NMiniKQL