mirror of
https://gitee.com/milvus-io/milvus.git
synced 2025-12-07 09:38:39 +08:00
enhance: support cache result cache for expr (#43923)
issue: #43878 Signed-off-by: luzhang <luzhang@zilliz.com> Co-authored-by: luzhang <luzhang@zilliz.com>
This commit is contained in:
parent
f1ce84996d
commit
8934c18792
@ -556,6 +556,9 @@ queryNode:
|
|||||||
levelZeroForwardPolicy: FilterByBF # delegator level zero deletion forward policy, possible option["FilterByBF", "RemoteLoad"]
|
levelZeroForwardPolicy: FilterByBF # delegator level zero deletion forward policy, possible option["FilterByBF", "RemoteLoad"]
|
||||||
streamingDeltaForwardPolicy: FilterByBF # delegator streaming deletion forward policy, possible option["FilterByBF", "Direct"]
|
streamingDeltaForwardPolicy: FilterByBF # delegator streaming deletion forward policy, possible option["FilterByBF", "Direct"]
|
||||||
forwardBatchSize: 4194304 # the batch size delegator uses for forwarding stream delete in loading procedure
|
forwardBatchSize: 4194304 # the batch size delegator uses for forwarding stream delete in loading procedure
|
||||||
|
exprCache:
|
||||||
|
enabled: false # enable expression result cache
|
||||||
|
capacityBytes: 268435456 # max capacity in bytes for expression result cache
|
||||||
dataSync:
|
dataSync:
|
||||||
flowGraph:
|
flowGraph:
|
||||||
maxQueueLength: 16 # The maximum size of task queue cache in flow graph in query node.
|
maxQueueLength: 16 # The maximum size of task queue cache in flow graph in query node.
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
#include "common/Tracer.h"
|
#include "common/Tracer.h"
|
||||||
#include "storage/ThreadPool.h"
|
#include "storage/ThreadPool.h"
|
||||||
#include "log/Log.h"
|
#include "log/Log.h"
|
||||||
|
#include "exec/expression/ExprCache.h"
|
||||||
|
|
||||||
std::once_flag traceFlag;
|
std::once_flag traceFlag;
|
||||||
std::once_flag cpuNumFlag;
|
std::once_flag cpuNumFlag;
|
||||||
@ -79,6 +80,17 @@ SetLogLevel(const char* level) {
|
|||||||
milvus::SetLogLevel(level);
|
milvus::SetLogLevel(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SetExprResCacheEnable(bool val) {
|
||||||
|
milvus::exec::ExprResCacheManager::SetEnabled(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SetExprResCacheCapacityBytes(int64_t bytes) {
|
||||||
|
milvus::exec::ExprResCacheManager::Instance().SetCapacityBytes(
|
||||||
|
static_cast<size_t>(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
InitTrace(CTraceConfig* config) {
|
InitTrace(CTraceConfig* config) {
|
||||||
auto traceConfig = milvus::tracer::TraceConfig{config->exporter,
|
auto traceConfig = milvus::tracer::TraceConfig{config->exporter,
|
||||||
|
|||||||
@ -64,6 +64,13 @@ InitTrace(CTraceConfig* config);
|
|||||||
void
|
void
|
||||||
SetTrace(CTraceConfig* config);
|
SetTrace(CTraceConfig* config);
|
||||||
|
|
||||||
|
// Expr result cache
|
||||||
|
void
|
||||||
|
SetExprResCacheEnable(bool val);
|
||||||
|
|
||||||
|
void
|
||||||
|
SetExprResCacheCapacityBytes(int64_t bytes);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -806,18 +806,17 @@ PhyBinaryRangeFilterExpr::ExecRangeVisitorImplForJsonForIndex() {
|
|||||||
};
|
};
|
||||||
bool is_growing = segment_->type() == SegmentType::Growing;
|
bool is_growing = segment_->type() == SegmentType::Growing;
|
||||||
bool is_strong_consistency = consistency_level_ == 0;
|
bool is_strong_consistency = consistency_level_ == 0;
|
||||||
cached_index_chunk_res_ = index
|
cached_index_chunk_res_ = std::make_shared<TargetBitmap>(
|
||||||
->FilterByPath(pointer,
|
std::move(index->FilterByPath(pointer,
|
||||||
active_count_,
|
active_count_,
|
||||||
is_growing,
|
is_growing,
|
||||||
is_strong_consistency,
|
is_strong_consistency,
|
||||||
filter_func)
|
filter_func)));
|
||||||
.clone();
|
|
||||||
cached_index_chunk_id_ = 0;
|
cached_index_chunk_id_ = 0;
|
||||||
}
|
}
|
||||||
TargetBitmap result;
|
TargetBitmap result;
|
||||||
result.append(
|
result.append(
|
||||||
cached_index_chunk_res_, current_data_global_pos_, real_batch_size);
|
*cached_index_chunk_res_, current_data_global_pos_, real_batch_size);
|
||||||
MoveCursor();
|
MoveCursor();
|
||||||
return std::make_shared<ColumnVector>(std::move(result),
|
return std::make_shared<ColumnVector>(std::move(result),
|
||||||
TargetBitmap(real_batch_size, true));
|
TargetBitmap(real_batch_size, true));
|
||||||
|
|||||||
@ -63,21 +63,24 @@ PhyExistsFilterExpr::EvalJsonExistsForIndex() {
|
|||||||
case JsonCastType::DataType::DOUBLE: {
|
case JsonCastType::DataType::DOUBLE: {
|
||||||
auto* json_index =
|
auto* json_index =
|
||||||
dynamic_cast<index::JsonInvertedIndex<double>*>(index);
|
dynamic_cast<index::JsonInvertedIndex<double>*>(index);
|
||||||
cached_index_chunk_res_ = json_index->Exists().clone();
|
cached_index_chunk_res_ = std::make_shared<TargetBitmap>(
|
||||||
|
std::move(json_index->Exists()));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case JsonCastType::DataType::VARCHAR: {
|
case JsonCastType::DataType::VARCHAR: {
|
||||||
auto* json_index =
|
auto* json_index =
|
||||||
dynamic_cast<index::JsonInvertedIndex<std::string>*>(index);
|
dynamic_cast<index::JsonInvertedIndex<std::string>*>(index);
|
||||||
cached_index_chunk_res_ = json_index->Exists().clone();
|
cached_index_chunk_res_ = std::make_shared<TargetBitmap>(
|
||||||
|
std::move(json_index->Exists()));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case JsonCastType::DataType::BOOL: {
|
case JsonCastType::DataType::BOOL: {
|
||||||
auto* json_index =
|
auto* json_index =
|
||||||
dynamic_cast<index::JsonInvertedIndex<bool>*>(index);
|
dynamic_cast<index::JsonInvertedIndex<bool>*>(index);
|
||||||
cached_index_chunk_res_ = json_index->Exists().clone();
|
cached_index_chunk_res_ = std::make_shared<TargetBitmap>(
|
||||||
|
std::move(json_index->Exists()));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +89,8 @@ PhyExistsFilterExpr::EvalJsonExistsForIndex() {
|
|||||||
dynamic_cast<index::JsonFlatIndex*>(index);
|
dynamic_cast<index::JsonFlatIndex*>(index);
|
||||||
auto executor =
|
auto executor =
|
||||||
json_flat_index->create_executor<double>(pointer);
|
json_flat_index->create_executor<double>(pointer);
|
||||||
cached_index_chunk_res_ = executor->IsNotNull().clone();
|
cached_index_chunk_res_ = std::make_shared<TargetBitmap>(
|
||||||
|
std::move(executor->IsNotNull()));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +102,7 @@ PhyExistsFilterExpr::EvalJsonExistsForIndex() {
|
|||||||
}
|
}
|
||||||
TargetBitmap res;
|
TargetBitmap res;
|
||||||
res.append(
|
res.append(
|
||||||
cached_index_chunk_res_, current_index_chunk_pos_, real_batch_size);
|
*cached_index_chunk_res_, current_index_chunk_pos_, real_batch_size);
|
||||||
current_index_chunk_pos_ += real_batch_size;
|
current_index_chunk_pos_ += real_batch_size;
|
||||||
return std::make_shared<ColumnVector>(std::move(res),
|
return std::make_shared<ColumnVector>(std::move(res),
|
||||||
TargetBitmap(real_batch_size, true));
|
TargetBitmap(real_batch_size, true));
|
||||||
@ -209,17 +213,16 @@ PhyExistsFilterExpr::EvalJsonExistsForDataSegmentForIndex() {
|
|||||||
};
|
};
|
||||||
bool is_growing = segment_->type() == SegmentType::Growing;
|
bool is_growing = segment_->type() == SegmentType::Growing;
|
||||||
bool is_strong_consistency = consistency_level_ == 0;
|
bool is_strong_consistency = consistency_level_ == 0;
|
||||||
cached_index_chunk_res_ = index
|
cached_index_chunk_res_ = std::make_shared<TargetBitmap>(
|
||||||
->FilterByPath(pointer,
|
std::move(index->FilterByPath(pointer,
|
||||||
active_count_,
|
active_count_,
|
||||||
is_growing,
|
is_growing,
|
||||||
is_strong_consistency,
|
is_strong_consistency,
|
||||||
filter_func)
|
filter_func)));
|
||||||
.clone();
|
|
||||||
}
|
}
|
||||||
TargetBitmap result;
|
TargetBitmap result;
|
||||||
result.append(
|
result.append(
|
||||||
cached_index_chunk_res_, current_data_global_pos_, real_batch_size);
|
*cached_index_chunk_res_, current_data_global_pos_, real_batch_size);
|
||||||
MoveCursor();
|
MoveCursor();
|
||||||
return std::make_shared<ColumnVector>(std::move(result),
|
return std::make_shared<ColumnVector>(std::move(result),
|
||||||
TargetBitmap(real_batch_size, true));
|
TargetBitmap(real_batch_size, true));
|
||||||
|
|||||||
@ -913,17 +913,19 @@ class SegmentExpr : public Expr {
|
|||||||
i);
|
i);
|
||||||
index_ptr = const_cast<Index*>(pw.get());
|
index_ptr = const_cast<Index*>(pw.get());
|
||||||
}
|
}
|
||||||
cached_index_chunk_res_ = std::move(func(index_ptr, values...));
|
cached_index_chunk_res_ = std::make_shared<TargetBitmap>(
|
||||||
|
std::move(func(index_ptr, values...)));
|
||||||
auto valid_result = index_ptr->IsNotNull();
|
auto valid_result = index_ptr->IsNotNull();
|
||||||
cached_index_chunk_valid_res_ = std::move(valid_result);
|
cached_index_chunk_valid_res_ =
|
||||||
|
std::make_shared<TargetBitmap>(std::move(valid_result));
|
||||||
cached_index_chunk_id_ = i;
|
cached_index_chunk_id_ = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto size = ProcessIndexOneChunk(result,
|
auto size = ProcessIndexOneChunk(result,
|
||||||
valid_result,
|
valid_result,
|
||||||
i,
|
i,
|
||||||
cached_index_chunk_res_,
|
*cached_index_chunk_res_,
|
||||||
cached_index_chunk_valid_res_,
|
*cached_index_chunk_valid_res_,
|
||||||
processed_rows);
|
processed_rows);
|
||||||
|
|
||||||
if (processed_rows + size >= batch_size_) {
|
if (processed_rows + size >= batch_size_) {
|
||||||
@ -1181,12 +1183,16 @@ class SegmentExpr : public Expr {
|
|||||||
TargetBitmap res = index_ptr->IsNotNull();
|
TargetBitmap res = index_ptr->IsNotNull();
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
cached_index_chunk_valid_res_ = execute_sub_batch(index_ptr);
|
cached_index_chunk_valid_res_ = std::make_shared<TargetBitmap>(
|
||||||
|
std::move(execute_sub_batch(index_ptr)));
|
||||||
cached_index_chunk_id_ = i;
|
cached_index_chunk_id_ = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto size = ProcessIndexOneChunkForValid(
|
auto size =
|
||||||
valid_result, i, cached_index_chunk_valid_res_, processed_rows);
|
ProcessIndexOneChunkForValid(valid_result,
|
||||||
|
i,
|
||||||
|
*cached_index_chunk_valid_res_,
|
||||||
|
processed_rows);
|
||||||
|
|
||||||
if (processed_rows + size >= batch_size_) {
|
if (processed_rows + size >= batch_size_) {
|
||||||
current_index_chunk_ = i;
|
current_index_chunk_ = i;
|
||||||
@ -1326,9 +1332,9 @@ class SegmentExpr : public Expr {
|
|||||||
|
|
||||||
// Cache for index scan to avoid search index every batch
|
// Cache for index scan to avoid search index every batch
|
||||||
int64_t cached_index_chunk_id_{-1};
|
int64_t cached_index_chunk_id_{-1};
|
||||||
TargetBitmap cached_index_chunk_res_{};
|
std::shared_ptr<TargetBitmap> cached_index_chunk_res_{nullptr};
|
||||||
// Cache for chunk valid res.
|
// Cache for chunk valid res.
|
||||||
TargetBitmap cached_index_chunk_valid_res_{};
|
std::shared_ptr<TargetBitmap> cached_index_chunk_valid_res_{nullptr};
|
||||||
|
|
||||||
// Cache for text match.
|
// Cache for text match.
|
||||||
std::shared_ptr<TargetBitmap> cached_match_res_{nullptr};
|
std::shared_ptr<TargetBitmap> cached_match_res_{nullptr};
|
||||||
|
|||||||
193
internal/core/src/exec/expression/ExprCache.cpp
Normal file
193
internal/core/src/exec/expression/ExprCache.cpp
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
// Licensed to the LF AI & Data foundation 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 "exec/expression/ExprCache.h"
|
||||||
|
|
||||||
|
namespace milvus {
|
||||||
|
namespace exec {
|
||||||
|
|
||||||
|
std::atomic<bool> ExprResCacheManager::enabled_{false};
|
||||||
|
|
||||||
|
ExprResCacheManager&
|
||||||
|
ExprResCacheManager::Instance() {
|
||||||
|
static ExprResCacheManager instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ExprResCacheManager::SetEnabled(bool enabled) {
|
||||||
|
enabled_.store(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ExprResCacheManager::IsEnabled() {
|
||||||
|
return enabled_.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ExprResCacheManager::SetCapacityBytes(size_t capacity_bytes) {
|
||||||
|
capacity_bytes_.store(capacity_bytes);
|
||||||
|
EnsureCapacity();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
ExprResCacheManager::GetCapacityBytes() const {
|
||||||
|
return capacity_bytes_.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
ExprResCacheManager::GetCurrentBytes() const {
|
||||||
|
return current_bytes_.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
ExprResCacheManager::GetEntryCount() const {
|
||||||
|
return concurrent_map_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ExprResCacheManager::Get(const Key& key, Value& out_value) {
|
||||||
|
if (!IsEnabled()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = concurrent_map_.find(key);
|
||||||
|
if (it == concurrent_map_.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
out_value = it->second.value;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lru_lock(lru_mutex_);
|
||||||
|
lru_list_.splice(lru_list_.begin(), lru_list_, it->second.lru_it);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG("get expr res cache, segment_id: {}, key: {}",
|
||||||
|
key.segment_id,
|
||||||
|
key.signature);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ExprResCacheManager::Put(const Key& key, const Value& value) {
|
||||||
|
if (!IsEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t estimated_bytes = EstimateBytes(value);
|
||||||
|
auto stored_value = value;
|
||||||
|
stored_value.bytes = estimated_bytes;
|
||||||
|
|
||||||
|
auto it = concurrent_map_.find(key);
|
||||||
|
if (it != concurrent_map_.end()) {
|
||||||
|
auto old_bytes = it->second.value.bytes;
|
||||||
|
it->second.value = stored_value;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lru_lock(lru_mutex_);
|
||||||
|
lru_list_.splice(lru_list_.begin(), lru_list_, it->second.lru_it);
|
||||||
|
current_bytes_.fetch_add(estimated_bytes - old_bytes);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ListIt list_it;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lru_lock(lru_mutex_);
|
||||||
|
lru_list_.push_front(key);
|
||||||
|
list_it = lru_list_.begin();
|
||||||
|
current_bytes_.fetch_add(estimated_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry entry(stored_value, list_it);
|
||||||
|
concurrent_map_.emplace(key, std::move(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_bytes_.load() > capacity_bytes_.load()) {
|
||||||
|
EnsureCapacity();
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG("put expr res cache, segment_id: {}, key: {}",
|
||||||
|
key.segment_id,
|
||||||
|
key.signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ExprResCacheManager::Clear() {
|
||||||
|
std::lock_guard<std::mutex> lru_lock(lru_mutex_);
|
||||||
|
|
||||||
|
concurrent_map_.clear();
|
||||||
|
lru_list_.clear();
|
||||||
|
current_bytes_.store(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
ExprResCacheManager::EstimateBytes(const Value& v) const {
|
||||||
|
size_t bytes = sizeof(Value);
|
||||||
|
if (v.result) {
|
||||||
|
bytes += (v.result->size() + 7) / 8;
|
||||||
|
}
|
||||||
|
if (v.valid_result) {
|
||||||
|
bytes += (v.valid_result->size() + 7) / 8;
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ExprResCacheManager::EnsureCapacity() {
|
||||||
|
std::lock_guard<std::mutex> lru_lock(lru_mutex_);
|
||||||
|
while (current_bytes_.load() > capacity_bytes_.load() &&
|
||||||
|
!lru_list_.empty()) {
|
||||||
|
const auto& back_key = lru_list_.back();
|
||||||
|
auto it = concurrent_map_.find(back_key);
|
||||||
|
if (it != concurrent_map_.end()) {
|
||||||
|
current_bytes_.fetch_sub(it->second.value.bytes);
|
||||||
|
concurrent_map_.unsafe_erase(it);
|
||||||
|
}
|
||||||
|
lru_list_.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
ExprResCacheManager::EraseSegment(int64_t segment_id) {
|
||||||
|
size_t erased = 0;
|
||||||
|
std::lock_guard<std::mutex> lru_lock(lru_mutex_);
|
||||||
|
for (auto it = lru_list_.begin(); it != lru_list_.end();) {
|
||||||
|
if (it->segment_id == segment_id) {
|
||||||
|
auto map_it = concurrent_map_.find(*it);
|
||||||
|
if (map_it != concurrent_map_.end()) {
|
||||||
|
current_bytes_.fetch_sub(map_it->second.value.bytes);
|
||||||
|
concurrent_map_.unsafe_erase(map_it);
|
||||||
|
}
|
||||||
|
it = lru_list_.erase(it);
|
||||||
|
++erased;
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO("erase segment cache, segment_id: {}, erased: {} entries",
|
||||||
|
segment_id,
|
||||||
|
erased);
|
||||||
|
return erased;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ExprResCacheManager::Init(size_t capacity_bytes, bool enabled) {
|
||||||
|
SetEnabled(enabled);
|
||||||
|
Instance().SetCapacityBytes(capacity_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace exec
|
||||||
|
} // namespace milvus
|
||||||
140
internal/core/src/exec/expression/ExprCache.h
Normal file
140
internal/core/src/exec/expression/ExprCache.h
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
// Licensed to the LF AI & Data foundation 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 <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <atomic>
|
||||||
|
#include <list>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <shared_mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <tbb/concurrent_unordered_map.h>
|
||||||
|
|
||||||
|
#include "common/Types.h"
|
||||||
|
#include "log/Log.h"
|
||||||
|
|
||||||
|
namespace milvus {
|
||||||
|
namespace exec {
|
||||||
|
|
||||||
|
// Process-level LRU cache for expression result bitsets.
|
||||||
|
class ExprResCacheManager {
|
||||||
|
public:
|
||||||
|
struct Key {
|
||||||
|
int64_t segment_id{0};
|
||||||
|
std::string signature; // expr signature including parameters
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator==(const Key& other) const {
|
||||||
|
return segment_id == other.segment_id &&
|
||||||
|
signature == other.signature;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct KeyHasher {
|
||||||
|
size_t
|
||||||
|
operator()(const Key& k) const noexcept {
|
||||||
|
std::hash<int64_t> h1;
|
||||||
|
std::hash<std::string> h2;
|
||||||
|
return (h1(k.segment_id) * 1315423911u) ^ h2(k.signature);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Value {
|
||||||
|
std::shared_ptr<TargetBitmap> result; // filter result bits
|
||||||
|
std::shared_ptr<TargetBitmap> valid_result; // valid bits
|
||||||
|
int64_t active_count{0}; // active count when cached
|
||||||
|
size_t bytes{0}; // approximate size in bytes
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
static ExprResCacheManager&
|
||||||
|
Instance();
|
||||||
|
static void
|
||||||
|
SetEnabled(bool enabled);
|
||||||
|
static bool
|
||||||
|
IsEnabled();
|
||||||
|
static void
|
||||||
|
Init(size_t capacity_bytes, bool enabled);
|
||||||
|
|
||||||
|
void
|
||||||
|
SetCapacityBytes(size_t capacity_bytes);
|
||||||
|
size_t
|
||||||
|
GetCapacityBytes() const;
|
||||||
|
size_t
|
||||||
|
GetCurrentBytes() const;
|
||||||
|
size_t
|
||||||
|
GetEntryCount() const;
|
||||||
|
|
||||||
|
// Try to get cached value. If found, returns true and fills out_value.
|
||||||
|
bool
|
||||||
|
Get(const Key& key, Value& out_value);
|
||||||
|
|
||||||
|
// Insert or update cache entry. The provided value.result must be non-null.
|
||||||
|
void
|
||||||
|
Put(const Key& key, const Value& value);
|
||||||
|
|
||||||
|
void
|
||||||
|
Clear();
|
||||||
|
|
||||||
|
// Erase all cache entries of a specific segment. Returns number of erased entries.
|
||||||
|
size_t
|
||||||
|
EraseSegment(int64_t segment_id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ExprResCacheManager() = default;
|
||||||
|
|
||||||
|
size_t
|
||||||
|
EstimateBytes(const Value& v) const;
|
||||||
|
void
|
||||||
|
EnsureCapacity();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::atomic<bool> enabled_;
|
||||||
|
std::atomic<size_t> capacity_bytes_{256ull * 1024ull *
|
||||||
|
1024ull}; // default 256MB
|
||||||
|
std::atomic<size_t> current_bytes_{0};
|
||||||
|
|
||||||
|
mutable std::mutex lru_mutex_;
|
||||||
|
std::list<Key> lru_list_;
|
||||||
|
using ListIt = std::list<Key>::iterator;
|
||||||
|
struct Entry {
|
||||||
|
Value value;
|
||||||
|
ListIt lru_it;
|
||||||
|
|
||||||
|
Entry() = default;
|
||||||
|
Entry(const Value& v, ListIt it) : value(v), lru_it(it) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
tbb::concurrent_unordered_map<Key, Entry, KeyHasher> concurrent_map_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper API: erase all cache for a given segment id, returns erased entry count
|
||||||
|
inline size_t
|
||||||
|
EraseSegmentCache(int64_t segment_id) {
|
||||||
|
if (!ExprResCacheManager::IsEnabled()) {
|
||||||
|
LOG_INFO("expr res cache is disabled, skip erase segment cache");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return ExprResCacheManager::Instance().EraseSegment(segment_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace exec
|
||||||
|
} // namespace milvus
|
||||||
@ -489,18 +489,17 @@ PhyJsonContainsFilterExpr::ExecJsonContainsByKeyIndex() {
|
|||||||
};
|
};
|
||||||
bool is_growing = segment_->type() == SegmentType::Growing;
|
bool is_growing = segment_->type() == SegmentType::Growing;
|
||||||
bool is_strong_consistency = consistency_level_ == 0;
|
bool is_strong_consistency = consistency_level_ == 0;
|
||||||
cached_index_chunk_res_ = index
|
cached_index_chunk_res_ = std::make_shared<TargetBitmap>(
|
||||||
->FilterByPath(pointer,
|
std::move(index->FilterByPath(pointer,
|
||||||
active_count_,
|
active_count_,
|
||||||
is_growing,
|
is_growing,
|
||||||
is_strong_consistency,
|
is_strong_consistency,
|
||||||
filter_func)
|
filter_func)));
|
||||||
.clone();
|
|
||||||
cached_index_chunk_id_ = 0;
|
cached_index_chunk_id_ = 0;
|
||||||
}
|
}
|
||||||
TargetBitmap result;
|
TargetBitmap result;
|
||||||
result.append(
|
result.append(
|
||||||
cached_index_chunk_res_, current_data_global_pos_, real_batch_size);
|
*cached_index_chunk_res_, current_data_global_pos_, real_batch_size);
|
||||||
MoveCursor();
|
MoveCursor();
|
||||||
return std::make_shared<ColumnVector>(std::move(result),
|
return std::make_shared<ColumnVector>(std::move(result),
|
||||||
TargetBitmap(real_batch_size, true));
|
TargetBitmap(real_batch_size, true));
|
||||||
@ -708,18 +707,17 @@ PhyJsonContainsFilterExpr::ExecJsonContainsArrayByKeyIndex() {
|
|||||||
};
|
};
|
||||||
bool is_growing = segment_->type() == SegmentType::Growing;
|
bool is_growing = segment_->type() == SegmentType::Growing;
|
||||||
bool is_strong_consistency = consistency_level_ == 0;
|
bool is_strong_consistency = consistency_level_ == 0;
|
||||||
cached_index_chunk_res_ = index
|
cached_index_chunk_res_ = std::make_shared<TargetBitmap>(
|
||||||
->FilterByPath(pointer,
|
std::move(index->FilterByPath(pointer,
|
||||||
active_count_,
|
active_count_,
|
||||||
is_growing,
|
is_growing,
|
||||||
is_strong_consistency,
|
is_strong_consistency,
|
||||||
filter_func)
|
filter_func)));
|
||||||
.clone();
|
|
||||||
cached_index_chunk_id_ = 0;
|
cached_index_chunk_id_ = 0;
|
||||||
}
|
}
|
||||||
TargetBitmap result;
|
TargetBitmap result;
|
||||||
result.append(
|
result.append(
|
||||||
cached_index_chunk_res_, current_data_global_pos_, real_batch_size);
|
*cached_index_chunk_res_, current_data_global_pos_, real_batch_size);
|
||||||
MoveCursor();
|
MoveCursor();
|
||||||
return std::make_shared<ColumnVector>(std::move(result),
|
return std::make_shared<ColumnVector>(std::move(result),
|
||||||
TargetBitmap(real_batch_size, true));
|
TargetBitmap(real_batch_size, true));
|
||||||
@ -1020,18 +1018,17 @@ PhyJsonContainsFilterExpr::ExecJsonContainsAllByKeyIndex() {
|
|||||||
};
|
};
|
||||||
bool is_growing = segment_->type() == SegmentType::Growing;
|
bool is_growing = segment_->type() == SegmentType::Growing;
|
||||||
bool is_strong_consistency = consistency_level_ == 0;
|
bool is_strong_consistency = consistency_level_ == 0;
|
||||||
cached_index_chunk_res_ = index
|
cached_index_chunk_res_ = std::make_shared<TargetBitmap>(
|
||||||
->FilterByPath(pointer,
|
std::move(index->FilterByPath(pointer,
|
||||||
active_count_,
|
active_count_,
|
||||||
is_growing,
|
is_growing,
|
||||||
is_strong_consistency,
|
is_strong_consistency,
|
||||||
filter_func)
|
filter_func)));
|
||||||
.clone();
|
|
||||||
cached_index_chunk_id_ = 0;
|
cached_index_chunk_id_ = 0;
|
||||||
}
|
}
|
||||||
TargetBitmap result;
|
TargetBitmap result;
|
||||||
result.append(
|
result.append(
|
||||||
cached_index_chunk_res_, current_data_global_pos_, real_batch_size);
|
*cached_index_chunk_res_, current_data_global_pos_, real_batch_size);
|
||||||
MoveCursor();
|
MoveCursor();
|
||||||
return std::make_shared<ColumnVector>(std::move(result),
|
return std::make_shared<ColumnVector>(std::move(result),
|
||||||
TargetBitmap(real_batch_size, true));
|
TargetBitmap(real_batch_size, true));
|
||||||
@ -1361,18 +1358,17 @@ PhyJsonContainsFilterExpr::ExecJsonContainsAllWithDiffTypeByKeyIndex() {
|
|||||||
};
|
};
|
||||||
bool is_growing = segment_->type() == SegmentType::Growing;
|
bool is_growing = segment_->type() == SegmentType::Growing;
|
||||||
bool is_strong_consistency = consistency_level_ == 0;
|
bool is_strong_consistency = consistency_level_ == 0;
|
||||||
cached_index_chunk_res_ = index
|
cached_index_chunk_res_ = std::make_shared<TargetBitmap>(
|
||||||
->FilterByPath(pointer,
|
std::move(index->FilterByPath(pointer,
|
||||||
active_count_,
|
active_count_,
|
||||||
is_growing,
|
is_growing,
|
||||||
is_strong_consistency,
|
is_strong_consistency,
|
||||||
filter_func)
|
filter_func)));
|
||||||
.clone();
|
|
||||||
cached_index_chunk_id_ = 0;
|
cached_index_chunk_id_ = 0;
|
||||||
}
|
}
|
||||||
TargetBitmap result;
|
TargetBitmap result;
|
||||||
result.append(
|
result.append(
|
||||||
cached_index_chunk_res_, current_data_global_pos_, real_batch_size);
|
*cached_index_chunk_res_, current_data_global_pos_, real_batch_size);
|
||||||
MoveCursor();
|
MoveCursor();
|
||||||
return std::make_shared<ColumnVector>(std::move(result),
|
return std::make_shared<ColumnVector>(std::move(result),
|
||||||
TargetBitmap(real_batch_size, true));
|
TargetBitmap(real_batch_size, true));
|
||||||
@ -1590,18 +1586,17 @@ PhyJsonContainsFilterExpr::ExecJsonContainsAllArrayByKeyIndex() {
|
|||||||
};
|
};
|
||||||
bool is_growing = segment_->type() == SegmentType::Growing;
|
bool is_growing = segment_->type() == SegmentType::Growing;
|
||||||
bool is_strong_consistency = consistency_level_ == 0;
|
bool is_strong_consistency = consistency_level_ == 0;
|
||||||
cached_index_chunk_res_ = index
|
cached_index_chunk_res_ = std::make_shared<TargetBitmap>(
|
||||||
->FilterByPath(pointer,
|
std::move(index->FilterByPath(pointer,
|
||||||
active_count_,
|
active_count_,
|
||||||
is_growing,
|
is_growing,
|
||||||
is_strong_consistency,
|
is_strong_consistency,
|
||||||
filter_func)
|
filter_func)));
|
||||||
.clone();
|
|
||||||
cached_index_chunk_id_ = 0;
|
cached_index_chunk_id_ = 0;
|
||||||
}
|
}
|
||||||
TargetBitmap result;
|
TargetBitmap result;
|
||||||
result.append(
|
result.append(
|
||||||
cached_index_chunk_res_, current_data_global_pos_, real_batch_size);
|
*cached_index_chunk_res_, current_data_global_pos_, real_batch_size);
|
||||||
MoveCursor();
|
MoveCursor();
|
||||||
return std::make_shared<ColumnVector>(std::move(result),
|
return std::make_shared<ColumnVector>(std::move(result),
|
||||||
TargetBitmap(real_batch_size, true));
|
TargetBitmap(real_batch_size, true));
|
||||||
@ -1907,18 +1902,17 @@ PhyJsonContainsFilterExpr::ExecJsonContainsWithDiffTypeByKeyIndex() {
|
|||||||
};
|
};
|
||||||
bool is_growing = segment_->type() == SegmentType::Growing;
|
bool is_growing = segment_->type() == SegmentType::Growing;
|
||||||
bool is_strong_consistency = consistency_level_ == 0;
|
bool is_strong_consistency = consistency_level_ == 0;
|
||||||
cached_index_chunk_res_ = index
|
cached_index_chunk_res_ = std::make_shared<TargetBitmap>(
|
||||||
->FilterByPath(pointer,
|
std::move(index->FilterByPath(pointer,
|
||||||
active_count_,
|
active_count_,
|
||||||
is_growing,
|
is_growing,
|
||||||
is_strong_consistency,
|
is_strong_consistency,
|
||||||
filter_func)
|
filter_func)));
|
||||||
.clone();
|
|
||||||
cached_index_chunk_id_ = 0;
|
cached_index_chunk_id_ = 0;
|
||||||
}
|
}
|
||||||
TargetBitmap result;
|
TargetBitmap result;
|
||||||
result.append(
|
result.append(
|
||||||
cached_index_chunk_res_, current_data_global_pos_, real_batch_size);
|
*cached_index_chunk_res_, current_data_global_pos_, real_batch_size);
|
||||||
MoveCursor();
|
MoveCursor();
|
||||||
return std::make_shared<ColumnVector>(std::move(result),
|
return std::make_shared<ColumnVector>(std::move(result),
|
||||||
TargetBitmap(real_batch_size, true));
|
TargetBitmap(real_batch_size, true));
|
||||||
|
|||||||
@ -693,19 +693,18 @@ PhyTermFilterExpr::ExecJsonInVariableByKeyIndex() {
|
|||||||
};
|
};
|
||||||
bool is_growing = segment_->type() == SegmentType::Growing;
|
bool is_growing = segment_->type() == SegmentType::Growing;
|
||||||
bool is_strong_consistency = consistency_level_ == 0;
|
bool is_strong_consistency = consistency_level_ == 0;
|
||||||
cached_index_chunk_res_ = index
|
cached_index_chunk_res_ = std::make_shared<TargetBitmap>(
|
||||||
->FilterByPath(pointer,
|
std::move(index->FilterByPath(pointer,
|
||||||
active_count_,
|
active_count_,
|
||||||
is_growing,
|
is_growing,
|
||||||
is_strong_consistency,
|
is_strong_consistency,
|
||||||
filter_func)
|
filter_func)));
|
||||||
.clone();
|
|
||||||
cached_index_chunk_id_ = 0;
|
cached_index_chunk_id_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
TargetBitmap result;
|
TargetBitmap result;
|
||||||
result.append(
|
result.append(
|
||||||
cached_index_chunk_res_, current_data_global_pos_, real_batch_size);
|
*cached_index_chunk_res_, current_data_global_pos_, real_batch_size);
|
||||||
MoveCursor();
|
MoveCursor();
|
||||||
return std::make_shared<ColumnVector>(std::move(result),
|
return std::make_shared<ColumnVector>(std::move(result),
|
||||||
TargetBitmap(real_batch_size, true));
|
TargetBitmap(real_batch_size, true));
|
||||||
|
|||||||
@ -19,6 +19,7 @@
|
|||||||
#include "common/EasyAssert.h"
|
#include "common/EasyAssert.h"
|
||||||
#include "common/Json.h"
|
#include "common/Json.h"
|
||||||
#include "common/Types.h"
|
#include "common/Types.h"
|
||||||
|
#include "exec/expression/ExprCache.h"
|
||||||
#include "common/type_c.h"
|
#include "common/type_c.h"
|
||||||
#include "log/Log.h"
|
#include "log/Log.h"
|
||||||
|
|
||||||
@ -1473,17 +1474,16 @@ PhyUnaryRangeFilterExpr::ExecRangeVisitorImplJsonForIndex() {
|
|||||||
};
|
};
|
||||||
bool is_growing = segment_->type() == SegmentType::Growing;
|
bool is_growing = segment_->type() == SegmentType::Growing;
|
||||||
bool is_strong_consistency = consistency_level_ == 0;
|
bool is_strong_consistency = consistency_level_ == 0;
|
||||||
cached_index_chunk_res_ = index
|
cached_index_chunk_res_ = std::make_shared<TargetBitmap>(
|
||||||
->FilterByPath(pointer,
|
std::move(index->FilterByPath(pointer,
|
||||||
active_count_,
|
active_count_,
|
||||||
is_growing,
|
is_growing,
|
||||||
is_strong_consistency,
|
is_strong_consistency,
|
||||||
filter_func)
|
filter_func)));
|
||||||
.clone();
|
|
||||||
}
|
}
|
||||||
TargetBitmap result;
|
TargetBitmap result;
|
||||||
result.append(
|
result.append(
|
||||||
cached_index_chunk_res_, current_data_global_pos_, real_batch_size);
|
*cached_index_chunk_res_, current_data_global_pos_, real_batch_size);
|
||||||
MoveCursor();
|
MoveCursor();
|
||||||
return std::make_shared<ColumnVector>(std::move(result),
|
return std::make_shared<ColumnVector>(std::move(result),
|
||||||
TargetBitmap(real_batch_size, true));
|
TargetBitmap(real_batch_size, true));
|
||||||
@ -1546,8 +1546,8 @@ PhyUnaryRangeFilterExpr::ExecRangeVisitorImplForPk(EvalCtx& context) {
|
|||||||
|
|
||||||
if (cached_index_chunk_id_ != 0) {
|
if (cached_index_chunk_id_ != 0) {
|
||||||
cached_index_chunk_id_ = 0;
|
cached_index_chunk_id_ = 0;
|
||||||
cached_index_chunk_res_.resize(active_count_);
|
cached_index_chunk_res_ = std::make_shared<TargetBitmap>(active_count_);
|
||||||
auto cache_view = cached_index_chunk_res_.view();
|
auto cache_view = cached_index_chunk_res_->view();
|
||||||
|
|
||||||
auto op_type = expr_->op_type_;
|
auto op_type = expr_->op_type_;
|
||||||
PkType pk = value_arg_.GetValue<IndexInnerType>();
|
PkType pk = value_arg_.GetValue<IndexInnerType>();
|
||||||
@ -1578,7 +1578,7 @@ PhyUnaryRangeFilterExpr::ExecRangeVisitorImplForPk(EvalCtx& context) {
|
|||||||
}
|
}
|
||||||
TargetBitmap result;
|
TargetBitmap result;
|
||||||
result.append(
|
result.append(
|
||||||
cached_index_chunk_res_, current_data_global_pos_, real_batch_size);
|
*cached_index_chunk_res_, current_data_global_pos_, real_batch_size);
|
||||||
MoveCursor();
|
MoveCursor();
|
||||||
return std::make_shared<ColumnVector>(std::move(result),
|
return std::make_shared<ColumnVector>(std::move(result),
|
||||||
TargetBitmap(real_batch_size, true));
|
TargetBitmap(real_batch_size, true));
|
||||||
@ -2005,6 +2005,25 @@ PhyUnaryRangeFilterExpr::ExecTextMatch() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto op_type = expr_->op_type_;
|
auto op_type = expr_->op_type_;
|
||||||
|
|
||||||
|
// Process-level LRU cache lookup by (segment_id, expr signature)
|
||||||
|
if (cached_match_res_ == nullptr &&
|
||||||
|
exec::ExprResCacheManager::IsEnabled() &&
|
||||||
|
segment_->type() == SegmentType::Sealed) {
|
||||||
|
exec::ExprResCacheManager::Key key{segment_->get_segment_id(),
|
||||||
|
this->ToString()};
|
||||||
|
exec::ExprResCacheManager::Value v;
|
||||||
|
if (exec::ExprResCacheManager::Instance().Get(key, v)) {
|
||||||
|
cached_match_res_ = v.result;
|
||||||
|
cached_index_chunk_valid_res_ = v.valid_result;
|
||||||
|
AssertInfo(cached_match_res_->size() == active_count_,
|
||||||
|
"internal error: expr res cache size {} not equal "
|
||||||
|
"expect active count {}",
|
||||||
|
cached_match_res_->size(),
|
||||||
|
active_count_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto func = [op_type, slop](Index* index,
|
auto func = [op_type, slop](Index* index,
|
||||||
const std::string& query) -> TargetBitmap {
|
const std::string& query) -> TargetBitmap {
|
||||||
if (op_type == proto::plan::OpType::TextMatch) {
|
if (op_type == proto::plan::OpType::TextMatch) {
|
||||||
@ -2028,13 +2047,26 @@ PhyUnaryRangeFilterExpr::ExecTextMatch() {
|
|||||||
auto res = std::move(func(index, query));
|
auto res = std::move(func(index, query));
|
||||||
auto valid_res = index->IsNotNull();
|
auto valid_res = index->IsNotNull();
|
||||||
cached_match_res_ = std::make_shared<TargetBitmap>(std::move(res));
|
cached_match_res_ = std::make_shared<TargetBitmap>(std::move(res));
|
||||||
cached_index_chunk_valid_res_ = std::move(valid_res);
|
cached_index_chunk_valid_res_ =
|
||||||
|
std::make_shared<TargetBitmap>(std::move(valid_res));
|
||||||
if (cached_match_res_->size() < active_count_) {
|
if (cached_match_res_->size() < active_count_) {
|
||||||
// some entities are not visible in inverted index.
|
// some entities are not visible in inverted index.
|
||||||
// only happend on growing segment.
|
// only happend on growing segment.
|
||||||
TargetBitmap tail(active_count_ - cached_match_res_->size());
|
TargetBitmap tail(active_count_ - cached_match_res_->size());
|
||||||
cached_match_res_->append(tail);
|
cached_match_res_->append(tail);
|
||||||
cached_index_chunk_valid_res_.append(tail);
|
cached_index_chunk_valid_res_->append(tail);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert into process-level cache
|
||||||
|
if (exec::ExprResCacheManager::IsEnabled() &&
|
||||||
|
segment_->type() == SegmentType::Sealed) {
|
||||||
|
exec::ExprResCacheManager::Key key{segment_->get_segment_id(),
|
||||||
|
this->ToString()};
|
||||||
|
exec::ExprResCacheManager::Value v;
|
||||||
|
v.result = cached_match_res_;
|
||||||
|
v.valid_result = cached_index_chunk_valid_res_;
|
||||||
|
v.active_count = active_count_;
|
||||||
|
exec::ExprResCacheManager::Instance().Put(key, v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2042,7 +2074,7 @@ PhyUnaryRangeFilterExpr::ExecTextMatch() {
|
|||||||
TargetBitmap valid_result;
|
TargetBitmap valid_result;
|
||||||
result.append(
|
result.append(
|
||||||
*cached_match_res_, current_data_global_pos_, real_batch_size);
|
*cached_match_res_, current_data_global_pos_, real_batch_size);
|
||||||
valid_result.append(cached_index_chunk_valid_res_,
|
valid_result.append(*cached_index_chunk_valid_res_,
|
||||||
current_data_global_pos_,
|
current_data_global_pos_,
|
||||||
real_batch_size);
|
real_batch_size);
|
||||||
MoveCursor();
|
MoveCursor();
|
||||||
@ -2100,17 +2132,17 @@ PhyUnaryRangeFilterExpr::ExecNgramMatch() {
|
|||||||
if (!res_opt.has_value()) {
|
if (!res_opt.has_value()) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
auto valid_res = index->IsNotNull();
|
|
||||||
cached_ngram_match_res_ =
|
cached_ngram_match_res_ =
|
||||||
std::make_shared<TargetBitmap>(std::move(res_opt.value()));
|
std::make_shared<TargetBitmap>(std::move(res_opt.value()));
|
||||||
cached_index_chunk_valid_res_ = std::move(valid_res);
|
cached_index_chunk_valid_res_ =
|
||||||
|
std::make_shared<TargetBitmap>(std::move(index->IsNotNull()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TargetBitmap result;
|
TargetBitmap result;
|
||||||
TargetBitmap valid_result;
|
TargetBitmap valid_result;
|
||||||
result.append(
|
result.append(
|
||||||
*cached_ngram_match_res_, current_data_global_pos_, real_batch_size);
|
*cached_ngram_match_res_, current_data_global_pos_, real_batch_size);
|
||||||
valid_result.append(cached_index_chunk_valid_res_,
|
valid_result.append(*cached_index_chunk_valid_res_,
|
||||||
current_data_global_pos_,
|
current_data_global_pos_,
|
||||||
real_batch_size);
|
real_batch_size);
|
||||||
MoveCursor();
|
MoveCursor();
|
||||||
|
|||||||
@ -690,7 +690,7 @@ BitmapIndex<T>::IsNull() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
const TargetBitmap
|
TargetBitmap
|
||||||
BitmapIndex<T>::IsNotNull() {
|
BitmapIndex<T>::IsNotNull() {
|
||||||
AssertInfo(is_built_, "index has not been built");
|
AssertInfo(is_built_, "index has not been built");
|
||||||
TargetBitmap res(total_num_rows_, true);
|
TargetBitmap res(total_num_rows_, true);
|
||||||
|
|||||||
@ -94,7 +94,7 @@ class BitmapIndex : public ScalarIndex<T> {
|
|||||||
const TargetBitmap
|
const TargetBitmap
|
||||||
IsNull() override;
|
IsNull() override;
|
||||||
|
|
||||||
const TargetBitmap
|
TargetBitmap
|
||||||
IsNotNull() override;
|
IsNotNull() override;
|
||||||
|
|
||||||
const TargetBitmap
|
const TargetBitmap
|
||||||
|
|||||||
@ -94,7 +94,7 @@ class HybridScalarIndex : public ScalarIndex<T> {
|
|||||||
return internal_index_->IsNull();
|
return internal_index_->IsNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
const TargetBitmap
|
TargetBitmap
|
||||||
IsNotNull() override {
|
IsNotNull() override {
|
||||||
return internal_index_->IsNotNull();
|
return internal_index_->IsNotNull();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -303,7 +303,7 @@ InvertedIndexTantivy<T>::IsNull() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
const TargetBitmap
|
TargetBitmap
|
||||||
InvertedIndexTantivy<T>::IsNotNull() {
|
InvertedIndexTantivy<T>::IsNotNull() {
|
||||||
int64_t count = Count();
|
int64_t count = Count();
|
||||||
TargetBitmap bitset(count, true);
|
TargetBitmap bitset(count, true);
|
||||||
|
|||||||
@ -147,7 +147,7 @@ class InvertedIndexTantivy : public ScalarIndex<T> {
|
|||||||
const TargetBitmap
|
const TargetBitmap
|
||||||
IsNull() override;
|
IsNull() override;
|
||||||
|
|
||||||
const TargetBitmap
|
TargetBitmap
|
||||||
IsNotNull() override;
|
IsNotNull() override;
|
||||||
|
|
||||||
const TargetBitmap
|
const TargetBitmap
|
||||||
|
|||||||
@ -53,7 +53,7 @@ class JsonFlatIndexQueryExecutor : public InvertedIndexTantivy<T> {
|
|||||||
return bitset;
|
return bitset;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TargetBitmap
|
TargetBitmap
|
||||||
IsNotNull() override {
|
IsNotNull() override {
|
||||||
TargetBitmap bitset(this->Count());
|
TargetBitmap bitset(this->Count());
|
||||||
this->wrapper_->json_exist_query(json_path_, &bitset);
|
this->wrapper_->json_exist_query(json_path_, &bitset);
|
||||||
|
|||||||
@ -70,7 +70,7 @@ JsonInvertedIndex<T>::build_index_for_json(
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
const TargetBitmap
|
TargetBitmap
|
||||||
JsonInvertedIndex<T>::Exists() {
|
JsonInvertedIndex<T>::Exists() {
|
||||||
int64_t count = this->Count();
|
int64_t count = this->Count();
|
||||||
TargetBitmap bitset(count, true);
|
TargetBitmap bitset(count, true);
|
||||||
@ -178,4 +178,4 @@ template class JsonInvertedIndex<int64_t>;
|
|||||||
template class JsonInvertedIndex<double>;
|
template class JsonInvertedIndex<double>;
|
||||||
template class JsonInvertedIndex<std::string>;
|
template class JsonInvertedIndex<std::string>;
|
||||||
|
|
||||||
} // namespace milvus::index
|
} // namespace milvus::index
|
||||||
|
|||||||
@ -167,7 +167,7 @@ class JsonInvertedIndex : public index::InvertedIndexTantivy<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns a bitmap indicating which rows have values that are indexed.
|
// Returns a bitmap indicating which rows have values that are indexed.
|
||||||
const TargetBitmap
|
TargetBitmap
|
||||||
Exists();
|
Exists();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@ -58,7 +58,7 @@ class JsonKeyStatsInvertedIndex : public InvertedIndexTantivy<std::string> {
|
|||||||
void
|
void
|
||||||
BuildWithFieldData(const std::vector<FieldDataPtr>& datas, bool nullable);
|
BuildWithFieldData(const std::vector<FieldDataPtr>& datas, bool nullable);
|
||||||
|
|
||||||
const TargetBitmap
|
TargetBitmap
|
||||||
FilterByPath(const std::string& path,
|
FilterByPath(const std::string& path,
|
||||||
int32_t row,
|
int32_t row,
|
||||||
bool is_growing,
|
bool is_growing,
|
||||||
|
|||||||
@ -89,7 +89,7 @@ class ScalarIndex : public IndexBase {
|
|||||||
virtual const TargetBitmap
|
virtual const TargetBitmap
|
||||||
IsNull() = 0;
|
IsNull() = 0;
|
||||||
|
|
||||||
virtual const TargetBitmap
|
virtual TargetBitmap
|
||||||
IsNotNull() = 0;
|
IsNotNull() = 0;
|
||||||
|
|
||||||
virtual const TargetBitmap
|
virtual const TargetBitmap
|
||||||
|
|||||||
@ -372,7 +372,7 @@ ScalarIndexSort<T>::IsNull() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
const TargetBitmap
|
TargetBitmap
|
||||||
ScalarIndexSort<T>::IsNotNull() {
|
ScalarIndexSort<T>::IsNotNull() {
|
||||||
AssertInfo(is_built_, "index has not been built");
|
AssertInfo(is_built_, "index has not been built");
|
||||||
TargetBitmap bitset(total_num_rows_, true);
|
TargetBitmap bitset(total_num_rows_, true);
|
||||||
|
|||||||
@ -91,7 +91,7 @@ class ScalarIndexSort : public ScalarIndex<T> {
|
|||||||
const TargetBitmap
|
const TargetBitmap
|
||||||
IsNull() override;
|
IsNull() override;
|
||||||
|
|
||||||
const TargetBitmap
|
TargetBitmap
|
||||||
IsNotNull() override;
|
IsNotNull() override;
|
||||||
|
|
||||||
const TargetBitmap
|
const TargetBitmap
|
||||||
|
|||||||
@ -303,7 +303,7 @@ StringIndexMarisa::ResetNull(TargetBitmap& bitset) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const TargetBitmap
|
TargetBitmap
|
||||||
StringIndexMarisa::IsNotNull() {
|
StringIndexMarisa::IsNotNull() {
|
||||||
TargetBitmap bitset(str_ids_.size());
|
TargetBitmap bitset(str_ids_.size());
|
||||||
for (size_t i = 0; i < bitset.size(); i++) {
|
for (size_t i = 0; i < bitset.size(); i++) {
|
||||||
|
|||||||
@ -74,7 +74,7 @@ class StringIndexMarisa : public StringIndex {
|
|||||||
const TargetBitmap
|
const TargetBitmap
|
||||||
IsNull() override;
|
IsNull() override;
|
||||||
|
|
||||||
const TargetBitmap
|
TargetBitmap
|
||||||
IsNotNull() override;
|
IsNotNull() override;
|
||||||
|
|
||||||
const TargetBitmap
|
const TargetBitmap
|
||||||
|
|||||||
@ -38,6 +38,7 @@
|
|||||||
#include "segcore/ChunkedSegmentSealedImpl.h"
|
#include "segcore/ChunkedSegmentSealedImpl.h"
|
||||||
#include "mmap/Types.h"
|
#include "mmap/Types.h"
|
||||||
#include "storage/RemoteChunkManagerSingleton.h"
|
#include "storage/RemoteChunkManagerSingleton.h"
|
||||||
|
#include "exec/expression/ExprCache.h"
|
||||||
|
|
||||||
////////////////////////////// common interfaces //////////////////////////////
|
////////////////////////////// common interfaces //////////////////////////////
|
||||||
CStatus
|
CStatus
|
||||||
@ -648,4 +649,16 @@ FinishLoad(CSegmentInterface c_segment) {
|
|||||||
} catch (std::exception& e) {
|
} catch (std::exception& e) {
|
||||||
return milvus::FailureCStatus(milvus::UnexpectedError, e.what());
|
return milvus::FailureCStatus(milvus::UnexpectedError, e.what());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CStatus
|
||||||
|
ExprResCacheEraseSegment(int64_t segment_id) {
|
||||||
|
SCOPE_CGO_CALL_METRIC();
|
||||||
|
|
||||||
|
try {
|
||||||
|
milvus::exec::ExprResCacheManager::Instance().EraseSegment(segment_id);
|
||||||
|
return milvus::SuccessCStatus();
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
return milvus::FailureCStatus(milvus::UnexpectedError, e.what());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -167,6 +167,9 @@ CreateTextIndex(CSegmentInterface c_segment, int64_t field_id);
|
|||||||
CStatus
|
CStatus
|
||||||
FinishLoad(CSegmentInterface c_segment);
|
FinishLoad(CSegmentInterface c_segment);
|
||||||
|
|
||||||
|
CStatus
|
||||||
|
ExprResCacheEraseSegment(int64_t segment_id);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -105,6 +105,7 @@ set(MILVUS_TEST_FILES
|
|||||||
test_chunked_column_group.cpp
|
test_chunked_column_group.cpp
|
||||||
test_group_chunk_translator.cpp
|
test_group_chunk_translator.cpp
|
||||||
test_chunked_segment_storage_v2.cpp
|
test_chunked_segment_storage_v2.cpp
|
||||||
|
test_expr_cache.cpp
|
||||||
test_thread_pool.cpp
|
test_thread_pool.cpp
|
||||||
test_json_flat_index.cpp
|
test_json_flat_index.cpp
|
||||||
test_vector_array.cpp
|
test_vector_array.cpp
|
||||||
|
|||||||
130
internal/core/unittest/test_expr_cache.cpp
Normal file
130
internal/core/unittest/test_expr_cache.cpp
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
// Licensed to the LF AI & Data foundation 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 <gtest/gtest.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "exec/expression/ExprCache.h"
|
||||||
|
#include "common/Types.h"
|
||||||
|
|
||||||
|
using milvus::exec::ExprResCacheManager;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
milvus::TargetBitmap
|
||||||
|
MakeBits(size_t n, bool v = true) {
|
||||||
|
milvus::TargetBitmap b(n);
|
||||||
|
if (v)
|
||||||
|
b.set();
|
||||||
|
else
|
||||||
|
b.reset();
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST(ExprResCacheManagerTest, PutGetBasic) {
|
||||||
|
auto& mgr = ExprResCacheManager::Instance();
|
||||||
|
ExprResCacheManager::SetEnabled(true);
|
||||||
|
mgr.Clear();
|
||||||
|
mgr.SetCapacityBytes(1ULL << 20); // 1MB
|
||||||
|
|
||||||
|
ExprResCacheManager::Key k{123, "expr:A"};
|
||||||
|
ExprResCacheManager::Value v;
|
||||||
|
v.result = std::make_shared<milvus::TargetBitmap>(MakeBits(128));
|
||||||
|
v.valid_result = std::make_shared<milvus::TargetBitmap>(MakeBits(128));
|
||||||
|
v.active_count = 128;
|
||||||
|
|
||||||
|
mgr.Put(k, v);
|
||||||
|
|
||||||
|
ExprResCacheManager::Value got;
|
||||||
|
ASSERT_TRUE(mgr.Get(k, got));
|
||||||
|
ASSERT_TRUE(got.result);
|
||||||
|
ASSERT_EQ(got.result->size(), 128);
|
||||||
|
ASSERT_TRUE(got.valid_result);
|
||||||
|
ASSERT_EQ(got.valid_result->size(), 128);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ExprResCacheManagerTest, LruEvictionByCapacity) {
|
||||||
|
auto& mgr = ExprResCacheManager::Instance();
|
||||||
|
ExprResCacheManager::SetEnabled(true);
|
||||||
|
mgr.Clear();
|
||||||
|
// roughly allow 2 entries
|
||||||
|
// (8192 / 8 * 2 + 32) * 2 = 4160
|
||||||
|
mgr.SetCapacityBytes(4300);
|
||||||
|
|
||||||
|
const size_t N = 8192; // bits
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
ExprResCacheManager::Key k{1, "expr:" + std::to_string(i)};
|
||||||
|
ExprResCacheManager::Value v;
|
||||||
|
v.result = std::make_shared<milvus::TargetBitmap>(MakeBits(N));
|
||||||
|
v.valid_result = std::make_shared<milvus::TargetBitmap>(MakeBits(N));
|
||||||
|
mgr.Put(k, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first one should be evicted by LRU
|
||||||
|
ExprResCacheManager::Value out;
|
||||||
|
ASSERT_FALSE(mgr.Get({1, "expr:0"}, out));
|
||||||
|
ASSERT_TRUE(mgr.Get({1, "expr:1"}, out));
|
||||||
|
ASSERT_TRUE(mgr.Get({1, "expr:2"}, out));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ExprResCacheManagerTest, EraseSegment) {
|
||||||
|
auto& mgr = ExprResCacheManager::Instance();
|
||||||
|
ExprResCacheManager::SetEnabled(true);
|
||||||
|
mgr.Clear();
|
||||||
|
mgr.SetCapacityBytes(1ULL << 20);
|
||||||
|
|
||||||
|
ExprResCacheManager::Key k1{10, "sig1"};
|
||||||
|
ExprResCacheManager::Key k2{10, "sig2"};
|
||||||
|
ExprResCacheManager::Key k3{11, "sig3"};
|
||||||
|
ExprResCacheManager::Value v;
|
||||||
|
v.result = std::make_shared<milvus::TargetBitmap>(MakeBits(64));
|
||||||
|
v.valid_result = std::make_shared<milvus::TargetBitmap>(MakeBits(64));
|
||||||
|
mgr.Put(k1, v);
|
||||||
|
mgr.Put(k2, v);
|
||||||
|
mgr.Put(k3, v);
|
||||||
|
|
||||||
|
size_t erased = mgr.EraseSegment(10);
|
||||||
|
ASSERT_EQ(erased, 2);
|
||||||
|
|
||||||
|
ExprResCacheManager::Value out;
|
||||||
|
ASSERT_FALSE(mgr.Get(k1, out));
|
||||||
|
ASSERT_FALSE(mgr.Get(k2, out));
|
||||||
|
ASSERT_TRUE(mgr.Get(k3, out));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ExprResCacheManagerTest, EnableDisable) {
|
||||||
|
auto& mgr = ExprResCacheManager::Instance();
|
||||||
|
mgr.Clear();
|
||||||
|
mgr.SetCapacityBytes(1ULL << 20);
|
||||||
|
|
||||||
|
ExprResCacheManager::SetEnabled(false);
|
||||||
|
ExprResCacheManager::Key k{7, "x"};
|
||||||
|
ExprResCacheManager::Value v;
|
||||||
|
v.result = std::make_shared<milvus::TargetBitmap>(MakeBits(32));
|
||||||
|
v.valid_result = std::make_shared<milvus::TargetBitmap>(MakeBits(32));
|
||||||
|
mgr.Put(k, v);
|
||||||
|
|
||||||
|
ExprResCacheManager::Value out;
|
||||||
|
// When disabled, Get should not hit
|
||||||
|
ASSERT_FALSE(mgr.Get(k, out));
|
||||||
|
|
||||||
|
ExprResCacheManager::SetEnabled(true);
|
||||||
|
mgr.Put(k, v);
|
||||||
|
ASSERT_TRUE(mgr.Get(k, out));
|
||||||
|
}
|
||||||
@ -23,6 +23,7 @@
|
|||||||
#include "expr/ITypeExpr.h"
|
#include "expr/ITypeExpr.h"
|
||||||
#include "segcore/segment_c.h"
|
#include "segcore/segment_c.h"
|
||||||
#include "test_utils/storage_test_utils.h"
|
#include "test_utils/storage_test_utils.h"
|
||||||
|
#include "exec/expression/ExprCache.h"
|
||||||
|
|
||||||
using namespace milvus;
|
using namespace milvus;
|
||||||
using namespace milvus::query;
|
using namespace milvus::query;
|
||||||
@ -1068,4 +1069,56 @@ TEST(TextMatch, ConcurrentReadWriteWithNull) {
|
|||||||
|
|
||||||
writer.join();
|
writer.join();
|
||||||
reader.join();
|
reader.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST(TextMatch, ExprResCacheSealed) {
|
||||||
|
using milvus::exec::ExprResCacheManager;
|
||||||
|
auto& mgr = ExprResCacheManager::Instance();
|
||||||
|
ExprResCacheManager::SetEnabled(true);
|
||||||
|
mgr.Clear();
|
||||||
|
mgr.SetCapacityBytes(1ULL << 20);
|
||||||
|
|
||||||
|
auto schema = GenTestSchema();
|
||||||
|
std::vector<std::string> raw_str = {"football, basketball, pingpang",
|
||||||
|
"swimming, football"};
|
||||||
|
|
||||||
|
int64_t N = 2;
|
||||||
|
uint64_t seed = 19190504;
|
||||||
|
auto raw_data = DataGen(schema, N, seed);
|
||||||
|
auto str_col = raw_data.raw_->mutable_fields_data()
|
||||||
|
->at(1)
|
||||||
|
.mutable_scalars()
|
||||||
|
->mutable_string_data()
|
||||||
|
->mutable_data();
|
||||||
|
for (int64_t i = 0; i < N; i++) {
|
||||||
|
str_col->at(i) = raw_str[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
auto seg = CreateSealedWithFieldDataLoaded(schema, raw_data);
|
||||||
|
seg->CreateTextIndex(FieldId(101));
|
||||||
|
|
||||||
|
ASSERT_EQ(mgr.GetEntryCount(), 0);
|
||||||
|
|
||||||
|
BitsetType final;
|
||||||
|
auto expr = GetMatchExpr(schema, "football", OpType::TextMatch);
|
||||||
|
final = ExecuteQueryExpr(expr, seg.get(), N, MAX_TIMESTAMP);
|
||||||
|
ASSERT_EQ(final.size(), N);
|
||||||
|
ASSERT_TRUE(final[0]);
|
||||||
|
ASSERT_TRUE(final[1]);
|
||||||
|
|
||||||
|
// Expect one cache entry inserted
|
||||||
|
ASSERT_EQ(mgr.GetEntryCount(), 1);
|
||||||
|
|
||||||
|
// Run again; should hit cache and not increase entries
|
||||||
|
final = ExecuteQueryExpr(expr, seg.get(), N, MAX_TIMESTAMP);
|
||||||
|
ASSERT_EQ(final.size(), N);
|
||||||
|
ASSERT_TRUE(final[0]);
|
||||||
|
ASSERT_TRUE(final[1]);
|
||||||
|
ASSERT_EQ(mgr.GetEntryCount(), 1);
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
mgr.Clear();
|
||||||
|
ExprResCacheManager::SetEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,6 +23,7 @@ package segments
|
|||||||
#include "segcore/collection_c.h"
|
#include "segcore/collection_c.h"
|
||||||
#include "segcore/plan_c.h"
|
#include "segcore/plan_c.h"
|
||||||
#include "segcore/reduce_c.h"
|
#include "segcore/reduce_c.h"
|
||||||
|
#include "common/init_c.h"
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
@ -1319,7 +1320,10 @@ func (s *LocalSegment) Release(ctx context.Context, opts ...releaseOption) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
usage := s.ResourceUsageEstimate()
|
if paramtable.Get().QueryNodeCfg.ExprResCacheEnabled.GetAsBool() {
|
||||||
|
// erase expr-cache for this segment before deleting C segment
|
||||||
|
C.ExprResCacheEraseSegment(C.int64_t(s.ID()))
|
||||||
|
}
|
||||||
|
|
||||||
GetDynamicPool().Submit(func() (any, error) {
|
GetDynamicPool().Submit(func() (any, error) {
|
||||||
C.DeleteSegment(ptr)
|
C.DeleteSegment(ptr)
|
||||||
@ -1327,6 +1331,7 @@ func (s *LocalSegment) Release(ctx context.Context, opts ...releaseOption) {
|
|||||||
}).Await()
|
}).Await()
|
||||||
|
|
||||||
// release reserved resource after the segment resource is really released.
|
// release reserved resource after the segment resource is really released.
|
||||||
|
usage := s.ResourceUsageEstimate()
|
||||||
s.manager.SubLogicalResource(usage)
|
s.manager.SubLogicalResource(usage)
|
||||||
|
|
||||||
log.Info("delete segment from memory")
|
log.Info("delete segment from memory")
|
||||||
|
|||||||
@ -312,6 +312,12 @@ func (node *QueryNode) InitSegcore() error {
|
|||||||
cEnableConfigParamTypeCheck := C.bool(paramtable.Get().CommonCfg.EnableConfigParamTypeCheck.GetAsBool())
|
cEnableConfigParamTypeCheck := C.bool(paramtable.Get().CommonCfg.EnableConfigParamTypeCheck.GetAsBool())
|
||||||
C.SetDefaultConfigParamTypeCheck(cEnableConfigParamTypeCheck)
|
C.SetDefaultConfigParamTypeCheck(cEnableConfigParamTypeCheck)
|
||||||
|
|
||||||
|
cExprResCacheEnabled := C.bool(paramtable.Get().QueryNodeCfg.ExprResCacheEnabled.GetAsBool())
|
||||||
|
C.SetExprResCacheEnable(cExprResCacheEnabled)
|
||||||
|
|
||||||
|
cExprResCacheCapacityBytes := C.int64_t(paramtable.Get().QueryNodeCfg.ExprResCacheCapacityBytes.GetAsInt64())
|
||||||
|
C.SetExprResCacheCapacityBytes(cExprResCacheCapacityBytes)
|
||||||
|
|
||||||
localDataRootPath := filepath.Join(paramtable.Get().LocalStorageCfg.Path.GetValue(), typeutil.QueryNodeRole)
|
localDataRootPath := filepath.Join(paramtable.Get().LocalStorageCfg.Path.GetValue(), typeutil.QueryNodeRole)
|
||||||
initcore.InitLocalChunkManager(localDataRootPath)
|
initcore.InitLocalChunkManager(localDataRootPath)
|
||||||
|
|
||||||
|
|||||||
@ -392,6 +392,24 @@ func SetupCoreConfigChangelCallback() {
|
|||||||
UpdateDefaultJSONKeyStatsCommitInterval(interval)
|
UpdateDefaultJSONKeyStatsCommitInterval(interval)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
paramtable.Get().QueryNodeCfg.ExprResCacheEnabled.RegisterCallback(func(ctx context.Context, key, oldValue, newValue string) error {
|
||||||
|
enable, err := strconv.ParseBool(newValue)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
UpdateExprResCacheEnable(enable)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
paramtable.Get().QueryNodeCfg.ExprResCacheCapacityBytes.RegisterCallback(func(ctx context.Context, key, oldValue, newValue string) error {
|
||||||
|
capacity, err := strconv.Atoi(newValue)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
UpdateExprResCacheCapacityBytes(capacity)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -64,6 +64,14 @@ func UpdateDefaultOptimizeExprEnable(enable bool) {
|
|||||||
C.SetDefaultOptimizeExprEnable(C.bool(enable))
|
C.SetDefaultOptimizeExprEnable(C.bool(enable))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UpdateExprResCacheEnable(enable bool) {
|
||||||
|
C.SetExprResCacheEnable(C.bool(enable))
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateExprResCacheCapacityBytes(capacity int) {
|
||||||
|
C.SetExprResCacheCapacityBytes(C.int64_t(capacity))
|
||||||
|
}
|
||||||
|
|
||||||
func UpdateDefaultJSONKeyStatsCommitInterval(interval int) {
|
func UpdateDefaultJSONKeyStatsCommitInterval(interval int) {
|
||||||
C.SetDefaultJSONKeyStatsCommitInterval(C.int64_t(interval))
|
C.SetDefaultJSONKeyStatsCommitInterval(C.int64_t(interval))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2988,6 +2988,9 @@ type queryNodeConfig struct {
|
|||||||
EnableWorkerSQCostMetrics ParamItem `refreshable:"true"`
|
EnableWorkerSQCostMetrics ParamItem `refreshable:"true"`
|
||||||
|
|
||||||
ExprEvalBatchSize ParamItem `refreshable:"false"`
|
ExprEvalBatchSize ParamItem `refreshable:"false"`
|
||||||
|
// expr cache
|
||||||
|
ExprResCacheEnabled ParamItem `refreshable:"false"`
|
||||||
|
ExprResCacheCapacityBytes ParamItem `refreshable:"false"`
|
||||||
|
|
||||||
// pipeline
|
// pipeline
|
||||||
CleanExcludeSegInterval ParamItem `refreshable:"false"`
|
CleanExcludeSegInterval ParamItem `refreshable:"false"`
|
||||||
@ -3975,6 +3978,27 @@ user-task-polling:
|
|||||||
}
|
}
|
||||||
p.ExprEvalBatchSize.Init(base.mgr)
|
p.ExprEvalBatchSize.Init(base.mgr)
|
||||||
|
|
||||||
|
// expr cache
|
||||||
|
p.ExprResCacheEnabled = ParamItem{
|
||||||
|
Key: "queryNode.exprCache.enabled",
|
||||||
|
FallbackKeys: []string{"enable_expr_cache"},
|
||||||
|
Version: "2.6.0",
|
||||||
|
DefaultValue: "false",
|
||||||
|
Doc: "enable expression result cache",
|
||||||
|
Export: true,
|
||||||
|
}
|
||||||
|
p.ExprResCacheEnabled.Init(base.mgr)
|
||||||
|
|
||||||
|
p.ExprResCacheCapacityBytes = ParamItem{
|
||||||
|
Key: "queryNode.exprCache.capacityBytes",
|
||||||
|
FallbackKeys: []string{"max_expr_cache_size"},
|
||||||
|
Version: "2.6.0",
|
||||||
|
DefaultValue: "268435456", // 256MB
|
||||||
|
Doc: "max capacity in bytes for expression result cache",
|
||||||
|
Export: true,
|
||||||
|
}
|
||||||
|
p.ExprResCacheCapacityBytes.Init(base.mgr)
|
||||||
|
|
||||||
p.JSONKeyStatsCommitInterval = ParamItem{
|
p.JSONKeyStatsCommitInterval = ParamItem{
|
||||||
Key: "queryNode.segcore.jsonKeyStatsCommitInterval",
|
Key: "queryNode.segcore.jsonKeyStatsCommitInterval",
|
||||||
Version: "2.5.0",
|
Version: "2.5.0",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user