Files
doris/be/src/olap/page_cache.h
Xinyi Zou cdbbf1e4ee [enhancement](memory) Add Memory GC when the available memory of the BE process is lacking (#14712)
When the system MemAvailable is less than the warning water mark, or the memory used by the BE process exceeds the mem soft limit, run minor gc and try to release cache.

When the MemAvailable of the system is less than the low water mark, or the memory used by the BE process exceeds the mem limit, run fucc gc, try to release the cache, and start canceling from the query with the largest memory usage until the memory of mem_limit * 20% is released.
2022-12-07 15:28:52 +08:00

158 lines
5.4 KiB
C++

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#pragma once
#include <memory>
#include <string>
#include <utility>
#include "gen_cpp/segment_v2.pb.h" // for cache allocation
#include "gutil/macros.h" // for DISALLOW_COPY_AND_ASSIGN
#include "olap/lru_cache.h"
#include "runtime/memory/mem_tracker.h"
namespace doris {
class PageCacheHandle;
// Wrapper around Cache, and used for cache page of column data
// in Segment.
// TODO(zc): We should add some metric to see cache hit/miss rate.
class StoragePageCache {
public:
// The unique key identifying entries in the page cache.
// Each cached page corresponds to a specific offset within
// a file.
//
// TODO(zc): Now we use file name(std::string) as a part of
// key, which is not efficient. We should make it better later
struct CacheKey {
CacheKey(std::string fname_, int64_t offset_) : fname(std::move(fname_)), offset(offset_) {}
std::string fname;
int64_t offset;
// Encode to a flat binary which can be used as LRUCache's key
std::string encode() const {
std::string key_buf(fname);
key_buf.append((char*)&offset, sizeof(offset));
return key_buf;
}
};
static constexpr uint32_t kDefaultNumShards = 16;
// Create global instance of this class
static void create_global_cache(size_t capacity, int32_t index_cache_percentage,
uint32_t num_shards = kDefaultNumShards);
// Return global instance.
// Client should call create_global_cache before.
static StoragePageCache* instance() { return _s_instance; }
StoragePageCache(size_t capacity, int32_t index_cache_percentage, uint32_t num_shards);
// Lookup the given page in the cache.
//
// If the page is found, the cache entry will be written into handle.
// PageCacheHandle will release cache entry to cache when it
// destructs.
//
// Cache type selection is determined by page_type argument
//
// Return true if entry is found, otherwise return false.
bool lookup(const CacheKey& key, PageCacheHandle* handle, segment_v2::PageTypePB page_type);
// Insert a page with key into this cache.
// Given handle will be set to valid reference.
// This function is thread-safe, and when two clients insert two same key
// concurrently, this function can assure that only one page is cached.
// The in_memory page will have higher priority.
void insert(const CacheKey& key, const Slice& data, PageCacheHandle* handle,
segment_v2::PageTypePB page_type, bool in_memory = false);
// Page cache available check.
// When percentage is set to 0 or 100, the index or data cache will not be allocated.
bool is_cache_available(segment_v2::PageTypePB page_type) {
return _get_page_cache(page_type) != nullptr;
}
void prune(segment_v2::PageTypePB page_type);
int64_t get_page_cache_mem_consumption(segment_v2::PageTypePB page_type) {
return _get_page_cache(page_type)->mem_consumption();
}
private:
StoragePageCache();
static StoragePageCache* _s_instance;
int32_t _index_cache_percentage = 0;
std::unique_ptr<Cache> _data_page_cache = nullptr;
std::unique_ptr<Cache> _index_page_cache = nullptr;
Cache* _get_page_cache(segment_v2::PageTypePB page_type) {
switch (page_type) {
case segment_v2::DATA_PAGE: {
return _data_page_cache.get();
}
case segment_v2::INDEX_PAGE:
return _index_page_cache.get();
default:
return nullptr;
}
}
};
// A handle for StoragePageCache entry. This class make it easy to handle
// Cache entry. Users don't need to release the obtained cache entry. This
// class will release the cache entry when it is destroyed.
class PageCacheHandle {
public:
PageCacheHandle() {}
PageCacheHandle(Cache* cache, Cache::Handle* handle) : _cache(cache), _handle(handle) {}
~PageCacheHandle() {
if (_handle != nullptr) {
_cache->release(_handle);
}
}
PageCacheHandle(PageCacheHandle&& other) noexcept {
// we can use std::exchange if we switch c++14 on
std::swap(_cache, other._cache);
std::swap(_handle, other._handle);
}
PageCacheHandle& operator=(PageCacheHandle&& other) noexcept {
std::swap(_cache, other._cache);
std::swap(_handle, other._handle);
return *this;
}
Cache* cache() const { return _cache; }
Slice data() const { return _cache->value_slice(_handle); }
private:
Cache* _cache = nullptr;
Cache::Handle* _handle = nullptr;
// Don't allow copy and assign
DISALLOW_COPY_AND_ASSIGN(PageCacheHandle);
};
} // namespace doris