FSTLRUGarbageCollector.mm 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. /*
  2. * Copyright 2018 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "Firestore/Source/Local/FSTLRUGarbageCollector.h"
  17. #include <chrono> //NOLINT(build/c++11)
  18. #include <queue>
  19. #include <utility>
  20. #import "Firestore/Source/Local/FSTPersistence.h"
  21. #include "Firestore/core/include/firebase/firestore/timestamp.h"
  22. #include "Firestore/core/src/firebase/firestore/api/settings.h"
  23. #include "Firestore/core/src/firebase/firestore/model/document_key.h"
  24. #include "Firestore/core/src/firebase/firestore/util/log.h"
  25. namespace api = firebase::firestore::api;
  26. using Millis = std::chrono::milliseconds;
  27. using firebase::Timestamp;
  28. using firebase::firestore::local::LruParams;
  29. using firebase::firestore::local::LruResults;
  30. using firebase::firestore::model::DocumentKey;
  31. using firebase::firestore::model::ListenSequenceNumber;
  32. using firebase::firestore::model::TargetId;
  33. const ListenSequenceNumber kFSTListenSequenceNumberInvalid = -1;
  34. static Millis::rep millisecondsBetween(const Timestamp &start, const Timestamp &end) {
  35. return std::chrono::duration_cast<Millis>(end.ToTimePoint() - start.ToTimePoint()).count();
  36. }
  37. namespace {
  38. /**
  39. * RollingSequenceNumberBuffer tracks the nth sequence number in a series. Sequence numbers may be
  40. * added out of order.
  41. */
  42. class RollingSequenceNumberBuffer {
  43. public:
  44. explicit RollingSequenceNumberBuffer(size_t max_elements)
  45. : queue_(std::priority_queue<ListenSequenceNumber>()), max_elements_(max_elements) {
  46. }
  47. RollingSequenceNumberBuffer(const RollingSequenceNumberBuffer &other) = delete;
  48. RollingSequenceNumberBuffer &operator=(const RollingSequenceNumberBuffer &other) = delete;
  49. void AddElement(ListenSequenceNumber sequence_number) {
  50. if (queue_.size() < max_elements_) {
  51. queue_.push(sequence_number);
  52. } else {
  53. ListenSequenceNumber highestValue = queue_.top();
  54. if (sequence_number < highestValue) {
  55. queue_.pop();
  56. queue_.push(sequence_number);
  57. }
  58. }
  59. }
  60. ListenSequenceNumber max_value() const {
  61. return queue_.top();
  62. }
  63. size_t size() const {
  64. return queue_.size();
  65. }
  66. private:
  67. std::priority_queue<ListenSequenceNumber> queue_;
  68. const size_t max_elements_;
  69. };
  70. } // namespace
  71. @implementation FSTLRUGarbageCollector {
  72. __weak id<FSTLRUDelegate> _delegate;
  73. LruParams _params;
  74. }
  75. - (instancetype)initWithDelegate:(id<FSTLRUDelegate>)delegate params:(LruParams)params {
  76. self = [super init];
  77. if (self) {
  78. _delegate = delegate;
  79. _params = std::move(params);
  80. }
  81. return self;
  82. }
  83. - (LruResults)collectWithLiveTargets:
  84. (const std::unordered_map<TargetId, FSTQueryData *> &)liveTargets {
  85. if (_params.minBytesThreshold == api::Settings::CacheSizeUnlimited) {
  86. LOG_DEBUG("Garbage collection skipped; disabled");
  87. return LruResults::DidNotRun();
  88. }
  89. size_t currentSize = [self byteSize];
  90. if (currentSize < _params.minBytesThreshold) {
  91. // Not enough on disk to warrant collection. Wait another timeout cycle.
  92. LOG_DEBUG("Garbage collection skipped; Cache size %s is lower than threshold %s", currentSize,
  93. _params.minBytesThreshold);
  94. return LruResults::DidNotRun();
  95. } else {
  96. LOG_DEBUG("Running garbage collection on cache of size: %s", currentSize);
  97. return [self runGCWithLiveTargets:liveTargets];
  98. }
  99. }
  100. - (LruResults)runGCWithLiveTargets:
  101. (const std::unordered_map<TargetId, FSTQueryData *> &)liveTargets {
  102. Timestamp start = Timestamp::Now();
  103. int sequenceNumbers = [self queryCountForPercentile:_params.percentileToCollect];
  104. // Cap at the configured max
  105. if (sequenceNumbers > _params.maximumSequenceNumbersToCollect) {
  106. sequenceNumbers = _params.maximumSequenceNumbersToCollect;
  107. }
  108. Timestamp countedTargets = Timestamp::Now();
  109. ListenSequenceNumber upperBound = [self sequenceNumberForQueryCount:sequenceNumbers];
  110. Timestamp foundUpperBound = Timestamp::Now();
  111. int numTargetsRemoved = [self removeQueriesUpThroughSequenceNumber:upperBound
  112. liveQueries:liveTargets];
  113. Timestamp removedTargets = Timestamp::Now();
  114. int numDocumentsRemoved = [self removeOrphanedDocumentsThroughSequenceNumber:upperBound];
  115. Timestamp removedDocuments = Timestamp::Now();
  116. std::string desc = "LRU Garbage Collection:\n";
  117. absl::StrAppend(&desc, "\tCounted targets in ", millisecondsBetween(start, countedTargets),
  118. "ms\n");
  119. absl::StrAppend(&desc, "\tDetermined least recently used ", sequenceNumbers,
  120. " sequence numbers in ", millisecondsBetween(countedTargets, foundUpperBound),
  121. "ms\n");
  122. absl::StrAppend(&desc, "\tRemoved ", numTargetsRemoved, " targets in ",
  123. millisecondsBetween(foundUpperBound, removedTargets), "ms\n");
  124. absl::StrAppend(&desc, "\tRemoved ", numDocumentsRemoved, " documents in ",
  125. millisecondsBetween(removedTargets, removedDocuments), "ms\n");
  126. absl::StrAppend(&desc, "Total duration: ", millisecondsBetween(start, removedDocuments), "ms");
  127. LOG_DEBUG(desc.c_str());
  128. return LruResults{/* didRun= */ true, sequenceNumbers, numTargetsRemoved, numDocumentsRemoved};
  129. }
  130. - (int)queryCountForPercentile:(NSUInteger)percentile {
  131. size_t totalCount = [_delegate sequenceNumberCount];
  132. int setSize = (int)((percentile / 100.0f) * totalCount);
  133. return setSize;
  134. }
  135. - (ListenSequenceNumber)sequenceNumberForQueryCount:(NSUInteger)queryCount {
  136. if (queryCount == 0) {
  137. return kFSTListenSequenceNumberInvalid;
  138. }
  139. RollingSequenceNumberBuffer buffer(queryCount);
  140. [_delegate enumerateTargetsUsingCallback:[&buffer](FSTQueryData *queryData) {
  141. buffer.AddElement(queryData.sequenceNumber);
  142. }];
  143. [_delegate enumerateMutationsUsingCallback:[&buffer](const DocumentKey &docKey,
  144. ListenSequenceNumber sequenceNumber) {
  145. buffer.AddElement(sequenceNumber);
  146. }];
  147. return buffer.max_value();
  148. }
  149. - (int)removeQueriesUpThroughSequenceNumber:(ListenSequenceNumber)sequenceNumber
  150. liveQueries:(const std::unordered_map<TargetId, FSTQueryData *> &)
  151. liveQueries {
  152. return [_delegate removeTargetsThroughSequenceNumber:sequenceNumber liveQueries:liveQueries];
  153. }
  154. - (int)removeOrphanedDocumentsThroughSequenceNumber:(ListenSequenceNumber)sequenceNumber {
  155. return [_delegate removeOrphanedDocumentsThroughSequenceNumber:sequenceNumber];
  156. }
  157. - (size_t)byteSize {
  158. return [_delegate byteSize];
  159. }
  160. @end