Currently, there are some useless includes in the codebase. We can use a tool named include-what-you-use to optimize these includes. By using a strict include-what-you-use policy, we can get lots of benefits from it.
283 lines
8.7 KiB
C++
283 lines
8.7 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.
|
|
#include "runtime/cache/result_cache.h"
|
|
|
|
#include <gen_cpp/internal_service.pb.h>
|
|
#include <glog/logging.h>
|
|
|
|
#include <iostream>
|
|
#include <list>
|
|
#include <utility>
|
|
|
|
#include "olap/olap_define.h"
|
|
#include "runtime/cache/cache_utils.h"
|
|
#include "util/doris_metrics.h"
|
|
|
|
namespace doris {
|
|
|
|
/**
|
|
* Remove the tail node of link
|
|
*/
|
|
ResultNode* ResultNodeList::pop() {
|
|
remove(_head);
|
|
return _head;
|
|
}
|
|
|
|
void ResultNodeList::remove(ResultNode* node) {
|
|
if (!node) return;
|
|
if (node == _head) _head = node->get_next();
|
|
if (node == _tail) _tail = node->get_prev();
|
|
node->unlink();
|
|
_node_count--;
|
|
}
|
|
|
|
void ResultNodeList::push_back(ResultNode* node) {
|
|
if (!node) return;
|
|
if (!_head) _head = node;
|
|
node->append(_tail);
|
|
_tail = node;
|
|
_node_count++;
|
|
}
|
|
|
|
void ResultNodeList::move_tail(ResultNode* node) {
|
|
if (!node || node == _tail) return;
|
|
if (!_head)
|
|
_head = node;
|
|
else if (node == _head)
|
|
_head = node->get_next();
|
|
node->unlink();
|
|
node->append(_tail);
|
|
_tail = node;
|
|
}
|
|
|
|
void ResultNodeList::delete_node(ResultNode** node) {
|
|
(*node)->clear();
|
|
SAFE_DELETE(*node);
|
|
}
|
|
|
|
void ResultNodeList::clear() {
|
|
LOG(INFO) << "clear result node list.";
|
|
while (_head) {
|
|
ResultNode* tmp_node = _head->get_next();
|
|
_head->clear();
|
|
SAFE_DELETE(_head);
|
|
_head = tmp_node;
|
|
}
|
|
_node_count = 0;
|
|
}
|
|
/**
|
|
* Find the node and update partition data
|
|
* New node, the node updated in the first partition will move to the tail of the list
|
|
*/
|
|
void ResultCache::update(const PUpdateCacheRequest* request, PCacheResponse* response) {
|
|
ResultNode* node;
|
|
PCacheStatus status;
|
|
bool update_first = false;
|
|
UniqueId sql_key = request->sql_key();
|
|
LOG(INFO) << "update cache, sql key:" << sql_key;
|
|
|
|
CacheWriteLock write_lock(_cache_mtx);
|
|
auto it = _node_map.find(sql_key);
|
|
if (it != _node_map.end()) {
|
|
node = it->second;
|
|
_cache_size -= node->get_data_size();
|
|
_partition_count -= node->get_partition_count();
|
|
status = node->update_partition(request, update_first);
|
|
} else {
|
|
node = _node_list.new_node(sql_key);
|
|
status = node->update_partition(request, update_first);
|
|
_node_list.push_back(node);
|
|
_node_map[sql_key] = node;
|
|
_node_count += 1;
|
|
}
|
|
if (update_first) {
|
|
_node_list.move_tail(node);
|
|
}
|
|
_cache_size += node->get_data_size();
|
|
_partition_count += node->get_partition_count();
|
|
response->set_status(status);
|
|
|
|
prune();
|
|
update_monitor();
|
|
}
|
|
|
|
/**
|
|
* Fetch cache through sql key, partition key, version and time
|
|
*/
|
|
void ResultCache::fetch(const PFetchCacheRequest* request, PFetchCacheResult* result) {
|
|
bool hit_first = false;
|
|
ResultNodeMap::iterator node_it;
|
|
const UniqueId sql_key = request->sql_key();
|
|
LOG(INFO) << "fetch cache, sql key:" << sql_key;
|
|
{
|
|
CacheReadLock read_lock(_cache_mtx);
|
|
node_it = _node_map.find(sql_key);
|
|
if (node_it == _node_map.end()) {
|
|
result->set_status(PCacheStatus::NO_SQL_KEY);
|
|
LOG(INFO) << "no such sql key:" << sql_key;
|
|
return;
|
|
}
|
|
ResultNode* node = node_it->second;
|
|
PartitionRowBatchList part_rowbatch_list;
|
|
PCacheStatus status = node->fetch_partition(request, part_rowbatch_list, hit_first);
|
|
|
|
for (auto part_it = part_rowbatch_list.begin(); part_it != part_rowbatch_list.end();
|
|
part_it++) {
|
|
PCacheValue* srcValue = (*part_it)->get_value();
|
|
if (srcValue != nullptr) {
|
|
PCacheValue* value = result->add_values();
|
|
value->CopyFrom(*srcValue);
|
|
LOG(INFO) << "fetch cache partition key:" << srcValue->param().partition_key();
|
|
} else {
|
|
LOG(WARNING) << "prowbatch of cache is null";
|
|
status = PCacheStatus::EMPTY_DATA;
|
|
break;
|
|
}
|
|
}
|
|
if (status == PCacheStatus::CACHE_OK && part_rowbatch_list.empty()) {
|
|
status = PCacheStatus::EMPTY_DATA;
|
|
}
|
|
result->set_status(status);
|
|
}
|
|
|
|
if (hit_first) {
|
|
CacheWriteLock write_lock(_cache_mtx);
|
|
_node_list.move_tail(node_it->second);
|
|
}
|
|
}
|
|
|
|
bool ResultCache::contains(const UniqueId& sql_key) {
|
|
CacheReadLock read_lock(_cache_mtx);
|
|
return _node_map.find(sql_key) != _node_map.end();
|
|
}
|
|
|
|
/**
|
|
* enum PClearType {
|
|
* CLEAR_ALL = 0,
|
|
* PRUNE_CACHE = 1,
|
|
* CLEAR_BEFORE_TIME = 2,
|
|
* CLEAR_SQL_KEY = 3
|
|
* };
|
|
*/
|
|
void ResultCache::clear(const PClearCacheRequest* request, PCacheResponse* response) {
|
|
LOG(INFO) << "clear cache type" << request->clear_type()
|
|
<< ", node size:" << _node_list.get_node_count() << ", map size:" << _node_map.size();
|
|
CacheWriteLock write_lock(_cache_mtx);
|
|
//0 clear, 1 prune, 2 before_time,3 sql_key
|
|
switch (request->clear_type()) {
|
|
case PClearType::CLEAR_ALL:
|
|
_node_list.clear();
|
|
_node_map.clear();
|
|
_cache_size = 0;
|
|
_node_count = 0;
|
|
_partition_count = 0;
|
|
break;
|
|
case PClearType::PRUNE_CACHE:
|
|
prune();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
update_monitor();
|
|
response->set_status(PCacheStatus::CACHE_OK);
|
|
}
|
|
|
|
//private method
|
|
ResultNode* find_min_time_node(ResultNode* result_node) {
|
|
if (result_node->get_prev()) {
|
|
if (result_node->get_prev()->first_partition_last_time() <=
|
|
result_node->first_partition_last_time()) {
|
|
return result_node->get_prev();
|
|
}
|
|
}
|
|
|
|
if (result_node->get_next()) {
|
|
if (result_node->get_next()->first_partition_last_time() <
|
|
result_node->first_partition_last_time()) {
|
|
return result_node->get_next();
|
|
}
|
|
}
|
|
return result_node;
|
|
}
|
|
|
|
/*
|
|
* Two-dimensional array, prune the min last_read_time PartitionRowBatch.
|
|
* The following example is the last read time array.
|
|
* 1 and 2 is the read time, nodes with pruning read time < 3
|
|
* Before:
|
|
* 1,2 //_head ResultNode*
|
|
* 1,2,3,4,5
|
|
* 2,4,3,6,8
|
|
* 5,7,9,11,13 //_tail ResultNode*
|
|
* After:
|
|
* 4,5 //_head
|
|
* 4,3,6,8
|
|
* 5,7,9,11,13 //_tail
|
|
*/
|
|
void ResultCache::prune() {
|
|
if (_cache_size <= (_max_size + _elasticity_size)) {
|
|
return;
|
|
}
|
|
LOG(INFO) << "begin prune cache, cache_size : " << _cache_size << ", max_size : " << _max_size
|
|
<< ", elasticity_size : " << _elasticity_size;
|
|
ResultNode* result_node = _node_list.get_head();
|
|
while (_cache_size > _max_size) {
|
|
if (result_node == nullptr) {
|
|
break;
|
|
}
|
|
result_node = find_min_time_node(result_node);
|
|
_cache_size -= result_node->prune_first();
|
|
if (result_node->get_data_size() == 0) {
|
|
ResultNode* next_node;
|
|
if (result_node->get_next()) {
|
|
next_node = result_node->get_next();
|
|
} else if (result_node->get_prev()) {
|
|
next_node = result_node->get_prev();
|
|
} else {
|
|
next_node = _node_list.get_head();
|
|
}
|
|
remove(result_node);
|
|
result_node = next_node;
|
|
}
|
|
}
|
|
LOG(INFO) << "finish prune, cache_size : " << _cache_size;
|
|
_node_count = _node_map.size();
|
|
_cache_size = 0;
|
|
_partition_count = 0;
|
|
for (auto node_it = _node_map.begin(); node_it != _node_map.end(); node_it++) {
|
|
_partition_count += node_it->second->get_partition_count();
|
|
_cache_size += node_it->second->get_data_size();
|
|
}
|
|
}
|
|
|
|
void ResultCache::remove(ResultNode* result_node) {
|
|
auto node_it = _node_map.find(result_node->get_sql_key());
|
|
if (node_it != _node_map.end()) {
|
|
_node_map.erase(node_it);
|
|
_node_list.remove(result_node);
|
|
_node_list.delete_node(&result_node);
|
|
}
|
|
}
|
|
|
|
void ResultCache::update_monitor() {
|
|
DorisMetrics::instance()->query_cache_memory_total_byte->set_value(_cache_size);
|
|
DorisMetrics::instance()->query_cache_sql_total_count->set_value(_node_count);
|
|
DorisMetrics::instance()->query_cache_partition_total_count->set_value(_partition_count);
|
|
}
|
|
|
|
} // namespace doris
|