Files
scylladb/cql3/prepared_statements_cache.hh
Yaniv Michael Kaul b499dc8e9d cql3: extract prepared_cache_key_type into standalone lightweight header
Move prepared_cache_key_type class and its std::hash / fmt::formatter
specializations from prepared_statements_cache.hh into a new header
cql3/prepared_cache_key_type.hh.

The new header only depends on bytes.hh, utils/hash.hh, and
cql3/dialect.hh -- it does NOT include utils/loading_cache.hh.
This allows code that needs the cache key type (e.g. for function
signatures) without pulling in the expensive loading_cache template
machinery.

prepared_statements_cache.hh now includes prepared_cache_key_type.hh,
so existing includers are unaffected.

No functional change. Prepares for breaking the loading_cache include
chain from query_processor.hh.
2026-04-15 04:20:57 +03:00

135 lines
4.2 KiB
C++

/*
* Copyright (C) 2017-present ScyllaDB
*
* Modified by ScyllaDB
*/
/*
* SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.1
*/
#pragma once
#include "utils/loading_cache.hh"
#include "utils/hash.hh"
#include "cql3/prepared_cache_key_type.hh"
#include "cql3/statements/prepared_statement.hh"
#include "cql3/column_specification.hh"
#include "cql3/dialect.hh"
namespace cql3 {
using prepared_cache_entry = std::unique_ptr<statements::prepared_statement>;
struct prepared_cache_entry_size {
size_t operator()(const prepared_cache_entry& val) {
// TODO: improve the size approximation
return 10000;
}
};
class prepared_statements_cache {
public:
struct stats {
uint64_t prepared_cache_evictions = 0;
uint64_t privileged_entries_evictions_on_size = 0;
uint64_t unprivileged_entries_evictions_on_size = 0;
};
static stats& shard_stats() {
static thread_local stats _stats;
return _stats;
}
struct prepared_cache_stats_updater {
static void inc_hits() noexcept {}
static void inc_misses() noexcept {}
static void inc_blocks() noexcept {}
static void inc_evictions() noexcept {
++shard_stats().prepared_cache_evictions;
}
static void inc_privileged_on_cache_size_eviction() noexcept {
++shard_stats().privileged_entries_evictions_on_size;
}
static void inc_unprivileged_on_cache_size_eviction() noexcept {
++shard_stats().unprivileged_entries_evictions_on_size;
}
};
private:
using cache_key_type = typename prepared_cache_key_type::cache_key_type;
// Keep the entry in the "unprivileged" cache section till 2 hits because
// every prepared statement is accessed at least twice in the cache:
// 1) During PREPARE
// 2) During EXECUTE
//
// Therefore a typical "pollution" (when a cache entry is used only once) would involve
// 2 cache hits.
using cache_type = utils::loading_cache<cache_key_type, prepared_cache_entry, 2, utils::loading_cache_reload_enabled::no, prepared_cache_entry_size, std::hash<cache_key_type>, std::equal_to<cache_key_type>, prepared_cache_stats_updater, prepared_cache_stats_updater>;
using cache_value_ptr = typename cache_type::value_ptr;
using checked_weak_ptr = typename statements::prepared_statement::checked_weak_ptr;
public:
static const std::chrono::minutes entry_expiry;
using key_type = prepared_cache_key_type;
using pinned_value_type = cache_value_ptr;
using value_type = checked_weak_ptr;
using statement_is_too_big = typename cache_type::entry_is_too_big;
private:
cache_type _cache;
public:
prepared_statements_cache(logging::logger& logger, size_t size)
: _cache(size, entry_expiry, logger)
{}
template <typename LoadFunc>
future<pinned_value_type> get_pinned(const key_type& key, LoadFunc&& load) {
return _cache.get_ptr(key.key(), [load = std::forward<LoadFunc>(load)] (const cache_key_type&) { return load(); });
}
template <typename LoadFunc>
future<value_type> get(const key_type& key, LoadFunc&& load) {
return get_pinned(key, std::forward<LoadFunc>(load)).then([] (cache_value_ptr v_ptr) {
return make_ready_future<value_type>((*v_ptr)->checked_weak_from_this());
});
}
// "Touch" the corresponding cache entry in order to bump up its reference count.
void touch(const key_type& key) {
// loading_cache::find() returns a value_ptr object which constructor does the "thouching".
_cache.find(key.key());
}
value_type find(const key_type& key) {
cache_value_ptr vp = _cache.find(key.key());
if (vp) {
return (*vp)->checked_weak_from_this();
}
return value_type();
}
template <typename Pred>
requires std::is_invocable_r_v<bool, Pred, ::shared_ptr<cql_statement>>
void remove_if(Pred&& pred) {
_cache.remove_if([&pred] (const prepared_cache_entry& e) {
return pred(e->statement);
});
}
size_t size() const {
return _cache.size();
}
size_t memory_footprint() const {
return _cache.memory_footprint();
}
future<> stop() {
return _cache.stop();
}
};
}