From 6976c690bda83689766c6152ffd6c33c43c9fdbf Mon Sep 17 00:00:00 2001 From: Kruglov Pavel <48961922+Avogar@users.noreply.github.com> Date: Tue, 2 Aug 2022 10:09:18 +0200 Subject: [PATCH 01/43] Revert "Revert "Update arrow to fix possible data race"" --- contrib/arrow | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/arrow b/contrib/arrow index efdcd015cfd..3e03c6de41a 160000 --- a/contrib/arrow +++ b/contrib/arrow @@ -1 +1 @@ -Subproject commit efdcd015cfdee1b6aa349c9ca227ca12c3d697f5 +Subproject commit 3e03c6de41a86df2fc54a61e9be1abaefeff6b0e From 3be13e4f925fdca311e32830e4bd569e63dada7b Mon Sep 17 00:00:00 2001 From: avogar Date: Tue, 2 Aug 2022 08:25:46 +0000 Subject: [PATCH 02/43] Try fix build under ppc64 --- contrib/arrow | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/arrow b/contrib/arrow index 3e03c6de41a..611e4a63107 160000 --- a/contrib/arrow +++ b/contrib/arrow @@ -1 +1 @@ -Subproject commit 3e03c6de41a86df2fc54a61e9be1abaefeff6b0e +Subproject commit 611e4a631072d822074f6ea119a2b8d20c8760ca From 2d7de7f683abed08412a333447ca99c2372f9742 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Fri, 5 Aug 2022 14:42:40 +0300 Subject: [PATCH 03/43] Remove dictionaries from prometheus metrics on DETACH/DROP Fixes: #23436 (cc @kitaisreal) Introduced-in: #9622 (cc @YiuRULE) Signed-off-by: Azat Khuzhin --- src/Interpreters/ExternalLoader.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Interpreters/ExternalLoader.cpp b/src/Interpreters/ExternalLoader.cpp index 1dcf48da130..704dff325b7 100644 --- a/src/Interpreters/ExternalLoader.cpp +++ b/src/Interpreters/ExternalLoader.cpp @@ -1299,6 +1299,7 @@ scope_guard ExternalLoader::addConfigRepository(std::unique_ptrremoveConfigRepository(ptr); + CurrentStatusInfo::unset(CurrentStatusInfo::DictionaryStatus, name); reloadConfig(name); }; } From 479ea9e6a6de10be44b9ddb8765134e836fdec4c Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Fri, 5 Aug 2022 15:06:34 +0300 Subject: [PATCH 04/43] tests: enable prometheus exporter Signed-off-by: Azat Khuzhin --- tests/config/config.d/prometheus.xml | 6 ++++++ tests/config/install.sh | 1 + 2 files changed, 7 insertions(+) create mode 100644 tests/config/config.d/prometheus.xml diff --git a/tests/config/config.d/prometheus.xml b/tests/config/config.d/prometheus.xml new file mode 100644 index 00000000000..7f8dbd1601f --- /dev/null +++ b/tests/config/config.d/prometheus.xml @@ -0,0 +1,6 @@ + + + /metrics + 9988 + + diff --git a/tests/config/install.sh b/tests/config/install.sh index 478601620e1..af492bb18b4 100755 --- a/tests/config/install.sh +++ b/tests/config/install.sh @@ -35,6 +35,7 @@ ln -sf $SRC_PATH/config.d/logging_no_rotate.xml $DEST_SERVER_PATH/config.d/ ln -sf $SRC_PATH/config.d/merge_tree.xml $DEST_SERVER_PATH/config.d/ ln -sf $SRC_PATH/config.d/metadata_cache.xml $DEST_SERVER_PATH/config.d/ ln -sf $SRC_PATH/config.d/tcp_with_proxy.xml $DEST_SERVER_PATH/config.d/ +ln -sf $SRC_PATH/config.d/prometheus.xml $DEST_SERVER_PATH/config.d/ ln -sf $SRC_PATH/config.d/top_level_domains_lists.xml $DEST_SERVER_PATH/config.d/ ln -sf $SRC_PATH/config.d/top_level_domains_path.xml $DEST_SERVER_PATH/config.d/ ln -sf $SRC_PATH/config.d/transactions.xml $DEST_SERVER_PATH/config.d/ From 96429e293a429b7eef3499c7dc689d6391aebd57 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Fri, 5 Aug 2022 15:20:37 +0300 Subject: [PATCH 05/43] tests: cover ClickHouseStatusInfo_DictionaryStatus in prometheus metrics Signed-off-by: Azat Khuzhin --- ...HouseStatusInfo_DictionaryStatus.reference | 18 +++++++++ ...s_ClickHouseStatusInfo_DictionaryStatus.sh | 37 +++++++++++++++++++ tests/queries/shell_config.sh | 4 ++ 3 files changed, 59 insertions(+) create mode 100644 tests/queries/0_stateless/02390_prometheus_ClickHouseStatusInfo_DictionaryStatus.reference create mode 100755 tests/queries/0_stateless/02390_prometheus_ClickHouseStatusInfo_DictionaryStatus.sh diff --git a/tests/queries/0_stateless/02390_prometheus_ClickHouseStatusInfo_DictionaryStatus.reference b/tests/queries/0_stateless/02390_prometheus_ClickHouseStatusInfo_DictionaryStatus.reference new file mode 100644 index 00000000000..50c91c3fa0c --- /dev/null +++ b/tests/queries/0_stateless/02390_prometheus_ClickHouseStatusInfo_DictionaryStatus.reference @@ -0,0 +1,18 @@ +status before reload +status after reload +NOT_LOADED 0 +LOADED 0 +FAILED 1 +LOADING 0 +FAILED_AND_RELOADING 0 +LOADED_AND_RELOADING 0 +NOT_EXIST 0 +status after reload, table exists +NOT_LOADED 0 +LOADED 1 +FAILED 0 +LOADING 0 +FAILED_AND_RELOADING 0 +LOADED_AND_RELOADING 0 +NOT_EXIST 0 +status after drop diff --git a/tests/queries/0_stateless/02390_prometheus_ClickHouseStatusInfo_DictionaryStatus.sh b/tests/queries/0_stateless/02390_prometheus_ClickHouseStatusInfo_DictionaryStatus.sh new file mode 100755 index 00000000000..43f6d62bd10 --- /dev/null +++ b/tests/queries/0_stateless/02390_prometheus_ClickHouseStatusInfo_DictionaryStatus.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +CUR_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CUR_DIR"/../shell_config.sh + +function get_dictionary_status() +{ + local name=$1 && shift + $CLICKHOUSE_CURL -sS "$CLICKHOUSE_URL_PROMETHEUS" | { + awk -F'[{}=," ]' -vname="$name" '/ClickHouseStatusInfo_DictionaryStatus{/ && $(NF-3) == name { print $4, $NF }' + } +} + +$CLICKHOUSE_CLIENT -q "CREATE DICTIONARY dict (key Int, value String) PRIMARY KEY key SOURCE(CLICKHOUSE(TABLE data)) LAYOUT(HASHED()) LIFETIME(0)" +uuid="$($CLICKHOUSE_CLIENT -q "SELECT uuid FROM system.dictionaries WHERE database = '$CLICKHOUSE_DATABASE' AND name = 'dict'")" + +echo 'status before reload' +get_dictionary_status "$uuid" + +# source table does not exists +# NOTE: when dictionary does not exist it produce BAD_ARGUMENTS error, so using UNKNOWN_TABLE is safe +$CLICKHOUSE_CLIENT -n -q "SYSTEM RELOAD DICTIONARY dict -- { serverError UNKNOWN_TABLE }" +echo 'status after reload' +get_dictionary_status "$uuid" + +# create source +$CLICKHOUSE_CLIENT -q "CREATE TABLE data (key Int, value String) Engine=Null" +$CLICKHOUSE_CLIENT -q "SYSTEM RELOAD DICTIONARY dict" +echo 'status after reload, table exists' +get_dictionary_status "$uuid" + +# remove dictionary +$CLICKHOUSE_CLIENT -q "DROP DICTIONARY dict" +$CLICKHOUSE_CLIENT -q "DROP TABLE data" +echo 'status after drop' +get_dictionary_status "$uuid" diff --git a/tests/queries/shell_config.sh b/tests/queries/shell_config.sh index ab5d5ddc1b6..963ac384148 100644 --- a/tests/queries/shell_config.sh +++ b/tests/queries/shell_config.sh @@ -66,6 +66,8 @@ export CLICKHOUSE_PORT_TCP_WITH_PROXY=${CLICKHOUSE_PORT_TCP_WITH_PROXY:=$(${CLIC export CLICKHOUSE_PORT_TCP_WITH_PROXY=${CLICKHOUSE_PORT_TCP_WITH_PROXY:="9010"} export CLICKHOUSE_PORT_HTTP=${CLICKHOUSE_PORT_HTTP:=$(${CLICKHOUSE_EXTRACT_CONFIG} --key=http_port 2>/dev/null)} export CLICKHOUSE_PORT_HTTP=${CLICKHOUSE_PORT_HTTP:="8123"} +export CLICKHOUSE_PORT_PROMTHEUS_PORT=${CLICKHOUSE_PORT_PROMTHEUS_PORT:=$(${CLICKHOUSE_EXTRACT_CONFIG} --key=prometheus.port 2>/dev/null)} +export CLICKHOUSE_PORT_PROMTHEUS_PORT=${CLICKHOUSE_PORT_PROMTHEUS_PORT:="9988"} export CLICKHOUSE_PORT_HTTPS=${CLICKHOUSE_PORT_HTTPS:=$(${CLICKHOUSE_EXTRACT_CONFIG} --try --key=https_port 2>/dev/null)} 2>/dev/null export CLICKHOUSE_PORT_HTTPS=${CLICKHOUSE_PORT_HTTPS:="8443"} export CLICKHOUSE_PORT_HTTP_PROTO=${CLICKHOUSE_PORT_HTTP_PROTO:="http"} @@ -98,6 +100,8 @@ then export CLICKHOUSE_URL_HTTPS="${CLICKHOUSE_URL_HTTPS}?${CLICKHOUSE_URL_PARAMS}" fi +export CLICKHOUSE_URL_PROMETHEUS=${CLICKHOUSE_URL_PROMETHEUS:="${CLICKHOUSE_PORT_HTTP_PROTO}://${CLICKHOUSE_HOST}:${CLICKHOUSE_PORT_PROMTHEUS_PORT}/metrics"} + export CLICKHOUSE_PORT_INTERSERVER=${CLICKHOUSE_PORT_INTERSERVER:=$(${CLICKHOUSE_EXTRACT_CONFIG} --try --key=interserver_http_port 2>/dev/null)} 2>/dev/null export CLICKHOUSE_PORT_INTERSERVER=${CLICKHOUSE_PORT_INTERSERVER:="9009"} export CLICKHOUSE_URL_INTERSERVER=${CLICKHOUSE_URL_INTERSERVER:="${CLICKHOUSE_PORT_HTTP_PROTO}://${CLICKHOUSE_HOST}:${CLICKHOUSE_PORT_INTERSERVER}/"} From 791377e4dc0247b9dec39281779bde927b7e8424 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Sat, 6 Aug 2022 13:00:53 +0300 Subject: [PATCH 06/43] tests: avoid prometheus.port overlap for replicated database Signed-off-by: Azat Khuzhin --- docker/test/stateless/run.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker/test/stateless/run.sh b/docker/test/stateless/run.sh index 075f588cae3..23210e6a7c9 100755 --- a/docker/test/stateless/run.sh +++ b/docker/test/stateless/run.sh @@ -58,6 +58,7 @@ if [[ -n "$USE_DATABASE_REPLICATED" ]] && [[ "$USE_DATABASE_REPLICATED" -eq 1 ]] --tcp_port 19000 --tcp_port_secure 19440 --http_port 18123 --https_port 18443 --interserver_http_port 19009 --tcp_with_proxy_port 19010 \ --mysql_port 19004 --postgresql_port 19005 \ --keeper_server.tcp_port 19181 --keeper_server.server_id 2 \ + --prometheus.port 19988 \ --macros.replica r2 # It doesn't work :( mkdir -p /var/run/clickhouse-server2 @@ -69,6 +70,7 @@ if [[ -n "$USE_DATABASE_REPLICATED" ]] && [[ "$USE_DATABASE_REPLICATED" -eq 1 ]] --tcp_port 29000 --tcp_port_secure 29440 --http_port 28123 --https_port 28443 --interserver_http_port 29009 --tcp_with_proxy_port 29010 \ --mysql_port 29004 --postgresql_port 29005 \ --keeper_server.tcp_port 29181 --keeper_server.server_id 3 \ + --prometheus.port 29988 \ --macros.shard s2 # It doesn't work :( MAX_RUN_TIME=$((MAX_RUN_TIME < 9000 ? MAX_RUN_TIME : 9000)) # min(MAX_RUN_TIME, 2.5 hours) From 5cd3558d07044a7990428ed62da9a9074634f4f5 Mon Sep 17 00:00:00 2001 From: Antonio Andelic Date: Mon, 8 Aug 2022 14:10:13 +0000 Subject: [PATCH 07/43] Use different root path for counter test --- .../src/jepsen/clickhouse_keeper/counter.clj | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/jepsen.clickhouse-keeper/src/jepsen/clickhouse_keeper/counter.clj b/tests/jepsen.clickhouse-keeper/src/jepsen/clickhouse_keeper/counter.clj index f82d3f4c348..60b29bd799a 100644 --- a/tests/jepsen.clickhouse-keeper/src/jepsen/clickhouse_keeper/counter.clj +++ b/tests/jepsen.clickhouse-keeper/src/jepsen/clickhouse_keeper/counter.clj @@ -9,6 +9,7 @@ [zookeeper :as zk]) (:import (org.apache.zookeeper ZooKeeper KeeperException KeeperException$BadVersionException))) +(def root-path "/counter") (defn r [_ _] {:type :invoke, :f :read}) (defn add [_ _] {:type :invoke, :f :add, :value (rand-int 5)}) @@ -20,17 +21,19 @@ :conn (zk-connect node 9181 30000)) :nodename node)) - (setup! [this test]) + (setup! [this test] + (exec-with-retries 30 (fn [] + (zk-create-if-not-exists conn root-path "")))) (invoke! [this test op] (case (:f op) :read (exec-with-retries 30 (fn [] (assoc op :type :ok - :value (count (zk-list conn "/"))))) + :value (count (zk-list conn root-path))))) :add (try (do - (zk-multi-create-many-seq-nodes conn "/seq-" (:value op)) + (zk-multi-create-many-seq-nodes conn (concat-path root-path "seq-") (:value op)) (assoc op :type :ok)) (catch Exception _ (assoc op :type :info, :error :connect-error))))) From 4c102adaf342ba80c7b335b69e988e66a30cf792 Mon Sep 17 00:00:00 2001 From: avogar Date: Tue, 9 Aug 2022 11:39:40 +0000 Subject: [PATCH 08/43] Fix deadlock with msan --- contrib/arrow | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/arrow b/contrib/arrow index 611e4a63107..b41ff445294 160000 --- a/contrib/arrow +++ b/contrib/arrow @@ -1 +1 @@ -Subproject commit 611e4a631072d822074f6ea119a2b8d20c8760ca +Subproject commit b41ff4452944d50a44ad9c6e4621b50f44e9742e From 7c85b62e4ed0e7a701d78a88024e12120699643d Mon Sep 17 00:00:00 2001 From: vdimir Date: Mon, 8 Aug 2022 09:10:01 +0000 Subject: [PATCH 09/43] Add setting type number with 'auto' --- src/Core/SettingsFields.cpp | 72 ++++++++++++++++++++++++++++++++----- src/Core/SettingsFields.h | 42 ++++++++++++++++++++-- 2 files changed, 103 insertions(+), 11 deletions(-) diff --git a/src/Core/SettingsFields.cpp b/src/Core/SettingsFields.cpp index d77a510d7f9..e3bf44d33a7 100644 --- a/src/Core/SettingsFields.cpp +++ b/src/Core/SettingsFields.cpp @@ -156,29 +156,85 @@ template struct SettingFieldNumber; namespace { - UInt64 stringToMaxThreads(const String & str) + bool isAutoSetting(const String & str) + { + return startsWith(str, "auto"); + } + + bool isAutoSetting(const Field & f) + { + return f.getType() == Field::Types::String && isAutoSetting(f.get()); + } + + template + T stringToNumberWithAuto(const String & str) { if (startsWith(str, "auto")) return 0; - return parseFromString(str); + return parseFromString(str); } - UInt64 fieldToMaxThreads(const Field & f) + template + T fieldToNumberWithAuto(const Field & f) { if (f.getType() == Field::Types::String) - return stringToMaxThreads(f.get()); + return stringToNumberWithAuto(f.get()); else - return applyVisitor(FieldVisitorConvertToNumber(), f); + return applyVisitor(FieldVisitorConvertToNumber(), f); } } -SettingFieldMaxThreads::SettingFieldMaxThreads(const Field & f) : SettingFieldMaxThreads(fieldToMaxThreads(f)) +template +SettingFieldNumberWithAuto::SettingFieldNumberWithAuto(const Field & f) + : is_auto(isAutoSetting(f)), value(fieldToNumberWithAuto(f)) +{ +} + +template +SettingFieldNumberWithAuto & SettingFieldNumberWithAuto::operator=(const Field & f) +{ + is_auto = isAutoSetting(f); + value = fieldToNumberWithAuto(f); + return *this; +} + +template +String SettingFieldNumberWithAuto::toString() const +{ + if (is_auto) + return "auto"; + else + return ::DB::toString(value); +} + +template +void SettingFieldNumberWithAuto::parseFromString(const String & str) +{ + is_auto = isAutoSetting(str); + value = stringToNumberWithAuto(str); +} + +template +void SettingFieldNumberWithAuto::writeBinary(WriteBuffer & out) const +{ + writeStringBinary(toString(), out); +} + +template +void SettingFieldNumberWithAuto::readBinary(ReadBuffer & in) +{ + String str; + readStringBinary(str, in); + parseFromString(str); +} + +SettingFieldMaxThreads::SettingFieldMaxThreads(const Field & f) : SettingFieldMaxThreads(fieldToNumberWithAuto(f)) { } SettingFieldMaxThreads & SettingFieldMaxThreads::operator=(const Field & f) { - *this = fieldToMaxThreads(f); + *this = fieldToNumberWithAuto(f); return *this; } @@ -192,7 +248,7 @@ String SettingFieldMaxThreads::toString() const void SettingFieldMaxThreads::parseFromString(const String & str) { - *this = stringToMaxThreads(str); + *this = stringToNumberWithAuto(str); } void SettingFieldMaxThreads::writeBinary(WriteBuffer & out) const diff --git a/src/Core/SettingsFields.h b/src/Core/SettingsFields.h index 20f2b34084e..f7946368fc5 100644 --- a/src/Core/SettingsFields.h +++ b/src/Core/SettingsFields.h @@ -59,10 +59,46 @@ using SettingFieldFloat = SettingFieldNumber; using SettingFieldBool = SettingFieldNumber; -/** Unlike SettingFieldUInt64, supports the value of 'auto' - the number of processor cores without taking into account SMT. - * A value of 0 is also treated as auto. - * When serializing, `auto` is written in the same way as 0. +/** Like SettingFieldNumber, but also supports the value of 'auto'. + * Note: 0 and 'auto' are not equal. By default when 'auto' is set the value is set to 0. + * But if you need to distinguish between 0 and 'auto', you can use the 'is_auto' flag. + * Serialized as string. */ +template +struct SettingFieldNumberWithAuto +{ + using Type = T; + + bool is_auto; + T value; + bool changed = false; + + explicit SettingFieldNumberWithAuto() : is_auto(true), value(0) {} + explicit SettingFieldNumberWithAuto(T x) : is_auto(false), value(x) {} + explicit SettingFieldNumberWithAuto(const Field & f); + + SettingFieldNumberWithAuto & operator=(T x) { is_auto = false; value = x; changed = true; return *this; } + SettingFieldNumberWithAuto & operator=(const Field & f); + + operator T() const { return value; } /// NOLINT + explicit operator Field() const { return is_auto ? Field("auto") : Field(value); } + + String toString() const; + void parseFromString(const String & str); + + void writeBinary(WriteBuffer & out) const; + void readBinary(ReadBuffer & in); + + T valueOr(T default_value) const { return is_auto ? default_value : value; } +}; + +using SettingFieldUInt64WithAuto = SettingFieldNumberWithAuto; +using SettingFieldInt64WithAuto = SettingFieldNumberWithAuto; + +/* Similar to SettingFieldNumberWithAuto with small differences to behave like regular UInt64, supported to compatibility. + * When setting to 'auto' it becames equal to the number of processor cores without taking into account SMT. + * A value of 0 is also treated as 'auto', so 'auto' is parsed and serialized in the same way as 0. + */ struct SettingFieldMaxThreads { bool is_auto; From 3189c40e6d80478a0a91431f7dba37bc85c20427 Mon Sep 17 00:00:00 2001 From: vdimir Date: Tue, 9 Aug 2022 16:15:28 +0000 Subject: [PATCH 10/43] Implement SettingField with auto is a wrapper --- src/Core/SettingsFields.cpp | 75 +++++----------------------------- src/Core/SettingsFields.h | 81 +++++++++++++++++++++++++------------ 2 files changed, 66 insertions(+), 90 deletions(-) diff --git a/src/Core/SettingsFields.cpp b/src/Core/SettingsFields.cpp index e3bf44d33a7..86b20da9e8c 100644 --- a/src/Core/SettingsFields.cpp +++ b/src/Core/SettingsFields.cpp @@ -153,88 +153,35 @@ template struct SettingFieldNumber; template struct SettingFieldNumber; template struct SettingFieldNumber; +template struct SettingWithAuto>; +template struct SettingWithAuto>; +template struct SettingWithAuto>; namespace { - bool isAutoSetting(const String & str) - { - return startsWith(str, "auto"); - } - - bool isAutoSetting(const Field & f) - { - return f.getType() == Field::Types::String && isAutoSetting(f.get()); - } - - template - T stringToNumberWithAuto(const String & str) + UInt64 stringToMaxThreads(const String & str) { if (startsWith(str, "auto")) return 0; - return parseFromString(str); + return parseFromString(str); } - template - T fieldToNumberWithAuto(const Field & f) + UInt64 fieldToMaxThreads(const Field & f) { if (f.getType() == Field::Types::String) - return stringToNumberWithAuto(f.get()); + return stringToMaxThreads(f.get()); else - return applyVisitor(FieldVisitorConvertToNumber(), f); + return applyVisitor(FieldVisitorConvertToNumber(), f); } } -template -SettingFieldNumberWithAuto::SettingFieldNumberWithAuto(const Field & f) - : is_auto(isAutoSetting(f)), value(fieldToNumberWithAuto(f)) -{ -} - -template -SettingFieldNumberWithAuto & SettingFieldNumberWithAuto::operator=(const Field & f) -{ - is_auto = isAutoSetting(f); - value = fieldToNumberWithAuto(f); - return *this; -} - -template -String SettingFieldNumberWithAuto::toString() const -{ - if (is_auto) - return "auto"; - else - return ::DB::toString(value); -} - -template -void SettingFieldNumberWithAuto::parseFromString(const String & str) -{ - is_auto = isAutoSetting(str); - value = stringToNumberWithAuto(str); -} - -template -void SettingFieldNumberWithAuto::writeBinary(WriteBuffer & out) const -{ - writeStringBinary(toString(), out); -} - -template -void SettingFieldNumberWithAuto::readBinary(ReadBuffer & in) -{ - String str; - readStringBinary(str, in); - parseFromString(str); -} - -SettingFieldMaxThreads::SettingFieldMaxThreads(const Field & f) : SettingFieldMaxThreads(fieldToNumberWithAuto(f)) +SettingFieldMaxThreads::SettingFieldMaxThreads(const Field & f) : SettingFieldMaxThreads(fieldToMaxThreads(f)) { } SettingFieldMaxThreads & SettingFieldMaxThreads::operator=(const Field & f) { - *this = fieldToNumberWithAuto(f); + *this = fieldToMaxThreads(f); return *this; } @@ -248,7 +195,7 @@ String SettingFieldMaxThreads::toString() const void SettingFieldMaxThreads::parseFromString(const String & str) { - *this = stringToNumberWithAuto(str); + *this = stringToMaxThreads(str); } void SettingFieldMaxThreads::writeBinary(WriteBuffer & out) const diff --git a/src/Core/SettingsFields.h b/src/Core/SettingsFields.h index f7946368fc5..0fcc1d6783f 100644 --- a/src/Core/SettingsFields.h +++ b/src/Core/SettingsFields.h @@ -58,45 +58,74 @@ using SettingFieldInt64 = SettingFieldNumber; using SettingFieldFloat = SettingFieldNumber; using SettingFieldBool = SettingFieldNumber; - -/** Like SettingFieldNumber, but also supports the value of 'auto'. - * Note: 0 and 'auto' are not equal. By default when 'auto' is set the value is set to 0. - * But if you need to distinguish between 0 and 'auto', you can use the 'is_auto' flag. - * Serialized as string. +/** Wraps any SettingField to support special value 'auto' that can be checked with `is_auto` flag. + * Note about serialization: + * The new versions with `SettingsWriteFormat::STRINGS_WITH_FLAGS` serialize values as a string. + * In legacy SettingsWriteFormat mode, functions `read/writeBinary` would serialize values as a binary, and 'is_auto' would be ignored. + * It's possible to upgrade settings from regular type to wrapped ones and keep compatibility with old versions, + * but when serializing 'auto' old version will see binary representation of the default value. */ -template -struct SettingFieldNumberWithAuto +template +struct SettingWithAuto { - using Type = T; + constexpr static auto keyword = "auto"; + static bool isAuto(const Field & f) { return f.getType() == Field::Types::String && f.safeGet() == keyword; } + static bool isAuto(const String & str) { return str == keyword; } - bool is_auto; - T value; + using Type = typename Base::Type; + + Base base; + bool is_auto = false; bool changed = false; - explicit SettingFieldNumberWithAuto() : is_auto(true), value(0) {} - explicit SettingFieldNumberWithAuto(T x) : is_auto(false), value(x) {} - explicit SettingFieldNumberWithAuto(const Field & f); + explicit SettingWithAuto() : is_auto(true) {} + explicit SettingWithAuto(Type val) : is_auto(false) { base = Base(val); } - SettingFieldNumberWithAuto & operator=(T x) { is_auto = false; value = x; changed = true; return *this; } - SettingFieldNumberWithAuto & operator=(const Field & f); + explicit SettingWithAuto(const Field & f) + : is_auto(isAuto(f)) + { + if (!is_auto) + base = Base(f); + } - operator T() const { return value; } /// NOLINT - explicit operator Field() const { return is_auto ? Field("auto") : Field(value); } + SettingWithAuto & operator=(const Field & f) + { + changed = true; + if (is_auto = isAuto(f); !is_auto) + base = f; + return *this; + } - String toString() const; - void parseFromString(const String & str); + explicit operator Field() const { return is_auto ? Field(keyword) : Field(base); } - void writeBinary(WriteBuffer & out) const; - void readBinary(ReadBuffer & in); + String toString() const { return is_auto ? keyword : base.toString(); } - T valueOr(T default_value) const { return is_auto ? default_value : value; } + void parseFromString(const String & str) + { + changed = true; + if (is_auto = isAuto(str); !is_auto) + base.parseFromString(str); + } + + void writeBinary(WriteBuffer & out) const + { + if (is_auto) + Base().writeBinary(out); /// serialize default value + else + base.writeBinary(out); + } + + void readBinary(ReadBuffer & in) { changed = true; is_auto = false; base.readBinary(in); } + + Type valueOr(Type default_value) const { return is_auto ? default_value : base.value; } }; -using SettingFieldUInt64WithAuto = SettingFieldNumberWithAuto; -using SettingFieldInt64WithAuto = SettingFieldNumberWithAuto; +using SettingFieldUInt64WithAuto = SettingWithAuto; +using SettingFieldInt64WithAuto = SettingWithAuto; +using SettingFieldFloatWithAuto = SettingWithAuto; -/* Similar to SettingFieldNumberWithAuto with small differences to behave like regular UInt64, supported to compatibility. - * When setting to 'auto' it becames equal to the number of processor cores without taking into account SMT. +/* Similar to SettingFieldUInt64WithAuto with small differences to behave like regular UInt64, supported to compatibility. + * When setting to 'auto' it becomes equal to the number of processor cores without taking into account SMT. * A value of 0 is also treated as 'auto', so 'auto' is parsed and serialized in the same way as 0. */ struct SettingFieldMaxThreads From ae1db8386bf28563f993d3927411968f7f4f9455 Mon Sep 17 00:00:00 2001 From: vdimir Date: Tue, 9 Aug 2022 16:16:42 +0000 Subject: [PATCH 11/43] Change type of insert_quorum to UInt64WithAuto --- src/Core/Settings.h | 2 +- src/Storages/StorageReplicatedMergeTree.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Core/Settings.h b/src/Core/Settings.h index 92af0576129..727e45e3e50 100644 --- a/src/Core/Settings.h +++ b/src/Core/Settings.h @@ -212,7 +212,7 @@ static constexpr UInt64 operator""_GiB(unsigned long long value) \ M(Bool, insert_deduplicate, true, "For INSERT queries in the replicated table, specifies that deduplication of insertings blocks should be performed", 0) \ \ - M(UInt64, insert_quorum, 0, "For INSERT queries in the replicated table, wait writing for the specified number of replicas and linearize the addition of the data. 0 - disabled.", 0) \ + M(UInt64WithAuto, insert_quorum, 0, "For INSERT queries in the replicated table, wait writing for the specified number of replicas and linearize the addition of the data. 0 - disabled.", 0) \ M(Milliseconds, insert_quorum_timeout, 600000, "", 0) \ M(Bool, insert_quorum_parallel, true, "For quorum INSERT queries - enable to make parallel inserts without linearizability", 0) \ M(UInt64, select_sequential_consistency, 0, "For SELECT queries from the replicated table, throw an exception if the replica does not have a chunk written with the quorum; do not read the parts that have not yet been written with the quorum.", 0) \ diff --git a/src/Storages/StorageReplicatedMergeTree.cpp b/src/Storages/StorageReplicatedMergeTree.cpp index b4fc6b34c9e..3e5c1ce6982 100644 --- a/src/Storages/StorageReplicatedMergeTree.cpp +++ b/src/Storages/StorageReplicatedMergeTree.cpp @@ -4443,8 +4443,9 @@ SinkToStoragePtr StorageReplicatedMergeTree::write(const ASTPtr & /*query*/, con bool deduplicate = storage_settings_ptr->replicated_deduplication_window != 0 && query_settings.insert_deduplicate; // TODO: should we also somehow pass list of columns to deduplicate on to the ReplicatedMergeTreeSink? + // TODO: insert_quorum = 'auto' would be supported in https://github.com/ClickHouse/ClickHouse/pull/39970, now it's same as 0. return std::make_shared( - *this, metadata_snapshot, query_settings.insert_quorum, + *this, metadata_snapshot, query_settings.insert_quorum.valueOr(0), query_settings.insert_quorum_timeout.totalMilliseconds(), query_settings.max_partitions_per_insert_block, query_settings.insert_quorum_parallel, From 21ace403ab98f4fc3b221de2376d09deb9c1ffde Mon Sep 17 00:00:00 2001 From: vdimir Date: Tue, 9 Aug 2022 17:04:15 +0000 Subject: [PATCH 12/43] Add test setting_value_auto --- .../0_stateless/02381_setting_value_auto.reference | 4 ++++ tests/queries/0_stateless/02381_setting_value_auto.sql | 10 ++++++++++ 2 files changed, 14 insertions(+) create mode 100644 tests/queries/0_stateless/02381_setting_value_auto.reference create mode 100644 tests/queries/0_stateless/02381_setting_value_auto.sql diff --git a/tests/queries/0_stateless/02381_setting_value_auto.reference b/tests/queries/0_stateless/02381_setting_value_auto.reference new file mode 100644 index 00000000000..72c87cf6f7d --- /dev/null +++ b/tests/queries/0_stateless/02381_setting_value_auto.reference @@ -0,0 +1,4 @@ +0 0 UInt64WithAuto +auto 1 UInt64WithAuto +0 1 UInt64WithAuto +1 1 UInt64WithAuto diff --git a/tests/queries/0_stateless/02381_setting_value_auto.sql b/tests/queries/0_stateless/02381_setting_value_auto.sql new file mode 100644 index 00000000000..5b536a9d749 --- /dev/null +++ b/tests/queries/0_stateless/02381_setting_value_auto.sql @@ -0,0 +1,10 @@ +SELECT value, changed, type FROM system.settings WHERE name = 'insert_quorum'; + +SET insert_quorum = 'auto'; +SELECT value, changed, type FROM system.settings WHERE name = 'insert_quorum'; + +SET insert_quorum = 0; +SELECT value, changed, type FROM system.settings WHERE name = 'insert_quorum'; + +SET insert_quorum = 1; +SELECT value, changed, type FROM system.settings WHERE name = 'insert_quorum'; From 347ffbf178f6279fbfa2363525c1f8b638f4e6fe Mon Sep 17 00:00:00 2001 From: avogar Date: Wed, 10 Aug 2022 14:19:27 +0000 Subject: [PATCH 13/43] Fix special build --- contrib/arrow | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/arrow b/contrib/arrow index b41ff445294..450a5638704 160000 --- a/contrib/arrow +++ b/contrib/arrow @@ -1 +1 @@ -Subproject commit b41ff4452944d50a44ad9c6e4621b50f44e9742e +Subproject commit 450a5638704386356f8e520080468fc9bc8bcaf8 From 32c63f43a113a5c2b12c573faf91f3e59988cfd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Mar=C3=ADn?= Date: Wed, 10 Aug 2022 17:54:56 +0200 Subject: [PATCH 14/43] Don't visit the AST for UDFs if none are registered --- src/Interpreters/TreeRewriter.cpp | 44 ++++++++++--------- .../UserDefinedSQLFunctionFactory.cpp | 5 +++ .../UserDefinedSQLFunctionFactory.h | 3 ++ 3 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/Interpreters/TreeRewriter.cpp b/src/Interpreters/TreeRewriter.cpp index 39f3453c7c4..9248e8eecb6 100644 --- a/src/Interpreters/TreeRewriter.cpp +++ b/src/Interpreters/TreeRewriter.cpp @@ -6,29 +6,30 @@ #include #include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include +#include +#include #include /// getSmallestColumn() -#include -#include -#include -#include +#include +#include +#include +#include +#include #include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -1405,8 +1406,11 @@ TreeRewriterResultPtr TreeRewriter::analyze( void TreeRewriter::normalize( ASTPtr & query, Aliases & aliases, const NameSet & source_columns_set, bool ignore_alias, const Settings & settings, bool allow_self_aliases, ContextPtr context_) { - UserDefinedSQLFunctionVisitor::Data data_user_defined_functions_visitor; - UserDefinedSQLFunctionVisitor(data_user_defined_functions_visitor).visit(query); + if (!UserDefinedSQLFunctionFactory::instance().empty()) + { + UserDefinedSQLFunctionVisitor::Data data_user_defined_functions_visitor; + UserDefinedSQLFunctionVisitor(data_user_defined_functions_visitor).visit(query); + } CustomizeCountDistinctVisitor::Data data_count_distinct{settings.count_distinct_implementation}; CustomizeCountDistinctVisitor(data_count_distinct).visit(query); diff --git a/src/Interpreters/UserDefinedSQLFunctionFactory.cpp b/src/Interpreters/UserDefinedSQLFunctionFactory.cpp index 698faac5fab..db11ee12b03 100644 --- a/src/Interpreters/UserDefinedSQLFunctionFactory.cpp +++ b/src/Interpreters/UserDefinedSQLFunctionFactory.cpp @@ -160,4 +160,9 @@ std::vector UserDefinedSQLFunctionFactory::getAllRegisteredNames() return registered_names; } +bool UserDefinedSQLFunctionFactory::empty() const +{ + std::lock_guard lock(mutex); + return function_name_to_create_query.size() == 0; +} } diff --git a/src/Interpreters/UserDefinedSQLFunctionFactory.h b/src/Interpreters/UserDefinedSQLFunctionFactory.h index 63bf5d73c15..db43bb7298e 100644 --- a/src/Interpreters/UserDefinedSQLFunctionFactory.h +++ b/src/Interpreters/UserDefinedSQLFunctionFactory.h @@ -43,6 +43,9 @@ public: /// Get all user defined functions registered names. std::vector getAllRegisteredNames() const override; + /// Check whether any UDFs have been registered + bool empty() const; + private: std::unordered_map function_name_to_create_query; mutable std::mutex mutex; From 7f54fa726b8d6f2207bad8bdde4effed980e8eaa Mon Sep 17 00:00:00 2001 From: KinderRiven <1339764596@qq.com> Date: Tue, 14 Jun 2022 19:08:27 +0800 Subject: [PATCH 15/43] Decoupling cache functions and algorithms --- src/Common/FileSegment.h | 2 +- src/Common/IFileCachePriority.h | 92 ++++++++++++ src/Common/LRUFileCache.cpp | 169 ++++++++++++---------- src/Common/tests/gtest_lru_file_cache.cpp | 8 +- 4 files changed, 186 insertions(+), 85 deletions(-) create mode 100644 src/Common/IFileCachePriority.h diff --git a/src/Common/FileSegment.h b/src/Common/FileSegment.h index 93cbf269a8e..4404d0e14be 100644 --- a/src/Common/FileSegment.h +++ b/src/Common/FileSegment.h @@ -27,7 +27,7 @@ using FileSegments = std::list; class FileSegment : boost::noncopyable { -friend class LRUFileCache; +friend class FileCache; friend struct FileSegmentsHolder; friend class FileSegmentRangeWriter; diff --git a/src/Common/IFileCachePriority.h b/src/Common/IFileCachePriority.h new file mode 100644 index 00000000000..35b82d61228 --- /dev/null +++ b/src/Common/IFileCachePriority.h @@ -0,0 +1,92 @@ +#pragma once + +#include +#include +#include + +namespace DB +{ + +class IFileCachePriority; +using FileCachePriorityPtr = std::shared_ptr; + +/// IFileCachePriority is used to maintain the priority of cached data. +class IFileCachePriority +{ +public: + using Key = UInt128; + + class IIterator; + friend class IIterator; + using Iterator = std::shared_ptr; + + struct FileCacheRecord + { + Key key; + size_t offset; + size_t size; + size_t hits = 0; + + FileCacheRecord(const Key & key_, size_t offset_, size_t size_) : key(key_), offset(offset_), size(size_) { } + }; + + /// It provides an iterator to traverse the cache priority. Under normal circumstances, + /// the iterator can only return the records that have been directly swapped out. + /// For example, in the LRU algorithm, it can traverse all records, but in the LRU-K, it + /// can only traverse the records in the low priority queue. + class IIterator + { + public: + virtual ~IIterator() = default; + + virtual void next() = 0; + + virtual bool vaild() const = 0; + + /// Mark a cache record as recently used, it will update the priority + /// of the cache record according to different cache algorithms. + virtual void use(std::lock_guard & cache_lock) = 0; + + /// Deletes an existing cached record. + virtual void remove(std::lock_guard & cache_lock) = 0; + + virtual Key & key() const = 0; + + virtual size_t offset() const = 0; + + virtual size_t size() const = 0; + + virtual size_t hits() const = 0; + + virtual Iterator getSnapshot() = 0; + + virtual void incrementSize(size_t size_increment, std::lock_guard & cache_lock) = 0; + }; + +public: + virtual ~IFileCachePriority() = default; + + /// Add a cache record that did not exist before, and throw a + /// logical exception if the cache block already exists. + virtual Iterator add(const Key & key, size_t offset, size_t size, std::lock_guard & cache_lock) = 0; + + /// Query whether a cache record exists. If it exists, return true. If not, return false. + virtual bool contains(const Key & key, size_t offset, std::lock_guard & cache_lock) = 0; + + virtual void removeAll(std::lock_guard & cache_lock) = 0; + + /// Returns an iterator pointing to the lowest priority cached record. + /// We can traverse all cached records through the iterator's next(). + virtual Iterator getNewIterator(std::lock_guard & cache_lock) = 0; + + virtual size_t getElementsNum(std::lock_guard & cache_lock) const = 0; + + size_t getCacheSize(std::lock_guard &) const { return cache_size; } + + virtual std::string toString(std::lock_guard & cache_lock) const = 0; + +protected: + size_t max_cache_size = 0; + size_t cache_size = 0; +}; +}; diff --git a/src/Common/LRUFileCache.cpp b/src/Common/LRUFileCache.cpp index 6306b6de059..817208c6c30 100644 --- a/src/Common/LRUFileCache.cpp +++ b/src/Common/LRUFileCache.cpp @@ -24,6 +24,8 @@ namespace ErrorCodes LRUFileCache::LRUFileCache(const String & cache_base_path_, const FileCacheSettings & cache_settings_) : IFileCache(cache_base_path_, cache_settings_) + , main_priority(std::make_shared()) + , stash_priority(std::make_shared()) , max_stash_element_size(cache_settings_.max_elements) , enable_cache_hits_threshold(cache_settings_.enable_cache_hits_threshold) , log(&Poco::Logger::get("LRUFileCache")) @@ -31,7 +33,7 @@ LRUFileCache::LRUFileCache(const String & cache_base_path_, const FileCacheSetti { } -void LRUFileCache::initialize() +void FileCache::initialize() { std::lock_guard cache_lock(mutex); if (!is_initialized) @@ -55,7 +57,7 @@ void LRUFileCache::initialize() } } -void LRUFileCache::useCell( +void FileCache::useCell( const FileSegmentCell & cell, FileSegments & result, std::lock_guard & cache_lock) { auto file_segment = cell.file_segment; @@ -75,11 +77,11 @@ void LRUFileCache::useCell( if (cell.queue_iterator) { /// Move to the end of the queue. The iterator remains valid. - queue.moveToEnd(*cell.queue_iterator, cache_lock); + cell.queue_iterator->use(cache_lock); } } -LRUFileCache::FileSegmentCell * LRUFileCache::getCell( +FileCache::FileSegmentCell * FileCache::getCell( const Key & key, size_t offset, std::lock_guard & /* cache_lock */) { auto it = files.find(key); @@ -94,7 +96,7 @@ LRUFileCache::FileSegmentCell * LRUFileCache::getCell( return &cell_it->second; } -FileSegments LRUFileCache::getImpl( +FileSegments FileCache::getImpl( const Key & key, const FileSegment::Range & range, std::lock_guard & cache_lock) { /// Given range = [left, right] and non-overlapping ordered set of file segments, @@ -145,12 +147,8 @@ FileSegments LRUFileCache::getImpl( if (range.left <= prev_cell_range.right) { - /// segment{k-1} segment{k} /// [________] [_____ /// [___________ - /// ^ - /// range.left - useCell(prev_cell, result, cache_lock); } } @@ -204,7 +202,7 @@ FileSegments LRUFileCache::splitRangeIntoCells( return file_segments; } -void LRUFileCache::fillHolesWithEmptyFileSegments( +void FileCache::fillHolesWithEmptyFileSegments( FileSegments & file_segments, const Key & key, const FileSegment::Range & range, @@ -326,7 +324,7 @@ FileSegmentsHolder LRUFileCache::getOrSet(const Key & key, size_t offset, size_t return FileSegmentsHolder(std::move(file_segments)); } -FileSegmentsHolder LRUFileCache::get(const Key & key, size_t offset, size_t size) +FileSegmentsHolder FileCache::get(const Key & key, size_t offset, size_t size) { assertInitialized(); @@ -379,20 +377,19 @@ LRUFileCache::FileSegmentCell * LRUFileCache::addCell( FileSegment::State result_state = state; if (state == FileSegment::State::EMPTY && enable_cache_hits_threshold) { - auto record = records.find({key, offset}); + auto record = stash_records.find({key, offset}); - if (record == records.end()) + if (record == stash_records.end()) { - auto queue_iter = stash_queue.add(key, offset, 0, cache_lock); - records.insert({{key, offset}, queue_iter}); + auto priority_iter = stash_priority->add(key, offset, 0, cache_lock); + stash_records.insert({{key, offset}, priority_iter}); - if (stash_queue.getElementsNum(cache_lock) > max_stash_element_size) + if (stash_priority->getElementsNum(cache_lock) > max_stash_element_size) { - auto remove_queue_iter = stash_queue.begin(); - records.erase({remove_queue_iter->key, remove_queue_iter->offset}); - stash_queue.remove(remove_queue_iter, cache_lock); + auto remove_priority_iter = stash_priority->getNewIterator(cache_lock); + stash_records.erase({remove_priority_iter->key(), remove_priority_iter->offset()}); + remove_priority_iter->remove(cache_lock); } - /// For segments that do not reach the download threshold, we do not download them, but directly read them result_state = FileSegment::State::SKIP_CACHE; } @@ -452,7 +449,7 @@ FileSegmentsHolder LRUFileCache::setDownloading( return FileSegmentsHolder(std::move(file_segments)); } -bool LRUFileCache::tryReserve(const Key & key, size_t offset, size_t size, std::lock_guard & cache_lock) +bool FileCache::tryReserve(const Key & key, size_t offset, size_t size, std::lock_guard & cache_lock) { auto query_context = enable_filesystem_query_cache_limit ? getCurrentQueryContext(cache_lock) : nullptr; if (!query_context) @@ -473,40 +470,40 @@ bool LRUFileCache::tryReserve(const Key & key, size_t offset, size_t size, std:: else { size_t removed_size = 0; - size_t queue_size = queue.getElementsNum(cache_lock); + size_t queue_size = main_priority->getElementsNum(cache_lock); auto * cell_for_reserve = getCell(key, offset, cache_lock); - std::vector ghost; + std::vector ghost; std::vector trash; std::vector to_evict; auto is_overflow = [&] { - return (max_size != 0 && queue.getTotalCacheSize(cache_lock) + size - removed_size > max_size) + return (max_size != 0 && main_priority->getCacheSize(cache_lock) + size - removed_size > max_size) || (max_element_size != 0 && queue_size > max_element_size) || (query_context->getCacheSize() + size - removed_size > query_context->getMaxCacheSize()); }; /// Select the cache from the LRU queue held by query for expulsion. - for (auto iter = query_context->queue().begin(); iter != query_context->queue().end(); iter++) + for (auto iter = query_context->getPriority()->getNewIterator(cache_lock); iter->vaild(); iter->next()) { if (!is_overflow()) break; - auto * cell = getCell(iter->key, iter->offset, cache_lock); + auto * cell = getCell(iter->key(), iter->offset(), cache_lock); if (!cell) { /// The cache corresponding to this record may be swapped out by /// other queries, so it has become invalid. - ghost.push_back(iter); - removed_size += iter->size; + ghost.push_back(iter->getSnapshot()); + removed_size += iter->size(); } else { size_t cell_size = cell->size(); - assert(iter->size == cell_size); + assert(iter->size() == cell_size); if (cell->releasable()) { @@ -548,7 +545,7 @@ bool LRUFileCache::tryReserve(const Key & key, size_t offset, size_t size, std:: } for (auto & iter : ghost) - query_context->remove(iter->key, iter->offset, iter->size, cache_lock); + query_context->remove(iter->key(), iter->offset(), iter->size(), cache_lock); if (is_overflow()) return false; @@ -557,9 +554,9 @@ bool LRUFileCache::tryReserve(const Key & key, size_t offset, size_t size, std:: { auto queue_iterator = cell_for_reserve->queue_iterator; if (queue_iterator) - queue.incrementSize(*queue_iterator, size, cache_lock); + queue_iterator->incrementSize(size, cache_lock); else - cell_for_reserve->queue_iterator = queue.add(key, offset, size, cache_lock); + cell_for_reserve->queue_iterator = main_priority->add(key, offset, size, cache_lock); } for (auto & cell : to_evict) @@ -573,11 +570,11 @@ bool LRUFileCache::tryReserve(const Key & key, size_t offset, size_t size, std:: } } -bool LRUFileCache::tryReserveForMainList( +bool FileCache::tryReserveForMainList( const Key & key, size_t offset, size_t size, QueryContextPtr query_context, std::lock_guard & cache_lock) { auto removed_size = 0; - size_t queue_size = queue.getElementsNum(cache_lock); + size_t queue_size = main_priority->getElementsNum(cache_lock); assert(queue_size <= max_element_size); /// Since space reservation is incremental, cache cell already exists if it's state is EMPTY. @@ -592,15 +589,18 @@ bool LRUFileCache::tryReserveForMainList( auto is_overflow = [&] { /// max_size == 0 means unlimited cache size, max_element_size means unlimited number of cache elements. - return (max_size != 0 && queue.getTotalCacheSize(cache_lock) + size - removed_size > max_size) + return (max_size != 0 && main_priority->getCacheSize(cache_lock) + size - removed_size > max_size) || (max_element_size != 0 && queue_size > max_element_size); }; std::vector to_evict; std::vector trash; - for (const auto & [entry_key, entry_offset, entry_size, _] : queue) + for (auto it = main_priority->getNewIterator(cache_lock); it->vaild(); it->next()) { + auto entry_key = it->key(); + auto entry_offset = it->offset(); + if (!is_overflow()) break; @@ -612,7 +612,7 @@ bool LRUFileCache::tryReserveForMainList( key.toString(), offset); size_t cell_size = cell->size(); - assert(entry_size == cell_size); + assert(it->size() == cell_size); /// It is guaranteed that cell is not removed from cache as long as /// pointer to corresponding file segment is hold by any other thread. @@ -671,9 +671,9 @@ bool LRUFileCache::tryReserveForMainList( /// If queue iterator already exists, we need to update the size after each space reservation. auto queue_iterator = cell_for_reserve->queue_iterator; if (queue_iterator) - queue.incrementSize(*queue_iterator, size, cache_lock); + queue_iterator->incrementSize(size, cache_lock); else - cell_for_reserve->queue_iterator = queue.add(key, offset, size, cache_lock); + cell_for_reserve->queue_iterator = main_priority->add(key, offset, size, cache_lock); } for (auto & cell : to_evict) @@ -682,7 +682,7 @@ bool LRUFileCache::tryReserveForMainList( remove_file_segment(file_segment); } - if (queue.getTotalCacheSize(cache_lock) > (1ull << 63)) + if (main_priority->getCacheSize(cache_lock) > (1ull << 63)) throw Exception(ErrorCodes::LOGICAL_ERROR, "Cache became inconsistent. There must be a bug"); if (query_context) @@ -751,10 +751,12 @@ void LRUFileCache::removeIfReleasable(bool remove_persistent_files) std::lock_guard cache_lock(mutex); - std::vector to_remove; - for (auto it = queue.begin(); it != queue.end();) + std::vector to_remove; + for (auto it = main_priority->getNewIterator(cache_lock); it->vaild(); it->next()) { - const auto & [key, offset, size, _] = *it++; + auto key = it->key(); + auto offset = it->offset(); + auto * cell = getCell(key, offset, cache_lock); if (!cell) throw Exception( @@ -776,6 +778,13 @@ void LRUFileCache::removeIfReleasable(bool remove_persistent_files) } } + for (auto & file_segment : to_remove) + { + std::lock_guard segment_lock(file_segment->mutex); + file_segment->detach(cache_lock, segment_lock); + remove(file_segment->key(), file_segment->offset(), cache_lock, segment_lock); + } + /// Remove all access information. records.clear(); stash_queue.removeAll(cache_lock); @@ -785,7 +794,7 @@ void LRUFileCache::removeIfReleasable(bool remove_persistent_files) #endif } -void LRUFileCache::remove( +void FileCache::remove( Key key, size_t offset, std::lock_guard & cache_lock, std::lock_guard & /* segment_lock */) { @@ -799,7 +808,7 @@ void LRUFileCache::remove( if (cell->queue_iterator) { - queue.remove(*cell->queue_iterator, cache_lock); + cell->queue_iterator->remove(cache_lock); } auto & offsets = files[key]; @@ -831,12 +840,12 @@ void LRUFileCache::remove( } } -void LRUFileCache::loadCacheInfoIntoMemory(std::lock_guard & cache_lock) +void FileCache::loadCacheInfoIntoMemory(std::lock_guard & cache_lock) { Key key; UInt64 offset = 0; size_t size = 0; - std::vector>> queue_entries; + std::vector>> queue_entries; /// cache_base_path / key_prefix / key / offset @@ -888,7 +897,7 @@ void LRUFileCache::loadCacheInfoIntoMemory(std::lock_guard & cache_l { auto * cell = addCell(key, offset, size, FileSegment::State::DOWNLOADED, is_persistent, cache_lock); if (cell) - queue_entries.emplace_back(*cell->queue_iterator, cell->file_segment); + queue_entries.emplace_back(cell->queue_iterator, cell->file_segment); } else { @@ -912,14 +921,14 @@ void LRUFileCache::loadCacheInfoIntoMemory(std::lock_guard & cache_l if (file_segment.expired()) continue; - queue.moveToEnd(it, cache_lock); + it->use(cache_lock); } #ifndef NDEBUG assertCacheCorrectness(cache_lock); #endif } -void LRUFileCache::reduceSizeToDownloaded( +void FileCache::reduceSizeToDownloaded( const Key & key, size_t offset, std::lock_guard & cache_lock, std::lock_guard & /* segment_lock */) { @@ -952,7 +961,7 @@ void LRUFileCache::reduceSizeToDownloaded( cell->file_segment = std::make_shared(offset, downloaded_size, key, this, FileSegment::State::DOWNLOADED); } -bool LRUFileCache::isLastFileSegmentHolder( +bool FileCache::isLastFileSegmentHolder( const Key & key, size_t offset, std::lock_guard & cache_lock, std::lock_guard & /* segment_lock */) { @@ -965,7 +974,7 @@ bool LRUFileCache::isLastFileSegmentHolder( return cell->file_segment.use_count() == 2; } -FileSegments LRUFileCache::getSnapshot() const +FileSegments FileCache::getSnapshot() const { std::lock_guard cache_lock(mutex); @@ -979,7 +988,7 @@ FileSegments LRUFileCache::getSnapshot() const return file_segments; } -std::vector LRUFileCache::tryGetCachePaths(const Key & key) +std::vector FileCache::tryGetCachePaths(const Key & key) { std::lock_guard cache_lock(mutex); @@ -996,42 +1005,42 @@ std::vector LRUFileCache::tryGetCachePaths(const Key & key) return cache_paths; } -size_t LRUFileCache::getUsedCacheSize() const +size_t FileCache::getUsedCacheSize() const { std::lock_guard cache_lock(mutex); return getUsedCacheSizeUnlocked(cache_lock); } -size_t LRUFileCache::getUsedCacheSizeUnlocked(std::lock_guard & cache_lock) const +size_t FileCache::getUsedCacheSizeUnlocked(std::lock_guard & cache_lock) const { - return queue.getTotalCacheSize(cache_lock); + return main_priority->getCacheSize(cache_lock); } -size_t LRUFileCache::getAvailableCacheSize() const +size_t FileCache::getAvailableCacheSize() const { std::lock_guard cache_lock(mutex); return getAvailableCacheSizeUnlocked(cache_lock); } -size_t LRUFileCache::getAvailableCacheSizeUnlocked(std::lock_guard & cache_lock) const +size_t FileCache::getAvailableCacheSizeUnlocked(std::lock_guard & cache_lock) const { return max_size - getUsedCacheSizeUnlocked(cache_lock); } -size_t LRUFileCache::getFileSegmentsNum() const +size_t FileCache::getFileSegmentsNum() const { std::lock_guard cache_lock(mutex); return getFileSegmentsNumUnlocked(cache_lock); } -size_t LRUFileCache::getFileSegmentsNumUnlocked(std::lock_guard & cache_lock) const +size_t FileCache::getFileSegmentsNumUnlocked(std::lock_guard & cache_lock) const { - return queue.getElementsNum(cache_lock); + return main_priority->getElementsNum(cache_lock); } -LRUFileCache::FileSegmentCell::FileSegmentCell( +FileCache::FileSegmentCell::FileSegmentCell( FileSegmentPtr file_segment_, - LRUFileCache * cache, + FileCache * cache, std::lock_guard & cache_lock) : file_segment(file_segment_) { @@ -1045,7 +1054,7 @@ LRUFileCache::FileSegmentCell::FileSegmentCell( { case FileSegment::State::DOWNLOADED: { - queue_iterator = cache->queue.add(file_segment->key(), file_segment->offset(), file_segment->range().size(), cache_lock); + queue_iterator = cache->main_priority->add(file_segment->key(), file_segment->offset(), file_segment->range().size(), cache_lock); break; } case FileSegment::State::SKIP_CACHE: @@ -1133,7 +1142,7 @@ String LRUFileCache::dumpStructure(const Key & key) return dumpStructureUnlocked(key, cache_lock); } -String LRUFileCache::dumpStructureUnlocked(const Key & key, std::lock_guard & cache_lock) +String FileCache::dumpStructureUnlocked(const Key & key, std::lock_guard & cache_lock) { WriteBufferFromOwnString result; const auto & cells_by_offset = files[key]; @@ -1141,11 +1150,11 @@ String LRUFileCache::dumpStructureUnlocked(const Key & key, std::lock_guardgetInfoForLog() << "\n"; - result << "\n\nQueue: " << queue.toString(cache_lock); + result << "\n\nPriority: " << main_priority->toString(cache_lock); return result.str(); } -void LRUFileCache::assertCacheCellsCorrectness( +void FileCache::assertCacheCellsCorrectness( const FileSegmentsByOffset & cells_by_offset, [[maybe_unused]] std::lock_guard & cache_lock) { for (const auto & [_, cell] : cells_by_offset) @@ -1156,30 +1165,32 @@ void LRUFileCache::assertCacheCellsCorrectness( if (file_segment->reserved_size != 0) { assert(cell.queue_iterator); - assert(queue.contains(file_segment->key(), file_segment->offset(), cache_lock)); + assert(priority.contains(file_segment->key(), file_segment->offset(), cache_lock)); } } } -void LRUFileCache::assertCacheCorrectness(const Key & key, std::lock_guard & cache_lock) +void FileCache::assertCacheCorrectness(const Key & key, std::lock_guard & cache_lock) { assertCacheCellsCorrectness(files[key], cache_lock); - assertQueueCorrectness(cache_lock); + assertPriorityCorrectness(cache_lock); } -void LRUFileCache::assertCacheCorrectness(std::lock_guard & cache_lock) +void FileCache::assertCacheCorrectness(std::lock_guard & cache_lock) { for (const auto & [key, cells_by_offset] : files) assertCacheCellsCorrectness(files[key], cache_lock); - assertQueueCorrectness(cache_lock); + assertPriorityCorrectness(cache_lock); } -void LRUFileCache::assertQueueCorrectness(std::lock_guard & cache_lock) +void FileCache::assertPriorityCorrectness(std::lock_guard & cache_lock) { [[maybe_unused]] size_t total_size = 0; - for (auto it = queue.begin(); it != queue.end();) + for (auto it = main_priority->getNewIterator(cache_lock); it->vaild(); it->next()) { - auto & [key, offset, size, _] = *it++; + auto key = it->key(); + auto offset = it->offset(); + auto size = it->size(); auto * cell = getCell(key, offset, cache_lock); if (!cell) @@ -1188,14 +1199,12 @@ void LRUFileCache::assertQueueCorrectness(std::lock_guard & cache_lo ErrorCodes::LOGICAL_ERROR, "Cache is in inconsistent state: LRU queue contains entries with no cache cell (assertCorrectness())"); } - assert(cell->size() == size); total_size += size; } - - assert(total_size == queue.getTotalCacheSize(cache_lock)); - assert(queue.getTotalCacheSize(cache_lock) <= max_size); - assert(queue.getElementsNum(cache_lock) <= max_element_size); + assert(total_size == main_priority->getCacheSize(cache_lock)); + assert(main_priority->getCacheSize(cache_lock) <= max_size); + assert(main_priority->getElementsNum(cache_lock) <= max_element_size); } } diff --git a/src/Common/tests/gtest_lru_file_cache.cpp b/src/Common/tests/gtest_lru_file_cache.cpp index 2f268e217df..8e7554f0418 100644 --- a/src/Common/tests/gtest_lru_file_cache.cpp +++ b/src/Common/tests/gtest_lru_file_cache.cpp @@ -85,7 +85,7 @@ void complete(const DB::FileSegmentsHolder & holder) } -TEST(LRUFileCache, get) +TEST(FileCache, get) { if (fs::exists(cache_base_path)) fs::remove_all(cache_base_path); @@ -103,7 +103,7 @@ TEST(LRUFileCache, get) DB::FileCacheSettings settings; settings.max_size = 30; settings.max_elements = 5; - auto cache = DB::LRUFileCache(cache_base_path, settings); + auto cache = DB::FileCache(cache_base_path, settings); cache.initialize(); auto key = cache.hash("key1"); @@ -479,7 +479,7 @@ TEST(LRUFileCache, get) { /// Test LRUCache::restore(). - auto cache2 = DB::LRUFileCache(cache_base_path, settings); + auto cache2 = DB::FileCache(cache_base_path, settings); cache2.initialize(); auto holder1 = cache2.getOrSet(key, 2, 28, false); /// Get [2, 29] @@ -499,7 +499,7 @@ TEST(LRUFileCache, get) auto settings2 = settings; settings2.max_file_segment_size = 10; - auto cache2 = DB::LRUFileCache(caches_dir / "cache2", settings2); + auto cache2 = DB::FileCache(caches_dir / "cache2", settings2); cache2.initialize(); auto holder1 = cache2.getOrSet(key, 0, 25, false); /// Get [0, 24] From ffaf44c1c1838fda438c4da374d2427e6576b0ff Mon Sep 17 00:00:00 2001 From: KinderRiven <1339764596@qq.com> Date: Tue, 14 Jun 2022 20:32:30 +0800 Subject: [PATCH 16/43] fix style --- src/Common/FileCache.h | 390 ++++++++++++++++++++++++++++++++ src/Common/IFileCachePriority.h | 2 +- src/Common/LRUFileCache.cpp | 8 +- 3 files changed, 395 insertions(+), 5 deletions(-) create mode 100644 src/Common/FileCache.h diff --git a/src/Common/FileCache.h b/src/Common/FileCache.h new file mode 100644 index 00000000000..13bca0e2dae --- /dev/null +++ b/src/Common/FileCache.h @@ -0,0 +1,390 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "FileCache_fwd.h" +#include +#include +#include +#include +#include +#include + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int LOGICAL_ERROR; +} + +class IFileCache; +using FileCachePtr = std::shared_ptr; + +/** + * Local cache for remote filesystem files, represented as a set of non-overlapping non-empty file segments. + */ +class IFileCache : private boost::noncopyable +{ +friend class FileSegment; +friend struct FileSegmentsHolder; +friend class FileSegmentRangeWriter; + +public: + using Key = UInt128; + using Downloader = std::unique_ptr; + + IFileCache( + const String & cache_base_path_, + const FileCacheSettings & cache_settings_); + + virtual ~IFileCache() = default; + + /// Restore cache from local filesystem. + virtual void initialize() = 0; + + virtual void remove(const Key & key) = 0; + + virtual void remove() = 0; + + static bool isReadOnly(); + + /// Cache capacity in bytes. + size_t capacity() const { return max_size; } + + static Key hash(const String & path); + + String getPathInLocalCache(const Key & key, size_t offset); + + String getPathInLocalCache(const Key & key); + + const String & getBasePath() const { return cache_base_path; } + + virtual std::vector tryGetCachePaths(const Key & key) = 0; + + /** + * Given an `offset` and `size` representing [offset, offset + size) bytes interval, + * return list of cached non-overlapping non-empty + * file segments `[segment1, ..., segmentN]` which intersect with given interval. + * + * Segments in returned list are ordered in ascending order and represent a full contiguous + * interval (no holes). Each segment in returned list has state: DOWNLOADED, DOWNLOADING or EMPTY. + * + * As long as pointers to returned file segments are hold + * it is guaranteed that these file segments are not removed from cache. + */ + virtual FileSegmentsHolder getOrSet(const Key & key, size_t offset, size_t size) = 0; + + /** + * Segments in returned list are ordered in ascending order and represent a full contiguous + * interval (no holes). Each segment in returned list has state: DOWNLOADED, DOWNLOADING or EMPTY. + * + * If file segment has state EMPTY, then it is also marked as "detached". E.g. it is "detached" + * from cache (not owned by cache), and as a result will never change it's state and will be destructed + * with the destruction of the holder, while in getOrSet() EMPTY file segments can eventually change + * it's state (and become DOWNLOADED). + */ + virtual FileSegmentsHolder get(const Key & key, size_t offset, size_t size) = 0; + + virtual FileSegmentsHolder setDownloading(const Key & key, size_t offset, size_t size) = 0; + + virtual FileSegments getSnapshot() const = 0; + + /// For debug. + virtual String dumpStructure(const Key & key) = 0; + + virtual size_t getUsedCacheSize() const = 0; + + virtual size_t getFileSegmentsNum() const = 0; + +protected: + String cache_base_path; + size_t max_size; + size_t max_element_size; + size_t max_file_segment_size; + + bool is_initialized = false; + + mutable std::mutex mutex; + + virtual bool tryReserve( + const Key & key, size_t offset, size_t size, + std::lock_guard & cache_lock) = 0; + + virtual void remove( + Key key, size_t offset, + std::lock_guard & cache_lock, + std::lock_guard & segment_lock) = 0; + + virtual bool isLastFileSegmentHolder( + const Key & key, size_t offset, + std::lock_guard & cache_lock, + std::lock_guard & segment_lock) = 0; + + /// If file segment was partially downloaded and then space reservation fails (because of no + /// space left), then update corresponding cache cell metadata (file segment size). + virtual void reduceSizeToDownloaded( + const Key & key, size_t offset, + std::lock_guard & cache_lock, + std::lock_guard & segment_lock) = 0; + + void assertInitialized() const; + +protected: + using KeyAndOffset = std::pair; + + struct KeyAndOffsetHash + { + std::size_t operator()(const KeyAndOffset & key) const + { + return std::hash()(key.first) ^ std::hash()(key.second); + } + }; + + using FileCacheRecords = std::unordered_map; + + /// Used to track and control the cache access of each query. + /// Through it, we can realize the processing of different queries by the cache layer. + struct QueryContext + { + FileCacheRecords records; + FileCachePriorityPtr priority; + + size_t cache_size = 0; + size_t max_cache_size; + + bool skip_download_if_exceeds_query_cache; + + QueryContext(size_t max_cache_size_, bool skip_download_if_exceeds_query_cache_) + : priority(std::make_shared()) + , max_cache_size(max_cache_size_) + , skip_download_if_exceeds_query_cache(skip_download_if_exceeds_query_cache_) {} + + void remove(const Key & key, size_t offset, size_t size, std::lock_guard & cache_lock) + { + if (cache_size < size) + throw Exception(ErrorCodes::LOGICAL_ERROR, "Deleted cache size exceeds existing cache size"); + + if (!skip_download_if_exceeds_query_cache) + { + auto record = records.find({key, offset}); + if (record != records.end()) + { + record->second->remove(cache_lock); + records.erase({key, offset}); + } + } + cache_size -= size; + } + + void reserve(const Key & key, size_t offset, size_t size, std::lock_guard & cache_lock) + { + if (cache_size + size > max_cache_size) + throw Exception(ErrorCodes::LOGICAL_ERROR, "Reserved cache size exceeds the remaining cache size"); + + if (!skip_download_if_exceeds_query_cache) + { + auto record = records.find({key, offset}); + if (record == records.end()) + { + auto queue_iter = priority->add(key, offset, 0, cache_lock); + record = records.insert({{key, offset}, queue_iter}).first; + } + record->second->incrementSize(size, cache_lock); + } + cache_size += size; + } + + void use(const Key & key, size_t offset, std::lock_guard & cache_lock) + { + if (!skip_download_if_exceeds_query_cache) + { + auto record = records.find({key, offset}); + if (record != records.end()) + record->second->use(cache_lock); + } + } + + size_t getMaxCacheSize() { return max_cache_size; } + + size_t getCacheSize() { return cache_size; } + + FileCachePriorityPtr getPriority() { return priority; } + + bool isSkipDownloadIfExceed() { return skip_download_if_exceeds_query_cache; } + }; + + using QueryContextPtr = std::shared_ptr; + using QueryContextMap = std::unordered_map; + + QueryContextMap query_map; + + bool enable_filesystem_query_cache_limit; + + QueryContextPtr getCurrentQueryContext(std::lock_guard & cache_lock); + + QueryContextPtr getQueryContext(const String & query_id, std::lock_guard & cache_lock); + + void removeQueryContext(const String & query_id); + + QueryContextPtr getOrSetQueryContext(const String & query_id, const ReadSettings & settings, std::lock_guard &); + +public: + /// Save a query context information, and adopt different cache policies + /// for different queries through the context cache layer. + struct QueryContextHolder : private boost::noncopyable + { + explicit QueryContextHolder(const String & query_id_, IFileCache * cache_, QueryContextPtr context_); + + QueryContextHolder() = default; + + ~QueryContextHolder(); + + String query_id {}; + IFileCache * cache = nullptr; + QueryContextPtr context = nullptr; + }; + + QueryContextHolder getQueryContextHolder(const String & query_id, const ReadSettings & settings); +}; + +class FileCache final : public IFileCache +{ +public: + FileCache( + const String & cache_base_path_, + const FileCacheSettings & cache_settings_); + + FileSegmentsHolder getOrSet(const Key & key, size_t offset, size_t size) override; + + FileSegmentsHolder get(const Key & key, size_t offset, size_t size) override; + + FileSegments getSnapshot() const override; + + void initialize() override; + + void remove(const Key & key) override; + + void remove() override; + + std::vector tryGetCachePaths(const Key & key) override; + + size_t getUsedCacheSize() const override; + + size_t getFileSegmentsNum() const override; + +private: + struct FileSegmentCell : private boost::noncopyable + { + FileSegmentPtr file_segment; + + /// Iterator is put here on first reservation attempt, if successful. + IFileCachePriority::Iterator queue_iterator; + + /// Pointer to file segment is always hold by the cache itself. + /// Apart from pointer in cache, it can be hold by cache users, when they call + /// getorSet(), but cache users always hold it via FileSegmentsHolder. + bool releasable() const { return file_segment.unique(); } + + size_t size() const { return file_segment->reserved_size; } + + FileSegmentCell(FileSegmentPtr file_segment_, FileCache * cache, std::lock_guard & cache_lock); + + FileSegmentCell(FileSegmentCell && other) noexcept + : file_segment(std::move(other.file_segment)) + , queue_iterator(other.queue_iterator) {} + }; + + using FileSegmentsByOffset = std::map; + using CachedFiles = std::unordered_map; + + CachedFiles files; + FileCachePriorityPtr main_priority; + + FileCacheRecords stash_records; + FileCachePriorityPtr stash_priority; + + size_t max_stash_element_size; + size_t enable_cache_hits_threshold; + + Poco::Logger * log; + + FileSegments getImpl( + const Key & key, const FileSegment::Range & range, + std::lock_guard & cache_lock); + + FileSegmentCell * getCell( + const Key & key, size_t offset, std::lock_guard & cache_lock); + + FileSegmentCell * addCell( + const Key & key, size_t offset, size_t size, + FileSegment::State state, std::lock_guard & cache_lock); + + void useCell(const FileSegmentCell & cell, FileSegments & result, std::lock_guard & cache_lock); + + bool tryReserve( + const Key & key, size_t offset, size_t size, + std::lock_guard & cache_lock) override; + + bool tryReserveForMainList( + const Key & key, size_t offset, size_t size, + QueryContextPtr query_context, + std::lock_guard & cache_lock); + + void remove( + Key key, size_t offset, + std::lock_guard & cache_lock, + std::lock_guard & segment_lock) override; + + bool isLastFileSegmentHolder( + const Key & key, size_t offset, + std::lock_guard & cache_lock, + std::lock_guard & segment_lock) override; + + void reduceSizeToDownloaded( + const Key & key, size_t offset, + std::lock_guard & cache_lock, + std::lock_guard & segment_lock) override; + + size_t getAvailableCacheSize() const; + + void loadCacheInfoIntoMemory(std::lock_guard & cache_lock); + + FileSegments splitRangeIntoCells( + const Key & key, size_t offset, size_t size, FileSegment::State state, std::lock_guard & cache_lock); + + String dumpStructureUnlocked(const Key & key_, std::lock_guard & cache_lock); + + void fillHolesWithEmptyFileSegments( + FileSegments & file_segments, const Key & key, const FileSegment::Range & range, bool fill_with_detached_file_segments, std::lock_guard & cache_lock); + + FileSegmentsHolder setDownloading(const Key & key, size_t offset, size_t size) override; + + size_t getUsedCacheSizeUnlocked(std::lock_guard & cache_lock) const; + + size_t getAvailableCacheSizeUnlocked(std::lock_guard & cache_lock) const; + + size_t getFileSegmentsNumUnlocked(std::lock_guard & cache_lock) const; + + void assertCacheCellsCorrectness(const FileSegmentsByOffset & cells_by_offset, std::lock_guard & cache_lock); + +public: + String dumpStructure(const Key & key_) override; + + void assertCacheCorrectness(const Key & key, std::lock_guard & cache_lock); + + void assertCacheCorrectness(std::lock_guard & cache_lock); + + void assertPriorityCorrectness(std::lock_guard & cache_lock); +}; + +} diff --git a/src/Common/IFileCachePriority.h b/src/Common/IFileCachePriority.h index 35b82d61228..a5186bbeea8 100644 --- a/src/Common/IFileCachePriority.h +++ b/src/Common/IFileCachePriority.h @@ -41,7 +41,7 @@ public: virtual void next() = 0; - virtual bool vaild() const = 0; + virtual bool valid() const = 0; /// Mark a cache record as recently used, it will update the priority /// of the cache record according to different cache algorithms. diff --git a/src/Common/LRUFileCache.cpp b/src/Common/LRUFileCache.cpp index 817208c6c30..54b07a81afe 100644 --- a/src/Common/LRUFileCache.cpp +++ b/src/Common/LRUFileCache.cpp @@ -486,7 +486,7 @@ bool FileCache::tryReserve(const Key & key, size_t offset, size_t size, std::loc }; /// Select the cache from the LRU queue held by query for expulsion. - for (auto iter = query_context->getPriority()->getNewIterator(cache_lock); iter->vaild(); iter->next()) + for (auto iter = query_context->getPriority()->getNewIterator(cache_lock); iter->valid(); iter->next()) { if (!is_overflow()) break; @@ -596,7 +596,7 @@ bool FileCache::tryReserveForMainList( std::vector to_evict; std::vector trash; - for (auto it = main_priority->getNewIterator(cache_lock); it->vaild(); it->next()) + for (auto it = main_priority->getNewIterator(cache_lock); it->valid(); it->next()) { auto entry_key = it->key(); auto entry_offset = it->offset(); @@ -752,7 +752,7 @@ void LRUFileCache::removeIfReleasable(bool remove_persistent_files) std::lock_guard cache_lock(mutex); std::vector to_remove; - for (auto it = main_priority->getNewIterator(cache_lock); it->vaild(); it->next()) + for (auto it = main_priority->getNewIterator(cache_lock); it->valid(); it->next()) { auto key = it->key(); auto offset = it->offset(); @@ -1186,7 +1186,7 @@ void FileCache::assertCacheCorrectness(std::lock_guard & cache_lock) void FileCache::assertPriorityCorrectness(std::lock_guard & cache_lock) { [[maybe_unused]] size_t total_size = 0; - for (auto it = main_priority->getNewIterator(cache_lock); it->vaild(); it->next()) + for (auto it = main_priority->getNewIterator(cache_lock); it->valid(); it->next()) { auto key = it->key(); auto offset = it->offset(); From 43cf7716574c5d4c99a16433d18143928a480d25 Mon Sep 17 00:00:00 2001 From: KinderRiven <1339764596@qq.com> Date: Tue, 14 Jun 2022 21:17:52 +0800 Subject: [PATCH 17/43] better --- src/Common/IFileCachePriority.h | 30 ++++++++++++++++++++++-------- src/Common/LRUFileCache.cpp | 2 +- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/Common/IFileCachePriority.h b/src/Common/IFileCachePriority.h index a5186bbeea8..677ccd76934 100644 --- a/src/Common/IFileCachePriority.h +++ b/src/Common/IFileCachePriority.h @@ -7,6 +7,11 @@ namespace DB { +namespace ErrorCodes +{ + extern const int NOT_IMPLEMENTED; +} + class IFileCachePriority; using FileCachePriorityPtr = std::shared_ptr; @@ -39,16 +44,29 @@ public: public: virtual ~IIterator() = default; - virtual void next() = 0; + virtual void next() { throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Not support next() for IIterator."); } - virtual bool valid() const = 0; + virtual bool valid() const { throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Not support valid() for IIterator."); } /// Mark a cache record as recently used, it will update the priority /// of the cache record according to different cache algorithms. - virtual void use(std::lock_guard & cache_lock) = 0; + virtual void use(std::lock_guard &) + { + throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Not support use() for IIterator."); + } /// Deletes an existing cached record. - virtual void remove(std::lock_guard & cache_lock) = 0; + virtual void remove(std::lock_guard &) + { + throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Not support remove() for IIterator."); + } + + virtual Iterator getSnapshot() { throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Not support getSnapshot() for IIterator."); } + + virtual void incrementSize(size_t, std::lock_guard &) + { + throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Not support incrementSize() for IIterator."); + } virtual Key & key() const = 0; @@ -57,10 +75,6 @@ public: virtual size_t size() const = 0; virtual size_t hits() const = 0; - - virtual Iterator getSnapshot() = 0; - - virtual void incrementSize(size_t size_increment, std::lock_guard & cache_lock) = 0; }; public: diff --git a/src/Common/LRUFileCache.cpp b/src/Common/LRUFileCache.cpp index 54b07a81afe..1a9924ba332 100644 --- a/src/Common/LRUFileCache.cpp +++ b/src/Common/LRUFileCache.cpp @@ -1165,7 +1165,7 @@ void FileCache::assertCacheCellsCorrectness( if (file_segment->reserved_size != 0) { assert(cell.queue_iterator); - assert(priority.contains(file_segment->key(), file_segment->offset(), cache_lock)); + assert(main_priority->contains(file_segment->key(), file_segment->offset(), cache_lock)); } } } From c5f90225103b521b8c2dafb68c50813657d6c65f Mon Sep 17 00:00:00 2001 From: KinderRiven <1339764596@qq.com> Date: Sun, 26 Jun 2022 03:05:54 +0800 Subject: [PATCH 18/43] fix --- .../{LRUFileCache.cpp => FileCache.cpp} | 99 ++----- src/Common/FileCache.h | 267 ++---------------- src/Common/FileCacheFactory.cpp | 4 +- src/Common/IFileCache.cpp | 8 +- src/Common/IFileCache.h | 63 +---- src/Common/IFileCachePriority.h | 14 +- src/Common/LRUFileCache.h | 202 +++++-------- src/Common/tests/gtest_lru_file_cache.cpp | 2 +- 8 files changed, 131 insertions(+), 528 deletions(-) rename src/Common/{LRUFileCache.cpp => FileCache.cpp} (92%) diff --git a/src/Common/LRUFileCache.cpp b/src/Common/FileCache.cpp similarity index 92% rename from src/Common/LRUFileCache.cpp rename to src/Common/FileCache.cpp index 1a9924ba332..56eb4c9e081 100644 --- a/src/Common/LRUFileCache.cpp +++ b/src/Common/FileCache.cpp @@ -1,4 +1,4 @@ -#include "LRUFileCache.h" +#include "FileCache.h" #include #include @@ -11,6 +11,7 @@ #include #include #include +#include namespace fs = std::filesystem; @@ -22,13 +23,13 @@ namespace ErrorCodes extern const int LOGICAL_ERROR; } -LRUFileCache::LRUFileCache(const String & cache_base_path_, const FileCacheSettings & cache_settings_) +FileCache::FileCache(const String & cache_base_path_, const FileCacheSettings & cache_settings_) : IFileCache(cache_base_path_, cache_settings_) , main_priority(std::make_shared()) , stash_priority(std::make_shared()) , max_stash_element_size(cache_settings_.max_elements) , enable_cache_hits_threshold(cache_settings_.enable_cache_hits_threshold) - , log(&Poco::Logger::get("LRUFileCache")) + , log(&Poco::Logger::get("FileCache")) , allow_to_remove_persistent_segments_from_cache_by_default(cache_settings_.allow_to_remove_persistent_segments_from_cache_by_default) { } @@ -173,7 +174,7 @@ FileSegments FileCache::getImpl( return result; } -FileSegments LRUFileCache::splitRangeIntoCells( +FileSegments FileCache::splitRangeIntoCells( const Key & key, size_t offset, size_t size, FileSegment::State state, bool is_persistent, std::lock_guard & cache_lock) { assert(size > 0); @@ -296,7 +297,7 @@ void FileCache::fillHolesWithEmptyFileSegments( } } -FileSegmentsHolder LRUFileCache::getOrSet(const Key & key, size_t offset, size_t size, bool is_persistent) +FileSegmentsHolder FileCache::getOrSet(const Key & key, size_t offset, size_t size, bool is_persistent) { assertInitialized(); @@ -356,7 +357,7 @@ FileSegmentsHolder FileCache::get(const Key & key, size_t offset, size_t size) return FileSegmentsHolder(std::move(file_segments)); } -LRUFileCache::FileSegmentCell * LRUFileCache::addCell( +FileCache::FileSegmentCell * FileCache::addCell( const Key & key, size_t offset, size_t size, FileSegment::State state, bool is_persistent, std::lock_guard & cache_lock) @@ -395,11 +396,9 @@ LRUFileCache::FileSegmentCell * LRUFileCache::addCell( } else { - auto queue_iter = record->second; - queue_iter->hits++; - stash_queue.moveToEnd(queue_iter, cache_lock); - - result_state = queue_iter->hits >= enable_cache_hits_threshold ? FileSegment::State::EMPTY : FileSegment::State::SKIP_CACHE; + auto priority_iter = record->second; + priority_iter->use(cache_lock); + result_state = priority_iter->hits() >= enable_cache_hits_threshold ? FileSegment::State::EMPTY : FileSegment::State::SKIP_CACHE; } } @@ -426,7 +425,7 @@ LRUFileCache::FileSegmentCell * LRUFileCache::addCell( return &(it->second); } -FileSegmentsHolder LRUFileCache::setDownloading( +FileSegmentsHolder FileCache::setDownloading( const Key & key, size_t offset, size_t size, @@ -691,7 +690,7 @@ bool FileCache::tryReserveForMainList( return true; } -void LRUFileCache::removeIfExists(const Key & key) +void FileCache::removeIfExists(const Key & key) { assertInitialized(); @@ -742,7 +741,7 @@ void LRUFileCache::removeIfExists(const Key & key) } } -void LRUFileCache::removeIfReleasable(bool remove_persistent_files) +void FileCache::removeIfReleasable(bool remove_persistent_files) { /// Try remove all cached files by cache_base_path. /// Only releasable file segments are evicted. @@ -786,8 +785,8 @@ void LRUFileCache::removeIfReleasable(bool remove_persistent_files) } /// Remove all access information. - records.clear(); - stash_queue.removeAll(cache_lock); + stash_records.clear(); + stash_priority->removeAll(cache_lock); #ifndef NDEBUG assertCacheCorrectness(cache_lock); @@ -1070,73 +1069,7 @@ FileCache::FileSegmentCell::FileSegmentCell( } } -IFileCache::LRUQueue::Iterator IFileCache::LRUQueue::add( - const IFileCache::Key & key, size_t offset, size_t size, std::lock_guard & /* cache_lock */) -{ -#ifndef NDEBUG - for (const auto & [entry_key, entry_offset, entry_size, entry_hits] : queue) - { - if (entry_key == key && entry_offset == offset) - throw Exception( - ErrorCodes::LOGICAL_ERROR, - "Attempt to add duplicate queue entry to queue. (Key: {}, offset: {}, size: {})", - key.toString(), offset, size); - } -#endif - - cache_size += size; - return queue.insert(queue.end(), FileKeyAndOffset(key, offset, size)); -} - -void IFileCache::LRUQueue::remove(Iterator queue_it, std::lock_guard & /* cache_lock */) -{ - cache_size -= queue_it->size; - queue.erase(queue_it); -} - -void IFileCache::LRUQueue::removeAll(std::lock_guard & /* cache_lock */) -{ - queue.clear(); - cache_size = 0; -} - -void IFileCache::LRUQueue::moveToEnd(Iterator queue_it, std::lock_guard & /* cache_lock */) -{ - queue.splice(queue.end(), queue, queue_it); -} - -void IFileCache::LRUQueue::incrementSize(Iterator queue_it, size_t size_increment, std::lock_guard & /* cache_lock */) -{ - cache_size += size_increment; - queue_it->size += size_increment; -} - -bool IFileCache::LRUQueue::contains( - const IFileCache::Key & key, size_t offset, std::lock_guard & /* cache_lock */) const -{ - /// This method is used for assertions in debug mode. - /// So we do not care about complexity here. - for (const auto & [entry_key, entry_offset, size, _] : queue) - { - if (key == entry_key && offset == entry_offset) - return true; - } - return false; -} - -String IFileCache::LRUQueue::toString(std::lock_guard & /* cache_lock */) const -{ - String result; - for (const auto & [key, offset, size, _] : queue) - { - if (!result.empty()) - result += ", "; - result += fmt::format("{}: [{}, {}]", key.toString(), offset, offset + size - 1); - } - return result; -} - -String LRUFileCache::dumpStructure(const Key & key) +String FileCache::dumpStructure(const Key & key) { std::lock_guard cache_lock(mutex); return dumpStructureUnlocked(key, cache_lock); diff --git a/src/Common/FileCache.h b/src/Common/FileCache.h index 13bca0e2dae..5aa32ac94ca 100644 --- a/src/Common/FileCache.h +++ b/src/Common/FileCache.h @@ -11,252 +11,18 @@ #include #include -#include "FileCache_fwd.h" -#include #include #include -#include -#include -#include +#include + namespace DB { -namespace ErrorCodes -{ - extern const int LOGICAL_ERROR; -} - -class IFileCache; -using FileCachePtr = std::shared_ptr; - /** * Local cache for remote filesystem files, represented as a set of non-overlapping non-empty file segments. + * Implements LRU eviction policy. */ -class IFileCache : private boost::noncopyable -{ -friend class FileSegment; -friend struct FileSegmentsHolder; -friend class FileSegmentRangeWriter; - -public: - using Key = UInt128; - using Downloader = std::unique_ptr; - - IFileCache( - const String & cache_base_path_, - const FileCacheSettings & cache_settings_); - - virtual ~IFileCache() = default; - - /// Restore cache from local filesystem. - virtual void initialize() = 0; - - virtual void remove(const Key & key) = 0; - - virtual void remove() = 0; - - static bool isReadOnly(); - - /// Cache capacity in bytes. - size_t capacity() const { return max_size; } - - static Key hash(const String & path); - - String getPathInLocalCache(const Key & key, size_t offset); - - String getPathInLocalCache(const Key & key); - - const String & getBasePath() const { return cache_base_path; } - - virtual std::vector tryGetCachePaths(const Key & key) = 0; - - /** - * Given an `offset` and `size` representing [offset, offset + size) bytes interval, - * return list of cached non-overlapping non-empty - * file segments `[segment1, ..., segmentN]` which intersect with given interval. - * - * Segments in returned list are ordered in ascending order and represent a full contiguous - * interval (no holes). Each segment in returned list has state: DOWNLOADED, DOWNLOADING or EMPTY. - * - * As long as pointers to returned file segments are hold - * it is guaranteed that these file segments are not removed from cache. - */ - virtual FileSegmentsHolder getOrSet(const Key & key, size_t offset, size_t size) = 0; - - /** - * Segments in returned list are ordered in ascending order and represent a full contiguous - * interval (no holes). Each segment in returned list has state: DOWNLOADED, DOWNLOADING or EMPTY. - * - * If file segment has state EMPTY, then it is also marked as "detached". E.g. it is "detached" - * from cache (not owned by cache), and as a result will never change it's state and will be destructed - * with the destruction of the holder, while in getOrSet() EMPTY file segments can eventually change - * it's state (and become DOWNLOADED). - */ - virtual FileSegmentsHolder get(const Key & key, size_t offset, size_t size) = 0; - - virtual FileSegmentsHolder setDownloading(const Key & key, size_t offset, size_t size) = 0; - - virtual FileSegments getSnapshot() const = 0; - - /// For debug. - virtual String dumpStructure(const Key & key) = 0; - - virtual size_t getUsedCacheSize() const = 0; - - virtual size_t getFileSegmentsNum() const = 0; - -protected: - String cache_base_path; - size_t max_size; - size_t max_element_size; - size_t max_file_segment_size; - - bool is_initialized = false; - - mutable std::mutex mutex; - - virtual bool tryReserve( - const Key & key, size_t offset, size_t size, - std::lock_guard & cache_lock) = 0; - - virtual void remove( - Key key, size_t offset, - std::lock_guard & cache_lock, - std::lock_guard & segment_lock) = 0; - - virtual bool isLastFileSegmentHolder( - const Key & key, size_t offset, - std::lock_guard & cache_lock, - std::lock_guard & segment_lock) = 0; - - /// If file segment was partially downloaded and then space reservation fails (because of no - /// space left), then update corresponding cache cell metadata (file segment size). - virtual void reduceSizeToDownloaded( - const Key & key, size_t offset, - std::lock_guard & cache_lock, - std::lock_guard & segment_lock) = 0; - - void assertInitialized() const; - -protected: - using KeyAndOffset = std::pair; - - struct KeyAndOffsetHash - { - std::size_t operator()(const KeyAndOffset & key) const - { - return std::hash()(key.first) ^ std::hash()(key.second); - } - }; - - using FileCacheRecords = std::unordered_map; - - /// Used to track and control the cache access of each query. - /// Through it, we can realize the processing of different queries by the cache layer. - struct QueryContext - { - FileCacheRecords records; - FileCachePriorityPtr priority; - - size_t cache_size = 0; - size_t max_cache_size; - - bool skip_download_if_exceeds_query_cache; - - QueryContext(size_t max_cache_size_, bool skip_download_if_exceeds_query_cache_) - : priority(std::make_shared()) - , max_cache_size(max_cache_size_) - , skip_download_if_exceeds_query_cache(skip_download_if_exceeds_query_cache_) {} - - void remove(const Key & key, size_t offset, size_t size, std::lock_guard & cache_lock) - { - if (cache_size < size) - throw Exception(ErrorCodes::LOGICAL_ERROR, "Deleted cache size exceeds existing cache size"); - - if (!skip_download_if_exceeds_query_cache) - { - auto record = records.find({key, offset}); - if (record != records.end()) - { - record->second->remove(cache_lock); - records.erase({key, offset}); - } - } - cache_size -= size; - } - - void reserve(const Key & key, size_t offset, size_t size, std::lock_guard & cache_lock) - { - if (cache_size + size > max_cache_size) - throw Exception(ErrorCodes::LOGICAL_ERROR, "Reserved cache size exceeds the remaining cache size"); - - if (!skip_download_if_exceeds_query_cache) - { - auto record = records.find({key, offset}); - if (record == records.end()) - { - auto queue_iter = priority->add(key, offset, 0, cache_lock); - record = records.insert({{key, offset}, queue_iter}).first; - } - record->second->incrementSize(size, cache_lock); - } - cache_size += size; - } - - void use(const Key & key, size_t offset, std::lock_guard & cache_lock) - { - if (!skip_download_if_exceeds_query_cache) - { - auto record = records.find({key, offset}); - if (record != records.end()) - record->second->use(cache_lock); - } - } - - size_t getMaxCacheSize() { return max_cache_size; } - - size_t getCacheSize() { return cache_size; } - - FileCachePriorityPtr getPriority() { return priority; } - - bool isSkipDownloadIfExceed() { return skip_download_if_exceeds_query_cache; } - }; - - using QueryContextPtr = std::shared_ptr; - using QueryContextMap = std::unordered_map; - - QueryContextMap query_map; - - bool enable_filesystem_query_cache_limit; - - QueryContextPtr getCurrentQueryContext(std::lock_guard & cache_lock); - - QueryContextPtr getQueryContext(const String & query_id, std::lock_guard & cache_lock); - - void removeQueryContext(const String & query_id); - - QueryContextPtr getOrSetQueryContext(const String & query_id, const ReadSettings & settings, std::lock_guard &); - -public: - /// Save a query context information, and adopt different cache policies - /// for different queries through the context cache layer. - struct QueryContextHolder : private boost::noncopyable - { - explicit QueryContextHolder(const String & query_id_, IFileCache * cache_, QueryContextPtr context_); - - QueryContextHolder() = default; - - ~QueryContextHolder(); - - String query_id {}; - IFileCache * cache = nullptr; - QueryContextPtr context = nullptr; - }; - - QueryContextHolder getQueryContextHolder(const String & query_id, const ReadSettings & settings); -}; - class FileCache final : public IFileCache { public: @@ -264,7 +30,7 @@ public: const String & cache_base_path_, const FileCacheSettings & cache_settings_); - FileSegmentsHolder getOrSet(const Key & key, size_t offset, size_t size) override; + FileSegmentsHolder getOrSet(const Key & key, size_t offset, size_t size, bool is_persistent) override; FileSegmentsHolder get(const Key & key, size_t offset, size_t size) override; @@ -272,9 +38,9 @@ public: void initialize() override; - void remove(const Key & key) override; + void removeIfExists(const Key & key) override; - void remove() override; + void removeIfReleasable(bool remove_persistent_files) override; std::vector tryGetCachePaths(const Key & key) override; @@ -293,7 +59,7 @@ private: /// Pointer to file segment is always hold by the cache itself. /// Apart from pointer in cache, it can be hold by cache users, when they call /// getorSet(), but cache users always hold it via FileSegmentsHolder. - bool releasable() const { return file_segment.unique(); } + bool releasable() const {return file_segment.unique(); } size_t size() const { return file_segment->reserved_size; } @@ -317,6 +83,7 @@ private: size_t enable_cache_hits_threshold; Poco::Logger * log; + bool allow_to_remove_persistent_segments_from_cache_by_default; FileSegments getImpl( const Key & key, const FileSegment::Range & range, @@ -327,7 +94,8 @@ private: FileSegmentCell * addCell( const Key & key, size_t offset, size_t size, - FileSegment::State state, std::lock_guard & cache_lock); + FileSegment::State state, bool is_persistent, + std::lock_guard & cache_lock); void useCell(const FileSegmentCell & cell, FileSegments & result, std::lock_guard & cache_lock); @@ -350,24 +118,19 @@ private: std::lock_guard & cache_lock, std::lock_guard & segment_lock) override; - void reduceSizeToDownloaded( - const Key & key, size_t offset, - std::lock_guard & cache_lock, - std::lock_guard & segment_lock) override; - size_t getAvailableCacheSize() const; void loadCacheInfoIntoMemory(std::lock_guard & cache_lock); FileSegments splitRangeIntoCells( - const Key & key, size_t offset, size_t size, FileSegment::State state, std::lock_guard & cache_lock); + const Key & key, size_t offset, size_t size, FileSegment::State state, bool is_persistent, std::lock_guard & cache_lock); String dumpStructureUnlocked(const Key & key_, std::lock_guard & cache_lock); void fillHolesWithEmptyFileSegments( - FileSegments & file_segments, const Key & key, const FileSegment::Range & range, bool fill_with_detached_file_segments, std::lock_guard & cache_lock); + FileSegments & file_segments, const Key & key, const FileSegment::Range & range, bool fill_with_detached_file_segments, bool is_persistent, std::lock_guard & cache_lock); - FileSegmentsHolder setDownloading(const Key & key, size_t offset, size_t size) override; + FileSegmentsHolder setDownloading(const Key & key, size_t offset, size_t size, bool is_persistent) override; size_t getUsedCacheSizeUnlocked(std::lock_guard & cache_lock) const; @@ -377,6 +140,10 @@ private: void assertCacheCellsCorrectness(const FileSegmentsByOffset & cells_by_offset, std::lock_guard & cache_lock); + void reduceSizeToDownloaded( + const Key & key, size_t offset, + std::lock_guard & cache_lock, std::lock_guard & /* segment_lock */) override; + public: String dumpStructure(const Key & key_) override; diff --git a/src/Common/FileCacheFactory.cpp b/src/Common/FileCacheFactory.cpp index 259c1d3f48e..b276760c0dd 100644 --- a/src/Common/FileCacheFactory.cpp +++ b/src/Common/FileCacheFactory.cpp @@ -1,5 +1,5 @@ #include "FileCacheFactory.h" -#include "LRUFileCache.h" +#include "FileCache.h" namespace DB { @@ -53,7 +53,7 @@ FileCachePtr FileCacheFactory::getOrCreate( return it->second->cache; } - auto cache = std::make_shared(cache_base_path, file_cache_settings); + auto cache = std::make_shared(cache_base_path, file_cache_settings); FileCacheData result{cache, file_cache_settings}; auto cache_it = caches.insert(caches.end(), std::move(result)); diff --git a/src/Common/IFileCache.cpp b/src/Common/IFileCache.cpp index 8fe434dd740..e3ed82d7b62 100644 --- a/src/Common/IFileCache.cpp +++ b/src/Common/IFileCache.cpp @@ -140,7 +140,7 @@ void IFileCache::QueryContext::remove(const Key & key, size_t offset, size_t siz auto record = records.find({key, offset}); if (record != records.end()) { - lru_queue.remove(record->second, cache_lock); + record->second->remove(cache_lock); records.erase({key, offset}); } } @@ -162,10 +162,10 @@ void IFileCache::QueryContext::reserve(const Key & key, size_t offset, size_t si auto record = records.find({key, offset}); if (record == records.end()) { - auto queue_iter = lru_queue.add(key, offset, 0, cache_lock); + auto queue_iter = priority->add(key, offset, 0, cache_lock); record = records.insert({{key, offset}, queue_iter}).first; } - record->second->size += size; + record->second->incrementSize(size, cache_lock); } cache_size += size; } @@ -177,7 +177,7 @@ void IFileCache::QueryContext::use(const Key & key, size_t offset, std::lock_gua auto record = records.find({key, offset}); if (record != records.end()) - lru_queue.moveToEnd(record->second, cache_lock); + record->second->use(cache_lock); } IFileCache::QueryContextHolder::QueryContextHolder( diff --git a/src/Common/IFileCache.h b/src/Common/IFileCache.h index c820d18cb95..f46a83d52cf 100644 --- a/src/Common/IFileCache.h +++ b/src/Common/IFileCache.h @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -28,16 +29,7 @@ friend struct FileSegmentsHolder; friend class FileSegmentRangeWriter; public: - struct Key - { - UInt128 key; - String toString() const; - - Key() = default; - explicit Key(const UInt128 & key_) : key(key_) {} - - bool operator==(const Key & other) const { return key == other.key; } - }; + using Key = IFileCachePriority::Key; IFileCache( const String & cache_base_path_, @@ -133,49 +125,6 @@ protected: void assertInitialized() const; - class LRUQueue - { - public: - struct FileKeyAndOffset - { - Key key; - size_t offset; - size_t size; - size_t hits = 0; - - FileKeyAndOffset(const Key & key_, size_t offset_, size_t size_) : key(key_), offset(offset_), size(size_) {} - }; - - using Iterator = typename std::list::iterator; - - size_t getTotalCacheSize(std::lock_guard & /* cache_lock */) const { return cache_size; } - - size_t getElementsNum(std::lock_guard & /* cache_lock */) const { return queue.size(); } - - Iterator add(const Key & key, size_t offset, size_t size, std::lock_guard & cache_lock); - - void remove(Iterator queue_it, std::lock_guard & cache_lock); - - void moveToEnd(Iterator queue_it, std::lock_guard & cache_lock); - - /// Space reservation for a file segment is incremental, so we need to be able to increment size of the queue entry. - void incrementSize(Iterator queue_it, size_t size_increment, std::lock_guard & cache_lock); - - String toString(std::lock_guard & cache_lock) const; - - bool contains(const Key & key, size_t offset, std::lock_guard & cache_lock) const; - - Iterator begin() { return queue.begin(); } - - Iterator end() { return queue.end(); } - - void removeAll(std::lock_guard & cache_lock); - - private: - std::list queue; - size_t cache_size = 0; - }; - using AccessKeyAndOffset = std::pair; struct KeyAndOffsetHash { @@ -185,14 +134,14 @@ protected: } }; - using AccessRecord = std::unordered_map; + using FileCacheRecords = std::unordered_map; /// Used to track and control the cache access of each query. /// Through it, we can realize the processing of different queries by the cache layer. struct QueryContext { - LRUQueue lru_queue; - AccessRecord records; + FileCacheRecords records; + FileCachePriorityPtr priority; size_t cache_size = 0; size_t max_cache_size; @@ -213,7 +162,7 @@ protected: size_t getCacheSize() const { return cache_size; } - LRUQueue & queue() { return lru_queue; } + FileCachePriorityPtr getPriority() { return priority; } bool isSkipDownloadIfExceed() const { return skip_download_if_exceeds_query_cache; } }; diff --git a/src/Common/IFileCachePriority.h b/src/Common/IFileCachePriority.h index 677ccd76934..a29d66c70be 100644 --- a/src/Common/IFileCachePriority.h +++ b/src/Common/IFileCachePriority.h @@ -3,6 +3,9 @@ #include #include #include +#include +#include +#include namespace DB { @@ -19,7 +22,16 @@ using FileCachePriorityPtr = std::shared_ptr; class IFileCachePriority { public: - using Key = UInt128; + struct Key + { + UInt128 key; + String toString() const; + + Key() = default; + explicit Key(const UInt128 & key_) : key(key_) {} + + bool operator==(const Key & other) const { return key == other.key; } + }; class IIterator; friend class IIterator; diff --git a/src/Common/LRUFileCache.h b/src/Common/LRUFileCache.h index 059fc0c22c9..0bd87b2b38c 100644 --- a/src/Common/LRUFileCache.h +++ b/src/Common/LRUFileCache.h @@ -1,157 +1,99 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - +#include namespace DB { -/** - * Local cache for remote filesystem files, represented as a set of non-overlapping non-empty file segments. - * Implements LRU eviction policy. - */ -class LRUFileCache final : public IFileCache +class LRUFileCache : public IFileCachePriority { public: - LRUFileCache( - const String & cache_base_path_, - const FileCacheSettings & cache_settings_); + using LRUQueue = std::list; + using LRUQueueIterator = typename LRUQueue::iterator; + class WriteableIterator; - FileSegmentsHolder getOrSet(const Key & key, size_t offset, size_t size, bool is_persistent) override; - - FileSegmentsHolder get(const Key & key, size_t offset, size_t size) override; - - FileSegments getSnapshot() const override; - - void initialize() override; - - void removeIfExists(const Key & key) override; - - void removeIfReleasable(bool remove_persistent_files) override; - - std::vector tryGetCachePaths(const Key & key) override; - - size_t getUsedCacheSize() const override; - - size_t getFileSegmentsNum() const override; - -private: - struct FileSegmentCell : private boost::noncopyable + class ReadableIterator : public IIterator { - FileSegmentPtr file_segment; + public: + ReadableIterator(LRUFileCache * file_cache_, LRUQueueIterator queue_iter_) : file_cache(file_cache_), queue_iter(queue_iter_) { } - /// Iterator is put here on first reservation attempt, if successful. - std::optional queue_iterator; + void next() override { queue_iter++; } - /// Pointer to file segment is always hold by the cache itself. - /// Apart from pointer in cache, it can be hold by cache users, when they call - /// getorSet(), but cache users always hold it via FileSegmentsHolder. - bool releasable() const {return file_segment.unique(); } + bool valid() const override { return queue_iter != file_cache->queue.end(); } - size_t size() const { return file_segment->reserved_size; } + Key & key() const override { return queue_iter->key; } - FileSegmentCell(FileSegmentPtr file_segment_, LRUFileCache * cache, std::lock_guard & cache_lock); + size_t offset() const override { return queue_iter->offset; } - FileSegmentCell(FileSegmentCell && other) noexcept - : file_segment(std::move(other.file_segment)) - , queue_iterator(other.queue_iterator) {} + size_t size() const override { return queue_iter->size; } + + size_t hits() const override { return queue_iter->hits; } + + Iterator getSnapshot() override { return std::make_shared(file_cache, queue_iter); } + + protected: + LRUFileCache * file_cache; + LRUQueueIterator queue_iter; }; - using FileSegmentsByOffset = std::map; - using CachedFiles = std::unordered_map; + class WriteableIterator : public ReadableIterator + { + public: + WriteableIterator(LRUFileCache * file_cache_, LRUQueueIterator queue_iter_) : ReadableIterator(file_cache_, queue_iter_) { } - CachedFiles files; - LRUQueue queue; + void remove(std::lock_guard &) override + { + file_cache->cache_size -= queue_iter->size; + file_cache->queue.erase(queue_iter); + } - LRUQueue stash_queue; - AccessRecord records; + void incrementSize(size_t size_increment, std::lock_guard &) override + { + file_cache->cache_size += size_increment; + queue_iter->size += size_increment; + } - size_t max_stash_element_size; - size_t enable_cache_hits_threshold; - - Poco::Logger * log; - bool allow_to_remove_persistent_segments_from_cache_by_default; - - FileSegments getImpl( - const Key & key, const FileSegment::Range & range, - std::lock_guard & cache_lock); - - FileSegmentCell * getCell( - const Key & key, size_t offset, std::lock_guard & cache_lock); - - FileSegmentCell * addCell( - const Key & key, size_t offset, size_t size, - FileSegment::State state, bool is_persistent, - std::lock_guard & cache_lock); - - void useCell(const FileSegmentCell & cell, FileSegments & result, std::lock_guard & cache_lock); - - bool tryReserve( - const Key & key, size_t offset, size_t size, - std::lock_guard & cache_lock) override; - - bool tryReserveForMainList( - const Key & key, size_t offset, size_t size, - QueryContextPtr query_context, - std::lock_guard & cache_lock); - - void remove( - Key key, size_t offset, - std::lock_guard & cache_lock, - std::lock_guard & segment_lock) override; - - bool isLastFileSegmentHolder( - const Key & key, size_t offset, - std::lock_guard & cache_lock, - std::lock_guard & segment_lock) override; - - size_t getAvailableCacheSize() const; - - void loadCacheInfoIntoMemory(std::lock_guard & cache_lock); - - FileSegments splitRangeIntoCells( - const Key & key, size_t offset, size_t size, FileSegment::State state, bool is_persistent, std::lock_guard & cache_lock); - - String dumpStructureUnlocked(const Key & key_, std::lock_guard & cache_lock); - - void fillHolesWithEmptyFileSegments( - FileSegments & file_segments, const Key & key, const FileSegment::Range & range, bool fill_with_detached_file_segments, bool is_persistent, std::lock_guard & cache_lock); - - FileSegmentsHolder setDownloading(const Key & key, size_t offset, size_t size, bool is_persistent) override; - - size_t getUsedCacheSizeUnlocked(std::lock_guard & cache_lock) const; - - size_t getAvailableCacheSizeUnlocked(std::lock_guard & cache_lock) const; - - size_t getFileSegmentsNumUnlocked(std::lock_guard & cache_lock) const; - - void assertCacheCellsCorrectness(const FileSegmentsByOffset & cells_by_offset, std::lock_guard & cache_lock); - - void reduceSizeToDownloaded( - const Key & key, size_t offset, - std::lock_guard & cache_lock, std::lock_guard & /* segment_lock */) override; + void use(std::lock_guard &) override + { + queue_iter->hits++; + file_cache->queue.splice(file_cache->queue.end(), file_cache->queue, queue_iter); + } + }; public: - String dumpStructure(const Key & key_) override; + LRUFileCache() = default; - void assertCacheCorrectness(const Key & key, std::lock_guard & cache_lock); + Iterator add(const Key & key, size_t offset, size_t size, std::lock_guard &) override + { + auto iter = queue.insert(queue.end(), FileCacheRecord(key, offset, size)); + cache_size += size; + return std::make_shared(this, iter); + } - void assertCacheCorrectness(std::lock_guard & cache_lock); + bool contains(const Key & key, size_t offset, std::lock_guard &) override + { + for (const auto & record : queue) + { + if (key == record.key && offset == record.offset) + return true; + } + return false; + } - void assertQueueCorrectness(std::lock_guard & cache_lock); + void removeAll(std::lock_guard &) override + { + queue.clear(); + cache_size = 0; + } + + Iterator getNewIterator(std::lock_guard &) override { return std::make_shared(this, queue.begin()); } + + size_t getElementsNum(std::lock_guard &) const override { return queue.size(); } + + std::string toString(std::lock_guard &) const override { return {}; } + +private: + LRUQueue queue; }; -} +}; diff --git a/src/Common/tests/gtest_lru_file_cache.cpp b/src/Common/tests/gtest_lru_file_cache.cpp index 8e7554f0418..ac942d97a32 100644 --- a/src/Common/tests/gtest_lru_file_cache.cpp +++ b/src/Common/tests/gtest_lru_file_cache.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include #include From 50fd740ec34825962e1f4e17c7bba2e84742692f Mon Sep 17 00:00:00 2001 From: KinderRiven <1339764596@qq.com> Date: Sun, 26 Jun 2022 20:35:02 +0800 Subject: [PATCH 19/43] fix --- src/Common/FileCache.cpp | 2 +- src/Common/IFileCachePriority.h | 6 +++--- src/Common/LRUFileCache.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Common/FileCache.cpp b/src/Common/FileCache.cpp index 56eb4c9e081..bf981cc466e 100644 --- a/src/Common/FileCache.cpp +++ b/src/Common/FileCache.cpp @@ -772,7 +772,7 @@ void FileCache::removeIfReleasable(bool remove_persistent_files) { std::lock_guard segment_lock(file_segment->mutex); file_segment->detach(cache_lock, segment_lock); - remove(file_segment->key(), file_segment->offset(), cache_lock, segment_lock); + to_remove.emplace_back(file_segment); } } } diff --git a/src/Common/IFileCachePriority.h b/src/Common/IFileCachePriority.h index a29d66c70be..2e73bb92841 100644 --- a/src/Common/IFileCachePriority.h +++ b/src/Common/IFileCachePriority.h @@ -3,9 +3,9 @@ #include #include #include +#include #include #include -#include namespace DB { @@ -28,7 +28,7 @@ public: String toString() const; Key() = default; - explicit Key(const UInt128 & key_) : key(key_) {} + explicit Key(const UInt128 & key_) : key(key_) { } bool operator==(const Key & other) const { return key == other.key; } }; @@ -80,7 +80,7 @@ public: throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Not support incrementSize() for IIterator."); } - virtual Key & key() const = 0; + virtual Key key() const = 0; virtual size_t offset() const = 0; diff --git a/src/Common/LRUFileCache.h b/src/Common/LRUFileCache.h index 0bd87b2b38c..6e42a7732d4 100644 --- a/src/Common/LRUFileCache.h +++ b/src/Common/LRUFileCache.h @@ -19,9 +19,9 @@ public: void next() override { queue_iter++; } - bool valid() const override { return queue_iter != file_cache->queue.end(); } + bool valid() const override { return (file_cache->queue.size() && (queue_iter != file_cache->queue.end())); } - Key & key() const override { return queue_iter->key; } + Key key() const override { return queue_iter->key; } size_t offset() const override { return queue_iter->offset; } From 9d83b93e88025f4052e5a32562377b26ab87a0cd Mon Sep 17 00:00:00 2001 From: KinderRiven Date: Wed, 10 Aug 2022 13:50:30 +0800 Subject: [PATCH 20/43] fix rebase --- src/Common/FileCache.cpp | 196 +++++++++++++- src/Common/FileCache.h | 247 ++++++++++++++---- src/Common/FileCacheType.h | 28 ++ src/Common/FileCache_fwd.h | 4 +- src/Common/FileSegment.cpp | 6 +- src/Common/FileSegment.h | 10 +- src/Common/IFileCache.cpp | 201 -------------- src/Common/IFileCache.h | 216 --------------- src/Common/IFileCachePriority.h | 68 ++--- ...{LRUFileCache.h => LRUFileCachePriority.h} | 41 +-- src/Common/tests/gtest_lru_file_cache.cpp | 2 +- src/Disks/IO/CachedReadBufferFromRemoteFS.cpp | 2 +- src/Disks/IO/CachedReadBufferFromRemoteFS.h | 6 +- .../ObjectStorages/DiskObjectStorage.cpp | 3 +- .../DiskObjectStorageCommon.cpp | 2 +- src/Disks/ObjectStorages/IObjectStorage.h | 1 + .../ObjectStorages/S3/S3ObjectStorage.cpp | 2 +- src/IO/WriteBufferFromS3.cpp | 2 +- src/Interpreters/AsynchronousMetrics.cpp | 2 +- .../InterpreterDescribeCacheQuery.cpp | 2 +- src/Interpreters/InterpreterSystemQuery.cpp | 2 +- .../System/StorageSystemFilesystemCache.cpp | 2 +- .../System/StorageSystemRemoteDataPaths.cpp | 2 +- 23 files changed, 480 insertions(+), 567 deletions(-) create mode 100644 src/Common/FileCacheType.h delete mode 100644 src/Common/IFileCache.cpp delete mode 100644 src/Common/IFileCache.h rename src/Common/{LRUFileCache.h => LRUFileCachePriority.h} (61%) diff --git a/src/Common/FileCache.cpp b/src/Common/FileCache.cpp index bf981cc466e..2a2fc68e768 100644 --- a/src/Common/FileCache.cpp +++ b/src/Common/FileCache.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include namespace fs = std::filesystem; @@ -23,10 +23,16 @@ namespace ErrorCodes extern const int LOGICAL_ERROR; } -FileCache::FileCache(const String & cache_base_path_, const FileCacheSettings & cache_settings_) - : IFileCache(cache_base_path_, cache_settings_) - , main_priority(std::make_shared()) - , stash_priority(std::make_shared()) +FileCache::FileCache( + const String & cache_base_path_, + const FileCacheSettings & cache_settings_) + : cache_base_path(cache_base_path_) + , max_size(cache_settings_.max_size) + , max_element_size(cache_settings_.max_elements) + , max_file_segment_size(cache_settings_.max_file_segment_size) + , enable_filesystem_query_cache_limit(cache_settings_.enable_filesystem_query_cache_limit) + , main_priority(std::make_shared()) + , stash_priority(std::make_shared()) , max_stash_element_size(cache_settings_.max_elements) , enable_cache_hits_threshold(cache_settings_.enable_cache_hits_threshold) , log(&Poco::Logger::get("FileCache")) @@ -34,6 +40,175 @@ FileCache::FileCache(const String & cache_base_path_, const FileCacheSettings & { } +String FileCache::Key::toString() const +{ + return getHexUIntLowercase(key); +} + +FileCache::Key FileCache::hash(const String & path) +{ + return Key(sipHash128(path.data(), path.size())); +} + +String FileCache::getPathInLocalCache(const Key & key, size_t offset, bool is_persistent) const +{ + auto key_str = key.toString(); + return fs::path(cache_base_path) + / key_str.substr(0, 3) + / key_str + / (std::to_string(offset) + (is_persistent ? "_persistent" : "")); +} + +String FileCache::getPathInLocalCache(const Key & key) const +{ + auto key_str = key.toString(); + return fs::path(cache_base_path) / key_str.substr(0, 3) / key_str; +} + +static bool isQueryInitialized() +{ + return CurrentThread::isInitialized() + && CurrentThread::get().getQueryContext() + && CurrentThread::getQueryId().size != 0; +} + +bool FileCache::isReadOnly() +{ + return !isQueryInitialized(); +} + +void FileCache::assertInitialized() const +{ + if (!is_initialized) + throw Exception(ErrorCodes::REMOTE_FS_OBJECT_CACHE_ERROR, "Cache not initialized"); +} + +FileCache::QueryContextPtr FileCache::getCurrentQueryContext(std::lock_guard & cache_lock) +{ + if (!isQueryInitialized()) + return nullptr; + + return getQueryContext(CurrentThread::getQueryId().toString(), cache_lock); +} + +FileCache::QueryContextPtr FileCache::getQueryContext(const String & query_id, std::lock_guard & /* cache_lock */) +{ + auto query_iter = query_map.find(query_id); + return (query_iter == query_map.end()) ? nullptr : query_iter->second; +} + +void FileCache::removeQueryContext(const String & query_id) +{ + std::lock_guard cache_lock(mutex); + auto query_iter = query_map.find(query_id); + + if (query_iter == query_map.end()) + { + throw Exception( + ErrorCodes::LOGICAL_ERROR, + "Attempt to release query context that does not exist (query_id: {})", + query_id); + } + + query_map.erase(query_iter); +} + +FileCache::QueryContextPtr FileCache::getOrSetQueryContext( + const String & query_id, const ReadSettings & settings, std::lock_guard & cache_lock) +{ + if (query_id.empty()) + return nullptr; + + auto context = getQueryContext(query_id, cache_lock); + if (context) + return context; + + auto query_context = std::make_shared(settings.max_query_cache_size, settings.skip_download_if_exceeds_query_cache); + auto query_iter = query_map.emplace(query_id, query_context).first; + return query_iter->second; +} + +FileCache::QueryContextHolder FileCache::getQueryContextHolder(const String & query_id, const ReadSettings & settings) +{ + std::lock_guard cache_lock(mutex); + + if (!enable_filesystem_query_cache_limit || settings.max_query_cache_size == 0) + return {}; + + /// if enable_filesystem_query_cache_limit is true, and max_query_cache_size large than zero, + /// we create context query for current query. + auto context = getOrSetQueryContext(query_id, settings, cache_lock); + return QueryContextHolder(query_id, this, context); +} + +void FileCache::QueryContext::remove(const Key & key, size_t offset, size_t size, std::lock_guard & cache_lock) +{ + if (cache_size < size) + throw Exception(ErrorCodes::LOGICAL_ERROR, "Deleted cache size exceeds existing cache size"); + + if (!skip_download_if_exceeds_query_cache) + { + auto record = records.find({key, offset}); + if (record != records.end()) + { + record->second->remove(cache_lock); + records.erase({key, offset}); + } + } + cache_size -= size; +} + +void FileCache::QueryContext::reserve(const Key & key, size_t offset, size_t size, std::lock_guard & cache_lock) +{ + if (cache_size + size > max_cache_size) + { + throw Exception( + ErrorCodes::LOGICAL_ERROR, + "Reserved cache size exceeds the remaining cache size (key: {}, offset: {})", + key.toString(), offset); + } + + if (!skip_download_if_exceeds_query_cache) + { + auto record = records.find({key, offset}); + if (record == records.end()) + { + auto queue_iter = priority->add(key, offset, 0, cache_lock); + record = records.insert({{key, offset}, queue_iter}).first; + } + record->second->incrementSize(size, cache_lock); + } + cache_size += size; +} + +void FileCache::QueryContext::use(const Key & key, size_t offset, std::lock_guard & cache_lock) +{ + if (skip_download_if_exceeds_query_cache) + return; + + auto record = records.find({key, offset}); + if (record != records.end()) + record->second->use(cache_lock); +} + +FileCache::QueryContextHolder::QueryContextHolder( + const String & query_id_, + FileCache * cache_, + FileCache::QueryContextPtr context_) + : query_id(query_id_) + , cache(cache_) + , context(context_) +{ +} + +FileCache::QueryContextHolder::~QueryContextHolder() +{ + /// If only the query_map and the current holder hold the context_query, + /// the query has been completed and the query_context is released. + if (context && context.use_count() == 2) + cache->removeQueryContext(query_id); +} + void FileCache::initialize() { std::lock_guard cache_lock(mutex); @@ -115,7 +290,7 @@ FileSegments FileCache::getImpl( files.erase(key); /// Note: it is guaranteed that there is no concurrency with files deletion, - /// because cache files are deleted only inside IFileCache and under cache lock. + /// because cache files are deleted only inside FileCache and under cache lock. if (fs::exists(key_path)) fs::remove_all(key_path); @@ -387,7 +562,7 @@ FileCache::FileSegmentCell * FileCache::addCell( if (stash_priority->getElementsNum(cache_lock) > max_stash_element_size) { - auto remove_priority_iter = stash_priority->getNewIterator(cache_lock); + auto remove_priority_iter = stash_priority->getNewIterator(cache_lock)->getWriteIterator(); stash_records.erase({remove_priority_iter->key(), remove_priority_iter->offset()}); remove_priority_iter->remove(cache_lock); } @@ -473,7 +648,7 @@ bool FileCache::tryReserve(const Key & key, size_t offset, size_t size, std::loc auto * cell_for_reserve = getCell(key, offset, cache_lock); - std::vector ghost; + std::vector ghost; std::vector trash; std::vector to_evict; @@ -496,7 +671,7 @@ bool FileCache::tryReserve(const Key & key, size_t offset, size_t size, std::loc { /// The cache corresponding to this record may be swapped out by /// other queries, so it has become invalid. - ghost.push_back(iter->getSnapshot()); + ghost.push_back(iter->getWriteIterator()); removed_size += iter->size(); } else @@ -844,10 +1019,9 @@ void FileCache::loadCacheInfoIntoMemory(std::lock_guard & cache_lock Key key; UInt64 offset = 0; size_t size = 0; - std::vector>> queue_entries; + std::vector>> queue_entries; /// cache_base_path / key_prefix / key / offset - if (!files.empty()) throw Exception( ErrorCodes::REMOTE_FS_OBJECT_CACHE_ERROR, diff --git a/src/Common/FileCache.h b/src/Common/FileCache.h index 5aa32ac94ca..bccd2dbd458 100644 --- a/src/Common/FileCache.h +++ b/src/Common/FileCache.h @@ -3,50 +3,196 @@ #include #include #include +#include #include #include #include #include #include #include -#include -#include +#include +#include +#include #include -#include - +#include +#include +#include namespace DB { /** * Local cache for remote filesystem files, represented as a set of non-overlapping non-empty file segments. - * Implements LRU eviction policy. - */ -class FileCache final : public IFileCache + */ +class FileCache : private boost::noncopyable { + friend class FileSegment; + friend class IFileCachePriority; + friend struct FileSegmentsHolder; + friend class FileSegmentRangeWriter; + public: - FileCache( - const String & cache_base_path_, - const FileCacheSettings & cache_settings_); + using Key = DB::FileCacheKey; - FileSegmentsHolder getOrSet(const Key & key, size_t offset, size_t size, bool is_persistent) override; + FileCache(const String & cache_base_path_, const FileCacheSettings & cache_settings_); - FileSegmentsHolder get(const Key & key, size_t offset, size_t size) override; + ~FileCache() = default; - FileSegments getSnapshot() const override; + /// Restore cache from local filesystem. + void initialize(); - void initialize() override; + void removeIfExists(const Key & key); - void removeIfExists(const Key & key) override; + void removeIfReleasable(bool remove_persistent_files); - void removeIfReleasable(bool remove_persistent_files) override; + static bool isReadOnly(); - std::vector tryGetCachePaths(const Key & key) override; + /// Cache capacity in bytes. + size_t capacity() const { return max_size; } - size_t getUsedCacheSize() const override; + static Key hash(const String & path); - size_t getFileSegmentsNum() const override; + String getPathInLocalCache(const Key & key, size_t offset, bool is_persistent) const; + + String getPathInLocalCache(const Key & key) const; + + const String & getBasePath() const { return cache_base_path; } + + std::vector tryGetCachePaths(const Key & key); + + /** + * Given an `offset` and `size` representing [offset, offset + size) bytes interval, + * return list of cached non-overlapping non-empty + * file segments `[segment1, ..., segmentN]` which intersect with given interval. + * + * Segments in returned list are ordered in ascending order and represent a full contiguous + * interval (no holes). Each segment in returned list has state: DOWNLOADED, DOWNLOADING or EMPTY. + * + * As long as pointers to returned file segments are hold + * it is guaranteed that these file segments are not removed from cache. + */ + FileSegmentsHolder getOrSet(const Key & key, size_t offset, size_t size, bool is_persistent); + + /** + * Segments in returned list are ordered in ascending order and represent a full contiguous + * interval (no holes). Each segment in returned list has state: DOWNLOADED, DOWNLOADING or EMPTY. + * + * If file segment has state EMPTY, then it is also marked as "detached". E.g. it is "detached" + * from cache (not owned by cache), and as a result will never change it's state and will be destructed + * with the destruction of the holder, while in getOrSet() EMPTY file segments can eventually change + * it's state (and become DOWNLOADED). + */ + FileSegmentsHolder get(const Key & key, size_t offset, size_t size); + + FileSegmentsHolder setDownloading(const Key & key, size_t offset, size_t size, bool is_persistent); + + FileSegments getSnapshot() const; + + /// For debug. + String dumpStructure(const Key & key); + + size_t getUsedCacheSize() const; + + size_t getFileSegmentsNum() const; + +private: + String cache_base_path; + size_t max_size; + size_t max_element_size; + size_t max_file_segment_size; + + bool is_initialized = false; + + mutable std::mutex mutex; + + bool tryReserve(const Key & key, size_t offset, size_t size, std::lock_guard & cache_lock); + + void remove(Key key, size_t offset, std::lock_guard & cache_lock, std::lock_guard & segment_lock); + + bool isLastFileSegmentHolder( + const Key & key, size_t offset, std::lock_guard & cache_lock, std::lock_guard & segment_lock); + + void reduceSizeToDownloaded( + const Key & key, size_t offset, std::lock_guard & cache_lock, std::lock_guard & /* segment_lock */); + + void assertInitialized() const; + + using AccessKeyAndOffset = std::pair; + struct KeyAndOffsetHash + { + std::size_t operator()(const AccessKeyAndOffset & key) const + { + return std::hash()(key.first.key) ^ std::hash()(key.second); + } + }; + + using FileCacheRecords = std::unordered_map; + + /// Used to track and control the cache access of each query. + /// Through it, we can realize the processing of different queries by the cache layer. + struct QueryContext + { + FileCacheRecords records; + FileCachePriorityPtr priority; + + size_t cache_size = 0; + size_t max_cache_size; + + bool skip_download_if_exceeds_query_cache; + + QueryContext(size_t max_cache_size_, bool skip_download_if_exceeds_query_cache_) + : max_cache_size(max_cache_size_), skip_download_if_exceeds_query_cache(skip_download_if_exceeds_query_cache_) + { + } + + void remove(const Key & key, size_t offset, size_t size, std::lock_guard & cache_lock); + + void reserve(const Key & key, size_t offset, size_t size, std::lock_guard & cache_lock); + + void use(const Key & key, size_t offset, std::lock_guard & cache_lock); + + size_t getMaxCacheSize() const { return max_cache_size; } + + size_t getCacheSize() const { return cache_size; } + + FileCachePriorityPtr getPriority() { return priority; } + + bool isSkipDownloadIfExceed() const { return skip_download_if_exceeds_query_cache; } + }; + + using QueryContextPtr = std::shared_ptr; + using QueryContextMap = std::unordered_map; + + QueryContextMap query_map; + + bool enable_filesystem_query_cache_limit; + + QueryContextPtr getCurrentQueryContext(std::lock_guard & cache_lock); + + QueryContextPtr getQueryContext(const String & query_id, std::lock_guard & cache_lock); + + void removeQueryContext(const String & query_id); + + QueryContextPtr getOrSetQueryContext(const String & query_id, const ReadSettings & settings, std::lock_guard &); + +public: + /// Save a query context information, and adopt different cache policies + /// for different queries through the context cache layer. + struct QueryContextHolder : private boost::noncopyable + { + QueryContextHolder(const String & query_id_, FileCache * cache_, QueryContextPtr context_); + + QueryContextHolder() = default; + + ~QueryContextHolder(); + + String query_id; + FileCache * cache = nullptr; + QueryContextPtr context; + }; + + QueryContextHolder getQueryContextHolder(const String & query_id, const ReadSettings & settings); private: struct FileSegmentCell : private boost::noncopyable @@ -54,20 +200,21 @@ private: FileSegmentPtr file_segment; /// Iterator is put here on first reservation attempt, if successful. - IFileCachePriority::Iterator queue_iterator; + IFileCachePriority::WriteIterator queue_iterator; /// Pointer to file segment is always hold by the cache itself. /// Apart from pointer in cache, it can be hold by cache users, when they call /// getorSet(), but cache users always hold it via FileSegmentsHolder. - bool releasable() const {return file_segment.unique(); } + bool releasable() const { return file_segment.unique(); } size_t size() const { return file_segment->reserved_size; } FileSegmentCell(FileSegmentPtr file_segment_, FileCache * cache, std::lock_guard & cache_lock); FileSegmentCell(FileSegmentCell && other) noexcept - : file_segment(std::move(other.file_segment)) - , queue_iterator(other.queue_iterator) {} + : file_segment(std::move(other.file_segment)), queue_iterator(other.queue_iterator) + { + } }; using FileSegmentsByOffset = std::map; @@ -85,52 +232,44 @@ private: Poco::Logger * log; bool allow_to_remove_persistent_segments_from_cache_by_default; - FileSegments getImpl( - const Key & key, const FileSegment::Range & range, - std::lock_guard & cache_lock); + FileSegments getImpl(const Key & key, const FileSegment::Range & range, std::lock_guard & cache_lock); - FileSegmentCell * getCell( - const Key & key, size_t offset, std::lock_guard & cache_lock); + FileSegmentCell * getCell(const Key & key, size_t offset, std::lock_guard & cache_lock); FileSegmentCell * addCell( - const Key & key, size_t offset, size_t size, - FileSegment::State state, bool is_persistent, + const Key & key, + size_t offset, + size_t size, + FileSegment::State state, + bool is_persistent, std::lock_guard & cache_lock); void useCell(const FileSegmentCell & cell, FileSegments & result, std::lock_guard & cache_lock); - bool tryReserve( - const Key & key, size_t offset, size_t size, - std::lock_guard & cache_lock) override; - bool tryReserveForMainList( - const Key & key, size_t offset, size_t size, - QueryContextPtr query_context, - std::lock_guard & cache_lock); - - void remove( - Key key, size_t offset, - std::lock_guard & cache_lock, - std::lock_guard & segment_lock) override; - - bool isLastFileSegmentHolder( - const Key & key, size_t offset, - std::lock_guard & cache_lock, - std::lock_guard & segment_lock) override; + const Key & key, size_t offset, size_t size, QueryContextPtr query_context, std::lock_guard & cache_lock); size_t getAvailableCacheSize() const; void loadCacheInfoIntoMemory(std::lock_guard & cache_lock); FileSegments splitRangeIntoCells( - const Key & key, size_t offset, size_t size, FileSegment::State state, bool is_persistent, std::lock_guard & cache_lock); + const Key & key, + size_t offset, + size_t size, + FileSegment::State state, + bool is_persistent, + std::lock_guard & cache_lock); String dumpStructureUnlocked(const Key & key_, std::lock_guard & cache_lock); void fillHolesWithEmptyFileSegments( - FileSegments & file_segments, const Key & key, const FileSegment::Range & range, bool fill_with_detached_file_segments, bool is_persistent, std::lock_guard & cache_lock); - - FileSegmentsHolder setDownloading(const Key & key, size_t offset, size_t size, bool is_persistent) override; + FileSegments & file_segments, + const Key & key, + const FileSegment::Range & range, + bool fill_with_detached_file_segments, + bool is_persistent, + std::lock_guard & cache_lock); size_t getUsedCacheSizeUnlocked(std::lock_guard & cache_lock) const; @@ -140,13 +279,7 @@ private: void assertCacheCellsCorrectness(const FileSegmentsByOffset & cells_by_offset, std::lock_guard & cache_lock); - void reduceSizeToDownloaded( - const Key & key, size_t offset, - std::lock_guard & cache_lock, std::lock_guard & /* segment_lock */) override; - public: - String dumpStructure(const Key & key_) override; - void assertCacheCorrectness(const Key & key, std::lock_guard & cache_lock); void assertCacheCorrectness(std::lock_guard & cache_lock); diff --git a/src/Common/FileCacheType.h b/src/Common/FileCacheType.h new file mode 100644 index 00000000000..9b3ec5a6af0 --- /dev/null +++ b/src/Common/FileCacheType.h @@ -0,0 +1,28 @@ +#pragma once +#include + +namespace DB +{ + +struct FileCacheKey +{ + UInt128 key; + String toString() const; + + FileCacheKey() = default; + explicit FileCacheKey(const UInt128 & key_) : key(key_) { } + + bool operator==(const FileCacheKey & other) const { return key == other.key; } +}; + +} + +namespace std +{ +template <> +struct hash +{ + std::size_t operator()(const DB::FileCacheKey & k) const { return hash()(k.key); } +}; + +} diff --git a/src/Common/FileCache_fwd.h b/src/Common/FileCache_fwd.h index 8a7c2eeb458..9f6b2a740fc 100644 --- a/src/Common/FileCache_fwd.h +++ b/src/Common/FileCache_fwd.h @@ -9,8 +9,8 @@ static constexpr int REMOTE_FS_OBJECTS_CACHE_DEFAULT_MAX_FILE_SEGMENT_SIZE = 100 static constexpr int REMOTE_FS_OBJECTS_CACHE_DEFAULT_MAX_ELEMENTS = 1024 * 1024; static constexpr int REMOTE_FS_OBJECTS_CACHE_ENABLE_HITS_THRESHOLD = 0; -class IFileCache; -using FileCachePtr = std::shared_ptr; +class FileCache; +using FileCachePtr = std::shared_ptr; struct FileCacheSettings; diff --git a/src/Common/FileSegment.cpp b/src/Common/FileSegment.cpp index c16d4658ae5..2aba93bbdb0 100644 --- a/src/Common/FileSegment.cpp +++ b/src/Common/FileSegment.cpp @@ -5,7 +5,7 @@ #include #include #include - +#include namespace CurrentMetrics { @@ -25,7 +25,7 @@ FileSegment::FileSegment( size_t offset_, size_t size_, const Key & key_, - IFileCache * cache_, + FileCache * cache_, State download_state_, bool is_persistent_) : segment_range(offset_, offset_ + size_ - 1) @@ -787,7 +787,7 @@ FileSegmentsHolder::~FileSegmentsHolder() /// FileSegmentsHolder right after calling file_segment->complete(), so on destruction here /// remain only uncompleted file segments. - IFileCache * cache = nullptr; + FileCache * cache = nullptr; for (auto file_segment_it = file_segments.begin(); file_segment_it != file_segments.end();) { diff --git a/src/Common/FileSegment.h b/src/Common/FileSegment.h index 4404d0e14be..b129b851d7c 100644 --- a/src/Common/FileSegment.h +++ b/src/Common/FileSegment.h @@ -1,11 +1,11 @@ #pragma once #include -#include #include #include #include #include +#include namespace Poco { class Logger; } @@ -17,7 +17,7 @@ extern const Metric CacheFileSegments; namespace DB { -class IFileCache; +class FileCache; class FileSegment; using FileSegmentPtr = std::shared_ptr; @@ -32,7 +32,7 @@ friend struct FileSegmentsHolder; friend class FileSegmentRangeWriter; public: - using Key = IFileCache::Key; + using Key = FileCacheKey; using RemoteFileReaderPtr = std::shared_ptr; using LocalCacheWriterPtr = std::unique_ptr; @@ -74,7 +74,7 @@ public: size_t offset_, size_t size_, const Key & key_, - IFileCache * cache_, + FileCache * cache_, State download_state_, bool is_persistent_ = false); @@ -234,7 +234,7 @@ private: mutable std::mutex download_mutex; Key file_key; - IFileCache * cache; + FileCache * cache; Poco::Logger * log; diff --git a/src/Common/IFileCache.cpp b/src/Common/IFileCache.cpp deleted file mode 100644 index e3ed82d7b62..00000000000 --- a/src/Common/IFileCache.cpp +++ /dev/null @@ -1,201 +0,0 @@ -#include "IFileCache.h" - -#include -#include -#include -#include -#include -#include - -namespace fs = std::filesystem; - -namespace DB -{ - -namespace ErrorCodes -{ - extern const int REMOTE_FS_OBJECT_CACHE_ERROR; - extern const int LOGICAL_ERROR; -} - -IFileCache::IFileCache( - const String & cache_base_path_, - const FileCacheSettings & cache_settings_) - : cache_base_path(cache_base_path_) - , max_size(cache_settings_.max_size) - , max_element_size(cache_settings_.max_elements) - , max_file_segment_size(cache_settings_.max_file_segment_size) - , enable_filesystem_query_cache_limit(cache_settings_.enable_filesystem_query_cache_limit) -{ -} - -String IFileCache::Key::toString() const -{ - return getHexUIntLowercase(key); -} - -IFileCache::Key IFileCache::hash(const String & path) -{ - return Key(sipHash128(path.data(), path.size())); -} - -String IFileCache::getPathInLocalCache(const Key & key, size_t offset, bool is_persistent) const -{ - auto key_str = key.toString(); - return fs::path(cache_base_path) - / key_str.substr(0, 3) - / key_str - / (std::to_string(offset) + (is_persistent ? "_persistent" : "")); -} - -String IFileCache::getPathInLocalCache(const Key & key) const -{ - auto key_str = key.toString(); - return fs::path(cache_base_path) / key_str.substr(0, 3) / key_str; -} - -static bool isQueryInitialized() -{ - return CurrentThread::isInitialized() - && CurrentThread::get().getQueryContext() - && !CurrentThread::getQueryId().empty(); -} - -bool IFileCache::isReadOnly() -{ - return !isQueryInitialized(); -} - -void IFileCache::assertInitialized() const -{ - if (!is_initialized) - throw Exception(ErrorCodes::REMOTE_FS_OBJECT_CACHE_ERROR, "Cache not initialized"); -} - -IFileCache::QueryContextPtr IFileCache::getCurrentQueryContext(std::lock_guard & cache_lock) -{ - if (!isQueryInitialized()) - return nullptr; - - return getQueryContext(std::string(CurrentThread::getQueryId()), cache_lock); -} - -IFileCache::QueryContextPtr IFileCache::getQueryContext(const String & query_id, std::lock_guard & /* cache_lock */) -{ - auto query_iter = query_map.find(query_id); - return (query_iter == query_map.end()) ? nullptr : query_iter->second; -} - -void IFileCache::removeQueryContext(const String & query_id) -{ - std::lock_guard cache_lock(mutex); - auto query_iter = query_map.find(query_id); - - if (query_iter == query_map.end()) - { - throw Exception( - ErrorCodes::LOGICAL_ERROR, - "Attempt to release query context that does not exist (query_id: {})", - query_id); - } - - query_map.erase(query_iter); -} - -IFileCache::QueryContextPtr IFileCache::getOrSetQueryContext( - const String & query_id, const ReadSettings & settings, std::lock_guard & cache_lock) -{ - if (query_id.empty()) - return nullptr; - - auto context = getQueryContext(query_id, cache_lock); - if (context) - return context; - - auto query_context = std::make_shared(settings.max_query_cache_size, settings.skip_download_if_exceeds_query_cache); - auto query_iter = query_map.emplace(query_id, query_context).first; - return query_iter->second; -} - -IFileCache::QueryContextHolder IFileCache::getQueryContextHolder(const String & query_id, const ReadSettings & settings) -{ - std::lock_guard cache_lock(mutex); - - if (!enable_filesystem_query_cache_limit || settings.max_query_cache_size == 0) - return {}; - - /// if enable_filesystem_query_cache_limit is true, and max_query_cache_size large than zero, - /// we create context query for current query. - auto context = getOrSetQueryContext(query_id, settings, cache_lock); - return QueryContextHolder(query_id, this, context); -} - -void IFileCache::QueryContext::remove(const Key & key, size_t offset, size_t size, std::lock_guard & cache_lock) -{ - if (cache_size < size) - throw Exception(ErrorCodes::LOGICAL_ERROR, "Deleted cache size exceeds existing cache size"); - - if (!skip_download_if_exceeds_query_cache) - { - auto record = records.find({key, offset}); - if (record != records.end()) - { - record->second->remove(cache_lock); - records.erase({key, offset}); - } - } - cache_size -= size; -} - -void IFileCache::QueryContext::reserve(const Key & key, size_t offset, size_t size, std::lock_guard & cache_lock) -{ - if (cache_size + size > max_cache_size) - { - throw Exception( - ErrorCodes::LOGICAL_ERROR, - "Reserved cache size exceeds the remaining cache size (key: {}, offset: {})", - key.toString(), offset); - } - - if (!skip_download_if_exceeds_query_cache) - { - auto record = records.find({key, offset}); - if (record == records.end()) - { - auto queue_iter = priority->add(key, offset, 0, cache_lock); - record = records.insert({{key, offset}, queue_iter}).first; - } - record->second->incrementSize(size, cache_lock); - } - cache_size += size; -} - -void IFileCache::QueryContext::use(const Key & key, size_t offset, std::lock_guard & cache_lock) -{ - if (skip_download_if_exceeds_query_cache) - return; - - auto record = records.find({key, offset}); - if (record != records.end()) - record->second->use(cache_lock); -} - -IFileCache::QueryContextHolder::QueryContextHolder( - const String & query_id_, - IFileCache * cache_, - IFileCache::QueryContextPtr context_) - : query_id(query_id_) - , cache(cache_) - , context(context_) -{ -} - -IFileCache::QueryContextHolder::~QueryContextHolder() -{ - /// If only the query_map and the current holder hold the context_query, - /// the query has been completed and the query_context is released. - if (context && context.use_count() == 2) - cache->removeQueryContext(query_id); -} - -} diff --git a/src/Common/IFileCache.h b/src/Common/IFileCache.h deleted file mode 100644 index f46a83d52cf..00000000000 --- a/src/Common/IFileCache.h +++ /dev/null @@ -1,216 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include -#include -#include - - -namespace DB -{ - -class FileSegment; -using FileSegmentPtr = std::shared_ptr; -using FileSegments = std::list; -struct FileSegmentsHolder; -struct ReadSettings; - -/** - * Local cache for remote filesystem files, represented as a set of non-overlapping non-empty file segments. - */ -class IFileCache : private boost::noncopyable -{ -friend class FileSegment; -friend struct FileSegmentsHolder; -friend class FileSegmentRangeWriter; - -public: - using Key = IFileCachePriority::Key; - - IFileCache( - const String & cache_base_path_, - const FileCacheSettings & cache_settings_); - - virtual ~IFileCache() = default; - - /// Restore cache from local filesystem. - virtual void initialize() = 0; - - virtual void removeIfExists(const Key & key) = 0; - - virtual void removeIfReleasable(bool remove_persistent_files) = 0; - - static bool isReadOnly(); - - /// Cache capacity in bytes. - size_t capacity() const { return max_size; } - - static Key hash(const String & path); - - String getPathInLocalCache(const Key & key, size_t offset, bool is_persistent) const; - - String getPathInLocalCache(const Key & key) const; - - const String & getBasePath() const { return cache_base_path; } - - virtual std::vector tryGetCachePaths(const Key & key) = 0; - - /** - * Given an `offset` and `size` representing [offset, offset + size) bytes interval, - * return list of cached non-overlapping non-empty - * file segments `[segment1, ..., segmentN]` which intersect with given interval. - * - * Segments in returned list are ordered in ascending order and represent a full contiguous - * interval (no holes). Each segment in returned list has state: DOWNLOADED, DOWNLOADING or EMPTY. - * - * As long as pointers to returned file segments are hold - * it is guaranteed that these file segments are not removed from cache. - */ - virtual FileSegmentsHolder getOrSet(const Key & key, size_t offset, size_t size, bool is_persistent) = 0; - - /** - * Segments in returned list are ordered in ascending order and represent a full contiguous - * interval (no holes). Each segment in returned list has state: DOWNLOADED, DOWNLOADING or EMPTY. - * - * If file segment has state EMPTY, then it is also marked as "detached". E.g. it is "detached" - * from cache (not owned by cache), and as a result will never change it's state and will be destructed - * with the destruction of the holder, while in getOrSet() EMPTY file segments can eventually change - * it's state (and become DOWNLOADED). - */ - virtual FileSegmentsHolder get(const Key & key, size_t offset, size_t size) = 0; - - virtual FileSegmentsHolder setDownloading(const Key & key, size_t offset, size_t size, bool is_persistent) = 0; - - virtual FileSegments getSnapshot() const = 0; - - /// For debug. - virtual String dumpStructure(const Key & key) = 0; - - virtual size_t getUsedCacheSize() const = 0; - - virtual size_t getFileSegmentsNum() const = 0; - -protected: - String cache_base_path; - size_t max_size; - size_t max_element_size; - size_t max_file_segment_size; - - bool is_initialized = false; - - mutable std::mutex mutex; - - virtual bool tryReserve( - const Key & key, size_t offset, size_t size, - std::lock_guard & cache_lock) = 0; - - virtual void remove( - Key key, size_t offset, - std::lock_guard & cache_lock, - std::lock_guard & segment_lock) = 0; - - virtual bool isLastFileSegmentHolder( - const Key & key, size_t offset, - std::lock_guard & cache_lock, - std::lock_guard & segment_lock) = 0; - - virtual void reduceSizeToDownloaded( - const Key & key, size_t offset, - std::lock_guard & cache_lock, - std::lock_guard & /* segment_lock */) = 0; - - void assertInitialized() const; - - using AccessKeyAndOffset = std::pair; - struct KeyAndOffsetHash - { - std::size_t operator()(const AccessKeyAndOffset & key) const - { - return std::hash()(key.first.key) ^ std::hash()(key.second); - } - }; - - using FileCacheRecords = std::unordered_map; - - /// Used to track and control the cache access of each query. - /// Through it, we can realize the processing of different queries by the cache layer. - struct QueryContext - { - FileCacheRecords records; - FileCachePriorityPtr priority; - - size_t cache_size = 0; - size_t max_cache_size; - - bool skip_download_if_exceeds_query_cache; - - QueryContext(size_t max_cache_size_, bool skip_download_if_exceeds_query_cache_) - : max_cache_size(max_cache_size_) - , skip_download_if_exceeds_query_cache(skip_download_if_exceeds_query_cache_) {} - - void remove(const Key & key, size_t offset, size_t size, std::lock_guard & cache_lock); - - void reserve(const Key & key, size_t offset, size_t size, std::lock_guard & cache_lock); - - void use(const Key & key, size_t offset, std::lock_guard & cache_lock); - - size_t getMaxCacheSize() const { return max_cache_size; } - - size_t getCacheSize() const { return cache_size; } - - FileCachePriorityPtr getPriority() { return priority; } - - bool isSkipDownloadIfExceed() const { return skip_download_if_exceeds_query_cache; } - }; - - using QueryContextPtr = std::shared_ptr; - using QueryContextMap = std::unordered_map; - - QueryContextMap query_map; - - bool enable_filesystem_query_cache_limit; - - QueryContextPtr getCurrentQueryContext(std::lock_guard & cache_lock); - - QueryContextPtr getQueryContext(const String & query_id, std::lock_guard & cache_lock); - - void removeQueryContext(const String & query_id); - - QueryContextPtr getOrSetQueryContext(const String & query_id, const ReadSettings & settings, std::lock_guard &); - -public: - /// Save a query context information, and adopt different cache policies - /// for different queries through the context cache layer. - struct QueryContextHolder : private boost::noncopyable - { - QueryContextHolder(const String & query_id_, IFileCache * cache_, QueryContextPtr context_); - - QueryContextHolder() = default; - - ~QueryContextHolder(); - - String query_id; - IFileCache * cache = nullptr; - QueryContextPtr context; - }; - - QueryContextHolder getQueryContextHolder(const String & query_id, const ReadSettings & settings); - -}; - -using FileCachePtr = std::shared_ptr; - -} - -namespace std -{ -template <> struct hash -{ - std::size_t operator()(const DB::IFileCache::Key & k) const { return hash()(k.key); } -}; - -} diff --git a/src/Common/IFileCachePriority.h b/src/Common/IFileCachePriority.h index 2e73bb92841..84e1a386d24 100644 --- a/src/Common/IFileCachePriority.h +++ b/src/Common/IFileCachePriority.h @@ -5,7 +5,8 @@ #include #include #include -#include +#include +#include namespace DB { @@ -15,6 +16,8 @@ namespace ErrorCodes extern const int NOT_IMPLEMENTED; } +class FileCache; + class IFileCachePriority; using FileCachePriorityPtr = std::shared_ptr; @@ -22,20 +25,14 @@ using FileCachePriorityPtr = std::shared_ptr; class IFileCachePriority { public: - struct Key - { - UInt128 key; - String toString() const; - - Key() = default; - explicit Key(const UInt128 & key_) : key(key_) { } - - bool operator==(const Key & other) const { return key == other.key; } - }; - class IIterator; friend class IIterator; - using Iterator = std::shared_ptr; + + using ReadIterator = std::shared_ptr; + using WriteIterator = std::shared_ptr; + + friend class FileCache; + using Key = FileCacheKey; struct FileCacheRecord { @@ -56,30 +53,6 @@ public: public: virtual ~IIterator() = default; - virtual void next() { throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Not support next() for IIterator."); } - - virtual bool valid() const { throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Not support valid() for IIterator."); } - - /// Mark a cache record as recently used, it will update the priority - /// of the cache record according to different cache algorithms. - virtual void use(std::lock_guard &) - { - throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Not support use() for IIterator."); - } - - /// Deletes an existing cached record. - virtual void remove(std::lock_guard &) - { - throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Not support remove() for IIterator."); - } - - virtual Iterator getSnapshot() { throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Not support getSnapshot() for IIterator."); } - - virtual void incrementSize(size_t, std::lock_guard &) - { - throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Not support incrementSize() for IIterator."); - } - virtual Key key() const = 0; virtual size_t offset() const = 0; @@ -87,6 +60,23 @@ public: virtual size_t size() const = 0; virtual size_t hits() const = 0; + + virtual void next() const = 0; + + virtual bool valid() const = 0; + + /// Mark a cache record as recently used, it will update the priority + /// of the cache record according to different cache algorithms. + virtual void use(std::lock_guard &) = 0; + + /// Deletes an existing cached record. + virtual void remove(std::lock_guard &) = 0; + + virtual WriteIterator getWriteIterator() const = 0; + + virtual void incrementSize(size_t, std::lock_guard &) = 0; + + virtual void seekToLowestPriority() const = 0; }; public: @@ -94,7 +84,7 @@ public: /// Add a cache record that did not exist before, and throw a /// logical exception if the cache block already exists. - virtual Iterator add(const Key & key, size_t offset, size_t size, std::lock_guard & cache_lock) = 0; + virtual WriteIterator add(const Key & key, size_t offset, size_t size, std::lock_guard & cache_lock) = 0; /// Query whether a cache record exists. If it exists, return true. If not, return false. virtual bool contains(const Key & key, size_t offset, std::lock_guard & cache_lock) = 0; @@ -103,7 +93,7 @@ public: /// Returns an iterator pointing to the lowest priority cached record. /// We can traverse all cached records through the iterator's next(). - virtual Iterator getNewIterator(std::lock_guard & cache_lock) = 0; + virtual ReadIterator getNewIterator(std::lock_guard & cache_lock) = 0; virtual size_t getElementsNum(std::lock_guard & cache_lock) const = 0; diff --git a/src/Common/LRUFileCache.h b/src/Common/LRUFileCachePriority.h similarity index 61% rename from src/Common/LRUFileCache.h rename to src/Common/LRUFileCachePriority.h index 6e42a7732d4..bc9badc0af6 100644 --- a/src/Common/LRUFileCache.h +++ b/src/Common/LRUFileCachePriority.h @@ -5,19 +5,23 @@ namespace DB { -class LRUFileCache : public IFileCachePriority +/// Based on the LRU algorithm implementation, the data with the lowest priority is stored at +/// the head of the queue, and the data with the highest priority is stored at the tail. +class LRUFileCachePriority : public IFileCachePriority { public: using LRUQueue = std::list; using LRUQueueIterator = typename LRUQueue::iterator; - class WriteableIterator; - class ReadableIterator : public IIterator + class LRUFileCacheIterator : public IIterator { public: - ReadableIterator(LRUFileCache * file_cache_, LRUQueueIterator queue_iter_) : file_cache(file_cache_), queue_iter(queue_iter_) { } + LRUFileCacheIterator(LRUFileCachePriority * file_cache_, LRUQueueIterator queue_iter_) + : file_cache(file_cache_), queue_iter(queue_iter_) + { + } - void next() override { queue_iter++; } + void next() const override { queue_iter++; } bool valid() const override { return (file_cache->queue.size() && (queue_iter != file_cache->queue.end())); } @@ -29,17 +33,9 @@ public: size_t hits() const override { return queue_iter->hits; } - Iterator getSnapshot() override { return std::make_shared(file_cache, queue_iter); } + WriteIterator getWriteIterator() const override { return std::make_shared(file_cache, queue_iter); } - protected: - LRUFileCache * file_cache; - LRUQueueIterator queue_iter; - }; - - class WriteableIterator : public ReadableIterator - { - public: - WriteableIterator(LRUFileCache * file_cache_, LRUQueueIterator queue_iter_) : ReadableIterator(file_cache_, queue_iter_) { } + void seekToLowestPriority() const override { queue_iter = file_cache->queue.begin(); } void remove(std::lock_guard &) override { @@ -58,16 +54,20 @@ public: queue_iter->hits++; file_cache->queue.splice(file_cache->queue.end(), file_cache->queue, queue_iter); } + + private: + mutable LRUFileCachePriority * file_cache; + mutable LRUQueueIterator queue_iter; }; public: - LRUFileCache() = default; + LRUFileCachePriority() = default; - Iterator add(const Key & key, size_t offset, size_t size, std::lock_guard &) override + WriteIterator add(const Key & key, size_t offset, size_t size, std::lock_guard &) override { auto iter = queue.insert(queue.end(), FileCacheRecord(key, offset, size)); cache_size += size; - return std::make_shared(this, iter); + return std::make_shared(this, iter); } bool contains(const Key & key, size_t offset, std::lock_guard &) override @@ -86,7 +86,10 @@ public: cache_size = 0; } - Iterator getNewIterator(std::lock_guard &) override { return std::make_shared(this, queue.begin()); } + ReadIterator getNewIterator(std::lock_guard &) override + { + return std::make_shared(this, queue.begin()); + } size_t getElementsNum(std::lock_guard &) const override { return queue.size(); } diff --git a/src/Common/tests/gtest_lru_file_cache.cpp b/src/Common/tests/gtest_lru_file_cache.cpp index ac942d97a32..3f481ee25ca 100644 --- a/src/Common/tests/gtest_lru_file_cache.cpp +++ b/src/Common/tests/gtest_lru_file_cache.cpp @@ -47,7 +47,7 @@ std::vector fromHolder(const DB::FileSegmentsHolder & holder return std::vector(holder.file_segments.begin(), holder.file_segments.end()); } -String getFileSegmentPath(const String & base_path, const DB::IFileCache::Key & key, size_t offset) +String getFileSegmentPath(const String & base_path, const DB::FileCache::Key & key, size_t offset) { auto key_str = key.toString(); return fs::path(base_path) / key_str.substr(0, 3) / key_str / DB::toString(offset); diff --git a/src/Disks/IO/CachedReadBufferFromRemoteFS.cpp b/src/Disks/IO/CachedReadBufferFromRemoteFS.cpp index a3d5cfc408d..a10e136334e 100644 --- a/src/Disks/IO/CachedReadBufferFromRemoteFS.cpp +++ b/src/Disks/IO/CachedReadBufferFromRemoteFS.cpp @@ -1024,7 +1024,7 @@ std::optional CachedReadBufferFromRemoteFS::getLastNonDownloadedOffset() void CachedReadBufferFromRemoteFS::assertCorrectness() const { - if (IFileCache::isReadOnly() && !settings.read_from_filesystem_cache_if_exists_otherwise_bypass_cache) + if (FileCache::isReadOnly() && !settings.read_from_filesystem_cache_if_exists_otherwise_bypass_cache) throw Exception(ErrorCodes::LOGICAL_ERROR, "Cache usage is not allowed"); } diff --git a/src/Disks/IO/CachedReadBufferFromRemoteFS.h b/src/Disks/IO/CachedReadBufferFromRemoteFS.h index aff29dd200c..7fe3af29ef7 100644 --- a/src/Disks/IO/CachedReadBufferFromRemoteFS.h +++ b/src/Disks/IO/CachedReadBufferFromRemoteFS.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include #include @@ -81,7 +81,7 @@ private: bool writeCache(char * data, size_t size, size_t offset, FileSegment & file_segment); Poco::Logger * log; - IFileCache::Key cache_key; + FileCache::Key cache_key; String remote_fs_object_path; FileCachePtr cache; ReadSettings settings; @@ -128,7 +128,7 @@ private: CurrentMetrics::Increment metric_increment{CurrentMetrics::FilesystemCacheReadBuffers}; ProfileEvents::Counters current_file_segment_counters; - IFileCache::QueryContextHolder query_context_holder; + FileCache::QueryContextHolder query_context_holder; bool is_persistent; }; diff --git a/src/Disks/ObjectStorages/DiskObjectStorage.cpp b/src/Disks/ObjectStorages/DiskObjectStorage.cpp index 970a971d5dc..0849a3f09e3 100644 --- a/src/Disks/ObjectStorages/DiskObjectStorage.cpp +++ b/src/Disks/ObjectStorages/DiskObjectStorage.cpp @@ -10,7 +10,8 @@ #include #include #include -#include +#include +#include #include #include #include diff --git a/src/Disks/ObjectStorages/DiskObjectStorageCommon.cpp b/src/Disks/ObjectStorages/DiskObjectStorageCommon.cpp index b8ab2f49202..499791caf94 100644 --- a/src/Disks/ObjectStorages/DiskObjectStorageCommon.cpp +++ b/src/Disks/ObjectStorages/DiskObjectStorageCommon.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/Disks/ObjectStorages/IObjectStorage.h b/src/Disks/ObjectStorages/IObjectStorage.h index 1ab2d75ff86..69c1c31403d 100644 --- a/src/Disks/ObjectStorages/IObjectStorage.h +++ b/src/Disks/ObjectStorages/IObjectStorage.h @@ -16,6 +16,7 @@ #include #include #include +#include #include diff --git a/src/Disks/ObjectStorages/S3/S3ObjectStorage.cpp b/src/Disks/ObjectStorages/S3/S3ObjectStorage.cpp index 25dafac4120..901deeebefc 100644 --- a/src/Disks/ObjectStorages/S3/S3ObjectStorage.cpp +++ b/src/Disks/ObjectStorages/S3/S3ObjectStorage.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/IO/WriteBufferFromS3.cpp b/src/IO/WriteBufferFromS3.cpp index 51f0c0d0743..79da7832a34 100644 --- a/src/IO/WriteBufferFromS3.cpp +++ b/src/IO/WriteBufferFromS3.cpp @@ -3,8 +3,8 @@ #if USE_AWS_S3 #include -#include #include +#include #include #include diff --git a/src/Interpreters/AsynchronousMetrics.cpp b/src/Interpreters/AsynchronousMetrics.cpp index 9fd27fc28b6..f9bc22dd110 100644 --- a/src/Interpreters/AsynchronousMetrics.cpp +++ b/src/Interpreters/AsynchronousMetrics.cpp @@ -12,9 +12,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/src/Interpreters/InterpreterDescribeCacheQuery.cpp b/src/Interpreters/InterpreterDescribeCacheQuery.cpp index dd6df26c6af..d7c13dbb077 100644 --- a/src/Interpreters/InterpreterDescribeCacheQuery.cpp +++ b/src/Interpreters/InterpreterDescribeCacheQuery.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/Interpreters/InterpreterSystemQuery.cpp b/src/Interpreters/InterpreterSystemQuery.cpp index 695ea53e65e..b37274a3152 100644 --- a/src/Interpreters/InterpreterSystemQuery.cpp +++ b/src/Interpreters/InterpreterSystemQuery.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/Storages/System/StorageSystemFilesystemCache.cpp b/src/Storages/System/StorageSystemFilesystemCache.cpp index 2baddadec90..6d711498091 100644 --- a/src/Storages/System/StorageSystemFilesystemCache.cpp +++ b/src/Storages/System/StorageSystemFilesystemCache.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/Storages/System/StorageSystemRemoteDataPaths.cpp b/src/Storages/System/StorageSystemRemoteDataPaths.cpp index a482f5d87ca..b224a72e787 100644 --- a/src/Storages/System/StorageSystemRemoteDataPaths.cpp +++ b/src/Storages/System/StorageSystemRemoteDataPaths.cpp @@ -1,7 +1,7 @@ #include "StorageSystemRemoteDataPaths.h" #include #include -#include +#include #include #include #include From 081cd4938ad2f3f8a1cddcbe42d9dc2698e37abf Mon Sep 17 00:00:00 2001 From: KinderRiven <1339764596@qq.com> Date: Tue, 28 Jun 2022 03:35:37 +0800 Subject: [PATCH 21/43] fix style --- src/Common/FileCache.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Common/FileCache.h b/src/Common/FileCache.h index bccd2dbd458..4d2654d1491 100644 --- a/src/Common/FileCache.h +++ b/src/Common/FileCache.h @@ -24,7 +24,7 @@ namespace DB /** * Local cache for remote filesystem files, represented as a set of non-overlapping non-empty file segments. - */ + */ class FileCache : private boost::noncopyable { friend class FileSegment; From 61b580aba449a7d7bb756659c287c3f8fbfc2564 Mon Sep 17 00:00:00 2001 From: KinderRiven <1339764596@qq.com> Date: Tue, 28 Jun 2022 03:50:44 +0800 Subject: [PATCH 22/43] add note --- src/Common/FileCache.h | 5 ++--- src/Common/IFileCachePriority.h | 3 +++ src/Common/LRUFileCachePriority.h | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Common/FileCache.h b/src/Common/FileCache.h index 4d2654d1491..f809f57f389 100644 --- a/src/Common/FileCache.h +++ b/src/Common/FileCache.h @@ -22,9 +22,8 @@ namespace DB { -/** - * Local cache for remote filesystem files, represented as a set of non-overlapping non-empty file segments. - */ +/// Local cache for remote filesystem files, represented as a set of non-overlapping non-empty file segments. +/// Different caching algorithms are implemented based on IFileCachePriority. class FileCache : private boost::noncopyable { friend class FileSegment; diff --git a/src/Common/IFileCachePriority.h b/src/Common/IFileCachePriority.h index 84e1a386d24..568b778d296 100644 --- a/src/Common/IFileCachePriority.h +++ b/src/Common/IFileCachePriority.h @@ -61,6 +61,7 @@ public: virtual size_t hits() const = 0; + /// Point the iterator to the next higher priority cache record. virtual void next() const = 0; virtual bool valid() const = 0; @@ -72,6 +73,8 @@ public: /// Deletes an existing cached record. virtual void remove(std::lock_guard &) = 0; + /// Get an iterator to handle write operations. Write iterators should only + /// be allowed to call remove, use and incrementSize methods. virtual WriteIterator getWriteIterator() const = 0; virtual void incrementSize(size_t, std::lock_guard &) = 0; diff --git a/src/Common/LRUFileCachePriority.h b/src/Common/LRUFileCachePriority.h index bc9badc0af6..10ad21672dd 100644 --- a/src/Common/LRUFileCachePriority.h +++ b/src/Common/LRUFileCachePriority.h @@ -5,8 +5,8 @@ namespace DB { -/// Based on the LRU algorithm implementation, the data with the lowest priority is stored at -/// the head of the queue, and the data with the highest priority is stored at the tail. +/// Based on the LRU algorithm implementation, the record with the lowest priority is stored at +/// the head of the queue, and the record with the highest priority is stored at the tail. class LRUFileCachePriority : public IFileCachePriority { public: From 164fa1ab0e5a76c5e124df11d5fb12345c0c131a Mon Sep 17 00:00:00 2001 From: KinderRiven <1339764596@qq.com> Date: Tue, 28 Jun 2022 14:36:41 +0800 Subject: [PATCH 23/43] fix build and style --- src/Common/FileCache.cpp | 4 +--- src/Common/FileCache.h | 2 +- src/Common/IFileCachePriority.h | 9 ++------- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/Common/FileCache.cpp b/src/Common/FileCache.cpp index 2a2fc68e768..8358504c70e 100644 --- a/src/Common/FileCache.cpp +++ b/src/Common/FileCache.cpp @@ -234,7 +234,7 @@ void FileCache::initialize() } void FileCache::useCell( - const FileSegmentCell & cell, FileSegments & result, std::lock_guard & cache_lock) + const FileSegmentCell & cell, FileSegments & result, std::lock_guard & cache_lock) const { auto file_segment = cell.file_segment; @@ -945,8 +945,6 @@ void FileCache::removeIfReleasable(bool remove_persistent_files) || remove_persistent_files || allow_to_remove_persistent_segments_from_cache_by_default)) { - std::lock_guard segment_lock(file_segment->mutex); - file_segment->detach(cache_lock, segment_lock); to_remove.emplace_back(file_segment); } } diff --git a/src/Common/FileCache.h b/src/Common/FileCache.h index f809f57f389..8c6a9396b43 100644 --- a/src/Common/FileCache.h +++ b/src/Common/FileCache.h @@ -243,7 +243,7 @@ private: bool is_persistent, std::lock_guard & cache_lock); - void useCell(const FileSegmentCell & cell, FileSegments & result, std::lock_guard & cache_lock); + void useCell(const FileSegmentCell & cell, FileSegments & result, std::lock_guard & cache_lock) const; bool tryReserveForMainList( const Key & key, size_t offset, size_t size, QueryContextPtr query_context, std::lock_guard & cache_lock); diff --git a/src/Common/IFileCachePriority.h b/src/Common/IFileCachePriority.h index 568b778d296..df3ffd9fd9c 100644 --- a/src/Common/IFileCachePriority.h +++ b/src/Common/IFileCachePriority.h @@ -11,13 +11,7 @@ namespace DB { -namespace ErrorCodes -{ - extern const int NOT_IMPLEMENTED; -} - class FileCache; - class IFileCachePriority; using FileCachePriorityPtr = std::shared_ptr; @@ -66,7 +60,7 @@ public: virtual bool valid() const = 0; - /// Mark a cache record as recently used, it will update the priority + /// Mark a cache record as recently used, it will update the priority /// of the cache record according to different cache algorithms. virtual void use(std::lock_guard &) = 0; @@ -79,6 +73,7 @@ public: virtual void incrementSize(size_t, std::lock_guard &) = 0; + /// Repoint the iterator to the record with the lowest priority. virtual void seekToLowestPriority() const = 0; }; From 1b01cc8ed943c0053cebe98a7464636f2b643c33 Mon Sep 17 00:00:00 2001 From: KinderRiven <1339764596@qq.com> Date: Wed, 29 Jun 2022 17:44:38 +0800 Subject: [PATCH 24/43] fix --- src/Common/FileCache.cpp | 38 +++++++++++++------------- src/Common/FileCacheType.h | 5 +++- src/Common/IFileCachePriority.h | 26 ++++++++---------- src/Common/LRUFileCachePriority.h | 45 ++++++++++++++++++++++--------- 4 files changed, 67 insertions(+), 47 deletions(-) diff --git a/src/Common/FileCache.cpp b/src/Common/FileCache.cpp index 8358504c70e..818cc0c1b76 100644 --- a/src/Common/FileCache.cpp +++ b/src/Common/FileCache.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -40,11 +39,6 @@ FileCache::FileCache( { } -String FileCache::Key::toString() const -{ - return getHexUIntLowercase(key); -} - FileCache::Key FileCache::hash(const String & path) { return Key(sipHash128(path.data(), path.size())); @@ -323,8 +317,11 @@ FileSegments FileCache::getImpl( if (range.left <= prev_cell_range.right) { + /// segment{k-1} segment{k} /// [________] [_____ /// [___________ + /// ^ + /// range.left useCell(prev_cell, result, cache_lock); } } @@ -562,7 +559,7 @@ FileCache::FileSegmentCell * FileCache::addCell( if (stash_priority->getElementsNum(cache_lock) > max_stash_element_size) { - auto remove_priority_iter = stash_priority->getNewIterator(cache_lock)->getWriteIterator(); + auto remove_priority_iter = stash_priority->getLowestPriorityWriteIterator(cache_lock); stash_records.erase({remove_priority_iter->key(), remove_priority_iter->offset()}); remove_priority_iter->remove(cache_lock); } @@ -648,7 +645,7 @@ bool FileCache::tryReserve(const Key & key, size_t offset, size_t size, std::loc auto * cell_for_reserve = getCell(key, offset, cache_lock); - std::vector ghost; + std::vector> ghost; std::vector trash; std::vector to_evict; @@ -660,7 +657,7 @@ bool FileCache::tryReserve(const Key & key, size_t offset, size_t size, std::loc }; /// Select the cache from the LRU queue held by query for expulsion. - for (auto iter = query_context->getPriority()->getNewIterator(cache_lock); iter->valid(); iter->next()) + for (auto iter = query_context->getPriority()->getLowestPriorityWriteIterator(cache_lock); iter->valid();) { if (!is_overflow()) break; @@ -671,8 +668,10 @@ bool FileCache::tryReserve(const Key & key, size_t offset, size_t size, std::loc { /// The cache corresponding to this record may be swapped out by /// other queries, so it has become invalid. - ghost.push_back(iter->getWriteIterator()); removed_size += iter->size(); + ghost.push_back({iter->key(), iter->offset(), iter->size()}); + /// next() + iter->remove(cache_lock); } else { @@ -700,6 +699,8 @@ bool FileCache::tryReserve(const Key & key, size_t offset, size_t size, std::loc removed_size += cell_size; --queue_size; } + + iter->next(); } } @@ -718,8 +719,8 @@ bool FileCache::tryReserve(const Key & key, size_t offset, size_t size, std::loc remove_file_segment(file_segment, cell->size()); } - for (auto & iter : ghost) - query_context->remove(iter->key(), iter->offset(), iter->size(), cache_lock); + for (auto & entry : ghost) + query_context->remove(std::get<0>(entry), std::get<1>(entry), std::get<2>(entry), cache_lock); if (is_overflow()) return false; @@ -770,7 +771,7 @@ bool FileCache::tryReserveForMainList( std::vector to_evict; std::vector trash; - for (auto it = main_priority->getNewIterator(cache_lock); it->valid(); it->next()) + for (auto it = main_priority->getLowestPriorityReadIterator(cache_lock); it->valid(); it->next()) { auto entry_key = it->key(); auto entry_offset = it->offset(); @@ -926,9 +927,9 @@ void FileCache::removeIfReleasable(bool remove_persistent_files) std::lock_guard cache_lock(mutex); std::vector to_remove; - for (auto it = main_priority->getNewIterator(cache_lock); it->valid(); it->next()) + for (auto it = main_priority->getLowestPriorityReadIterator(cache_lock); it->valid(); it->next()) { - auto key = it->key(); + const auto & key = it->key(); auto offset = it->offset(); auto * cell = getCell(key, offset, cache_lock); @@ -1247,7 +1248,7 @@ String FileCache::dumpStructure(const Key & key) return dumpStructureUnlocked(key, cache_lock); } -String FileCache::dumpStructureUnlocked(const Key & key, std::lock_guard & cache_lock) +String FileCache::dumpStructureUnlocked(const Key & key, std::lock_guard &) { WriteBufferFromOwnString result; const auto & cells_by_offset = files[key]; @@ -1255,7 +1256,6 @@ String FileCache::dumpStructureUnlocked(const Key & key, std::lock_guardgetInfoForLog() << "\n"; - result << "\n\nPriority: " << main_priority->toString(cache_lock); return result.str(); } @@ -1291,9 +1291,9 @@ void FileCache::assertCacheCorrectness(std::lock_guard & cache_lock) void FileCache::assertPriorityCorrectness(std::lock_guard & cache_lock) { [[maybe_unused]] size_t total_size = 0; - for (auto it = main_priority->getNewIterator(cache_lock); it->valid(); it->next()) + for (auto it = main_priority->getLowestPriorityReadIterator(cache_lock); it->valid(); it->next()) { - auto key = it->key(); + const auto & key = it->key(); auto offset = it->offset(); auto size = it->size(); diff --git a/src/Common/FileCacheType.h b/src/Common/FileCacheType.h index 9b3ec5a6af0..cf4ab5d20c5 100644 --- a/src/Common/FileCacheType.h +++ b/src/Common/FileCacheType.h @@ -1,5 +1,6 @@ #pragma once #include +#include namespace DB { @@ -7,9 +8,11 @@ namespace DB struct FileCacheKey { UInt128 key; - String toString() const; + + String toString() const { return getHexUIntLowercase(key); } FileCacheKey() = default; + explicit FileCacheKey(const UInt128 & key_) : key(key_) { } bool operator==(const FileCacheKey & other) const { return key == other.key; } diff --git a/src/Common/IFileCachePriority.h b/src/Common/IFileCachePriority.h index df3ffd9fd9c..8b448a5ef9d 100644 --- a/src/Common/IFileCachePriority.h +++ b/src/Common/IFileCachePriority.h @@ -21,13 +21,13 @@ class IFileCachePriority public: class IIterator; friend class IIterator; + friend class FileCache; + + using Key = FileCacheKey; using ReadIterator = std::shared_ptr; using WriteIterator = std::shared_ptr; - friend class FileCache; - using Key = FileCacheKey; - struct FileCacheRecord { Key key; @@ -47,7 +47,7 @@ public: public: virtual ~IIterator() = default; - virtual Key key() const = 0; + virtual const Key & key() const = 0; virtual size_t offset() const = 0; @@ -64,17 +64,11 @@ public: /// of the cache record according to different cache algorithms. virtual void use(std::lock_guard &) = 0; - /// Deletes an existing cached record. + /// Deletes an existing cached record. And to avoid pointer suspension + /// the iterator should automatically point to the next record. virtual void remove(std::lock_guard &) = 0; - /// Get an iterator to handle write operations. Write iterators should only - /// be allowed to call remove, use and incrementSize methods. - virtual WriteIterator getWriteIterator() const = 0; - virtual void incrementSize(size_t, std::lock_guard &) = 0; - - /// Repoint the iterator to the record with the lowest priority. - virtual void seekToLowestPriority() const = 0; }; public: @@ -84,6 +78,7 @@ public: /// logical exception if the cache block already exists. virtual WriteIterator add(const Key & key, size_t offset, size_t size, std::lock_guard & cache_lock) = 0; + /// This method is used for assertions in debug mode. So we do not care about complexity here. /// Query whether a cache record exists. If it exists, return true. If not, return false. virtual bool contains(const Key & key, size_t offset, std::lock_guard & cache_lock) = 0; @@ -91,14 +86,15 @@ public: /// Returns an iterator pointing to the lowest priority cached record. /// We can traverse all cached records through the iterator's next(). - virtual ReadIterator getNewIterator(std::lock_guard & cache_lock) = 0; + virtual ReadIterator getLowestPriorityReadIterator(std::lock_guard & cache_lock) = 0; + + /// The same as getLowestPriorityReadIterator(), but it is writeable. + virtual WriteIterator getLowestPriorityWriteIterator(std::lock_guard & cache_lock) = 0; virtual size_t getElementsNum(std::lock_guard & cache_lock) const = 0; size_t getCacheSize(std::lock_guard &) const { return cache_size; } - virtual std::string toString(std::lock_guard & cache_lock) const = 0; - protected: size_t max_cache_size = 0; size_t cache_size = 0; diff --git a/src/Common/LRUFileCachePriority.h b/src/Common/LRUFileCachePriority.h index 10ad21672dd..ecbe2b47bd8 100644 --- a/src/Common/LRUFileCachePriority.h +++ b/src/Common/LRUFileCachePriority.h @@ -5,11 +5,16 @@ namespace DB { +namespace ErrorCodes +{ + extern const int LOGICAL_ERROR; +} + /// Based on the LRU algorithm implementation, the record with the lowest priority is stored at /// the head of the queue, and the record with the highest priority is stored at the tail. class LRUFileCachePriority : public IFileCachePriority { -public: +private: using LRUQueue = std::list; using LRUQueueIterator = typename LRUQueue::iterator; @@ -23,9 +28,9 @@ public: void next() const override { queue_iter++; } - bool valid() const override { return (file_cache->queue.size() && (queue_iter != file_cache->queue.end())); } + bool valid() const override { return queue_iter != file_cache->queue.end(); } - Key key() const override { return queue_iter->key; } + const Key & key() const override { return queue_iter->key; } size_t offset() const override { return queue_iter->offset; } @@ -33,14 +38,12 @@ public: size_t hits() const override { return queue_iter->hits; } - WriteIterator getWriteIterator() const override { return std::make_shared(file_cache, queue_iter); } - - void seekToLowestPriority() const override { queue_iter = file_cache->queue.begin(); } - void remove(std::lock_guard &) override { - file_cache->cache_size -= queue_iter->size; - file_cache->queue.erase(queue_iter); + auto remove_iter = queue_iter; + queue_iter++; + file_cache->cache_size -= remove_iter->size; + file_cache->queue.erase(remove_iter); } void incrementSize(size_t size_increment, std::lock_guard &) override @@ -65,6 +68,18 @@ public: WriteIterator add(const Key & key, size_t offset, size_t size, std::lock_guard &) override { +#ifndef NDEBUG + for (const auto & entry : queue) + { + if (entry.key() == key && entry.offset() == offset) + throw Exception( + ErrorCodes::LOGICAL_ERROR, + "Attempt to add duplicate queue entry to queue. (Key: {}, offset: {}, size: {})", + entry.key().toString(), + entry.offset(), + entry.size()); + } +#endif auto iter = queue.insert(queue.end(), FileCacheRecord(key, offset, size)); cache_size += size; return std::make_shared(this, iter); @@ -86,14 +101,20 @@ public: cache_size = 0; } - ReadIterator getNewIterator(std::lock_guard &) override + ReadIterator getLowestPriorityReadIterator(std::lock_guard &) override { return std::make_shared(this, queue.begin()); } - size_t getElementsNum(std::lock_guard &) const override { return queue.size(); } + WriteIterator getLowestPriorityWriteIterator(std::lock_guard &) override + { + return std::make_shared(this, queue.begin()); + } - std::string toString(std::lock_guard &) const override { return {}; } + size_t getElementsNum(std::lock_guard &) const override + { + return queue.size(); + } private: LRUQueue queue; From d2b5581632e8025a605d7832212d689a4fd85266 Mon Sep 17 00:00:00 2001 From: KinderRiven <1339764596@qq.com> Date: Wed, 29 Jun 2022 21:28:19 +0800 Subject: [PATCH 25/43] fix --- src/Common/FileCache.cpp | 2 +- src/Common/IFileCachePriority.h | 1 - src/Common/LRUFileCachePriority.cpp | 62 ++++++++++++ src/Common/LRUFileCachePriority.h | 146 ++++++++++------------------ 4 files changed, 115 insertions(+), 96 deletions(-) create mode 100644 src/Common/LRUFileCachePriority.cpp diff --git a/src/Common/FileCache.cpp b/src/Common/FileCache.cpp index 818cc0c1b76..f49f441969c 100644 --- a/src/Common/FileCache.cpp +++ b/src/Common/FileCache.cpp @@ -773,7 +773,7 @@ bool FileCache::tryReserveForMainList( for (auto it = main_priority->getLowestPriorityReadIterator(cache_lock); it->valid(); it->next()) { - auto entry_key = it->key(); + const auto & entry_key = it->key(); auto entry_offset = it->offset(); if (!is_overflow()) diff --git a/src/Common/IFileCachePriority.h b/src/Common/IFileCachePriority.h index 8b448a5ef9d..691f23a1b54 100644 --- a/src/Common/IFileCachePriority.h +++ b/src/Common/IFileCachePriority.h @@ -24,7 +24,6 @@ public: friend class FileCache; using Key = FileCacheKey; - using ReadIterator = std::shared_ptr; using WriteIterator = std::shared_ptr; diff --git a/src/Common/LRUFileCachePriority.cpp b/src/Common/LRUFileCachePriority.cpp new file mode 100644 index 00000000000..ed13e58e5fd --- /dev/null +++ b/src/Common/LRUFileCachePriority.cpp @@ -0,0 +1,62 @@ +#include + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int LOGICAL_ERROR; +} + +IFileCachePriority::WriteIterator +LRUFileCachePriority::add(const Key & key, size_t offset, size_t size, std::lock_guard &) override +{ +#ifndef NDEBUG + for (const auto & entry : queue) + { + if (entry.key == key && entry.offset == offset) + throw Exception( + ErrorCodes::LOGICAL_ERROR, + "Attempt to add duplicate queue entry to queue. (Key: {}, offset: {}, size: {})", + entry.key.toString(), + entry.offset, + entry.size); + } +#endif + auto iter = queue.insert(queue.end(), FileCacheRecord(key, offset, size)); + cache_size += size; + return std::make_shared(this, iter); +} + +bool LRUFileCachePriority::contains(const Key & key, size_t offset, std::lock_guard &) override +{ + for (const auto & record : queue) + { + if (key == record.key && offset == record.offset) + return true; + } + return false; +} + +void LRUFileCachePriority::removeAll(std::lock_guard &) override +{ + queue.clear(); + cache_size = 0; +} + +IFileCachePriority::ReadIterator LRUFileCachePriority::getLowestPriorityReadIterator(std::lock_guard &) override +{ + return std::make_shared(this, queue.begin()); +} + +IFileCachePriority::WriteIterator LRUFileCachePriority::getLowestPriorityWriteIterator(std::lock_guard &) override +{ + return std::make_shared(this, queue.begin()); +} + +size_t LRUFileCachePriority::getElementsNum(std::lock_guard &) const override +{ + return queue.size(); +} + +}; diff --git a/src/Common/LRUFileCachePriority.h b/src/Common/LRUFileCachePriority.h index ecbe2b47bd8..20d1e05b9f0 100644 --- a/src/Common/LRUFileCachePriority.h +++ b/src/Common/LRUFileCachePriority.h @@ -5,11 +5,6 @@ namespace DB { -namespace ErrorCodes -{ - extern const int LOGICAL_ERROR; -} - /// Based on the LRU algorithm implementation, the record with the lowest priority is stored at /// the head of the queue, and the record with the highest priority is stored at the tail. class LRUFileCachePriority : public IFileCachePriority @@ -17,107 +12,70 @@ class LRUFileCachePriority : public IFileCachePriority private: using LRUQueue = std::list; using LRUQueueIterator = typename LRUQueue::iterator; - - class LRUFileCacheIterator : public IIterator - { - public: - LRUFileCacheIterator(LRUFileCachePriority * file_cache_, LRUQueueIterator queue_iter_) - : file_cache(file_cache_), queue_iter(queue_iter_) - { - } - - void next() const override { queue_iter++; } - - bool valid() const override { return queue_iter != file_cache->queue.end(); } - - const Key & key() const override { return queue_iter->key; } - - size_t offset() const override { return queue_iter->offset; } - - size_t size() const override { return queue_iter->size; } - - size_t hits() const override { return queue_iter->hits; } - - void remove(std::lock_guard &) override - { - auto remove_iter = queue_iter; - queue_iter++; - file_cache->cache_size -= remove_iter->size; - file_cache->queue.erase(remove_iter); - } - - void incrementSize(size_t size_increment, std::lock_guard &) override - { - file_cache->cache_size += size_increment; - queue_iter->size += size_increment; - } - - void use(std::lock_guard &) override - { - queue_iter->hits++; - file_cache->queue.splice(file_cache->queue.end(), file_cache->queue, queue_iter); - } - - private: - mutable LRUFileCachePriority * file_cache; - mutable LRUQueueIterator queue_iter; - }; + class LRUFileCacheIterator; public: LRUFileCachePriority() = default; - WriteIterator add(const Key & key, size_t offset, size_t size, std::lock_guard &) override - { -#ifndef NDEBUG - for (const auto & entry : queue) - { - if (entry.key() == key && entry.offset() == offset) - throw Exception( - ErrorCodes::LOGICAL_ERROR, - "Attempt to add duplicate queue entry to queue. (Key: {}, offset: {}, size: {})", - entry.key().toString(), - entry.offset(), - entry.size()); - } -#endif - auto iter = queue.insert(queue.end(), FileCacheRecord(key, offset, size)); - cache_size += size; - return std::make_shared(this, iter); - } + WriteIterator add(const Key & key, size_t offset, size_t size, std::lock_guard &) override; - bool contains(const Key & key, size_t offset, std::lock_guard &) override - { - for (const auto & record : queue) - { - if (key == record.key && offset == record.offset) - return true; - } - return false; - } + bool contains(const Key & key, size_t offset, std::lock_guard &) override; - void removeAll(std::lock_guard &) override - { - queue.clear(); - cache_size = 0; - } + void removeAll(std::lock_guard &) override; - ReadIterator getLowestPriorityReadIterator(std::lock_guard &) override - { - return std::make_shared(this, queue.begin()); - } + ReadIterator getLowestPriorityReadIterator(std::lock_guard &) override; - WriteIterator getLowestPriorityWriteIterator(std::lock_guard &) override - { - return std::make_shared(this, queue.begin()); - } + WriteIterator getLowestPriorityWriteIterator(std::lock_guard &) override; - size_t getElementsNum(std::lock_guard &) const override - { - return queue.size(); - } + size_t getElementsNum(std::lock_guard &) const override; private: LRUQueue queue; }; +class LRUFileCachePriority::LRUFileCacheIterator : public IFileCachePriority::IIterator +{ +public: + LRUFileCacheIterator(LRUFileCachePriority * file_cache_, LRUFileCachePriority::LRUQueueIterator queue_iter_) + : file_cache(file_cache_), queue_iter(queue_iter_) + { + } + + void next() const override { queue_iter++; } + + bool valid() const override { return queue_iter != file_cache->queue.end(); } + + const Key & key() const override { return queue_iter->key; } + + size_t offset() const override { return queue_iter->offset; } + + size_t size() const override { return queue_iter->size; } + + size_t hits() const override { return queue_iter->hits; } + + void remove(std::lock_guard &) override + { + auto remove_iter = queue_iter; + queue_iter++; + file_cache->cache_size -= remove_iter->size; + file_cache->queue.erase(remove_iter); + } + + void incrementSize(size_t size_increment, std::lock_guard &) override + { + file_cache->cache_size += size_increment; + queue_iter->size += size_increment; + } + + void use(std::lock_guard &) override + { + queue_iter->hits++; + file_cache->queue.splice(file_cache->queue.end(), file_cache->queue, queue_iter); + } + +private: + mutable LRUFileCachePriority * file_cache; + mutable LRUFileCachePriority::LRUQueueIterator queue_iter; +}; + }; From f6a58bff4ca1ef3ada0b0b942f675a812dec6e47 Mon Sep 17 00:00:00 2001 From: KinderRiven <1339764596@qq.com> Date: Wed, 29 Jun 2022 22:45:34 +0800 Subject: [PATCH 26/43] fix build --- src/Common/IFileCachePriority.h | 6 ------ src/Common/LRUFileCachePriority.cpp | 13 ++++++------- src/Common/LRUFileCachePriority.h | 3 ++- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/Common/IFileCachePriority.h b/src/Common/IFileCachePriority.h index 691f23a1b54..fe925fd275d 100644 --- a/src/Common/IFileCachePriority.h +++ b/src/Common/IFileCachePriority.h @@ -1,17 +1,14 @@ #pragma once -#include #include #include #include #include -#include #include namespace DB { -class FileCache; class IFileCachePriority; using FileCachePriorityPtr = std::shared_ptr; @@ -20,9 +17,6 @@ class IFileCachePriority { public: class IIterator; - friend class IIterator; - friend class FileCache; - using Key = FileCacheKey; using ReadIterator = std::shared_ptr; using WriteIterator = std::shared_ptr; diff --git a/src/Common/LRUFileCachePriority.cpp b/src/Common/LRUFileCachePriority.cpp index ed13e58e5fd..c54b65f6ee0 100644 --- a/src/Common/LRUFileCachePriority.cpp +++ b/src/Common/LRUFileCachePriority.cpp @@ -8,8 +8,7 @@ namespace ErrorCodes extern const int LOGICAL_ERROR; } -IFileCachePriority::WriteIterator -LRUFileCachePriority::add(const Key & key, size_t offset, size_t size, std::lock_guard &) override +IFileCachePriority::WriteIterator LRUFileCachePriority::add(const Key & key, size_t offset, size_t size, std::lock_guard &) { #ifndef NDEBUG for (const auto & entry : queue) @@ -28,7 +27,7 @@ LRUFileCachePriority::add(const Key & key, size_t offset, size_t size, std::lock return std::make_shared(this, iter); } -bool LRUFileCachePriority::contains(const Key & key, size_t offset, std::lock_guard &) override +bool LRUFileCachePriority::contains(const Key & key, size_t offset, std::lock_guard &) { for (const auto & record : queue) { @@ -38,23 +37,23 @@ bool LRUFileCachePriority::contains(const Key & key, size_t offset, std::lock_gu return false; } -void LRUFileCachePriority::removeAll(std::lock_guard &) override +void LRUFileCachePriority::removeAll(std::lock_guard &) { queue.clear(); cache_size = 0; } -IFileCachePriority::ReadIterator LRUFileCachePriority::getLowestPriorityReadIterator(std::lock_guard &) override +IFileCachePriority::ReadIterator LRUFileCachePriority::getLowestPriorityReadIterator(std::lock_guard &) { return std::make_shared(this, queue.begin()); } -IFileCachePriority::WriteIterator LRUFileCachePriority::getLowestPriorityWriteIterator(std::lock_guard &) override +IFileCachePriority::WriteIterator LRUFileCachePriority::getLowestPriorityWriteIterator(std::lock_guard &) { return std::make_shared(this, queue.begin()); } -size_t LRUFileCachePriority::getElementsNum(std::lock_guard &) const override +size_t LRUFileCachePriority::getElementsNum(std::lock_guard &) const { return queue.size(); } diff --git a/src/Common/LRUFileCachePriority.h b/src/Common/LRUFileCachePriority.h index 20d1e05b9f0..250a55480f9 100644 --- a/src/Common/LRUFileCachePriority.h +++ b/src/Common/LRUFileCachePriority.h @@ -1,5 +1,6 @@ #pragma once +#include #include namespace DB @@ -10,9 +11,9 @@ namespace DB class LRUFileCachePriority : public IFileCachePriority { private: + class LRUFileCacheIterator; using LRUQueue = std::list; using LRUQueueIterator = typename LRUQueue::iterator; - class LRUFileCacheIterator; public: LRUFileCachePriority() = default; From fbaa70b3130e1ae66ec0fdefed5f2921dc55c9cf Mon Sep 17 00:00:00 2001 From: KinderRiven Date: Thu, 28 Jul 2022 13:23:57 +0800 Subject: [PATCH 27/43] fix --- src/Common/FileCache.cpp | 4 ++-- src/Disks/ObjectStorages/LocalObjectStorage.cpp | 2 +- src/Disks/ObjectStorages/S3/S3ObjectStorage.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Common/FileCache.cpp b/src/Common/FileCache.cpp index f49f441969c..2d13c23e837 100644 --- a/src/Common/FileCache.cpp +++ b/src/Common/FileCache.cpp @@ -63,7 +63,7 @@ static bool isQueryInitialized() { return CurrentThread::isInitialized() && CurrentThread::get().getQueryContext() - && CurrentThread::getQueryId().size != 0; + && CurrentThread::getQueryId().size() != 0; } bool FileCache::isReadOnly() @@ -82,7 +82,7 @@ FileCache::QueryContextPtr FileCache::getCurrentQueryContext(std::lock_guard & /* cache_lock */) diff --git a/src/Disks/ObjectStorages/LocalObjectStorage.cpp b/src/Disks/ObjectStorages/LocalObjectStorage.cpp index c052f2f0d77..af6dab0b8a6 100644 --- a/src/Disks/ObjectStorages/LocalObjectStorage.cpp +++ b/src/Disks/ObjectStorages/LocalObjectStorage.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/Disks/ObjectStorages/S3/S3ObjectStorage.cpp b/src/Disks/ObjectStorages/S3/S3ObjectStorage.cpp index 901deeebefc..e017e19c06c 100644 --- a/src/Disks/ObjectStorages/S3/S3ObjectStorage.cpp +++ b/src/Disks/ObjectStorages/S3/S3ObjectStorage.cpp @@ -128,7 +128,7 @@ void S3ObjectStorage::removeCacheIfExists(const std::string & path_key) if (!cache || path_key.empty()) return; - IFileCache::Key key = cache->hash(path_key); + FileCache::Key key = cache->hash(path_key); cache->removeIfExists(key); } @@ -500,7 +500,7 @@ ReadSettings S3ObjectStorage::patchSettings(const ReadSettings & read_settings) ReadSettings settings{read_settings}; if (cache) { - if (IFileCache::isReadOnly()) + if (FileCache::isReadOnly()) settings.read_from_filesystem_cache_if_exists_otherwise_bypass_cache = true; settings.remote_fs_cache = cache; From 76e0aad69e361e9a25823b7c9287b785a5111517 Mon Sep 17 00:00:00 2001 From: KinderRiven Date: Tue, 9 Aug 2022 20:21:57 +0800 Subject: [PATCH 28/43] fix --- src/Common/FileCache.cpp | 32 ++++++++++++------- src/Common/FileCache.h | 4 +-- src/Common/IFileCachePriority.h | 2 +- src/Common/LRUFileCachePriority.h | 10 +++--- src/Disks/IO/ReadBufferFromRemoteFSGather.cpp | 2 +- 5 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/Common/FileCache.cpp b/src/Common/FileCache.cpp index 2d13c23e837..d97d20310c8 100644 --- a/src/Common/FileCache.cpp +++ b/src/Common/FileCache.cpp @@ -30,8 +30,8 @@ FileCache::FileCache( , max_element_size(cache_settings_.max_elements) , max_file_segment_size(cache_settings_.max_file_segment_size) , enable_filesystem_query_cache_limit(cache_settings_.enable_filesystem_query_cache_limit) - , main_priority(std::make_shared()) - , stash_priority(std::make_shared()) + , main_priority(std::make_unique()) + , stash_priority(std::make_unique()) , max_stash_element_size(cache_settings_.max_elements) , enable_cache_hits_threshold(cache_settings_.enable_cache_hits_threshold) , log(&Poco::Logger::get("FileCache")) @@ -145,7 +145,7 @@ void FileCache::QueryContext::remove(const Key & key, size_t offset, size_t size auto record = records.find({key, offset}); if (record != records.end()) { - record->second->remove(cache_lock); + record->second->removeAndGetNext(cache_lock); records.erase({key, offset}); } } @@ -561,7 +561,7 @@ FileCache::FileSegmentCell * FileCache::addCell( { auto remove_priority_iter = stash_priority->getLowestPriorityWriteIterator(cache_lock); stash_records.erase({remove_priority_iter->key(), remove_priority_iter->offset()}); - remove_priority_iter->remove(cache_lock); + remove_priority_iter->removeAndGetNext(cache_lock); } /// For segments that do not reach the download threshold, we do not download them, but directly read them result_state = FileSegment::State::SKIP_CACHE; @@ -645,7 +645,17 @@ bool FileCache::tryReserve(const Key & key, size_t offset, size_t size, std::loc auto * cell_for_reserve = getCell(key, offset, cache_lock); - std::vector> ghost; + struct Segment + { + Key key; + size_t offset; + size_t size; + + Segment(Key key_, size_t offset_, size_t size_) + : key(key_), offset(offset_), size(size_) {} + }; + + std::vector ghost; std::vector trash; std::vector to_evict; @@ -669,9 +679,9 @@ bool FileCache::tryReserve(const Key & key, size_t offset, size_t size, std::loc /// The cache corresponding to this record may be swapped out by /// other queries, so it has become invalid. removed_size += iter->size(); - ghost.push_back({iter->key(), iter->offset(), iter->size()}); + ghost.push_back(Segment(iter->key(), iter->offset(), iter->size())); /// next() - iter->remove(cache_lock); + iter->removeAndGetNext(cache_lock); } else { @@ -720,7 +730,7 @@ bool FileCache::tryReserve(const Key & key, size_t offset, size_t size, std::loc } for (auto & entry : ghost) - query_context->remove(std::get<0>(entry), std::get<1>(entry), std::get<2>(entry), cache_lock); + query_context->remove(entry.key, entry.offset, entry.size, cache_lock); if (is_overflow()) return false; @@ -926,7 +936,7 @@ void FileCache::removeIfReleasable(bool remove_persistent_files) std::lock_guard cache_lock(mutex); - std::vector to_remove; + std::vector to_remove; for (auto it = main_priority->getLowestPriorityReadIterator(cache_lock); it->valid(); it->next()) { const auto & key = it->key(); @@ -946,7 +956,7 @@ void FileCache::removeIfReleasable(bool remove_persistent_files) || remove_persistent_files || allow_to_remove_persistent_segments_from_cache_by_default)) { - to_remove.emplace_back(file_segment); + to_remove.emplace_back(file_segment.get()); } } } @@ -981,7 +991,7 @@ void FileCache::remove( if (cell->queue_iterator) { - cell->queue_iterator->remove(cache_lock); + cell->queue_iterator->removeAndGetNext(cache_lock); } auto & offsets = files[key]; diff --git a/src/Common/FileCache.h b/src/Common/FileCache.h index 8c6a9396b43..7a25632be68 100644 --- a/src/Common/FileCache.h +++ b/src/Common/FileCache.h @@ -220,10 +220,10 @@ private: using CachedFiles = std::unordered_map; CachedFiles files; - FileCachePriorityPtr main_priority; + std::unique_ptr main_priority; FileCacheRecords stash_records; - FileCachePriorityPtr stash_priority; + std::unique_ptr stash_priority; size_t max_stash_element_size; size_t enable_cache_hits_threshold; diff --git a/src/Common/IFileCachePriority.h b/src/Common/IFileCachePriority.h index fe925fd275d..59ce3c0aebb 100644 --- a/src/Common/IFileCachePriority.h +++ b/src/Common/IFileCachePriority.h @@ -59,7 +59,7 @@ public: /// Deletes an existing cached record. And to avoid pointer suspension /// the iterator should automatically point to the next record. - virtual void remove(std::lock_guard &) = 0; + virtual void removeAndGetNext(std::lock_guard &) = 0; virtual void incrementSize(size_t, std::lock_guard &) = 0; }; diff --git a/src/Common/LRUFileCachePriority.h b/src/Common/LRUFileCachePriority.h index 250a55480f9..0f5755e1cb8 100644 --- a/src/Common/LRUFileCachePriority.h +++ b/src/Common/LRUFileCachePriority.h @@ -54,12 +54,10 @@ public: size_t hits() const override { return queue_iter->hits; } - void remove(std::lock_guard &) override + void removeAndGetNext(std::lock_guard &) override { - auto remove_iter = queue_iter; - queue_iter++; - file_cache->cache_size -= remove_iter->size; - file_cache->queue.erase(remove_iter); + file_cache->cache_size -= queue_iter->size; + queue_iter = file_cache->queue.erase(queue_iter); } void incrementSize(size_t size_increment, std::lock_guard &) override @@ -75,7 +73,7 @@ public: } private: - mutable LRUFileCachePriority * file_cache; + LRUFileCachePriority * file_cache; mutable LRUFileCachePriority::LRUQueueIterator queue_iter; }; diff --git a/src/Disks/IO/ReadBufferFromRemoteFSGather.cpp b/src/Disks/IO/ReadBufferFromRemoteFSGather.cpp index 3ac4ea07945..f21e2bd7642 100644 --- a/src/Disks/IO/ReadBufferFromRemoteFSGather.cpp +++ b/src/Disks/IO/ReadBufferFromRemoteFSGather.cpp @@ -35,7 +35,7 @@ ReadBufferFromRemoteFSGather::ReadBufferFromRemoteFSGather( with_cache = settings.remote_fs_cache && settings.enable_filesystem_cache - && (!IFileCache::isReadOnly() || settings.read_from_filesystem_cache_if_exists_otherwise_bypass_cache); + && (!FileCache::isReadOnly() || settings.read_from_filesystem_cache_if_exists_otherwise_bypass_cache); } SeekableReadBufferPtr ReadBufferFromRemoteFSGather::createImplementationBuffer(const String & path, size_t file_size) From 2ae02a49214f86ff2f00e59a6d599e21db7dc2ee Mon Sep 17 00:00:00 2001 From: KinderRiven Date: Tue, 9 Aug 2022 20:38:49 +0800 Subject: [PATCH 29/43] fix style --- src/Common/FileCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Common/FileCache.cpp b/src/Common/FileCache.cpp index d97d20310c8..2c8e62ac124 100644 --- a/src/Common/FileCache.cpp +++ b/src/Common/FileCache.cpp @@ -651,7 +651,7 @@ bool FileCache::tryReserve(const Key & key, size_t offset, size_t size, std::loc size_t offset; size_t size; - Segment(Key key_, size_t offset_, size_t size_) + Segment(Key key_, size_t offset_, size_t size_) : key(key_), offset(offset_), size(size_) {} }; From 9ba94e64f97d738e17c0d99dfb5ed7c556ac9956 Mon Sep 17 00:00:00 2001 From: KinderRiven Date: Wed, 10 Aug 2022 16:11:06 +0800 Subject: [PATCH 30/43] fix --- src/Common/FileCache.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Common/FileCache.cpp b/src/Common/FileCache.cpp index 2c8e62ac124..5a0145f0018 100644 --- a/src/Common/FileCache.cpp +++ b/src/Common/FileCache.cpp @@ -936,7 +936,7 @@ void FileCache::removeIfReleasable(bool remove_persistent_files) std::lock_guard cache_lock(mutex); - std::vector to_remove; + std::vector to_remove; for (auto it = main_priority->getLowestPriorityReadIterator(cache_lock); it->valid(); it->next()) { const auto & key = it->key(); @@ -956,7 +956,7 @@ void FileCache::removeIfReleasable(bool remove_persistent_files) || remove_persistent_files || allow_to_remove_persistent_segments_from_cache_by_default)) { - to_remove.emplace_back(file_segment.get()); + to_remove.emplace_back(file_segment); } } } From 9b7f87677dd4fa8f09c4c58c90630941a4d97746 Mon Sep 17 00:00:00 2001 From: KinderRiven Date: Wed, 10 Aug 2022 22:04:43 +0800 Subject: [PATCH 31/43] fix --- src/Common/FileCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Common/FileCache.cpp b/src/Common/FileCache.cpp index 5a0145f0018..47b7d57ae66 100644 --- a/src/Common/FileCache.cpp +++ b/src/Common/FileCache.cpp @@ -63,7 +63,7 @@ static bool isQueryInitialized() { return CurrentThread::isInitialized() && CurrentThread::get().getQueryContext() - && CurrentThread::getQueryId().size() != 0; + && !CurrentThread::getQueryId().empty(); } bool FileCache::isReadOnly() From 1aa7bbcbbd8b8c0bbd4f4dca6bc35ebde7837945 Mon Sep 17 00:00:00 2001 From: KinderRiven Date: Wed, 10 Aug 2022 23:19:26 +0800 Subject: [PATCH 32/43] fix unique_ptr --- src/Common/IFileCachePriority.h | 2 +- src/Common/LRUFileCachePriority.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Common/IFileCachePriority.h b/src/Common/IFileCachePriority.h index 59ce3c0aebb..f80266f9eea 100644 --- a/src/Common/IFileCachePriority.h +++ b/src/Common/IFileCachePriority.h @@ -18,7 +18,7 @@ class IFileCachePriority public: class IIterator; using Key = FileCacheKey; - using ReadIterator = std::shared_ptr; + using ReadIterator = std::unique_ptr; using WriteIterator = std::shared_ptr; struct FileCacheRecord diff --git a/src/Common/LRUFileCachePriority.cpp b/src/Common/LRUFileCachePriority.cpp index c54b65f6ee0..b4c4bfa338b 100644 --- a/src/Common/LRUFileCachePriority.cpp +++ b/src/Common/LRUFileCachePriority.cpp @@ -45,7 +45,7 @@ void LRUFileCachePriority::removeAll(std::lock_guard &) IFileCachePriority::ReadIterator LRUFileCachePriority::getLowestPriorityReadIterator(std::lock_guard &) { - return std::make_shared(this, queue.begin()); + return std::make_unique(this, queue.begin()); } IFileCachePriority::WriteIterator LRUFileCachePriority::getLowestPriorityWriteIterator(std::lock_guard &) From bb46bfa6d9e28cf4f48c9796e86f09a22314c07a Mon Sep 17 00:00:00 2001 From: Alexander Tokmakov Date: Wed, 10 Aug 2022 21:20:52 +0300 Subject: [PATCH 33/43] Update process_functional_tests_result.py --- docker/test/util/process_functional_tests_result.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/test/util/process_functional_tests_result.py b/docker/test/util/process_functional_tests_result.py index 647989e8421..28f3e211157 100755 --- a/docker/test/util/process_functional_tests_result.py +++ b/docker/test/util/process_functional_tests_result.py @@ -86,7 +86,7 @@ def process_test_log(log_path): test_end = True test_results = [ - (test[0], test[1], test[2], "".join(test[3]))[:4096] for test in test_results + (test[0], test[1], test[2], "".join(test[3])[:4096]) for test in test_results ] return ( From ce9c0c2da3459d63c5a95c7495f47e7467624012 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Mar=C3=ADn?= Date: Wed, 10 Aug 2022 21:53:11 +0200 Subject: [PATCH 34/43] Style --- src/Interpreters/UserDefinedSQLFunctionFactory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Interpreters/UserDefinedSQLFunctionFactory.cpp b/src/Interpreters/UserDefinedSQLFunctionFactory.cpp index db11ee12b03..2f876f00cc3 100644 --- a/src/Interpreters/UserDefinedSQLFunctionFactory.cpp +++ b/src/Interpreters/UserDefinedSQLFunctionFactory.cpp @@ -163,6 +163,6 @@ std::vector UserDefinedSQLFunctionFactory::getAllRegisteredNames() bool UserDefinedSQLFunctionFactory::empty() const { std::lock_guard lock(mutex); - return function_name_to_create_query.size() == 0; + return function_name_to_create_query.empty(); } } From aa42a42e0f5120d587e8f68bb27a1d1ddfcdfc08 Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Thu, 11 Aug 2022 08:08:27 +0000 Subject: [PATCH 35/43] Fix documentation of "modulo(a, b)" Fixes #39287 ClickHouse uses the same semantics for modulo on floats as Python, i.e. 4.2 % 2.0 = 0.2 and not as previously documented: 4.2 % 2.0 --> (drop decimal places) --> 4 % 2 = 0. Fixed the documentation. --- docs/en/sql-reference/functions/arithmetic-functions.md | 6 +++--- docs/ru/sql-reference/functions/arithmetic-functions.md | 2 +- docs/zh/sql-reference/functions/arithmetic-functions.md | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/en/sql-reference/functions/arithmetic-functions.md b/docs/en/sql-reference/functions/arithmetic-functions.md index b8d2f171bc8..45df5f7f227 100644 --- a/docs/en/sql-reference/functions/arithmetic-functions.md +++ b/docs/en/sql-reference/functions/arithmetic-functions.md @@ -55,9 +55,9 @@ Differs from ‘intDiv’ in that it returns zero when dividing by zero or when ## modulo(a, b), a % b operator -Calculates the remainder after division. -If arguments are floating-point numbers, they are pre-converted to integers by dropping the decimal portion. -The remainder is taken in the same sense as in C++. Truncated division is used for negative numbers. +Calculates the remainder when dividing `a` by `b`. +The result type is an integer if both inputs are integers. If one of the inputs is a floating-point number, the result is a floating-point number. +The remainder is computed like in C++. Truncated division is used for negative numbers. An exception is thrown when dividing by zero or when dividing a minimal negative number by minus one. ## moduloOrZero(a, b) diff --git a/docs/ru/sql-reference/functions/arithmetic-functions.md b/docs/ru/sql-reference/functions/arithmetic-functions.md index c8f2e31cb0b..19af81e609d 100644 --- a/docs/ru/sql-reference/functions/arithmetic-functions.md +++ b/docs/ru/sql-reference/functions/arithmetic-functions.md @@ -56,7 +56,7 @@ SELECT toTypeName(0), toTypeName(0 + 0), toTypeName(0 + 0 + 0), toTypeName(0 + 0 ## modulo(a, b), оператор a % b {#modulo} Вычисляет остаток от деления. -Если аргументы - числа с плавающей запятой, то они предварительно преобразуются в целые числа, путём отбрасывания дробной части. +Тип результата - целое число, если оба входа - целые числа. Если один из входов является числом с плавающей точкой, результатом будет число с плавающей точкой. Берётся остаток в том же смысле, как это делается в C++. По факту, для отрицательных чисел, используется truncated division. При делении на ноль или при делении минимального отрицательного числа на минус единицу, кидается исключение. diff --git a/docs/zh/sql-reference/functions/arithmetic-functions.md b/docs/zh/sql-reference/functions/arithmetic-functions.md index 15bec0d2107..acba761b619 100644 --- a/docs/zh/sql-reference/functions/arithmetic-functions.md +++ b/docs/zh/sql-reference/functions/arithmetic-functions.md @@ -54,7 +54,7 @@ SELECT toTypeName(0), toTypeName(0 + 0), toTypeName(0 + 0 + 0), toTypeName(0 + 0 ## modulo(a, b), a % b operator {#modulo} 计算除法后的余数。 -如果参数是浮点数,则通过删除小数部分将它们预转换为整数。 +如果两个输入都是整数,结果类型是整数。如果其中一个输入是浮点数,则结果是浮点数。 其余部分与C++中的含义相同。截断除法用于负数。 除以零或将最小负数除以-1时抛出异常。 From 1f10c4be8cb6193693c0bf27cc84f8eee5b8191a Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Thu, 11 Aug 2022 11:25:33 +0200 Subject: [PATCH 36/43] Update docs/ru/sql-reference/functions/arithmetic-functions.md Co-authored-by: Nikolay Degterinsky <43110995+evillique@users.noreply.github.com> --- docs/ru/sql-reference/functions/arithmetic-functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ru/sql-reference/functions/arithmetic-functions.md b/docs/ru/sql-reference/functions/arithmetic-functions.md index 19af81e609d..ba4340093b4 100644 --- a/docs/ru/sql-reference/functions/arithmetic-functions.md +++ b/docs/ru/sql-reference/functions/arithmetic-functions.md @@ -56,7 +56,7 @@ SELECT toTypeName(0), toTypeName(0 + 0), toTypeName(0 + 0 + 0), toTypeName(0 + 0 ## modulo(a, b), оператор a % b {#modulo} Вычисляет остаток от деления. -Тип результата - целое число, если оба входа - целые числа. Если один из входов является числом с плавающей точкой, результатом будет число с плавающей точкой. +Тип результата - целое число, если оба аргумента - целые числа. Если один из аргументов является числом с плавающей точкой, результатом будет число с плавающей точкой. Берётся остаток в том же смысле, как это делается в C++. По факту, для отрицательных чисел, используется truncated division. При делении на ноль или при делении минимального отрицательного числа на минус единицу, кидается исключение. From 9f455329994fa93ba96be984a28f08befb436d28 Mon Sep 17 00:00:00 2001 From: "Mikhail f. Shiryaev" Date: Thu, 11 Aug 2022 12:05:16 +0200 Subject: [PATCH 37/43] Use a job ID as ref text --- tests/ci/report.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ci/report.py b/tests/ci/report.py index f4569f75b82..7d84185b863 100644 --- a/tests/ci/report.py +++ b/tests/ci/report.py @@ -239,8 +239,8 @@ def create_test_html_report( ) raw_log_name = os.path.basename(raw_log_url) - if raw_log_name.endswith("?check_suite_focus=true"): - raw_log_name = "Job (github actions)" + if "?" in raw_log_name: + raw_log_name = raw_log_name.split("?")[0] result = HTML_BASE_TEST_TEMPLATE.format( title=_format_header(header, branch_name), From 261ccc35cf2004c2f0603717a42a95e0ecbe8b2c Mon Sep 17 00:00:00 2001 From: Vladimir C Date: Thu, 11 Aug 2022 12:18:44 +0200 Subject: [PATCH 38/43] Rename SettingAutoWrapper, add comment to readBinary Co-authored-by: Azat Khuzhin Co-authored-by: Maksim Kita --- src/Core/Settings.h | 2 +- src/Core/SettingsFields.cpp | 6 ++--- src/Core/SettingsFields.h | 24 ++++++++++++------- .../02381_setting_value_auto.reference | 8 +++---- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/Core/Settings.h b/src/Core/Settings.h index 727e45e3e50..f5108031dfb 100644 --- a/src/Core/Settings.h +++ b/src/Core/Settings.h @@ -212,7 +212,7 @@ static constexpr UInt64 operator""_GiB(unsigned long long value) \ M(Bool, insert_deduplicate, true, "For INSERT queries in the replicated table, specifies that deduplication of insertings blocks should be performed", 0) \ \ - M(UInt64WithAuto, insert_quorum, 0, "For INSERT queries in the replicated table, wait writing for the specified number of replicas and linearize the addition of the data. 0 - disabled.", 0) \ + M(UInt64Auto, insert_quorum, 0, "For INSERT queries in the replicated table, wait writing for the specified number of replicas and linearize the addition of the data. 0 - disabled.", 0) \ M(Milliseconds, insert_quorum_timeout, 600000, "", 0) \ M(Bool, insert_quorum_parallel, true, "For quorum INSERT queries - enable to make parallel inserts without linearizability", 0) \ M(UInt64, select_sequential_consistency, 0, "For SELECT queries from the replicated table, throw an exception if the replica does not have a chunk written with the quorum; do not read the parts that have not yet been written with the quorum.", 0) \ diff --git a/src/Core/SettingsFields.cpp b/src/Core/SettingsFields.cpp index 86b20da9e8c..5b1b6b10cc2 100644 --- a/src/Core/SettingsFields.cpp +++ b/src/Core/SettingsFields.cpp @@ -153,9 +153,9 @@ template struct SettingFieldNumber; template struct SettingFieldNumber; template struct SettingFieldNumber; -template struct SettingWithAuto>; -template struct SettingWithAuto>; -template struct SettingWithAuto>; +template struct SettingAutoWrapper>; +template struct SettingAutoWrapper>; +template struct SettingAutoWrapper>; namespace { diff --git a/src/Core/SettingsFields.h b/src/Core/SettingsFields.h index 0fcc1d6783f..68c6e85796e 100644 --- a/src/Core/SettingsFields.h +++ b/src/Core/SettingsFields.h @@ -66,7 +66,7 @@ using SettingFieldBool = SettingFieldNumber; * but when serializing 'auto' old version will see binary representation of the default value. */ template -struct SettingWithAuto +struct SettingAutoWrapper { constexpr static auto keyword = "auto"; static bool isAuto(const Field & f) { return f.getType() == Field::Types::String && f.safeGet() == keyword; } @@ -78,17 +78,17 @@ struct SettingWithAuto bool is_auto = false; bool changed = false; - explicit SettingWithAuto() : is_auto(true) {} - explicit SettingWithAuto(Type val) : is_auto(false) { base = Base(val); } + explicit SettingAutoWrapper() : is_auto(true) {} + explicit SettingAutoWrapper(Type val) : is_auto(false) { base = Base(val); } - explicit SettingWithAuto(const Field & f) + explicit SettingAutoWrapper(const Field & f) : is_auto(isAuto(f)) { if (!is_auto) base = Base(f); } - SettingWithAuto & operator=(const Field & f) + SettingAutoWrapper & operator=(const Field & f) { changed = true; if (is_auto = isAuto(f); !is_auto) @@ -115,16 +115,22 @@ struct SettingWithAuto base.writeBinary(out); } + /* + * That it is fine to reset `is_auto` here and to use default value in case `is_auto` + * because settings will be serialized only if changed. + * If they were changed they were requested to use explicit value instead of `auto`. + * And so interactions between client-server, and server-server (distributed queries), should be OK. + */ void readBinary(ReadBuffer & in) { changed = true; is_auto = false; base.readBinary(in); } Type valueOr(Type default_value) const { return is_auto ? default_value : base.value; } }; -using SettingFieldUInt64WithAuto = SettingWithAuto; -using SettingFieldInt64WithAuto = SettingWithAuto; -using SettingFieldFloatWithAuto = SettingWithAuto; +using SettingFieldUInt64Auto = SettingAutoWrapper; +using SettingFieldInt64Auto = SettingAutoWrapper; +using SettingFieldFloatAuto = SettingAutoWrapper; -/* Similar to SettingFieldUInt64WithAuto with small differences to behave like regular UInt64, supported to compatibility. +/* Similar to SettingFieldUInt64Auto with small differences to behave like regular UInt64, supported to compatibility. * When setting to 'auto' it becomes equal to the number of processor cores without taking into account SMT. * A value of 0 is also treated as 'auto', so 'auto' is parsed and serialized in the same way as 0. */ diff --git a/tests/queries/0_stateless/02381_setting_value_auto.reference b/tests/queries/0_stateless/02381_setting_value_auto.reference index 72c87cf6f7d..acc5025da5e 100644 --- a/tests/queries/0_stateless/02381_setting_value_auto.reference +++ b/tests/queries/0_stateless/02381_setting_value_auto.reference @@ -1,4 +1,4 @@ -0 0 UInt64WithAuto -auto 1 UInt64WithAuto -0 1 UInt64WithAuto -1 1 UInt64WithAuto +0 0 UInt64Auto +auto 1 UInt64Auto +0 1 UInt64Auto +1 1 UInt64Auto From 61ad12279e20d58be2870df775b4197bb996c03f Mon Sep 17 00:00:00 2001 From: vdimir Date: Thu, 11 Aug 2022 10:24:12 +0000 Subject: [PATCH 39/43] Delete files DictionaryJoinAdapter.h/cpp Follow-up for https://github.com/ClickHouse/ClickHouse/pull/38956 --- src/Interpreters/DictionaryJoinAdapter.cpp | 8 -------- src/Interpreters/DictionaryJoinAdapter.h | 7 ------- src/Interpreters/ExpressionAnalyzer.cpp | 1 - 3 files changed, 16 deletions(-) delete mode 100644 src/Interpreters/DictionaryJoinAdapter.cpp delete mode 100644 src/Interpreters/DictionaryJoinAdapter.h diff --git a/src/Interpreters/DictionaryJoinAdapter.cpp b/src/Interpreters/DictionaryJoinAdapter.cpp deleted file mode 100644 index bf0ad373204..00000000000 --- a/src/Interpreters/DictionaryJoinAdapter.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include - - -namespace DB -{ - - -} diff --git a/src/Interpreters/DictionaryJoinAdapter.h b/src/Interpreters/DictionaryJoinAdapter.h deleted file mode 100644 index dade5da94e6..00000000000 --- a/src/Interpreters/DictionaryJoinAdapter.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - - -namespace DB -{ - -} diff --git a/src/Interpreters/ExpressionAnalyzer.cpp b/src/Interpreters/ExpressionAnalyzer.cpp index 0d4fc28c5ba..105d46eed1f 100644 --- a/src/Interpreters/ExpressionAnalyzer.cpp +++ b/src/Interpreters/ExpressionAnalyzer.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include From d1051d822c02804292ac1e9486c104247e37418f Mon Sep 17 00:00:00 2001 From: Jianmei Zhang <66244986+zhangjmruc@users.noreply.github.com> Date: Thu, 11 Aug 2022 18:39:40 +0800 Subject: [PATCH 40/43] Use getSerializedFileExtension() to get correct file extension for index (#40095) --- src/Storages/MergeTree/MutateTask.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Storages/MergeTree/MutateTask.cpp b/src/Storages/MergeTree/MutateTask.cpp index d52948d71c1..63aabd20115 100644 --- a/src/Storages/MergeTree/MutateTask.cpp +++ b/src/Storages/MergeTree/MutateTask.cpp @@ -490,7 +490,8 @@ static NameSet collectFilesToSkip( for (const auto & index : indices_to_recalc) { - files_to_skip.insert(index->getFileName() + ".idx"); + /// Since MinMax index has .idx2 extension, we need to add correct extension. + files_to_skip.insert(index->getFileName() + index->getSerializedFileExtension()); files_to_skip.insert(index->getFileName() + mrk_extension); } From 96776d30287914a3bc0eba38dfdf2965c9bbec70 Mon Sep 17 00:00:00 2001 From: Vladimir C Date: Thu, 11 Aug 2022 13:15:28 +0200 Subject: [PATCH 41/43] Trim trailing whitespaces in SettingsFields.h --- src/Core/SettingsFields.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/SettingsFields.h b/src/Core/SettingsFields.h index 68c6e85796e..f01ac37d3cc 100644 --- a/src/Core/SettingsFields.h +++ b/src/Core/SettingsFields.h @@ -115,7 +115,7 @@ struct SettingAutoWrapper base.writeBinary(out); } - /* + /* * That it is fine to reset `is_auto` here and to use default value in case `is_auto` * because settings will be serialized only if changed. * If they were changed they were requested to use explicit value instead of `auto`. From cad311565c94d6799d4d854655a7ce470169379f Mon Sep 17 00:00:00 2001 From: Alexander Tokmakov Date: Thu, 11 Aug 2022 21:57:04 +0300 Subject: [PATCH 42/43] Update 02390_prometheus_ClickHouseStatusInfo_DictionaryStatus.sh --- .../02390_prometheus_ClickHouseStatusInfo_DictionaryStatus.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/queries/0_stateless/02390_prometheus_ClickHouseStatusInfo_DictionaryStatus.sh b/tests/queries/0_stateless/02390_prometheus_ClickHouseStatusInfo_DictionaryStatus.sh index 43f6d62bd10..65025858e20 100755 --- a/tests/queries/0_stateless/02390_prometheus_ClickHouseStatusInfo_DictionaryStatus.sh +++ b/tests/queries/0_stateless/02390_prometheus_ClickHouseStatusInfo_DictionaryStatus.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash +# Tags: no-ordinary-database CUR_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) # shellcheck source=../shell_config.sh From 55ff5463227f29e669cfcbd7766d0afffdb508cf Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Thu, 11 Aug 2022 20:42:59 +0000 Subject: [PATCH 43/43] Fix typo --- docs/en/sql-reference/functions/type-conversion-functions.md | 2 +- docs/ru/sql-reference/functions/type-conversion-functions.md | 2 +- docs/zh/sql-reference/functions/type-conversion-functions.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/en/sql-reference/functions/type-conversion-functions.md b/docs/en/sql-reference/functions/type-conversion-functions.md index 3612de7a0d4..ecdf34bf7ee 100644 --- a/docs/en/sql-reference/functions/type-conversion-functions.md +++ b/docs/en/sql-reference/functions/type-conversion-functions.md @@ -1241,7 +1241,7 @@ Same as for [parseDateTime64BestEffort](#parsedatetime64besteffort), except that ## toLowCardinality -Converts input parameter to the [LowCardianlity](../../sql-reference/data-types/lowcardinality.md) version of same data type. +Converts input parameter to the [LowCardinality](../../sql-reference/data-types/lowcardinality.md) version of same data type. To convert data from the `LowCardinality` data type use the [CAST](#type_conversion_function-cast) function. For example, `CAST(x as String)`. diff --git a/docs/ru/sql-reference/functions/type-conversion-functions.md b/docs/ru/sql-reference/functions/type-conversion-functions.md index 679aa00073e..7635bda78e6 100644 --- a/docs/ru/sql-reference/functions/type-conversion-functions.md +++ b/docs/ru/sql-reference/functions/type-conversion-functions.md @@ -1162,7 +1162,7 @@ FORMAT PrettyCompactMonoBlock; ## toLowCardinality {#tolowcardinality} -Преобразует входные данные в версию [LowCardianlity](../data-types/lowcardinality.md) того же типа данных. +Преобразует входные данные в версию [LowCardinality](../data-types/lowcardinality.md) того же типа данных. Чтобы преобразовать данные из типа `LowCardinality`, используйте функцию [CAST](#type_conversion_function-cast). Например, `CAST(x as String)`. diff --git a/docs/zh/sql-reference/functions/type-conversion-functions.md b/docs/zh/sql-reference/functions/type-conversion-functions.md index b72dc438e0d..d2330df6cb1 100644 --- a/docs/zh/sql-reference/functions/type-conversion-functions.md +++ b/docs/zh/sql-reference/functions/type-conversion-functions.md @@ -512,7 +512,7 @@ SELECT parseDateTimeBestEffort('10 20:19') ## toLowCardinality {#tolowcardinality} -把输入值转换为[LowCardianlity](../data-types/lowcardinality.md)的相同类型的数据。 +把输入值转换为[LowCardinality](../data-types/lowcardinality.md)的相同类型的数据。 如果要把`LowCardinality`类型的数据转换为其他类型,使用[CAST](#type_conversion_function-cast)函数。比如:`CAST(x as String)`。