FSTLRUGarbageCollector.mm 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  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/model/document_key.h"
  23. #include "Firestore/core/src/firebase/firestore/util/log.h"
  24. using Millis = std::chrono::milliseconds;
  25. using firebase::Timestamp;
  26. using firebase::firestore::local::LruParams;
  27. using firebase::firestore::local::LruResults;
  28. using firebase::firestore::model::DocumentKey;
  29. using firebase::firestore::model::ListenSequenceNumber;
  30. using firebase::firestore::model::TargetId;
  31. const int64_t kFIRFirestoreCacheSizeUnlimited = LruParams::CacheSizeUnlimited;
  32. const ListenSequenceNumber kFSTListenSequenceNumberInvalid = -1;
  33. static Millis::rep millisecondsBetween(const Timestamp &start, const Timestamp &end) {
  34. return std::chrono::duration_cast<Millis>(end.ToTimePoint() - start.ToTimePoint()).count();
  35. }
  36. /**
  37. * RollingSequenceNumberBuffer tracks the nth sequence number in a series. Sequence numbers may be
  38. * added out of order.
  39. */
  40. class RollingSequenceNumberBuffer {
  41. public:
  42. explicit RollingSequenceNumberBuffer(size_t max_elements)
  43. : queue_(std::priority_queue<ListenSequenceNumber>()), max_elements_(max_elements) {
  44. }
  45. RollingSequenceNumberBuffer(const RollingSequenceNumberBuffer &other) = delete;
  46. RollingSequenceNumberBuffer &operator=(const RollingSequenceNumberBuffer &other) = delete;
  47. void AddElement(ListenSequenceNumber sequence_number) {
  48. if (queue_.size() < max_elements_) {
  49. queue_.push(sequence_number);
  50. } else {
  51. ListenSequenceNumber highestValue = queue_.top();
  52. if (sequence_number < highestValue) {
  53. queue_.pop();
  54. queue_.push(sequence_number);
  55. }
  56. }
  57. }
  58. ListenSequenceNumber max_value() const {
  59. return queue_.top();
  60. }
  61. size_t size() const {
  62. return queue_.size();
  63. }
  64. private:
  65. std::priority_queue<ListenSequenceNumber> queue_;
  66. const size_t max_elements_;
  67. };
  68. @implementation FSTLRUGarbageCollector {
  69. __weak id<FSTLRUDelegate> _delegate;
  70. LruParams _params;
  71. }
  72. - (instancetype)initWithDelegate:(id<FSTLRUDelegate>)delegate params:(LruParams)params {
  73. self = [super init];
  74. if (self) {
  75. _delegate = delegate;
  76. _params = std::move(params);
  77. }
  78. return self;
  79. }
  80. - (LruResults)collectWithLiveTargets:
  81. (const std::unordered_map<TargetId, FSTQueryData *> &)liveTargets {
  82. if (_params.minBytesThreshold == kFIRFirestoreCacheSizeUnlimited) {
  83. LOG_DEBUG("Garbage collection skipped; disabled");
  84. return LruResults::DidNotRun();
  85. }
  86. size_t currentSize = [self byteSize];
  87. if (currentSize < _params.minBytesThreshold) {
  88. // Not enough on disk to warrant collection. Wait another timeout cycle.
  89. LOG_DEBUG("Garbage collection skipped; Cache size %s is lower than threshold %s", currentSize,
  90. _params.minBytesThreshold);
  91. return LruResults::DidNotRun();
  92. } else {
  93. LOG_DEBUG("Running garbage collection on cache of size: %s", currentSize);
  94. return [self runGCWithLiveTargets:liveTargets];
  95. }
  96. }
  97. - (LruResults)runGCWithLiveTargets:
  98. (const std::unordered_map<TargetId, FSTQueryData *> &)liveTargets {
  99. Timestamp start = Timestamp::Now();
  100. int sequenceNumbers = [self queryCountForPercentile:_params.percentileToCollect];
  101. // Cap at the configured max
  102. if (sequenceNumbers > _params.maximumSequenceNumbersToCollect) {
  103. sequenceNumbers = _params.maximumSequenceNumbersToCollect;
  104. }
  105. Timestamp countedTargets = Timestamp::Now();
  106. ListenSequenceNumber upperBound = [self sequenceNumberForQueryCount:sequenceNumbers];
  107. Timestamp foundUpperBound = Timestamp::Now();
  108. int numTargetsRemoved = [self removeQueriesUpThroughSequenceNumber:upperBound
  109. liveQueries:liveTargets];
  110. Timestamp removedTargets = Timestamp::Now();
  111. int numDocumentsRemoved = [self removeOrphanedDocumentsThroughSequenceNumber:upperBound];
  112. Timestamp removedDocuments = Timestamp::Now();
  113. std::string desc = "LRU Garbage Collection:\n";
  114. absl::StrAppend(&desc, "\tCounted targets in ", millisecondsBetween(start, countedTargets),
  115. "ms\n");
  116. absl::StrAppend(&desc, "\tDetermined least recently used ", sequenceNumbers,
  117. " sequence numbers in ", millisecondsBetween(countedTargets, foundUpperBound),
  118. "ms\n");
  119. absl::StrAppend(&desc, "\tRemoved ", numTargetsRemoved, " targets in ",
  120. millisecondsBetween(foundUpperBound, removedTargets), "ms\n");
  121. absl::StrAppend(&desc, "\tRemoved ", numDocumentsRemoved, " documents in ",
  122. millisecondsBetween(removedTargets, removedDocuments), "ms\n");
  123. absl::StrAppend(&desc, "Total duration: ", millisecondsBetween(start, removedDocuments), "ms");
  124. LOG_DEBUG(desc.c_str());
  125. return LruResults{/* didRun= */ true, sequenceNumbers, numTargetsRemoved, numDocumentsRemoved};
  126. }
  127. - (int)queryCountForPercentile:(NSUInteger)percentile {
  128. size_t totalCount = [_delegate sequenceNumberCount];
  129. int setSize = (int)((percentile / 100.0f) * totalCount);
  130. return setSize;
  131. }
  132. - (ListenSequenceNumber)sequenceNumberForQueryCount:(NSUInteger)queryCount {
  133. if (queryCount == 0) {
  134. return kFSTListenSequenceNumberInvalid;
  135. }
  136. RollingSequenceNumberBuffer buffer(queryCount);
  137. // Pointer is necessary to access stack-allocated buffer from a block.
  138. RollingSequenceNumberBuffer *ptr_to_buffer = &buffer;
  139. [_delegate enumerateTargetsUsingBlock:^(FSTQueryData *queryData, BOOL *stop) {
  140. ptr_to_buffer->AddElement(queryData.sequenceNumber);
  141. }];
  142. [_delegate enumerateMutationsUsingBlock:^(const DocumentKey &docKey,
  143. ListenSequenceNumber sequenceNumber, BOOL *stop) {
  144. ptr_to_buffer->AddElement(sequenceNumber);
  145. }];
  146. return buffer.max_value();
  147. }
  148. - (int)removeQueriesUpThroughSequenceNumber:(ListenSequenceNumber)sequenceNumber
  149. liveQueries:(const std::unordered_map<TargetId, FSTQueryData *> &)
  150. liveQueries {
  151. return [_delegate removeTargetsThroughSequenceNumber:sequenceNumber liveQueries:liveQueries];
  152. }
  153. - (int)removeOrphanedDocumentsThroughSequenceNumber:(ListenSequenceNumber)sequenceNumber {
  154. return [_delegate removeOrphanedDocumentsThroughSequenceNumber:sequenceNumber];
  155. }
  156. - (size_t)byteSize {
  157. return [_delegate byteSize];
  158. }
  159. @end