FSTLRUGarbageCollector.mm 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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. #include "Firestore/core/include/firebase/firestore/timestamp.h"
  23. #include "Firestore/core/src/firebase/firestore/model/document_key.h"
  24. #include "Firestore/core/src/firebase/firestore/util/log.h"
  25. using Millis = std::chrono::milliseconds;
  26. using firebase::Timestamp;
  27. using firebase::firestore::local::LruParams;
  28. using firebase::firestore::local::LruResults;
  29. using firebase::firestore::model::DocumentKey;
  30. using firebase::firestore::model::ListenSequenceNumber;
  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:(NSDictionary<NSNumber *, FSTQueryData *> *)liveTargets {
  81. if (_params.minBytesThreshold == kFIRFirestoreCacheSizeUnlimited) {
  82. LOG_DEBUG("Garbage collection skipped; disabled");
  83. return LruResults::DidNotRun();
  84. }
  85. size_t currentSize = [self byteSize];
  86. if (currentSize < _params.minBytesThreshold) {
  87. // Not enough on disk to warrant collection. Wait another timeout cycle.
  88. LOG_DEBUG("Garbage collection skipped; Cache size %s is lower than threshold %s", currentSize,
  89. _params.minBytesThreshold);
  90. return LruResults::DidNotRun();
  91. } else {
  92. LOG_DEBUG("Running garbage collection on cache of size: %s", currentSize);
  93. return [self runGCWithLiveTargets:liveTargets];
  94. }
  95. }
  96. - (LruResults)runGCWithLiveTargets:(NSDictionary<NSNumber *, FSTQueryData *> *)liveTargets {
  97. Timestamp start = Timestamp::Now();
  98. int sequenceNumbers = [self queryCountForPercentile:_params.percentileToCollect];
  99. // Cap at the configured max
  100. if (sequenceNumbers > _params.maximumSequenceNumbersToCollect) {
  101. sequenceNumbers = _params.maximumSequenceNumbersToCollect;
  102. }
  103. Timestamp countedTargets = Timestamp::Now();
  104. ListenSequenceNumber upperBound = [self sequenceNumberForQueryCount:sequenceNumbers];
  105. Timestamp foundUpperBound = Timestamp::Now();
  106. int numTargetsRemoved = [self removeQueriesUpThroughSequenceNumber:upperBound
  107. liveQueries:liveTargets];
  108. Timestamp removedTargets = Timestamp::Now();
  109. int numDocumentsRemoved = [self removeOrphanedDocumentsThroughSequenceNumber:upperBound];
  110. Timestamp removedDocuments = Timestamp::Now();
  111. std::string desc = "LRU Garbage Collection:\n";
  112. absl::StrAppend(&desc, "\tCounted targets in ", millisecondsBetween(start, countedTargets),
  113. "ms\n");
  114. absl::StrAppend(&desc, "\tDetermined least recently used ", sequenceNumbers,
  115. " sequence numbers in ", millisecondsBetween(countedTargets, foundUpperBound),
  116. "ms\n");
  117. absl::StrAppend(&desc, "\tRemoved ", numTargetsRemoved, " targets in ",
  118. millisecondsBetween(foundUpperBound, removedTargets), "ms\n");
  119. absl::StrAppend(&desc, "\tRemoved ", numDocumentsRemoved, " documents in ",
  120. millisecondsBetween(removedTargets, removedDocuments), "ms\n");
  121. absl::StrAppend(&desc, "Total duration: ", millisecondsBetween(start, removedDocuments), "ms");
  122. LOG_DEBUG(desc.c_str());
  123. return LruResults{/* didRun= */ true, sequenceNumbers, numTargetsRemoved, numDocumentsRemoved};
  124. }
  125. - (int)queryCountForPercentile:(NSUInteger)percentile {
  126. size_t totalCount = [_delegate sequenceNumberCount];
  127. int setSize = (int)((percentile / 100.0f) * totalCount);
  128. return setSize;
  129. }
  130. - (ListenSequenceNumber)sequenceNumberForQueryCount:(NSUInteger)queryCount {
  131. if (queryCount == 0) {
  132. return kFSTListenSequenceNumberInvalid;
  133. }
  134. RollingSequenceNumberBuffer buffer(queryCount);
  135. // Pointer is necessary to access stack-allocated buffer from a block.
  136. RollingSequenceNumberBuffer *ptr_to_buffer = &buffer;
  137. [_delegate enumerateTargetsUsingBlock:^(FSTQueryData *queryData, BOOL *stop) {
  138. ptr_to_buffer->AddElement(queryData.sequenceNumber);
  139. }];
  140. [_delegate enumerateMutationsUsingBlock:^(const DocumentKey &docKey,
  141. ListenSequenceNumber sequenceNumber, BOOL *stop) {
  142. ptr_to_buffer->AddElement(sequenceNumber);
  143. }];
  144. return buffer.max_value();
  145. }
  146. - (int)removeQueriesUpThroughSequenceNumber:(ListenSequenceNumber)sequenceNumber
  147. liveQueries:
  148. (NSDictionary<NSNumber *, FSTQueryData *> *)liveQueries {
  149. return [_delegate removeTargetsThroughSequenceNumber:sequenceNumber liveQueries:liveQueries];
  150. }
  151. - (int)removeOrphanedDocumentsThroughSequenceNumber:(ListenSequenceNumber)sequenceNumber {
  152. return [_delegate removeOrphanedDocumentsThroughSequenceNumber:sequenceNumber];
  153. }
  154. - (size_t)byteSize {
  155. return [_delegate byteSize];
  156. }
  157. @end