Merge pull request #68710 from ClickHouse/backport/24.8/68477

Backport #68477 to 24.8: Drop query cache by tag
This commit is contained in:
Robert Schulze 2024-08-22 15:31:28 +02:00 committed by GitHub
commit 9fe11c4df8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 138 additions and 9 deletions

View File

@ -155,6 +155,8 @@ SELECT 1 SETTINGS use_query_cache = true, query_cache_tag = 'tag 1';
SELECT 1 SETTINGS use_query_cache = true, query_cache_tag = 'tag 2'; SELECT 1 SETTINGS use_query_cache = true, query_cache_tag = 'tag 2';
``` ```
To remove only entries with tag `tag` from the query cache, you can use statement `SYSTEM DROP QUERY CACHE TAG 'tag'`.
ClickHouse reads table data in blocks of [max_block_size](settings/settings.md#setting-max_block_size) rows. Due to filtering, aggregation, ClickHouse reads table data in blocks of [max_block_size](settings/settings.md#setting-max_block_size) rows. Due to filtering, aggregation,
etc., result blocks are typically much smaller than 'max_block_size' but there are also cases where they are much bigger. Setting etc., result blocks are typically much smaller than 'max_block_size' but there are also cases where they are much bigger. Setting
[query_cache_squash_partial_results](settings/settings.md#query-cache-squash-partial-results) (enabled by default) controls if result blocks [query_cache_squash_partial_results](settings/settings.md#query-cache-squash-partial-results) (enabled by default) controls if result blocks

View File

@ -136,7 +136,13 @@ The compiled expression cache is enabled/disabled with the query/user/profile-le
## DROP QUERY CACHE ## DROP QUERY CACHE
```sql
SYSTEM DROP QUERY CACHE;
SYSTEM DROP QUERY CACHE TAG '<tag>'
````
Clears the [query cache](../../operations/query-cache.md). Clears the [query cache](../../operations/query-cache.md).
If a tag is specified, only query cache entries with the specified tag are deleted.
## DROP FORMAT SCHEMA CACHE {#system-drop-schema-format} ## DROP FORMAT SCHEMA CACHE {#system-drop-schema-format}

View File

@ -197,6 +197,12 @@ public:
cache_policy->remove(key); cache_policy->remove(key);
} }
void remove(std::function<bool(const Key&, const MappedPtr &)> predicate)
{
std::lock_guard lock(mutex);
cache_policy->remove(predicate);
}
size_t sizeInBytes() const size_t sizeInBytes() const
{ {
std::lock_guard lock(mutex); std::lock_guard lock(mutex);

View File

@ -55,6 +55,7 @@ public:
virtual void set(const Key & key, const MappedPtr & mapped) = 0; virtual void set(const Key & key, const MappedPtr & mapped) = 0;
virtual void remove(const Key & key) = 0; virtual void remove(const Key & key) = 0;
virtual void remove(std::function<bool(const Key & key, const MappedPtr & mapped)> predicate) = 0;
virtual void clear() = 0; virtual void clear() = 0;
virtual std::vector<KeyMapped> dump() const = 0; virtual std::vector<KeyMapped> dump() const = 0;

View File

@ -79,6 +79,22 @@ public:
cells.erase(it); cells.erase(it);
} }
void remove(std::function<bool(const Key &, const MappedPtr &)> predicate) override
{
for (auto it = cells.begin(); it != cells.end();)
{
if (predicate(it->first, it->second.value))
{
Cell & cell = it->second;
current_size_in_bytes -= cell.size;
queue.erase(cell.queue_iterator);
it = cells.erase(it);
}
else
++it;
}
}
MappedPtr get(const Key & key) override MappedPtr get(const Key & key) override
{ {
auto it = cells.find(key); auto it = cells.find(key);

View File

@ -95,6 +95,27 @@ public:
cells.erase(it); cells.erase(it);
} }
void remove(std::function<bool(const Key &, const MappedPtr &)> predicate) override
{
for (auto it = cells.begin(); it != cells.end();)
{
if (predicate(it->first, it->second.value))
{
auto & cell = it->second;
current_size_in_bytes -= cell.size;
if (cell.is_protected)
current_protected_size -= cell.size;
auto & queue = cell.is_protected ? protected_queue : probationary_queue;
queue.erase(cell.queue_iterator);
it = cells.erase(it);
}
else
++it;
}
}
MappedPtr get(const Key & key) override MappedPtr get(const Key & key) override
{ {
auto it = cells.find(key); auto it = cells.find(key);

View File

@ -145,6 +145,23 @@ public:
size_in_bytes -= sz; size_in_bytes -= sz;
} }
void remove(std::function<bool(const Key &, const MappedPtr &)> predicate) override
{
for (auto it = cache.begin(); it != cache.end();)
{
if (predicate(it->first, it->second))
{
size_t sz = weight_function(*it->second);
if (it->first.user_id.has_value())
Base::user_quotas->decreaseActual(*it->first.user_id, sz);
it = cache.erase(it);
size_in_bytes -= sz;
}
else
++it;
}
}
MappedPtr get(const Key & key) override MappedPtr get(const Key & key) override
{ {
auto it = cache.find(key); auto it = cache.find(key);

View File

@ -619,9 +619,18 @@ QueryCache::Writer QueryCache::createWriter(const Key & key, std::chrono::millis
return Writer(cache, key, max_entry_size_in_bytes, max_entry_size_in_rows, min_query_runtime, squash_partial_results, max_block_size); return Writer(cache, key, max_entry_size_in_bytes, max_entry_size_in_rows, min_query_runtime, squash_partial_results, max_block_size);
} }
void QueryCache::clear() void QueryCache::clear(const std::optional<String> & tag)
{ {
cache.clear(); if (tag)
{
auto predicate = [tag](const Key & key, const Cache::MappedPtr &) { return key.tag == tag.value(); };
cache.remove(predicate);
}
else
{
cache.clear();
}
std::lock_guard lock(mutex); std::lock_guard lock(mutex);
times_executed.clear(); times_executed.clear();
} }

View File

@ -211,7 +211,7 @@ public:
Reader createReader(const Key & key); Reader createReader(const Key & key);
Writer createWriter(const Key & key, std::chrono::milliseconds min_query_runtime, bool squash_partial_results, size_t max_block_size, size_t max_query_cache_size_in_bytes_quota, size_t max_query_cache_entries_quota); Writer createWriter(const Key & key, std::chrono::milliseconds min_query_runtime, bool squash_partial_results, size_t max_block_size, size_t max_query_cache_size_in_bytes_quota, size_t max_query_cache_entries_quota);
void clear(); void clear(const std::optional<String> & tag);
size_t sizeInBytes() const; size_t sizeInBytes() const;
size_t count() const; size_t count() const;

View File

@ -3225,12 +3225,12 @@ QueryCachePtr Context::getQueryCache() const
return shared->query_cache; return shared->query_cache;
} }
void Context::clearQueryCache() const void Context::clearQueryCache(const std::optional<String> & tag) const
{ {
std::lock_guard lock(shared->mutex); std::lock_guard lock(shared->mutex);
if (shared->query_cache) if (shared->query_cache)
shared->query_cache->clear(); shared->query_cache->clear(tag);
} }
void Context::clearCaches() const void Context::clearCaches() const

View File

@ -1068,7 +1068,7 @@ public:
void setQueryCache(size_t max_size_in_bytes, size_t max_entries, size_t max_entry_size_in_bytes, size_t max_entry_size_in_rows); void setQueryCache(size_t max_size_in_bytes, size_t max_entries, size_t max_entry_size_in_bytes, size_t max_entry_size_in_rows);
void updateQueryCacheConfiguration(const Poco::Util::AbstractConfiguration & config); void updateQueryCacheConfiguration(const Poco::Util::AbstractConfiguration & config);
std::shared_ptr<QueryCache> getQueryCache() const; std::shared_ptr<QueryCache> getQueryCache() const;
void clearQueryCache() const; void clearQueryCache(const std::optional<String> & tag) const;
/** Clear the caches of the uncompressed blocks and marks. /** Clear the caches of the uncompressed blocks and marks.
* This is usually done when renaming tables, changing the type of columns, deleting a table. * This is usually done when renaming tables, changing the type of columns, deleting a table.

View File

@ -369,9 +369,12 @@ BlockIO InterpreterSystemQuery::execute()
system_context->clearMMappedFileCache(); system_context->clearMMappedFileCache();
break; break;
case Type::DROP_QUERY_CACHE: case Type::DROP_QUERY_CACHE:
{
getContext()->checkAccess(AccessType::SYSTEM_DROP_QUERY_CACHE); getContext()->checkAccess(AccessType::SYSTEM_DROP_QUERY_CACHE);
getContext()->clearQueryCache(); getContext()->clearQueryCache(query.query_cache_tag);
break; break;
}
case Type::DROP_COMPILED_EXPRESSION_CACHE: case Type::DROP_COMPILED_EXPRESSION_CACHE:
#if USE_EMBEDDED_COMPILER #if USE_EMBEDDED_COMPILER
getContext()->checkAccess(AccessType::SYSTEM_DROP_COMPILED_EXPRESSION_CACHE); getContext()->checkAccess(AccessType::SYSTEM_DROP_COMPILED_EXPRESSION_CACHE);

View File

@ -130,6 +130,8 @@ public:
String disk; String disk;
UInt64 seconds{}; UInt64 seconds{};
std::optional<String> query_cache_tag;
String filesystem_cache_name; String filesystem_cache_name;
std::string key_to_drop; std::string key_to_drop;
std::optional<size_t> offset_to_drop; std::optional<size_t> offset_to_drop;

View File

@ -470,6 +470,7 @@ namespace DB
MR_MACROS(TABLE_OVERRIDE, "TABLE OVERRIDE") \ MR_MACROS(TABLE_OVERRIDE, "TABLE OVERRIDE") \
MR_MACROS(TABLE, "TABLE") \ MR_MACROS(TABLE, "TABLE") \
MR_MACROS(TABLES, "TABLES") \ MR_MACROS(TABLES, "TABLES") \
MR_MACROS(TAG, "TAG") \
MR_MACROS(TAGS, "TAGS") \ MR_MACROS(TAGS, "TAGS") \
MR_MACROS(TAGS_INNER_UUID, "TAGS INNER UUID") \ MR_MACROS(TAGS_INNER_UUID, "TAGS INNER UUID") \
MR_MACROS(TEMPORARY_TABLE, "TEMPORARY TABLE") \ MR_MACROS(TEMPORARY_TABLE, "TEMPORARY TABLE") \

View File

@ -470,6 +470,16 @@ bool ParserSystemQuery::parseImpl(IParser::Pos & pos, ASTPtr & node, Expected &
res->seconds = seconds->as<ASTLiteral>()->value.safeGet<UInt64>(); res->seconds = seconds->as<ASTLiteral>()->value.safeGet<UInt64>();
break; break;
} }
case Type::DROP_QUERY_CACHE:
{
ParserLiteral tag_parser;
ASTPtr ast;
if (ParserKeyword{Keyword::TAG}.ignore(pos, expected) && tag_parser.parse(pos, ast, expected))
res->query_cache_tag = std::make_optional<String>(ast->as<ASTLiteral>()->value.safeGet<String>());
if (!parseQueryWithOnCluster(res, pos, expected))
return false;
break;
}
case Type::DROP_FILESYSTEM_CACHE: case Type::DROP_FILESYSTEM_CACHE:
{ {
ParserLiteral path_parser; ParserLiteral path_parser;

View File

@ -1,3 +1,17 @@
Cache query result in query cache
1 1
1 1
DROP entries with a certain tag, no entry will match
1
After a full DROP, the cache is empty now
0
Cache query result with different or no tag in query cache
1
1
1
2
4
DROP entries with certain tags
2
1
0 0

View File

@ -4,10 +4,31 @@
-- (it's silly to use what will be tested below but we have to assume other tests cluttered the query cache) -- (it's silly to use what will be tested below but we have to assume other tests cluttered the query cache)
SYSTEM DROP QUERY CACHE; SYSTEM DROP QUERY CACHE;
-- Cache query result in query cache SELECT 'Cache query result in query cache';
SELECT 1 SETTINGS use_query_cache = true; SELECT 1 SETTINGS use_query_cache = true;
SELECT count(*) FROM system.query_cache; SELECT count(*) FROM system.query_cache;
-- No query results are cached after DROP SELECT 'DROP entries with a certain tag, no entry will match';
SYSTEM DROP QUERY CACHE TAG 'tag';
SELECT count(*) FROM system.query_cache;
SELECT 'After a full DROP, the cache is empty now';
SYSTEM DROP QUERY CACHE; SYSTEM DROP QUERY CACHE;
SELECT count(*) FROM system.query_cache; SELECT count(*) FROM system.query_cache;
-- More tests for DROP with tags:
SELECT 'Cache query result with different or no tag in query cache';
SELECT 1 SETTINGS use_query_cache = true;
SELECT 1 SETTINGS use_query_cache = true, query_cache_tag = 'abc';
SELECT 1 SETTINGS use_query_cache = true, query_cache_tag = 'def';
SELECT 2 SETTINGS use_query_cache = true;
SELECT count(*) FROM system.query_cache;
SELECT 'DROP entries with certain tags';
SYSTEM DROP QUERY CACHE TAG '';
SELECT count(*) FROM system.query_cache;
SYSTEM DROP QUERY CACHE TAG 'def';
SELECT count(*) FROM system.query_cache;
SYSTEM DROP QUERY CACHE TAG 'abc';
SELECT count(*) FROM system.query_cache;