FSTLRUGarbageCollector.mm 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  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/FSTMutationQueue.h"
  21. #import "Firestore/Source/Local/FSTPersistence.h"
  22. #import "Firestore/Source/Local/FSTQueryCache.h"
  23. #include "Firestore/core/include/firebase/firestore/timestamp.h"
  24. #include "Firestore/core/src/firebase/firestore/model/document_key.h"
  25. #include "Firestore/core/src/firebase/firestore/util/log.h"
  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. const int64_t kFIRFirestoreCacheSizeUnlimited = LruParams::CacheSizeUnlimited;
  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. /**
  38. * RollingSequenceNumberBuffer tracks the nth sequence number in a series. Sequence numbers may be
  39. * added out of order.
  40. */
  41. class RollingSequenceNumberBuffer {
  42. public:
  43. explicit RollingSequenceNumberBuffer(size_t max_elements)
  44. : queue_(std::priority_queue<ListenSequenceNumber>()), max_elements_(max_elements) {
  45. }
  46. RollingSequenceNumberBuffer(const RollingSequenceNumberBuffer &other) = delete;
  47. RollingSequenceNumberBuffer &operator=(const RollingSequenceNumberBuffer &other) = delete;
  48. void AddElement(ListenSequenceNumber sequence_number) {
  49. if (queue_.size() < max_elements_) {
  50. queue_.push(sequence_number);
  51. } else {
  52. ListenSequenceNumber highestValue = queue_.top();
  53. if (sequence_number < highestValue) {
  54. queue_.pop();
  55. queue_.push(sequence_number);
  56. }
  57. }
  58. }
  59. ListenSequenceNumber max_value() const {
  60. return queue_.top();
  61. }
  62. size_t size() const {
  63. return queue_.size();
  64. }
  65. private:
  66. std::priority_queue<ListenSequenceNumber> queue_;
  67. const size_t max_elements_;
  68. };
  69. @implementation FSTLRUGarbageCollector {
  70. id<FSTLRUDelegate> _delegate;
  71. LruParams _params;
  72. }
  73. - (instancetype)initWithDelegate:(id<FSTLRUDelegate>)delegate params:(LruParams)params {
  74. self = [super init];
  75. if (self) {
  76. _delegate = delegate;
  77. _params = std::move(params);
  78. }
  79. return self;
  80. }
  81. - (LruResults)collectWithLiveTargets:(NSDictionary<NSNumber *, 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:(NSDictionary<NSNumber *, FSTQueryData *> *)liveTargets {
  98. Timestamp start = Timestamp::Now();
  99. int sequenceNumbers = [self queryCountForPercentile:_params.percentileToCollect];
  100. // Cap at the configured max
  101. if (sequenceNumbers > _params.maximumSequenceNumbersToCollect) {
  102. sequenceNumbers = _params.maximumSequenceNumbersToCollect;
  103. }
  104. Timestamp countedTargets = Timestamp::Now();
  105. ListenSequenceNumber upperBound = [self sequenceNumberForQueryCount:sequenceNumbers];
  106. Timestamp foundUpperBound = Timestamp::Now();
  107. int numTargetsRemoved =
  108. [self removeQueriesUpThroughSequenceNumber:upperBound liveQueries:liveTargets];
  109. Timestamp removedTargets = Timestamp::Now();
  110. int numDocumentsRemoved = [self removeOrphanedDocumentsThroughSequenceNumber:upperBound];
  111. Timestamp removedDocuments = Timestamp::Now();
  112. std::string desc = "LRU Garbage Collection:\n";
  113. absl::StrAppend(&desc, "\tCounted targets in ", millisecondsBetween(start, countedTargets),
  114. "ms\n");
  115. absl::StrAppend(&desc, "\tDetermined least recently used ", sequenceNumbers,
  116. " sequence numbers in ", millisecondsBetween(countedTargets, foundUpperBound),
  117. "ms\n");
  118. absl::StrAppend(&desc, "\tRemoved ", numTargetsRemoved, " targets in ",
  119. millisecondsBetween(foundUpperBound, removedTargets), "ms\n");
  120. absl::StrAppend(&desc, "\tRemoved ", numDocumentsRemoved, " documents in ",
  121. millisecondsBetween(removedTargets, removedDocuments), "ms\n");
  122. absl::StrAppend(&desc, "Total duration: ", millisecondsBetween(start, removedDocuments), "ms");
  123. LOG_DEBUG(desc.c_str());
  124. return LruResults{/* didRun= */ true, sequenceNumbers, numTargetsRemoved, numDocumentsRemoved};
  125. }
  126. - (int)queryCountForPercentile:(NSUInteger)percentile {
  127. int totalCount = [_delegate sequenceNumberCount];
  128. int setSize = (int)((percentile / 100.0f) * totalCount);
  129. return setSize;
  130. }
  131. - (ListenSequenceNumber)sequenceNumberForQueryCount:(NSUInteger)queryCount {
  132. if (queryCount == 0) {
  133. return kFSTListenSequenceNumberInvalid;
  134. }
  135. RollingSequenceNumberBuffer buffer(queryCount);
  136. // Pointer is necessary to access stack-allocated buffer from a block.
  137. RollingSequenceNumberBuffer *ptr_to_buffer = &buffer;
  138. [_delegate enumerateTargetsUsingBlock:^(FSTQueryData *queryData, BOOL *stop) {
  139. ptr_to_buffer->AddElement(queryData.sequenceNumber);
  140. }];
  141. [_delegate enumerateMutationsUsingBlock:^(const DocumentKey &docKey,
  142. ListenSequenceNumber sequenceNumber, BOOL *stop) {
  143. ptr_to_buffer->AddElement(sequenceNumber);
  144. }];
  145. return buffer.max_value();
  146. }
  147. - (int)removeQueriesUpThroughSequenceNumber:(ListenSequenceNumber)sequenceNumber
  148. liveQueries:
  149. (NSDictionary<NSNumber *, FSTQueryData *> *)liveQueries {
  150. return [_delegate removeTargetsThroughSequenceNumber:sequenceNumber liveQueries:liveQueries];
  151. }
  152. - (int)removeOrphanedDocumentsThroughSequenceNumber:(ListenSequenceNumber)sequenceNumber {
  153. return [_delegate removeOrphanedDocumentsThroughSequenceNumber:sequenceNumber];
  154. }
  155. - (size_t)byteSize {
  156. return [_delegate byteSize];
  157. }
  158. @end