| 802 | } |
| 803 | |
| 804 | uint64_t AsyncDataCache::shrink(uint64_t targetBytes) { |
| 805 | BOLT_CHECK_GT(targetBytes, 0); |
| 806 | |
| 807 | RECORD_METRIC_VALUE(kMetricCacheShrinkCount); |
| 808 | LOG(INFO) << "Try to shrink cache to free up " |
| 809 | << bolt::succinctBytes(targetBytes) << " memory"; |
| 810 | |
| 811 | const uint64_t minBytesToEvict = 8UL << 20; |
| 812 | uint64_t evictedBytes{0}; |
| 813 | uint64_t shrinkTimeUs{0}; |
| 814 | { |
| 815 | MicrosecondTimer timer(&shrinkTimeUs); |
| 816 | for (int shard = 0; shard < shards_.size(); ++shard) { |
| 817 | memory::Allocation unused; |
| 818 | evictedBytes += shards_[shardCounter_++ & (kShardMask)]->evict( |
| 819 | std::max<uint64_t>(minBytesToEvict, targetBytes - evictedBytes), |
| 820 | // Cache shrink is triggered when server is under low memory pressure |
| 821 | // so need to free up memory as soon as possible. So we always avoid |
| 822 | // triggering ssd save to accelerate the cache evictions. |
| 823 | true, |
| 824 | 0, |
| 825 | unused); |
| 826 | BOLT_CHECK(unused.empty()); |
| 827 | if (evictedBytes >= targetBytes) { |
| 828 | break; |
| 829 | } |
| 830 | } |
| 831 | // Call unmap to free up to 'targetBytes' unused memory space back to |
| 832 | // operating system after shrink. |
| 833 | allocator_->unmap(memory::AllocationTraits::numPages(targetBytes)); |
| 834 | } |
| 835 | |
| 836 | RECORD_HISTOGRAM_METRIC_VALUE(kMetricCacheShrinkTimeMs, shrinkTimeUs / 1'000); |
| 837 | LOG(INFO) << "Freed " << bolt::succinctBytes(evictedBytes) |
| 838 | << " cache memory, spent " << bolt::succinctMicros(shrinkTimeUs) |
| 839 | << "\n" |
| 840 | << toString(); |
| 841 | return evictedBytes; |
| 842 | } |
| 843 | |
| 844 | bool AsyncDataCache::canTryAllocate( |
| 845 | int32_t numPages, |