2016-02-09 23:12:00 +00:00
|
|
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
2017-07-15 23:03:42 +00:00
|
|
|
// This source code is licensed under both the GPLv2 (found in the
|
|
|
|
// COPYING file in the root directory) and Apache 2.0 License
|
|
|
|
// (found in the LICENSE.Apache file in the root directory).
|
2013-10-16 21:59:46 +00:00
|
|
|
//
|
2011-03-18 22:37:00 +00:00
|
|
|
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
|
|
|
|
2016-12-22 22:44:01 +00:00
|
|
|
#ifndef __STDC_FORMAT_MACROS
|
|
|
|
#define __STDC_FORMAT_MACROS
|
|
|
|
#endif
|
|
|
|
|
2017-04-06 02:02:00 +00:00
|
|
|
#include "cache/lru_cache.h"
|
2016-08-16 21:43:41 +00:00
|
|
|
|
2011-03-18 22:37:00 +00:00
|
|
|
#include <assert.h>
|
2011-06-29 00:30:50 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2016-12-22 22:44:01 +00:00
|
|
|
#include <string>
|
2011-03-18 22:37:00 +00:00
|
|
|
|
|
|
|
#include "util/mutexlock.h"
|
|
|
|
|
2013-10-04 04:49:15 +00:00
|
|
|
namespace rocksdb {
|
2011-03-18 22:37:00 +00:00
|
|
|
|
2017-07-24 17:46:21 +00:00
|
|
|
LRUHandleTable::LRUHandleTable() : list_(nullptr), length_(0), elems_(0) {
|
2016-08-16 21:43:41 +00:00
|
|
|
Resize();
|
|
|
|
}
|
2016-07-15 17:41:36 +00:00
|
|
|
|
2016-08-16 21:43:41 +00:00
|
|
|
LRUHandleTable::~LRUHandleTable() {
|
|
|
|
ApplyToAllCacheEntries([](LRUHandle* h) {
|
|
|
|
if (h->refs == 1) {
|
|
|
|
h->Free();
|
Modifed the LRU cache eviction code so that it doesn't evict blocks which have exteranl references
Summary:
Currently, blocks which have more than one reference (ie referenced by something other than cache itself) are evicted from cache. This doesn't make much sense:
- blocks are still in RAM, so the RAM usage reported by the cache is incorrect
- if the same block is needed by another iterator, it will be loaded and decompressed again
This diff changes the reference counting scheme a bit. Previously, if the cache contained the block, this was accounted for in its refcount. After this change, the refcount is only used to track external references. There is a boolean flag which indicates whether or not the block is contained in the cache.
This diff also changes how LRU list is used. Previously, both hashtable and the LRU list contained all blocks. After this change, the LRU list contains blocks with the refcount==0, ie those which can be evicted from the cache.
Note that this change still allows for cache to grow beyond its capacity. This happens when all blocks are pinned (ie refcount>0). This is consistent with the current behavior. The cache's insert function never fails. I spent lots of time trying to make table_reader and other places work with the insert which might failed. It turned out to be pretty hard. It might really destabilize some customers, so finally, I decided against doing this.
table_cache_remove_scan_count_limit option will be unneeded after this change, but I will remove it in the following diff, if this one gets approved
Test Plan: Ran tests, made sure they pass
Reviewers: sdong, ljin
Differential Revision: https://reviews.facebook.net/D25503
2014-10-21 18:49:13 +00:00
|
|
|
}
|
2016-08-16 21:43:41 +00:00
|
|
|
});
|
|
|
|
delete[] list_;
|
|
|
|
}
|
2011-06-29 00:30:50 +00:00
|
|
|
|
2016-08-16 21:43:41 +00:00
|
|
|
LRUHandle* LRUHandleTable::Lookup(const Slice& key, uint32_t hash) {
|
|
|
|
return *FindPointer(key, hash);
|
|
|
|
}
|
2011-06-29 00:30:50 +00:00
|
|
|
|
2016-08-16 21:43:41 +00:00
|
|
|
LRUHandle* LRUHandleTable::Insert(LRUHandle* h) {
|
|
|
|
LRUHandle** ptr = FindPointer(h->key(), h->hash);
|
|
|
|
LRUHandle* old = *ptr;
|
|
|
|
h->next_hash = (old == nullptr ? nullptr : old->next_hash);
|
|
|
|
*ptr = h;
|
|
|
|
if (old == nullptr) {
|
|
|
|
++elems_;
|
|
|
|
if (elems_ > length_) {
|
|
|
|
// Since each cache entry is fairly large, we aim for a small
|
|
|
|
// average linked list length (<= 1).
|
|
|
|
Resize();
|
2011-03-18 22:37:00 +00:00
|
|
|
}
|
2011-06-29 00:30:50 +00:00
|
|
|
}
|
2016-08-16 21:43:41 +00:00
|
|
|
return old;
|
|
|
|
}
|
2011-06-29 00:30:50 +00:00
|
|
|
|
2016-08-16 21:43:41 +00:00
|
|
|
LRUHandle* LRUHandleTable::Remove(const Slice& key, uint32_t hash) {
|
|
|
|
LRUHandle** ptr = FindPointer(key, hash);
|
|
|
|
LRUHandle* result = *ptr;
|
|
|
|
if (result != nullptr) {
|
|
|
|
*ptr = result->next_hash;
|
|
|
|
--elems_;
|
2011-06-29 00:30:50 +00:00
|
|
|
}
|
2016-08-16 21:43:41 +00:00
|
|
|
return result;
|
|
|
|
}
|
2011-06-29 00:30:50 +00:00
|
|
|
|
2016-08-16 21:43:41 +00:00
|
|
|
LRUHandle** LRUHandleTable::FindPointer(const Slice& key, uint32_t hash) {
|
|
|
|
LRUHandle** ptr = &list_[hash & (length_ - 1)];
|
|
|
|
while (*ptr != nullptr && ((*ptr)->hash != hash || key != (*ptr)->key())) {
|
|
|
|
ptr = &(*ptr)->next_hash;
|
2011-06-29 00:30:50 +00:00
|
|
|
}
|
2016-08-16 21:43:41 +00:00
|
|
|
return ptr;
|
|
|
|
}
|
2011-03-18 22:37:00 +00:00
|
|
|
|
2016-08-16 21:43:41 +00:00
|
|
|
void LRUHandleTable::Resize() {
|
|
|
|
uint32_t new_length = 16;
|
|
|
|
while (new_length < elems_ * 1.5) {
|
|
|
|
new_length *= 2;
|
|
|
|
}
|
|
|
|
LRUHandle** new_list = new LRUHandle*[new_length];
|
|
|
|
memset(new_list, 0, sizeof(new_list[0]) * new_length);
|
|
|
|
uint32_t count = 0;
|
|
|
|
for (uint32_t i = 0; i < length_; i++) {
|
|
|
|
LRUHandle* h = list_[i];
|
|
|
|
while (h != nullptr) {
|
|
|
|
LRUHandle* next = h->next_hash;
|
|
|
|
uint32_t hash = h->hash;
|
|
|
|
LRUHandle** ptr = &new_list[hash & (new_length - 1)];
|
|
|
|
h->next_hash = *ptr;
|
|
|
|
*ptr = h;
|
|
|
|
h = next;
|
|
|
|
count++;
|
2011-03-18 22:37:00 +00:00
|
|
|
}
|
2011-06-29 00:30:50 +00:00
|
|
|
}
|
2016-08-16 21:43:41 +00:00
|
|
|
assert(elems_ == count);
|
|
|
|
delete[] list_;
|
|
|
|
list_ = new_list;
|
|
|
|
length_ = new_length;
|
|
|
|
}
|
2011-03-18 22:37:00 +00:00
|
|
|
|
2018-05-24 01:53:17 +00:00
|
|
|
LRUCacheShard::LRUCacheShard(size_t capacity, bool strict_capacity_limit,
|
|
|
|
double high_pri_pool_ratio)
|
|
|
|
: capacity_(0),
|
|
|
|
high_pri_pool_usage_(0),
|
|
|
|
strict_capacity_limit_(strict_capacity_limit),
|
|
|
|
high_pri_pool_ratio_(high_pri_pool_ratio),
|
|
|
|
high_pri_pool_capacity_(0),
|
|
|
|
usage_(0),
|
2017-10-27 18:24:12 +00:00
|
|
|
lru_usage_(0) {
|
2011-03-18 22:37:00 +00:00
|
|
|
// Make empty circular linked list
|
|
|
|
lru_.next = &lru_;
|
|
|
|
lru_.prev = &lru_;
|
2016-08-19 23:43:31 +00:00
|
|
|
lru_low_pri_ = &lru_;
|
2018-05-24 01:53:17 +00:00
|
|
|
SetCapacity(capacity);
|
2011-03-18 22:37:00 +00:00
|
|
|
}
|
|
|
|
|
2016-07-15 17:41:36 +00:00
|
|
|
LRUCacheShard::~LRUCacheShard() {}
|
2011-03-18 22:37:00 +00:00
|
|
|
|
2016-07-15 17:41:36 +00:00
|
|
|
bool LRUCacheShard::Unref(LRUHandle* e) {
|
2011-03-18 22:37:00 +00:00
|
|
|
assert(e->refs > 0);
|
|
|
|
e->refs--;
|
2013-10-07 22:37:40 +00:00
|
|
|
return e->refs == 0;
|
|
|
|
}
|
2013-04-04 01:53:42 +00:00
|
|
|
|
Modifed the LRU cache eviction code so that it doesn't evict blocks which have exteranl references
Summary:
Currently, blocks which have more than one reference (ie referenced by something other than cache itself) are evicted from cache. This doesn't make much sense:
- blocks are still in RAM, so the RAM usage reported by the cache is incorrect
- if the same block is needed by another iterator, it will be loaded and decompressed again
This diff changes the reference counting scheme a bit. Previously, if the cache contained the block, this was accounted for in its refcount. After this change, the refcount is only used to track external references. There is a boolean flag which indicates whether or not the block is contained in the cache.
This diff also changes how LRU list is used. Previously, both hashtable and the LRU list contained all blocks. After this change, the LRU list contains blocks with the refcount==0, ie those which can be evicted from the cache.
Note that this change still allows for cache to grow beyond its capacity. This happens when all blocks are pinned (ie refcount>0). This is consistent with the current behavior. The cache's insert function never fails. I spent lots of time trying to make table_reader and other places work with the insert which might failed. It turned out to be pretty hard. It might really destabilize some customers, so finally, I decided against doing this.
table_cache_remove_scan_count_limit option will be unneeded after this change, but I will remove it in the following diff, if this one gets approved
Test Plan: Ran tests, made sure they pass
Reviewers: sdong, ljin
Differential Revision: https://reviews.facebook.net/D25503
2014-10-21 18:49:13 +00:00
|
|
|
// Call deleter and free
|
2011-03-18 22:37:00 +00:00
|
|
|
|
2016-07-15 17:41:36 +00:00
|
|
|
void LRUCacheShard::EraseUnRefEntries() {
|
Adding pin_l0_filter_and_index_blocks_in_cache feature and related fixes.
Summary:
When a block based table file is opened, if prefetch_index_and_filter is true, it will prefetch the index and filter blocks, putting them into the block cache.
What this feature adds: when a L0 block based table file is opened, if pin_l0_filter_and_index_blocks_in_cache is true in the options (and prefetch_index_and_filter is true), then the filter and index blocks aren't released back to the block cache at the end of BlockBasedTableReader::Open(). Instead the table reader takes ownership of them, hence pinning them, ie. the LRU cache will never push them out. Meanwhile in the table reader, further accesses will not hit the block cache, thus avoiding lock contention.
Test Plan:
'export TEST_TMPDIR=/dev/shm/ && DISABLE_JEMALLOC=1 OPT=-g make all valgrind_check -j32' is OK.
I didn't run the Java tests, I don't have Java set up on my devserver.
Reviewers: sdong
Reviewed By: sdong
Subscribers: andrewkr, dhruba
Differential Revision: https://reviews.facebook.net/D56133
2016-04-01 17:42:39 +00:00
|
|
|
autovector<LRUHandle*> last_reference_list;
|
|
|
|
{
|
|
|
|
MutexLock l(&mutex_);
|
|
|
|
while (lru_.next != &lru_) {
|
|
|
|
LRUHandle* old = lru_.next;
|
2016-08-19 23:43:31 +00:00
|
|
|
assert(old->InCache());
|
Adding pin_l0_filter_and_index_blocks_in_cache feature and related fixes.
Summary:
When a block based table file is opened, if prefetch_index_and_filter is true, it will prefetch the index and filter blocks, putting them into the block cache.
What this feature adds: when a L0 block based table file is opened, if pin_l0_filter_and_index_blocks_in_cache is true in the options (and prefetch_index_and_filter is true), then the filter and index blocks aren't released back to the block cache at the end of BlockBasedTableReader::Open(). Instead the table reader takes ownership of them, hence pinning them, ie. the LRU cache will never push them out. Meanwhile in the table reader, further accesses will not hit the block cache, thus avoiding lock contention.
Test Plan:
'export TEST_TMPDIR=/dev/shm/ && DISABLE_JEMALLOC=1 OPT=-g make all valgrind_check -j32' is OK.
I didn't run the Java tests, I don't have Java set up on my devserver.
Reviewers: sdong
Reviewed By: sdong
Subscribers: andrewkr, dhruba
Differential Revision: https://reviews.facebook.net/D56133
2016-04-01 17:42:39 +00:00
|
|
|
assert(old->refs ==
|
|
|
|
1); // LRU list contains elements which may be evicted
|
|
|
|
LRU_Remove(old);
|
|
|
|
table_.Remove(old->key(), old->hash);
|
2016-08-19 23:43:31 +00:00
|
|
|
old->SetInCache(false);
|
Adding pin_l0_filter_and_index_blocks_in_cache feature and related fixes.
Summary:
When a block based table file is opened, if prefetch_index_and_filter is true, it will prefetch the index and filter blocks, putting them into the block cache.
What this feature adds: when a L0 block based table file is opened, if pin_l0_filter_and_index_blocks_in_cache is true in the options (and prefetch_index_and_filter is true), then the filter and index blocks aren't released back to the block cache at the end of BlockBasedTableReader::Open(). Instead the table reader takes ownership of them, hence pinning them, ie. the LRU cache will never push them out. Meanwhile in the table reader, further accesses will not hit the block cache, thus avoiding lock contention.
Test Plan:
'export TEST_TMPDIR=/dev/shm/ && DISABLE_JEMALLOC=1 OPT=-g make all valgrind_check -j32' is OK.
I didn't run the Java tests, I don't have Java set up on my devserver.
Reviewers: sdong
Reviewed By: sdong
Subscribers: andrewkr, dhruba
Differential Revision: https://reviews.facebook.net/D56133
2016-04-01 17:42:39 +00:00
|
|
|
Unref(old);
|
|
|
|
usage_ -= old->charge;
|
|
|
|
last_reference_list.push_back(old);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto entry : last_reference_list) {
|
|
|
|
entry->Free();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-15 17:41:36 +00:00
|
|
|
void LRUCacheShard::ApplyToAllCacheEntries(void (*callback)(void*, size_t),
|
|
|
|
bool thread_safe) {
|
2014-05-02 20:24:04 +00:00
|
|
|
if (thread_safe) {
|
|
|
|
mutex_.Lock();
|
|
|
|
}
|
2016-05-24 06:35:23 +00:00
|
|
|
table_.ApplyToAllCacheEntries(
|
|
|
|
[callback](LRUHandle* h) { callback(h->value, h->charge); });
|
2014-05-02 20:24:04 +00:00
|
|
|
if (thread_safe) {
|
|
|
|
mutex_.Unlock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-19 23:43:31 +00:00
|
|
|
void LRUCacheShard::TEST_GetLRUList(LRUHandle** lru, LRUHandle** lru_low_pri) {
|
|
|
|
*lru = &lru_;
|
|
|
|
*lru_low_pri = lru_low_pri_;
|
|
|
|
}
|
|
|
|
|
2017-07-17 21:53:15 +00:00
|
|
|
size_t LRUCacheShard::TEST_GetLRUSize() {
|
|
|
|
LRUHandle* lru_handle = lru_.next;
|
|
|
|
size_t lru_size = 0;
|
|
|
|
while (lru_handle != &lru_) {
|
|
|
|
lru_size++;
|
|
|
|
lru_handle = lru_handle->next;
|
|
|
|
}
|
|
|
|
return lru_size;
|
|
|
|
}
|
|
|
|
|
2017-11-28 18:35:17 +00:00
|
|
|
double LRUCacheShard::GetHighPriPoolRatio() {
|
|
|
|
MutexLock l(&mutex_);
|
|
|
|
return high_pri_pool_ratio_;
|
|
|
|
}
|
|
|
|
|
2016-07-15 17:41:36 +00:00
|
|
|
void LRUCacheShard::LRU_Remove(LRUHandle* e) {
|
Modifed the LRU cache eviction code so that it doesn't evict blocks which have exteranl references
Summary:
Currently, blocks which have more than one reference (ie referenced by something other than cache itself) are evicted from cache. This doesn't make much sense:
- blocks are still in RAM, so the RAM usage reported by the cache is incorrect
- if the same block is needed by another iterator, it will be loaded and decompressed again
This diff changes the reference counting scheme a bit. Previously, if the cache contained the block, this was accounted for in its refcount. After this change, the refcount is only used to track external references. There is a boolean flag which indicates whether or not the block is contained in the cache.
This diff also changes how LRU list is used. Previously, both hashtable and the LRU list contained all blocks. After this change, the LRU list contains blocks with the refcount==0, ie those which can be evicted from the cache.
Note that this change still allows for cache to grow beyond its capacity. This happens when all blocks are pinned (ie refcount>0). This is consistent with the current behavior. The cache's insert function never fails. I spent lots of time trying to make table_reader and other places work with the insert which might failed. It turned out to be pretty hard. It might really destabilize some customers, so finally, I decided against doing this.
table_cache_remove_scan_count_limit option will be unneeded after this change, but I will remove it in the following diff, if this one gets approved
Test Plan: Ran tests, made sure they pass
Reviewers: sdong, ljin
Differential Revision: https://reviews.facebook.net/D25503
2014-10-21 18:49:13 +00:00
|
|
|
assert(e->next != nullptr);
|
|
|
|
assert(e->prev != nullptr);
|
2016-08-19 23:43:31 +00:00
|
|
|
if (lru_low_pri_ == e) {
|
|
|
|
lru_low_pri_ = e->prev;
|
|
|
|
}
|
2011-03-18 22:37:00 +00:00
|
|
|
e->next->prev = e->prev;
|
|
|
|
e->prev->next = e->next;
|
Modifed the LRU cache eviction code so that it doesn't evict blocks which have exteranl references
Summary:
Currently, blocks which have more than one reference (ie referenced by something other than cache itself) are evicted from cache. This doesn't make much sense:
- blocks are still in RAM, so the RAM usage reported by the cache is incorrect
- if the same block is needed by another iterator, it will be loaded and decompressed again
This diff changes the reference counting scheme a bit. Previously, if the cache contained the block, this was accounted for in its refcount. After this change, the refcount is only used to track external references. There is a boolean flag which indicates whether or not the block is contained in the cache.
This diff also changes how LRU list is used. Previously, both hashtable and the LRU list contained all blocks. After this change, the LRU list contains blocks with the refcount==0, ie those which can be evicted from the cache.
Note that this change still allows for cache to grow beyond its capacity. This happens when all blocks are pinned (ie refcount>0). This is consistent with the current behavior. The cache's insert function never fails. I spent lots of time trying to make table_reader and other places work with the insert which might failed. It turned out to be pretty hard. It might really destabilize some customers, so finally, I decided against doing this.
table_cache_remove_scan_count_limit option will be unneeded after this change, but I will remove it in the following diff, if this one gets approved
Test Plan: Ran tests, made sure they pass
Reviewers: sdong, ljin
Differential Revision: https://reviews.facebook.net/D25503
2014-10-21 18:49:13 +00:00
|
|
|
e->prev = e->next = nullptr;
|
2015-06-18 20:56:31 +00:00
|
|
|
lru_usage_ -= e->charge;
|
2016-08-19 23:43:31 +00:00
|
|
|
if (e->InHighPriPool()) {
|
|
|
|
assert(high_pri_pool_usage_ >= e->charge);
|
|
|
|
high_pri_pool_usage_ -= e->charge;
|
|
|
|
}
|
2011-03-18 22:37:00 +00:00
|
|
|
}
|
|
|
|
|
2016-08-19 23:43:31 +00:00
|
|
|
void LRUCacheShard::LRU_Insert(LRUHandle* e) {
|
Modifed the LRU cache eviction code so that it doesn't evict blocks which have exteranl references
Summary:
Currently, blocks which have more than one reference (ie referenced by something other than cache itself) are evicted from cache. This doesn't make much sense:
- blocks are still in RAM, so the RAM usage reported by the cache is incorrect
- if the same block is needed by another iterator, it will be loaded and decompressed again
This diff changes the reference counting scheme a bit. Previously, if the cache contained the block, this was accounted for in its refcount. After this change, the refcount is only used to track external references. There is a boolean flag which indicates whether or not the block is contained in the cache.
This diff also changes how LRU list is used. Previously, both hashtable and the LRU list contained all blocks. After this change, the LRU list contains blocks with the refcount==0, ie those which can be evicted from the cache.
Note that this change still allows for cache to grow beyond its capacity. This happens when all blocks are pinned (ie refcount>0). This is consistent with the current behavior. The cache's insert function never fails. I spent lots of time trying to make table_reader and other places work with the insert which might failed. It turned out to be pretty hard. It might really destabilize some customers, so finally, I decided against doing this.
table_cache_remove_scan_count_limit option will be unneeded after this change, but I will remove it in the following diff, if this one gets approved
Test Plan: Ran tests, made sure they pass
Reviewers: sdong, ljin
Differential Revision: https://reviews.facebook.net/D25503
2014-10-21 18:49:13 +00:00
|
|
|
assert(e->next == nullptr);
|
|
|
|
assert(e->prev == nullptr);
|
2018-05-24 22:45:49 +00:00
|
|
|
if (high_pri_pool_ratio_ > 0 && (e->IsHighPri() || e->HasHit())) {
|
2016-08-19 23:43:31 +00:00
|
|
|
// Inset "e" to head of LRU list.
|
|
|
|
e->next = &lru_;
|
|
|
|
e->prev = lru_.prev;
|
|
|
|
e->prev->next = e;
|
|
|
|
e->next->prev = e;
|
|
|
|
e->SetInHighPriPool(true);
|
|
|
|
high_pri_pool_usage_ += e->charge;
|
|
|
|
MaintainPoolSize();
|
|
|
|
} else {
|
|
|
|
// Insert "e" to the head of low-pri pool. Note that when
|
|
|
|
// high_pri_pool_ratio is 0, head of low-pri pool is also head of LRU list.
|
|
|
|
e->next = lru_low_pri_->next;
|
|
|
|
e->prev = lru_low_pri_;
|
|
|
|
e->prev->next = e;
|
|
|
|
e->next->prev = e;
|
|
|
|
e->SetInHighPriPool(false);
|
|
|
|
lru_low_pri_ = e;
|
|
|
|
}
|
2015-06-18 20:56:31 +00:00
|
|
|
lru_usage_ += e->charge;
|
2011-03-18 22:37:00 +00:00
|
|
|
}
|
|
|
|
|
2016-08-19 23:43:31 +00:00
|
|
|
void LRUCacheShard::MaintainPoolSize() {
|
|
|
|
while (high_pri_pool_usage_ > high_pri_pool_capacity_) {
|
|
|
|
// Overflow last entry in high-pri pool to low-pri pool.
|
|
|
|
lru_low_pri_ = lru_low_pri_->next;
|
|
|
|
assert(lru_low_pri_ != &lru_);
|
|
|
|
lru_low_pri_->SetInHighPriPool(false);
|
|
|
|
high_pri_pool_usage_ -= lru_low_pri_->charge;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-15 17:41:36 +00:00
|
|
|
void LRUCacheShard::EvictFromLRU(size_t charge,
|
|
|
|
autovector<LRUHandle*>* deleted) {
|
2015-04-24 21:12:58 +00:00
|
|
|
while (usage_ + charge > capacity_ && lru_.next != &lru_) {
|
|
|
|
LRUHandle* old = lru_.next;
|
2016-08-19 23:43:31 +00:00
|
|
|
assert(old->InCache());
|
2015-04-24 21:12:58 +00:00
|
|
|
assert(old->refs == 1); // LRU list contains elements which may be evicted
|
|
|
|
LRU_Remove(old);
|
|
|
|
table_.Remove(old->key(), old->hash);
|
2016-08-19 23:43:31 +00:00
|
|
|
old->SetInCache(false);
|
2015-04-24 21:12:58 +00:00
|
|
|
Unref(old);
|
|
|
|
usage_ -= old->charge;
|
|
|
|
deleted->push_back(old);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-15 17:41:36 +00:00
|
|
|
void LRUCacheShard::SetCapacity(size_t capacity) {
|
2015-04-24 21:12:58 +00:00
|
|
|
autovector<LRUHandle*> last_reference_list;
|
|
|
|
{
|
|
|
|
MutexLock l(&mutex_);
|
|
|
|
capacity_ = capacity;
|
2016-08-19 23:43:31 +00:00
|
|
|
high_pri_pool_capacity_ = capacity_ * high_pri_pool_ratio_;
|
2015-04-24 21:12:58 +00:00
|
|
|
EvictFromLRU(0, &last_reference_list);
|
|
|
|
}
|
|
|
|
// we free the entries here outside of mutex for
|
|
|
|
// performance reasons
|
|
|
|
for (auto entry : last_reference_list) {
|
|
|
|
entry->Free();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-15 17:41:36 +00:00
|
|
|
void LRUCacheShard::SetStrictCapacityLimit(bool strict_capacity_limit) {
|
2016-03-11 01:35:19 +00:00
|
|
|
MutexLock l(&mutex_);
|
|
|
|
strict_capacity_limit_ = strict_capacity_limit;
|
|
|
|
}
|
|
|
|
|
2016-07-15 17:41:36 +00:00
|
|
|
Cache::Handle* LRUCacheShard::Lookup(const Slice& key, uint32_t hash) {
|
2011-03-18 22:37:00 +00:00
|
|
|
MutexLock l(&mutex_);
|
2011-08-22 21:08:51 +00:00
|
|
|
LRUHandle* e = table_.Lookup(key, hash);
|
2013-03-01 02:04:58 +00:00
|
|
|
if (e != nullptr) {
|
2016-08-19 23:43:31 +00:00
|
|
|
assert(e->InCache());
|
Modifed the LRU cache eviction code so that it doesn't evict blocks which have exteranl references
Summary:
Currently, blocks which have more than one reference (ie referenced by something other than cache itself) are evicted from cache. This doesn't make much sense:
- blocks are still in RAM, so the RAM usage reported by the cache is incorrect
- if the same block is needed by another iterator, it will be loaded and decompressed again
This diff changes the reference counting scheme a bit. Previously, if the cache contained the block, this was accounted for in its refcount. After this change, the refcount is only used to track external references. There is a boolean flag which indicates whether or not the block is contained in the cache.
This diff also changes how LRU list is used. Previously, both hashtable and the LRU list contained all blocks. After this change, the LRU list contains blocks with the refcount==0, ie those which can be evicted from the cache.
Note that this change still allows for cache to grow beyond its capacity. This happens when all blocks are pinned (ie refcount>0). This is consistent with the current behavior. The cache's insert function never fails. I spent lots of time trying to make table_reader and other places work with the insert which might failed. It turned out to be pretty hard. It might really destabilize some customers, so finally, I decided against doing this.
table_cache_remove_scan_count_limit option will be unneeded after this change, but I will remove it in the following diff, if this one gets approved
Test Plan: Ran tests, made sure they pass
Reviewers: sdong, ljin
Differential Revision: https://reviews.facebook.net/D25503
2014-10-21 18:49:13 +00:00
|
|
|
if (e->refs == 1) {
|
|
|
|
LRU_Remove(e);
|
|
|
|
}
|
2011-03-18 22:37:00 +00:00
|
|
|
e->refs++;
|
2018-05-24 22:45:49 +00:00
|
|
|
e->SetHit();
|
2011-03-18 22:37:00 +00:00
|
|
|
}
|
2011-08-22 21:08:51 +00:00
|
|
|
return reinterpret_cast<Cache::Handle*>(e);
|
2011-03-18 22:37:00 +00:00
|
|
|
}
|
|
|
|
|
2017-01-11 00:48:23 +00:00
|
|
|
bool LRUCacheShard::Ref(Cache::Handle* h) {
|
|
|
|
LRUHandle* handle = reinterpret_cast<LRUHandle*>(h);
|
|
|
|
MutexLock l(&mutex_);
|
2017-01-26 18:41:30 +00:00
|
|
|
if (handle->InCache() && handle->refs == 1) {
|
|
|
|
LRU_Remove(handle);
|
2017-01-11 00:48:23 +00:00
|
|
|
}
|
2017-01-26 18:41:30 +00:00
|
|
|
handle->refs++;
|
|
|
|
return true;
|
2017-01-11 00:48:23 +00:00
|
|
|
}
|
|
|
|
|
2016-08-19 23:43:31 +00:00
|
|
|
void LRUCacheShard::SetHighPriorityPoolRatio(double high_pri_pool_ratio) {
|
|
|
|
MutexLock l(&mutex_);
|
|
|
|
high_pri_pool_ratio_ = high_pri_pool_ratio;
|
|
|
|
high_pri_pool_capacity_ = capacity_ * high_pri_pool_ratio_;
|
|
|
|
MaintainPoolSize();
|
|
|
|
}
|
|
|
|
|
2017-04-24 18:21:47 +00:00
|
|
|
bool LRUCacheShard::Release(Cache::Handle* handle, bool force_erase) {
|
2016-03-11 01:35:19 +00:00
|
|
|
if (handle == nullptr) {
|
2017-04-24 18:21:47 +00:00
|
|
|
return false;
|
2016-03-11 01:35:19 +00:00
|
|
|
}
|
2013-10-07 22:37:40 +00:00
|
|
|
LRUHandle* e = reinterpret_cast<LRUHandle*>(handle);
|
|
|
|
bool last_reference = false;
|
|
|
|
{
|
|
|
|
MutexLock l(&mutex_);
|
|
|
|
last_reference = Unref(e);
|
Modifed the LRU cache eviction code so that it doesn't evict blocks which have exteranl references
Summary:
Currently, blocks which have more than one reference (ie referenced by something other than cache itself) are evicted from cache. This doesn't make much sense:
- blocks are still in RAM, so the RAM usage reported by the cache is incorrect
- if the same block is needed by another iterator, it will be loaded and decompressed again
This diff changes the reference counting scheme a bit. Previously, if the cache contained the block, this was accounted for in its refcount. After this change, the refcount is only used to track external references. There is a boolean flag which indicates whether or not the block is contained in the cache.
This diff also changes how LRU list is used. Previously, both hashtable and the LRU list contained all blocks. After this change, the LRU list contains blocks with the refcount==0, ie those which can be evicted from the cache.
Note that this change still allows for cache to grow beyond its capacity. This happens when all blocks are pinned (ie refcount>0). This is consistent with the current behavior. The cache's insert function never fails. I spent lots of time trying to make table_reader and other places work with the insert which might failed. It turned out to be pretty hard. It might really destabilize some customers, so finally, I decided against doing this.
table_cache_remove_scan_count_limit option will be unneeded after this change, but I will remove it in the following diff, if this one gets approved
Test Plan: Ran tests, made sure they pass
Reviewers: sdong, ljin
Differential Revision: https://reviews.facebook.net/D25503
2014-10-21 18:49:13 +00:00
|
|
|
if (last_reference) {
|
|
|
|
usage_ -= e->charge;
|
|
|
|
}
|
2016-08-19 23:43:31 +00:00
|
|
|
if (e->refs == 1 && e->InCache()) {
|
Modifed the LRU cache eviction code so that it doesn't evict blocks which have exteranl references
Summary:
Currently, blocks which have more than one reference (ie referenced by something other than cache itself) are evicted from cache. This doesn't make much sense:
- blocks are still in RAM, so the RAM usage reported by the cache is incorrect
- if the same block is needed by another iterator, it will be loaded and decompressed again
This diff changes the reference counting scheme a bit. Previously, if the cache contained the block, this was accounted for in its refcount. After this change, the refcount is only used to track external references. There is a boolean flag which indicates whether or not the block is contained in the cache.
This diff also changes how LRU list is used. Previously, both hashtable and the LRU list contained all blocks. After this change, the LRU list contains blocks with the refcount==0, ie those which can be evicted from the cache.
Note that this change still allows for cache to grow beyond its capacity. This happens when all blocks are pinned (ie refcount>0). This is consistent with the current behavior. The cache's insert function never fails. I spent lots of time trying to make table_reader and other places work with the insert which might failed. It turned out to be pretty hard. It might really destabilize some customers, so finally, I decided against doing this.
table_cache_remove_scan_count_limit option will be unneeded after this change, but I will remove it in the following diff, if this one gets approved
Test Plan: Ran tests, made sure they pass
Reviewers: sdong, ljin
Differential Revision: https://reviews.facebook.net/D25503
2014-10-21 18:49:13 +00:00
|
|
|
// The item is still in cache, and nobody else holds a reference to it
|
2017-04-24 18:21:47 +00:00
|
|
|
if (usage_ > capacity_ || force_erase) {
|
Modifed the LRU cache eviction code so that it doesn't evict blocks which have exteranl references
Summary:
Currently, blocks which have more than one reference (ie referenced by something other than cache itself) are evicted from cache. This doesn't make much sense:
- blocks are still in RAM, so the RAM usage reported by the cache is incorrect
- if the same block is needed by another iterator, it will be loaded and decompressed again
This diff changes the reference counting scheme a bit. Previously, if the cache contained the block, this was accounted for in its refcount. After this change, the refcount is only used to track external references. There is a boolean flag which indicates whether or not the block is contained in the cache.
This diff also changes how LRU list is used. Previously, both hashtable and the LRU list contained all blocks. After this change, the LRU list contains blocks with the refcount==0, ie those which can be evicted from the cache.
Note that this change still allows for cache to grow beyond its capacity. This happens when all blocks are pinned (ie refcount>0). This is consistent with the current behavior. The cache's insert function never fails. I spent lots of time trying to make table_reader and other places work with the insert which might failed. It turned out to be pretty hard. It might really destabilize some customers, so finally, I decided against doing this.
table_cache_remove_scan_count_limit option will be unneeded after this change, but I will remove it in the following diff, if this one gets approved
Test Plan: Ran tests, made sure they pass
Reviewers: sdong, ljin
Differential Revision: https://reviews.facebook.net/D25503
2014-10-21 18:49:13 +00:00
|
|
|
// the cache is full
|
|
|
|
// The LRU list must be empty since the cache is full
|
2017-04-24 18:21:47 +00:00
|
|
|
assert(!(usage_ > capacity_) || lru_.next == &lru_);
|
Modifed the LRU cache eviction code so that it doesn't evict blocks which have exteranl references
Summary:
Currently, blocks which have more than one reference (ie referenced by something other than cache itself) are evicted from cache. This doesn't make much sense:
- blocks are still in RAM, so the RAM usage reported by the cache is incorrect
- if the same block is needed by another iterator, it will be loaded and decompressed again
This diff changes the reference counting scheme a bit. Previously, if the cache contained the block, this was accounted for in its refcount. After this change, the refcount is only used to track external references. There is a boolean flag which indicates whether or not the block is contained in the cache.
This diff also changes how LRU list is used. Previously, both hashtable and the LRU list contained all blocks. After this change, the LRU list contains blocks with the refcount==0, ie those which can be evicted from the cache.
Note that this change still allows for cache to grow beyond its capacity. This happens when all blocks are pinned (ie refcount>0). This is consistent with the current behavior. The cache's insert function never fails. I spent lots of time trying to make table_reader and other places work with the insert which might failed. It turned out to be pretty hard. It might really destabilize some customers, so finally, I decided against doing this.
table_cache_remove_scan_count_limit option will be unneeded after this change, but I will remove it in the following diff, if this one gets approved
Test Plan: Ran tests, made sure they pass
Reviewers: sdong, ljin
Differential Revision: https://reviews.facebook.net/D25503
2014-10-21 18:49:13 +00:00
|
|
|
// take this opportunity and remove the item
|
|
|
|
table_.Remove(e->key(), e->hash);
|
2016-08-19 23:43:31 +00:00
|
|
|
e->SetInCache(false);
|
Modifed the LRU cache eviction code so that it doesn't evict blocks which have exteranl references
Summary:
Currently, blocks which have more than one reference (ie referenced by something other than cache itself) are evicted from cache. This doesn't make much sense:
- blocks are still in RAM, so the RAM usage reported by the cache is incorrect
- if the same block is needed by another iterator, it will be loaded and decompressed again
This diff changes the reference counting scheme a bit. Previously, if the cache contained the block, this was accounted for in its refcount. After this change, the refcount is only used to track external references. There is a boolean flag which indicates whether or not the block is contained in the cache.
This diff also changes how LRU list is used. Previously, both hashtable and the LRU list contained all blocks. After this change, the LRU list contains blocks with the refcount==0, ie those which can be evicted from the cache.
Note that this change still allows for cache to grow beyond its capacity. This happens when all blocks are pinned (ie refcount>0). This is consistent with the current behavior. The cache's insert function never fails. I spent lots of time trying to make table_reader and other places work with the insert which might failed. It turned out to be pretty hard. It might really destabilize some customers, so finally, I decided against doing this.
table_cache_remove_scan_count_limit option will be unneeded after this change, but I will remove it in the following diff, if this one gets approved
Test Plan: Ran tests, made sure they pass
Reviewers: sdong, ljin
Differential Revision: https://reviews.facebook.net/D25503
2014-10-21 18:49:13 +00:00
|
|
|
Unref(e);
|
|
|
|
usage_ -= e->charge;
|
|
|
|
last_reference = true;
|
|
|
|
} else {
|
|
|
|
// put the item on the list to be potentially freed
|
2016-08-19 23:43:31 +00:00
|
|
|
LRU_Insert(e);
|
Modifed the LRU cache eviction code so that it doesn't evict blocks which have exteranl references
Summary:
Currently, blocks which have more than one reference (ie referenced by something other than cache itself) are evicted from cache. This doesn't make much sense:
- blocks are still in RAM, so the RAM usage reported by the cache is incorrect
- if the same block is needed by another iterator, it will be loaded and decompressed again
This diff changes the reference counting scheme a bit. Previously, if the cache contained the block, this was accounted for in its refcount. After this change, the refcount is only used to track external references. There is a boolean flag which indicates whether or not the block is contained in the cache.
This diff also changes how LRU list is used. Previously, both hashtable and the LRU list contained all blocks. After this change, the LRU list contains blocks with the refcount==0, ie those which can be evicted from the cache.
Note that this change still allows for cache to grow beyond its capacity. This happens when all blocks are pinned (ie refcount>0). This is consistent with the current behavior. The cache's insert function never fails. I spent lots of time trying to make table_reader and other places work with the insert which might failed. It turned out to be pretty hard. It might really destabilize some customers, so finally, I decided against doing this.
table_cache_remove_scan_count_limit option will be unneeded after this change, but I will remove it in the following diff, if this one gets approved
Test Plan: Ran tests, made sure they pass
Reviewers: sdong, ljin
Differential Revision: https://reviews.facebook.net/D25503
2014-10-21 18:49:13 +00:00
|
|
|
}
|
|
|
|
}
|
2013-10-07 22:37:40 +00:00
|
|
|
}
|
Modifed the LRU cache eviction code so that it doesn't evict blocks which have exteranl references
Summary:
Currently, blocks which have more than one reference (ie referenced by something other than cache itself) are evicted from cache. This doesn't make much sense:
- blocks are still in RAM, so the RAM usage reported by the cache is incorrect
- if the same block is needed by another iterator, it will be loaded and decompressed again
This diff changes the reference counting scheme a bit. Previously, if the cache contained the block, this was accounted for in its refcount. After this change, the refcount is only used to track external references. There is a boolean flag which indicates whether or not the block is contained in the cache.
This diff also changes how LRU list is used. Previously, both hashtable and the LRU list contained all blocks. After this change, the LRU list contains blocks with the refcount==0, ie those which can be evicted from the cache.
Note that this change still allows for cache to grow beyond its capacity. This happens when all blocks are pinned (ie refcount>0). This is consistent with the current behavior. The cache's insert function never fails. I spent lots of time trying to make table_reader and other places work with the insert which might failed. It turned out to be pretty hard. It might really destabilize some customers, so finally, I decided against doing this.
table_cache_remove_scan_count_limit option will be unneeded after this change, but I will remove it in the following diff, if this one gets approved
Test Plan: Ran tests, made sure they pass
Reviewers: sdong, ljin
Differential Revision: https://reviews.facebook.net/D25503
2014-10-21 18:49:13 +00:00
|
|
|
|
|
|
|
// free outside of mutex
|
2013-10-07 22:37:40 +00:00
|
|
|
if (last_reference) {
|
Modifed the LRU cache eviction code so that it doesn't evict blocks which have exteranl references
Summary:
Currently, blocks which have more than one reference (ie referenced by something other than cache itself) are evicted from cache. This doesn't make much sense:
- blocks are still in RAM, so the RAM usage reported by the cache is incorrect
- if the same block is needed by another iterator, it will be loaded and decompressed again
This diff changes the reference counting scheme a bit. Previously, if the cache contained the block, this was accounted for in its refcount. After this change, the refcount is only used to track external references. There is a boolean flag which indicates whether or not the block is contained in the cache.
This diff also changes how LRU list is used. Previously, both hashtable and the LRU list contained all blocks. After this change, the LRU list contains blocks with the refcount==0, ie those which can be evicted from the cache.
Note that this change still allows for cache to grow beyond its capacity. This happens when all blocks are pinned (ie refcount>0). This is consistent with the current behavior. The cache's insert function never fails. I spent lots of time trying to make table_reader and other places work with the insert which might failed. It turned out to be pretty hard. It might really destabilize some customers, so finally, I decided against doing this.
table_cache_remove_scan_count_limit option will be unneeded after this change, but I will remove it in the following diff, if this one gets approved
Test Plan: Ran tests, made sure they pass
Reviewers: sdong, ljin
Differential Revision: https://reviews.facebook.net/D25503
2014-10-21 18:49:13 +00:00
|
|
|
e->Free();
|
2013-10-07 22:37:40 +00:00
|
|
|
}
|
2017-04-24 18:21:47 +00:00
|
|
|
return last_reference;
|
2011-03-18 22:37:00 +00:00
|
|
|
}
|
|
|
|
|
2016-07-15 17:41:36 +00:00
|
|
|
Status LRUCacheShard::Insert(const Slice& key, uint32_t hash, void* value,
|
|
|
|
size_t charge,
|
|
|
|
void (*deleter)(const Slice& key, void* value),
|
2016-08-19 23:43:31 +00:00
|
|
|
Cache::Handle** handle, Cache::Priority priority) {
|
Modifed the LRU cache eviction code so that it doesn't evict blocks which have exteranl references
Summary:
Currently, blocks which have more than one reference (ie referenced by something other than cache itself) are evicted from cache. This doesn't make much sense:
- blocks are still in RAM, so the RAM usage reported by the cache is incorrect
- if the same block is needed by another iterator, it will be loaded and decompressed again
This diff changes the reference counting scheme a bit. Previously, if the cache contained the block, this was accounted for in its refcount. After this change, the refcount is only used to track external references. There is a boolean flag which indicates whether or not the block is contained in the cache.
This diff also changes how LRU list is used. Previously, both hashtable and the LRU list contained all blocks. After this change, the LRU list contains blocks with the refcount==0, ie those which can be evicted from the cache.
Note that this change still allows for cache to grow beyond its capacity. This happens when all blocks are pinned (ie refcount>0). This is consistent with the current behavior. The cache's insert function never fails. I spent lots of time trying to make table_reader and other places work with the insert which might failed. It turned out to be pretty hard. It might really destabilize some customers, so finally, I decided against doing this.
table_cache_remove_scan_count_limit option will be unneeded after this change, but I will remove it in the following diff, if this one gets approved
Test Plan: Ran tests, made sure they pass
Reviewers: sdong, ljin
Differential Revision: https://reviews.facebook.net/D25503
2014-10-21 18:49:13 +00:00
|
|
|
// Allocate the memory here outside of the mutex
|
|
|
|
// If the cache is full, we'll have to release it
|
|
|
|
// It shouldn't happen very often though.
|
2015-12-04 23:12:07 +00:00
|
|
|
LRUHandle* e = reinterpret_cast<LRUHandle*>(
|
2016-05-24 06:35:23 +00:00
|
|
|
new char[sizeof(LRUHandle) - 1 + key.size()]);
|
2016-03-11 01:35:19 +00:00
|
|
|
Status s;
|
2014-01-02 19:26:57 +00:00
|
|
|
autovector<LRUHandle*> last_reference_list;
|
2013-12-11 16:33:29 +00:00
|
|
|
|
|
|
|
e->value = value;
|
|
|
|
e->deleter = deleter;
|
|
|
|
e->charge = charge;
|
|
|
|
e->key_length = key.size();
|
2017-10-27 18:24:12 +00:00
|
|
|
e->flags = 0;
|
2013-12-11 16:33:29 +00:00
|
|
|
e->hash = hash;
|
2016-03-11 01:35:19 +00:00
|
|
|
e->refs = (handle == nullptr
|
|
|
|
? 1
|
|
|
|
: 2); // One from LRUCache, one for the returned handle
|
Modifed the LRU cache eviction code so that it doesn't evict blocks which have exteranl references
Summary:
Currently, blocks which have more than one reference (ie referenced by something other than cache itself) are evicted from cache. This doesn't make much sense:
- blocks are still in RAM, so the RAM usage reported by the cache is incorrect
- if the same block is needed by another iterator, it will be loaded and decompressed again
This diff changes the reference counting scheme a bit. Previously, if the cache contained the block, this was accounted for in its refcount. After this change, the refcount is only used to track external references. There is a boolean flag which indicates whether or not the block is contained in the cache.
This diff also changes how LRU list is used. Previously, both hashtable and the LRU list contained all blocks. After this change, the LRU list contains blocks with the refcount==0, ie those which can be evicted from the cache.
Note that this change still allows for cache to grow beyond its capacity. This happens when all blocks are pinned (ie refcount>0). This is consistent with the current behavior. The cache's insert function never fails. I spent lots of time trying to make table_reader and other places work with the insert which might failed. It turned out to be pretty hard. It might really destabilize some customers, so finally, I decided against doing this.
table_cache_remove_scan_count_limit option will be unneeded after this change, but I will remove it in the following diff, if this one gets approved
Test Plan: Ran tests, made sure they pass
Reviewers: sdong, ljin
Differential Revision: https://reviews.facebook.net/D25503
2014-10-21 18:49:13 +00:00
|
|
|
e->next = e->prev = nullptr;
|
2016-08-19 23:43:31 +00:00
|
|
|
e->SetInCache(true);
|
|
|
|
e->SetPriority(priority);
|
2013-12-11 16:33:29 +00:00
|
|
|
memcpy(e->key_data, key.data(), key.size());
|
2013-10-07 22:37:40 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
MutexLock l(&mutex_);
|
|
|
|
|
2013-10-10 00:04:40 +00:00
|
|
|
// Free the space following strict LRU policy until enough space
|
Modifed the LRU cache eviction code so that it doesn't evict blocks which have exteranl references
Summary:
Currently, blocks which have more than one reference (ie referenced by something other than cache itself) are evicted from cache. This doesn't make much sense:
- blocks are still in RAM, so the RAM usage reported by the cache is incorrect
- if the same block is needed by another iterator, it will be loaded and decompressed again
This diff changes the reference counting scheme a bit. Previously, if the cache contained the block, this was accounted for in its refcount. After this change, the refcount is only used to track external references. There is a boolean flag which indicates whether or not the block is contained in the cache.
This diff also changes how LRU list is used. Previously, both hashtable and the LRU list contained all blocks. After this change, the LRU list contains blocks with the refcount==0, ie those which can be evicted from the cache.
Note that this change still allows for cache to grow beyond its capacity. This happens when all blocks are pinned (ie refcount>0). This is consistent with the current behavior. The cache's insert function never fails. I spent lots of time trying to make table_reader and other places work with the insert which might failed. It turned out to be pretty hard. It might really destabilize some customers, so finally, I decided against doing this.
table_cache_remove_scan_count_limit option will be unneeded after this change, but I will remove it in the following diff, if this one gets approved
Test Plan: Ran tests, made sure they pass
Reviewers: sdong, ljin
Differential Revision: https://reviews.facebook.net/D25503
2014-10-21 18:49:13 +00:00
|
|
|
// is freed or the lru list is empty
|
2015-04-24 21:12:58 +00:00
|
|
|
EvictFromLRU(charge, &last_reference_list);
|
Modifed the LRU cache eviction code so that it doesn't evict blocks which have exteranl references
Summary:
Currently, blocks which have more than one reference (ie referenced by something other than cache itself) are evicted from cache. This doesn't make much sense:
- blocks are still in RAM, so the RAM usage reported by the cache is incorrect
- if the same block is needed by another iterator, it will be loaded and decompressed again
This diff changes the reference counting scheme a bit. Previously, if the cache contained the block, this was accounted for in its refcount. After this change, the refcount is only used to track external references. There is a boolean flag which indicates whether or not the block is contained in the cache.
This diff also changes how LRU list is used. Previously, both hashtable and the LRU list contained all blocks. After this change, the LRU list contains blocks with the refcount==0, ie those which can be evicted from the cache.
Note that this change still allows for cache to grow beyond its capacity. This happens when all blocks are pinned (ie refcount>0). This is consistent with the current behavior. The cache's insert function never fails. I spent lots of time trying to make table_reader and other places work with the insert which might failed. It turned out to be pretty hard. It might really destabilize some customers, so finally, I decided against doing this.
table_cache_remove_scan_count_limit option will be unneeded after this change, but I will remove it in the following diff, if this one gets approved
Test Plan: Ran tests, made sure they pass
Reviewers: sdong, ljin
Differential Revision: https://reviews.facebook.net/D25503
2014-10-21 18:49:13 +00:00
|
|
|
|
2016-08-23 20:53:49 +00:00
|
|
|
if (usage_ - lru_usage_ + charge > capacity_ &&
|
|
|
|
(strict_capacity_limit_ || handle == nullptr)) {
|
2016-03-11 01:35:19 +00:00
|
|
|
if (handle == nullptr) {
|
2016-08-23 20:53:49 +00:00
|
|
|
// Don't insert the entry but still return ok, as if the entry inserted
|
|
|
|
// into cache and get evicted immediately.
|
2016-03-11 01:35:19 +00:00
|
|
|
last_reference_list.push_back(e);
|
|
|
|
} else {
|
|
|
|
delete[] reinterpret_cast<char*>(e);
|
|
|
|
*handle = nullptr;
|
2016-08-23 20:53:49 +00:00
|
|
|
s = Status::Incomplete("Insert failed due to LRU cache being full.");
|
2016-03-11 01:35:19 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// insert into the cache
|
|
|
|
// note that the cache might get larger than its capacity if not enough
|
|
|
|
// space was freed
|
|
|
|
LRUHandle* old = table_.Insert(e);
|
|
|
|
usage_ += e->charge;
|
|
|
|
if (old != nullptr) {
|
2016-08-19 23:43:31 +00:00
|
|
|
old->SetInCache(false);
|
2016-03-11 01:35:19 +00:00
|
|
|
if (Unref(old)) {
|
|
|
|
usage_ -= old->charge;
|
|
|
|
// old is on LRU because it's in cache and its reference count
|
|
|
|
// was just 1 (Unref returned 0)
|
|
|
|
LRU_Remove(old);
|
|
|
|
last_reference_list.push_back(old);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (handle == nullptr) {
|
2016-08-19 23:43:31 +00:00
|
|
|
LRU_Insert(e);
|
2016-03-11 01:35:19 +00:00
|
|
|
} else {
|
|
|
|
*handle = reinterpret_cast<Cache::Handle*>(e);
|
2013-10-07 22:37:40 +00:00
|
|
|
}
|
2016-03-11 01:35:19 +00:00
|
|
|
s = Status::OK();
|
2013-10-07 22:37:40 +00:00
|
|
|
}
|
2011-03-18 22:37:00 +00:00
|
|
|
}
|
|
|
|
|
2013-10-07 22:37:40 +00:00
|
|
|
// we free the entries here outside of mutex for
|
|
|
|
// performance reasons
|
|
|
|
for (auto entry : last_reference_list) {
|
Modifed the LRU cache eviction code so that it doesn't evict blocks which have exteranl references
Summary:
Currently, blocks which have more than one reference (ie referenced by something other than cache itself) are evicted from cache. This doesn't make much sense:
- blocks are still in RAM, so the RAM usage reported by the cache is incorrect
- if the same block is needed by another iterator, it will be loaded and decompressed again
This diff changes the reference counting scheme a bit. Previously, if the cache contained the block, this was accounted for in its refcount. After this change, the refcount is only used to track external references. There is a boolean flag which indicates whether or not the block is contained in the cache.
This diff also changes how LRU list is used. Previously, both hashtable and the LRU list contained all blocks. After this change, the LRU list contains blocks with the refcount==0, ie those which can be evicted from the cache.
Note that this change still allows for cache to grow beyond its capacity. This happens when all blocks are pinned (ie refcount>0). This is consistent with the current behavior. The cache's insert function never fails. I spent lots of time trying to make table_reader and other places work with the insert which might failed. It turned out to be pretty hard. It might really destabilize some customers, so finally, I decided against doing this.
table_cache_remove_scan_count_limit option will be unneeded after this change, but I will remove it in the following diff, if this one gets approved
Test Plan: Ran tests, made sure they pass
Reviewers: sdong, ljin
Differential Revision: https://reviews.facebook.net/D25503
2014-10-21 18:49:13 +00:00
|
|
|
entry->Free();
|
2011-03-18 22:37:00 +00:00
|
|
|
}
|
|
|
|
|
2016-03-11 01:35:19 +00:00
|
|
|
return s;
|
2011-03-18 22:37:00 +00:00
|
|
|
}
|
|
|
|
|
2016-07-15 17:41:36 +00:00
|
|
|
void LRUCacheShard::Erase(const Slice& key, uint32_t hash) {
|
2013-10-07 22:37:40 +00:00
|
|
|
LRUHandle* e;
|
|
|
|
bool last_reference = false;
|
|
|
|
{
|
|
|
|
MutexLock l(&mutex_);
|
|
|
|
e = table_.Remove(key, hash);
|
|
|
|
if (e != nullptr) {
|
|
|
|
last_reference = Unref(e);
|
Modifed the LRU cache eviction code so that it doesn't evict blocks which have exteranl references
Summary:
Currently, blocks which have more than one reference (ie referenced by something other than cache itself) are evicted from cache. This doesn't make much sense:
- blocks are still in RAM, so the RAM usage reported by the cache is incorrect
- if the same block is needed by another iterator, it will be loaded and decompressed again
This diff changes the reference counting scheme a bit. Previously, if the cache contained the block, this was accounted for in its refcount. After this change, the refcount is only used to track external references. There is a boolean flag which indicates whether or not the block is contained in the cache.
This diff also changes how LRU list is used. Previously, both hashtable and the LRU list contained all blocks. After this change, the LRU list contains blocks with the refcount==0, ie those which can be evicted from the cache.
Note that this change still allows for cache to grow beyond its capacity. This happens when all blocks are pinned (ie refcount>0). This is consistent with the current behavior. The cache's insert function never fails. I spent lots of time trying to make table_reader and other places work with the insert which might failed. It turned out to be pretty hard. It might really destabilize some customers, so finally, I decided against doing this.
table_cache_remove_scan_count_limit option will be unneeded after this change, but I will remove it in the following diff, if this one gets approved
Test Plan: Ran tests, made sure they pass
Reviewers: sdong, ljin
Differential Revision: https://reviews.facebook.net/D25503
2014-10-21 18:49:13 +00:00
|
|
|
if (last_reference) {
|
|
|
|
usage_ -= e->charge;
|
|
|
|
}
|
2016-08-19 23:43:31 +00:00
|
|
|
if (last_reference && e->InCache()) {
|
Modifed the LRU cache eviction code so that it doesn't evict blocks which have exteranl references
Summary:
Currently, blocks which have more than one reference (ie referenced by something other than cache itself) are evicted from cache. This doesn't make much sense:
- blocks are still in RAM, so the RAM usage reported by the cache is incorrect
- if the same block is needed by another iterator, it will be loaded and decompressed again
This diff changes the reference counting scheme a bit. Previously, if the cache contained the block, this was accounted for in its refcount. After this change, the refcount is only used to track external references. There is a boolean flag which indicates whether or not the block is contained in the cache.
This diff also changes how LRU list is used. Previously, both hashtable and the LRU list contained all blocks. After this change, the LRU list contains blocks with the refcount==0, ie those which can be evicted from the cache.
Note that this change still allows for cache to grow beyond its capacity. This happens when all blocks are pinned (ie refcount>0). This is consistent with the current behavior. The cache's insert function never fails. I spent lots of time trying to make table_reader and other places work with the insert which might failed. It turned out to be pretty hard. It might really destabilize some customers, so finally, I decided against doing this.
table_cache_remove_scan_count_limit option will be unneeded after this change, but I will remove it in the following diff, if this one gets approved
Test Plan: Ran tests, made sure they pass
Reviewers: sdong, ljin
Differential Revision: https://reviews.facebook.net/D25503
2014-10-21 18:49:13 +00:00
|
|
|
LRU_Remove(e);
|
|
|
|
}
|
2016-08-19 23:43:31 +00:00
|
|
|
e->SetInCache(false);
|
2013-10-07 22:37:40 +00:00
|
|
|
}
|
|
|
|
}
|
Modifed the LRU cache eviction code so that it doesn't evict blocks which have exteranl references
Summary:
Currently, blocks which have more than one reference (ie referenced by something other than cache itself) are evicted from cache. This doesn't make much sense:
- blocks are still in RAM, so the RAM usage reported by the cache is incorrect
- if the same block is needed by another iterator, it will be loaded and decompressed again
This diff changes the reference counting scheme a bit. Previously, if the cache contained the block, this was accounted for in its refcount. After this change, the refcount is only used to track external references. There is a boolean flag which indicates whether or not the block is contained in the cache.
This diff also changes how LRU list is used. Previously, both hashtable and the LRU list contained all blocks. After this change, the LRU list contains blocks with the refcount==0, ie those which can be evicted from the cache.
Note that this change still allows for cache to grow beyond its capacity. This happens when all blocks are pinned (ie refcount>0). This is consistent with the current behavior. The cache's insert function never fails. I spent lots of time trying to make table_reader and other places work with the insert which might failed. It turned out to be pretty hard. It might really destabilize some customers, so finally, I decided against doing this.
table_cache_remove_scan_count_limit option will be unneeded after this change, but I will remove it in the following diff, if this one gets approved
Test Plan: Ran tests, made sure they pass
Reviewers: sdong, ljin
Differential Revision: https://reviews.facebook.net/D25503
2014-10-21 18:49:13 +00:00
|
|
|
|
2013-10-07 22:37:40 +00:00
|
|
|
// mutex not held here
|
|
|
|
// last_reference will only be true if e != nullptr
|
|
|
|
if (last_reference) {
|
Modifed the LRU cache eviction code so that it doesn't evict blocks which have exteranl references
Summary:
Currently, blocks which have more than one reference (ie referenced by something other than cache itself) are evicted from cache. This doesn't make much sense:
- blocks are still in RAM, so the RAM usage reported by the cache is incorrect
- if the same block is needed by another iterator, it will be loaded and decompressed again
This diff changes the reference counting scheme a bit. Previously, if the cache contained the block, this was accounted for in its refcount. After this change, the refcount is only used to track external references. There is a boolean flag which indicates whether or not the block is contained in the cache.
This diff also changes how LRU list is used. Previously, both hashtable and the LRU list contained all blocks. After this change, the LRU list contains blocks with the refcount==0, ie those which can be evicted from the cache.
Note that this change still allows for cache to grow beyond its capacity. This happens when all blocks are pinned (ie refcount>0). This is consistent with the current behavior. The cache's insert function never fails. I spent lots of time trying to make table_reader and other places work with the insert which might failed. It turned out to be pretty hard. It might really destabilize some customers, so finally, I decided against doing this.
table_cache_remove_scan_count_limit option will be unneeded after this change, but I will remove it in the following diff, if this one gets approved
Test Plan: Ran tests, made sure they pass
Reviewers: sdong, ljin
Differential Revision: https://reviews.facebook.net/D25503
2014-10-21 18:49:13 +00:00
|
|
|
e->Free();
|
2011-03-18 22:37:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-16 21:43:41 +00:00
|
|
|
size_t LRUCacheShard::GetUsage() const {
|
|
|
|
MutexLock l(&mutex_);
|
|
|
|
return usage_;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t LRUCacheShard::GetPinnedUsage() const {
|
|
|
|
MutexLock l(&mutex_);
|
|
|
|
assert(usage_ >= lru_usage_);
|
|
|
|
return usage_ - lru_usage_;
|
|
|
|
}
|
|
|
|
|
2016-12-22 22:44:01 +00:00
|
|
|
std::string LRUCacheShard::GetPrintableOptions() const {
|
|
|
|
const int kBufferSize = 200;
|
|
|
|
char buffer[kBufferSize];
|
|
|
|
{
|
|
|
|
MutexLock l(&mutex_);
|
|
|
|
snprintf(buffer, kBufferSize, " high_pri_pool_ratio: %.3lf\n",
|
|
|
|
high_pri_pool_ratio_);
|
|
|
|
}
|
|
|
|
return std::string(buffer);
|
|
|
|
}
|
|
|
|
|
2016-08-23 20:44:13 +00:00
|
|
|
LRUCache::LRUCache(size_t capacity, int num_shard_bits,
|
|
|
|
bool strict_capacity_limit, double high_pri_pool_ratio)
|
|
|
|
: ShardedCache(capacity, num_shard_bits, strict_capacity_limit) {
|
2017-07-17 21:53:15 +00:00
|
|
|
num_shards_ = 1 << num_shard_bits;
|
2018-05-24 01:53:17 +00:00
|
|
|
shards_ = reinterpret_cast<LRUCacheShard*>(
|
|
|
|
port::cacheline_aligned_alloc(sizeof(LRUCacheShard) * num_shards_));
|
|
|
|
size_t per_shard = (capacity + (num_shards_ - 1)) / num_shards_;
|
2017-07-17 21:53:15 +00:00
|
|
|
for (int i = 0; i < num_shards_; i++) {
|
2018-05-24 01:53:17 +00:00
|
|
|
new (&shards_[i])
|
|
|
|
LRUCacheShard(per_shard, strict_capacity_limit, high_pri_pool_ratio);
|
2011-08-22 21:08:51 +00:00
|
|
|
}
|
2016-08-23 20:44:13 +00:00
|
|
|
}
|
2011-08-22 21:08:51 +00:00
|
|
|
|
2018-05-24 01:53:17 +00:00
|
|
|
LRUCache::~LRUCache() {
|
2018-05-29 22:11:22 +00:00
|
|
|
if (shards_ != nullptr) {
|
|
|
|
assert(num_shards_ > 0);
|
|
|
|
for (int i = 0; i < num_shards_; i++) {
|
|
|
|
shards_[i].~LRUCacheShard();
|
|
|
|
}
|
|
|
|
port::cacheline_aligned_free(shards_);
|
2018-05-24 01:53:17 +00:00
|
|
|
}
|
|
|
|
}
|
2014-01-28 18:35:48 +00:00
|
|
|
|
2016-08-23 20:44:13 +00:00
|
|
|
CacheShard* LRUCache::GetShard(int shard) {
|
|
|
|
return reinterpret_cast<CacheShard*>(&shards_[shard]);
|
|
|
|
}
|
2016-03-11 01:35:19 +00:00
|
|
|
|
2016-08-23 20:44:13 +00:00
|
|
|
const CacheShard* LRUCache::GetShard(int shard) const {
|
|
|
|
return reinterpret_cast<CacheShard*>(&shards_[shard]);
|
|
|
|
}
|
2015-10-07 22:17:20 +00:00
|
|
|
|
2016-08-23 20:44:13 +00:00
|
|
|
void* LRUCache::Value(Handle* handle) {
|
|
|
|
return reinterpret_cast<const LRUHandle*>(handle)->value;
|
|
|
|
}
|
2015-10-07 22:17:20 +00:00
|
|
|
|
2016-08-23 20:44:13 +00:00
|
|
|
size_t LRUCache::GetCharge(Handle* handle) const {
|
|
|
|
return reinterpret_cast<const LRUHandle*>(handle)->charge;
|
|
|
|
}
|
2014-01-28 18:35:48 +00:00
|
|
|
|
2016-08-23 20:44:13 +00:00
|
|
|
uint32_t LRUCache::GetHash(Handle* handle) const {
|
|
|
|
return reinterpret_cast<const LRUHandle*>(handle)->hash;
|
|
|
|
}
|
2014-05-02 20:24:04 +00:00
|
|
|
|
2017-08-30 04:41:46 +00:00
|
|
|
void LRUCache::DisownData() {
|
|
|
|
// Do not drop data if compile with ASAN to suppress leak warning.
|
2018-06-28 04:59:09 +00:00
|
|
|
#if defined(__clang__)
|
|
|
|
#if !defined(__has_feature) || !__has_feature(address_sanitizer)
|
|
|
|
shards_ = nullptr;
|
|
|
|
num_shards_ = 0;
|
|
|
|
#endif
|
2018-06-28 19:16:10 +00:00
|
|
|
#else // __clang__
|
2017-08-30 04:41:46 +00:00
|
|
|
#ifndef __SANITIZE_ADDRESS__
|
|
|
|
shards_ = nullptr;
|
2018-05-29 22:11:22 +00:00
|
|
|
num_shards_ = 0;
|
2017-08-30 04:41:46 +00:00
|
|
|
#endif // !__SANITIZE_ADDRESS__
|
2018-06-28 04:59:09 +00:00
|
|
|
#endif // __clang__
|
2017-08-30 04:41:46 +00:00
|
|
|
}
|
2011-03-18 22:37:00 +00:00
|
|
|
|
2017-07-17 21:53:15 +00:00
|
|
|
size_t LRUCache::TEST_GetLRUSize() {
|
|
|
|
size_t lru_size_of_all_shards = 0;
|
|
|
|
for (int i = 0; i < num_shards_; i++) {
|
|
|
|
lru_size_of_all_shards += shards_[i].TEST_GetLRUSize();
|
|
|
|
}
|
|
|
|
return lru_size_of_all_shards;
|
|
|
|
}
|
|
|
|
|
2017-11-28 18:35:17 +00:00
|
|
|
double LRUCache::GetHighPriPoolRatio() {
|
|
|
|
double result = 0.0;
|
|
|
|
if (num_shards_ > 0) {
|
|
|
|
result = shards_[0].GetHighPriPoolRatio();
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<Cache> NewLRUCache(const LRUCacheOptions& cache_opts) {
|
|
|
|
return NewLRUCache(cache_opts.capacity, cache_opts.num_shard_bits,
|
|
|
|
cache_opts.strict_capacity_limit,
|
|
|
|
cache_opts.high_pri_pool_ratio);
|
|
|
|
}
|
|
|
|
|
2016-05-24 06:35:23 +00:00
|
|
|
std::shared_ptr<Cache> NewLRUCache(size_t capacity, int num_shard_bits,
|
2016-08-19 23:43:31 +00:00
|
|
|
bool strict_capacity_limit,
|
|
|
|
double high_pri_pool_ratio) {
|
2013-12-11 00:21:49 +00:00
|
|
|
if (num_shard_bits >= 20) {
|
2013-03-01 02:04:58 +00:00
|
|
|
return nullptr; // the cache cannot be sharded into too many fine pieces
|
2012-08-29 16:47:53 +00:00
|
|
|
}
|
2016-08-19 23:43:31 +00:00
|
|
|
if (high_pri_pool_ratio < 0.0 || high_pri_pool_ratio > 1.0) {
|
|
|
|
// invalid high_pri_pool_ratio
|
|
|
|
return nullptr;
|
|
|
|
}
|
2017-01-27 14:35:41 +00:00
|
|
|
if (num_shard_bits < 0) {
|
|
|
|
num_shard_bits = GetDefaultCacheShardBits(capacity);
|
|
|
|
}
|
2016-07-15 17:41:36 +00:00
|
|
|
return std::make_shared<LRUCache>(capacity, num_shard_bits,
|
2016-08-19 23:43:31 +00:00
|
|
|
strict_capacity_limit, high_pri_pool_ratio);
|
2012-05-17 00:22:33 +00:00
|
|
|
}
|
|
|
|
|
2013-10-04 04:49:15 +00:00
|
|
|
} // namespace rocksdb
|