mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 23:21:59 +00:00
Merge remote-tracking branch 'origin/master' into pr-right-joins
This commit is contained in:
commit
ed2da98474
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -332,7 +332,7 @@
|
|||||||
url = https://github.com/ClickHouse/usearch.git
|
url = https://github.com/ClickHouse/usearch.git
|
||||||
[submodule "contrib/SimSIMD"]
|
[submodule "contrib/SimSIMD"]
|
||||||
path = contrib/SimSIMD
|
path = contrib/SimSIMD
|
||||||
url = https://github.com/ashvardanian/SimSIMD.git
|
url = https://github.com/ClickHouse/SimSIMD.git
|
||||||
[submodule "contrib/FP16"]
|
[submodule "contrib/FP16"]
|
||||||
path = contrib/FP16
|
path = contrib/FP16
|
||||||
url = https://github.com/Maratyszcza/FP16.git
|
url = https://github.com/Maratyszcza/FP16.git
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <cctz/time_zone.h>
|
#include <cctz/time_zone.h>
|
||||||
|
#include <fmt/core.h>
|
||||||
|
|
||||||
|
|
||||||
inline std::string to_string(const std::time_t & time)
|
inline std::string to_string(const std::time_t & time)
|
||||||
@ -11,18 +12,6 @@ inline std::string to_string(const std::time_t & time)
|
|||||||
return cctz::format("%Y-%m-%d %H:%M:%S", std::chrono::system_clock::from_time_t(time), cctz::local_time_zone());
|
return cctz::format("%Y-%m-%d %H:%M:%S", std::chrono::system_clock::from_time_t(time), cctz::local_time_zone());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Clock, typename Duration = typename Clock::duration>
|
|
||||||
std::string to_string(const std::chrono::time_point<Clock, Duration> & tp)
|
|
||||||
{
|
|
||||||
// Don't use DateLUT because it shows weird characters for
|
|
||||||
// TimePoint::max(). I wish we could use C++20 format, but it's not
|
|
||||||
// there yet.
|
|
||||||
// return DateLUT::instance().timeToString(std::chrono::system_clock::to_time_t(tp));
|
|
||||||
|
|
||||||
auto in_time_t = std::chrono::system_clock::to_time_t(tp);
|
|
||||||
return to_string(in_time_t);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Rep, typename Period = std::ratio<1>>
|
template <typename Rep, typename Period = std::ratio<1>>
|
||||||
std::string to_string(const std::chrono::duration<Rep, Period> & duration)
|
std::string to_string(const std::chrono::duration<Rep, Period> & duration)
|
||||||
{
|
{
|
||||||
@ -33,6 +22,20 @@ std::string to_string(const std::chrono::duration<Rep, Period> & duration)
|
|||||||
return std::to_string(seconds_as_double.count()) + "s";
|
return std::to_string(seconds_as_double.count()) + "s";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Clock, typename Duration = typename Clock::duration>
|
||||||
|
std::string to_string(const std::chrono::time_point<Clock, Duration> & tp)
|
||||||
|
{
|
||||||
|
// Don't use DateLUT because it shows weird characters for
|
||||||
|
// TimePoint::max(). I wish we could use C++20 format, but it's not
|
||||||
|
// there yet.
|
||||||
|
// return DateLUT::instance().timeToString(std::chrono::system_clock::to_time_t(tp));
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<Clock, std::chrono::system_clock>)
|
||||||
|
return to_string(std::chrono::system_clock::to_time_t(tp));
|
||||||
|
else
|
||||||
|
return to_string(tp.time_since_epoch());
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Clock, typename Duration = typename Clock::duration>
|
template <typename Clock, typename Duration = typename Clock::duration>
|
||||||
std::ostream & operator<<(std::ostream & o, const std::chrono::time_point<Clock, Duration> & tp)
|
std::ostream & operator<<(std::ostream & o, const std::chrono::time_point<Clock, Duration> & tp)
|
||||||
{
|
{
|
||||||
@ -44,3 +47,23 @@ std::ostream & operator<<(std::ostream & o, const std::chrono::duration<Rep, Per
|
|||||||
{
|
{
|
||||||
return o << to_string(duration);
|
return o << to_string(duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Clock, typename Duration>
|
||||||
|
struct fmt::formatter<std::chrono::time_point<Clock, Duration>> : fmt::formatter<std::string>
|
||||||
|
{
|
||||||
|
template <typename FormatCtx>
|
||||||
|
auto format(const std::chrono::time_point<Clock, Duration> & tp, FormatCtx & ctx) const
|
||||||
|
{
|
||||||
|
return fmt::formatter<std::string>::format(::to_string(tp), ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Rep, typename Period>
|
||||||
|
struct fmt::formatter<std::chrono::duration<Rep, Period>> : fmt::formatter<std::string>
|
||||||
|
{
|
||||||
|
template <typename FormatCtx>
|
||||||
|
auto format(const std::chrono::duration<Rep, Period> & duration, FormatCtx & ctx) const
|
||||||
|
{
|
||||||
|
return fmt::formatter<std::string>::format(::to_string(duration), ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
2
contrib/SimSIMD
vendored
2
contrib/SimSIMD
vendored
@ -1 +1 @@
|
|||||||
Subproject commit ff51434d90c66f916e94ff05b24530b127aa4cff
|
Subproject commit 935fef2964bc38e995c5f465b42259a35b8cf0d3
|
2
contrib/arrow
vendored
2
contrib/arrow
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 5cfccd8ea65f33d4517e7409815d761c7650b45d
|
Subproject commit 6e2574f5013a005c050c9a7787d341aef09d0063
|
@ -213,13 +213,19 @@ target_include_directories(_orc SYSTEM PRIVATE
|
|||||||
set(LIBRARY_DIR "${ClickHouse_SOURCE_DIR}/contrib/arrow/cpp/src/arrow")
|
set(LIBRARY_DIR "${ClickHouse_SOURCE_DIR}/contrib/arrow/cpp/src/arrow")
|
||||||
|
|
||||||
# arrow/cpp/src/arrow/CMakeLists.txt (ARROW_SRCS + ARROW_COMPUTE + ARROW_IPC)
|
# arrow/cpp/src/arrow/CMakeLists.txt (ARROW_SRCS + ARROW_COMPUTE + ARROW_IPC)
|
||||||
|
# find . \( -iname \*.cc -o -iname \*.cpp -o -iname \*.c \) | sort | awk '{print "\"${LIBRARY_DIR}" substr($1,2) "\"" }' | grep -v 'test.cc' | grep -v 'json' | grep -v 'flight' \|
|
||||||
|
# grep -v 'csv' | grep -v 'acero' | grep -v 'dataset' | grep -v 'testing' | grep -v 'gpu' | grep -v 'engine' | grep -v 'filesystem' | grep -v 'benchmark.cc'
|
||||||
set(ARROW_SRCS
|
set(ARROW_SRCS
|
||||||
|
"${LIBRARY_DIR}/adapters/orc/adapter.cc"
|
||||||
|
"${LIBRARY_DIR}/adapters/orc/options.cc"
|
||||||
|
"${LIBRARY_DIR}/adapters/orc/util.cc"
|
||||||
"${LIBRARY_DIR}/array/array_base.cc"
|
"${LIBRARY_DIR}/array/array_base.cc"
|
||||||
"${LIBRARY_DIR}/array/array_binary.cc"
|
"${LIBRARY_DIR}/array/array_binary.cc"
|
||||||
"${LIBRARY_DIR}/array/array_decimal.cc"
|
"${LIBRARY_DIR}/array/array_decimal.cc"
|
||||||
"${LIBRARY_DIR}/array/array_dict.cc"
|
"${LIBRARY_DIR}/array/array_dict.cc"
|
||||||
"${LIBRARY_DIR}/array/array_nested.cc"
|
"${LIBRARY_DIR}/array/array_nested.cc"
|
||||||
"${LIBRARY_DIR}/array/array_primitive.cc"
|
"${LIBRARY_DIR}/array/array_primitive.cc"
|
||||||
|
"${LIBRARY_DIR}/array/array_run_end.cc"
|
||||||
"${LIBRARY_DIR}/array/builder_adaptive.cc"
|
"${LIBRARY_DIR}/array/builder_adaptive.cc"
|
||||||
"${LIBRARY_DIR}/array/builder_base.cc"
|
"${LIBRARY_DIR}/array/builder_base.cc"
|
||||||
"${LIBRARY_DIR}/array/builder_binary.cc"
|
"${LIBRARY_DIR}/array/builder_binary.cc"
|
||||||
@ -227,124 +233,26 @@ set(ARROW_SRCS
|
|||||||
"${LIBRARY_DIR}/array/builder_dict.cc"
|
"${LIBRARY_DIR}/array/builder_dict.cc"
|
||||||
"${LIBRARY_DIR}/array/builder_nested.cc"
|
"${LIBRARY_DIR}/array/builder_nested.cc"
|
||||||
"${LIBRARY_DIR}/array/builder_primitive.cc"
|
"${LIBRARY_DIR}/array/builder_primitive.cc"
|
||||||
"${LIBRARY_DIR}/array/builder_union.cc"
|
|
||||||
"${LIBRARY_DIR}/array/builder_run_end.cc"
|
"${LIBRARY_DIR}/array/builder_run_end.cc"
|
||||||
"${LIBRARY_DIR}/array/array_run_end.cc"
|
"${LIBRARY_DIR}/array/builder_union.cc"
|
||||||
"${LIBRARY_DIR}/array/concatenate.cc"
|
"${LIBRARY_DIR}/array/concatenate.cc"
|
||||||
"${LIBRARY_DIR}/array/data.cc"
|
"${LIBRARY_DIR}/array/data.cc"
|
||||||
"${LIBRARY_DIR}/array/diff.cc"
|
"${LIBRARY_DIR}/array/diff.cc"
|
||||||
"${LIBRARY_DIR}/array/util.cc"
|
"${LIBRARY_DIR}/array/util.cc"
|
||||||
"${LIBRARY_DIR}/array/validate.cc"
|
"${LIBRARY_DIR}/array/validate.cc"
|
||||||
"${LIBRARY_DIR}/builder.cc"
|
|
||||||
"${LIBRARY_DIR}/buffer.cc"
|
"${LIBRARY_DIR}/buffer.cc"
|
||||||
"${LIBRARY_DIR}/chunked_array.cc"
|
"${LIBRARY_DIR}/builder.cc"
|
||||||
"${LIBRARY_DIR}/chunk_resolver.cc"
|
|
||||||
"${LIBRARY_DIR}/compare.cc"
|
|
||||||
"${LIBRARY_DIR}/config.cc"
|
|
||||||
"${LIBRARY_DIR}/datum.cc"
|
|
||||||
"${LIBRARY_DIR}/device.cc"
|
|
||||||
"${LIBRARY_DIR}/extension_type.cc"
|
|
||||||
"${LIBRARY_DIR}/memory_pool.cc"
|
|
||||||
"${LIBRARY_DIR}/pretty_print.cc"
|
|
||||||
"${LIBRARY_DIR}/record_batch.cc"
|
|
||||||
"${LIBRARY_DIR}/result.cc"
|
|
||||||
"${LIBRARY_DIR}/scalar.cc"
|
|
||||||
"${LIBRARY_DIR}/sparse_tensor.cc"
|
|
||||||
"${LIBRARY_DIR}/status.cc"
|
|
||||||
"${LIBRARY_DIR}/table.cc"
|
|
||||||
"${LIBRARY_DIR}/table_builder.cc"
|
|
||||||
"${LIBRARY_DIR}/tensor.cc"
|
|
||||||
"${LIBRARY_DIR}/tensor/coo_converter.cc"
|
|
||||||
"${LIBRARY_DIR}/tensor/csf_converter.cc"
|
|
||||||
"${LIBRARY_DIR}/tensor/csx_converter.cc"
|
|
||||||
"${LIBRARY_DIR}/type.cc"
|
|
||||||
"${LIBRARY_DIR}/visitor.cc"
|
|
||||||
"${LIBRARY_DIR}/c/bridge.cc"
|
"${LIBRARY_DIR}/c/bridge.cc"
|
||||||
"${LIBRARY_DIR}/io/buffered.cc"
|
"${LIBRARY_DIR}/c/dlpack.cc"
|
||||||
"${LIBRARY_DIR}/io/caching.cc"
|
"${LIBRARY_DIR}/chunk_resolver.cc"
|
||||||
"${LIBRARY_DIR}/io/compressed.cc"
|
"${LIBRARY_DIR}/chunked_array.cc"
|
||||||
"${LIBRARY_DIR}/io/file.cc"
|
"${LIBRARY_DIR}/compare.cc"
|
||||||
"${LIBRARY_DIR}/io/hdfs.cc"
|
|
||||||
"${LIBRARY_DIR}/io/hdfs_internal.cc"
|
|
||||||
"${LIBRARY_DIR}/io/interfaces.cc"
|
|
||||||
"${LIBRARY_DIR}/io/memory.cc"
|
|
||||||
"${LIBRARY_DIR}/io/slow.cc"
|
|
||||||
"${LIBRARY_DIR}/io/stdio.cc"
|
|
||||||
"${LIBRARY_DIR}/io/transform.cc"
|
|
||||||
"${LIBRARY_DIR}/util/async_util.cc"
|
|
||||||
"${LIBRARY_DIR}/util/basic_decimal.cc"
|
|
||||||
"${LIBRARY_DIR}/util/bit_block_counter.cc"
|
|
||||||
"${LIBRARY_DIR}/util/bit_run_reader.cc"
|
|
||||||
"${LIBRARY_DIR}/util/bit_util.cc"
|
|
||||||
"${LIBRARY_DIR}/util/bitmap.cc"
|
|
||||||
"${LIBRARY_DIR}/util/bitmap_builders.cc"
|
|
||||||
"${LIBRARY_DIR}/util/bitmap_ops.cc"
|
|
||||||
"${LIBRARY_DIR}/util/bpacking.cc"
|
|
||||||
"${LIBRARY_DIR}/util/cancel.cc"
|
|
||||||
"${LIBRARY_DIR}/util/compression.cc"
|
|
||||||
"${LIBRARY_DIR}/util/counting_semaphore.cc"
|
|
||||||
"${LIBRARY_DIR}/util/cpu_info.cc"
|
|
||||||
"${LIBRARY_DIR}/util/decimal.cc"
|
|
||||||
"${LIBRARY_DIR}/util/delimiting.cc"
|
|
||||||
"${LIBRARY_DIR}/util/formatting.cc"
|
|
||||||
"${LIBRARY_DIR}/util/future.cc"
|
|
||||||
"${LIBRARY_DIR}/util/int_util.cc"
|
|
||||||
"${LIBRARY_DIR}/util/io_util.cc"
|
|
||||||
"${LIBRARY_DIR}/util/logging.cc"
|
|
||||||
"${LIBRARY_DIR}/util/key_value_metadata.cc"
|
|
||||||
"${LIBRARY_DIR}/util/memory.cc"
|
|
||||||
"${LIBRARY_DIR}/util/mutex.cc"
|
|
||||||
"${LIBRARY_DIR}/util/string.cc"
|
|
||||||
"${LIBRARY_DIR}/util/string_builder.cc"
|
|
||||||
"${LIBRARY_DIR}/util/task_group.cc"
|
|
||||||
"${LIBRARY_DIR}/util/tdigest.cc"
|
|
||||||
"${LIBRARY_DIR}/util/thread_pool.cc"
|
|
||||||
"${LIBRARY_DIR}/util/time.cc"
|
|
||||||
"${LIBRARY_DIR}/util/trie.cc"
|
|
||||||
"${LIBRARY_DIR}/util/unreachable.cc"
|
|
||||||
"${LIBRARY_DIR}/util/uri.cc"
|
|
||||||
"${LIBRARY_DIR}/util/utf8.cc"
|
|
||||||
"${LIBRARY_DIR}/util/value_parsing.cc"
|
|
||||||
"${LIBRARY_DIR}/util/byte_size.cc"
|
|
||||||
"${LIBRARY_DIR}/util/debug.cc"
|
|
||||||
"${LIBRARY_DIR}/util/tracing.cc"
|
|
||||||
"${LIBRARY_DIR}/util/atfork_internal.cc"
|
|
||||||
"${LIBRARY_DIR}/util/crc32.cc"
|
|
||||||
"${LIBRARY_DIR}/util/hashing.cc"
|
|
||||||
"${LIBRARY_DIR}/util/ree_util.cc"
|
|
||||||
"${LIBRARY_DIR}/util/union_util.cc"
|
|
||||||
"${LIBRARY_DIR}/vendored/base64.cpp"
|
|
||||||
"${LIBRARY_DIR}/vendored/datetime/tz.cpp"
|
|
||||||
"${LIBRARY_DIR}/vendored/musl/strptime.c"
|
|
||||||
"${LIBRARY_DIR}/vendored/uriparser/UriCommon.c"
|
|
||||||
"${LIBRARY_DIR}/vendored/uriparser/UriCompare.c"
|
|
||||||
"${LIBRARY_DIR}/vendored/uriparser/UriEscape.c"
|
|
||||||
"${LIBRARY_DIR}/vendored/uriparser/UriFile.c"
|
|
||||||
"${LIBRARY_DIR}/vendored/uriparser/UriIp4Base.c"
|
|
||||||
"${LIBRARY_DIR}/vendored/uriparser/UriIp4.c"
|
|
||||||
"${LIBRARY_DIR}/vendored/uriparser/UriMemory.c"
|
|
||||||
"${LIBRARY_DIR}/vendored/uriparser/UriNormalizeBase.c"
|
|
||||||
"${LIBRARY_DIR}/vendored/uriparser/UriNormalize.c"
|
|
||||||
"${LIBRARY_DIR}/vendored/uriparser/UriParseBase.c"
|
|
||||||
"${LIBRARY_DIR}/vendored/uriparser/UriParse.c"
|
|
||||||
"${LIBRARY_DIR}/vendored/uriparser/UriQuery.c"
|
|
||||||
"${LIBRARY_DIR}/vendored/uriparser/UriRecompose.c"
|
|
||||||
"${LIBRARY_DIR}/vendored/uriparser/UriResolve.c"
|
|
||||||
"${LIBRARY_DIR}/vendored/uriparser/UriShorten.c"
|
|
||||||
"${LIBRARY_DIR}/vendored/double-conversion/bignum.cc"
|
|
||||||
"${LIBRARY_DIR}/vendored/double-conversion/bignum-dtoa.cc"
|
|
||||||
"${LIBRARY_DIR}/vendored/double-conversion/cached-powers.cc"
|
|
||||||
"${LIBRARY_DIR}/vendored/double-conversion/double-to-string.cc"
|
|
||||||
"${LIBRARY_DIR}/vendored/double-conversion/fast-dtoa.cc"
|
|
||||||
"${LIBRARY_DIR}/vendored/double-conversion/fixed-dtoa.cc"
|
|
||||||
"${LIBRARY_DIR}/vendored/double-conversion/string-to-double.cc"
|
|
||||||
"${LIBRARY_DIR}/vendored/double-conversion/strtod.cc"
|
|
||||||
|
|
||||||
"${LIBRARY_DIR}/compute/api_aggregate.cc"
|
"${LIBRARY_DIR}/compute/api_aggregate.cc"
|
||||||
"${LIBRARY_DIR}/compute/api_scalar.cc"
|
"${LIBRARY_DIR}/compute/api_scalar.cc"
|
||||||
"${LIBRARY_DIR}/compute/api_vector.cc"
|
"${LIBRARY_DIR}/compute/api_vector.cc"
|
||||||
"${LIBRARY_DIR}/compute/cast.cc"
|
"${LIBRARY_DIR}/compute/cast.cc"
|
||||||
"${LIBRARY_DIR}/compute/exec.cc"
|
"${LIBRARY_DIR}/compute/exec.cc"
|
||||||
|
"${LIBRARY_DIR}/compute/expression.cc"
|
||||||
"${LIBRARY_DIR}/compute/function.cc"
|
"${LIBRARY_DIR}/compute/function.cc"
|
||||||
"${LIBRARY_DIR}/compute/function_internal.cc"
|
"${LIBRARY_DIR}/compute/function_internal.cc"
|
||||||
"${LIBRARY_DIR}/compute/kernel.cc"
|
"${LIBRARY_DIR}/compute/kernel.cc"
|
||||||
@ -355,6 +263,7 @@ set(ARROW_SRCS
|
|||||||
"${LIBRARY_DIR}/compute/kernels/aggregate_var_std.cc"
|
"${LIBRARY_DIR}/compute/kernels/aggregate_var_std.cc"
|
||||||
"${LIBRARY_DIR}/compute/kernels/codegen_internal.cc"
|
"${LIBRARY_DIR}/compute/kernels/codegen_internal.cc"
|
||||||
"${LIBRARY_DIR}/compute/kernels/hash_aggregate.cc"
|
"${LIBRARY_DIR}/compute/kernels/hash_aggregate.cc"
|
||||||
|
"${LIBRARY_DIR}/compute/kernels/ree_util_internal.cc"
|
||||||
"${LIBRARY_DIR}/compute/kernels/row_encoder.cc"
|
"${LIBRARY_DIR}/compute/kernels/row_encoder.cc"
|
||||||
"${LIBRARY_DIR}/compute/kernels/scalar_arithmetic.cc"
|
"${LIBRARY_DIR}/compute/kernels/scalar_arithmetic.cc"
|
||||||
"${LIBRARY_DIR}/compute/kernels/scalar_boolean.cc"
|
"${LIBRARY_DIR}/compute/kernels/scalar_boolean.cc"
|
||||||
@ -382,30 +291,139 @@ set(ARROW_SRCS
|
|||||||
"${LIBRARY_DIR}/compute/kernels/vector_cumulative_ops.cc"
|
"${LIBRARY_DIR}/compute/kernels/vector_cumulative_ops.cc"
|
||||||
"${LIBRARY_DIR}/compute/kernels/vector_hash.cc"
|
"${LIBRARY_DIR}/compute/kernels/vector_hash.cc"
|
||||||
"${LIBRARY_DIR}/compute/kernels/vector_nested.cc"
|
"${LIBRARY_DIR}/compute/kernels/vector_nested.cc"
|
||||||
|
"${LIBRARY_DIR}/compute/kernels/vector_pairwise.cc"
|
||||||
"${LIBRARY_DIR}/compute/kernels/vector_rank.cc"
|
"${LIBRARY_DIR}/compute/kernels/vector_rank.cc"
|
||||||
"${LIBRARY_DIR}/compute/kernels/vector_replace.cc"
|
"${LIBRARY_DIR}/compute/kernels/vector_replace.cc"
|
||||||
|
"${LIBRARY_DIR}/compute/kernels/vector_run_end_encode.cc"
|
||||||
"${LIBRARY_DIR}/compute/kernels/vector_select_k.cc"
|
"${LIBRARY_DIR}/compute/kernels/vector_select_k.cc"
|
||||||
"${LIBRARY_DIR}/compute/kernels/vector_selection.cc"
|
"${LIBRARY_DIR}/compute/kernels/vector_selection.cc"
|
||||||
"${LIBRARY_DIR}/compute/kernels/vector_sort.cc"
|
|
||||||
"${LIBRARY_DIR}/compute/kernels/vector_selection_internal.cc"
|
|
||||||
"${LIBRARY_DIR}/compute/kernels/vector_selection_filter_internal.cc"
|
"${LIBRARY_DIR}/compute/kernels/vector_selection_filter_internal.cc"
|
||||||
|
"${LIBRARY_DIR}/compute/kernels/vector_selection_internal.cc"
|
||||||
"${LIBRARY_DIR}/compute/kernels/vector_selection_take_internal.cc"
|
"${LIBRARY_DIR}/compute/kernels/vector_selection_take_internal.cc"
|
||||||
"${LIBRARY_DIR}/compute/light_array.cc"
|
"${LIBRARY_DIR}/compute/kernels/vector_sort.cc"
|
||||||
"${LIBRARY_DIR}/compute/registry.cc"
|
"${LIBRARY_DIR}/compute/key_hash_internal.cc"
|
||||||
"${LIBRARY_DIR}/compute/expression.cc"
|
"${LIBRARY_DIR}/compute/key_map_internal.cc"
|
||||||
|
"${LIBRARY_DIR}/compute/light_array_internal.cc"
|
||||||
"${LIBRARY_DIR}/compute/ordering.cc"
|
"${LIBRARY_DIR}/compute/ordering.cc"
|
||||||
|
"${LIBRARY_DIR}/compute/registry.cc"
|
||||||
"${LIBRARY_DIR}/compute/row/compare_internal.cc"
|
"${LIBRARY_DIR}/compute/row/compare_internal.cc"
|
||||||
"${LIBRARY_DIR}/compute/row/encode_internal.cc"
|
"${LIBRARY_DIR}/compute/row/encode_internal.cc"
|
||||||
"${LIBRARY_DIR}/compute/row/grouper.cc"
|
"${LIBRARY_DIR}/compute/row/grouper.cc"
|
||||||
"${LIBRARY_DIR}/compute/row/row_internal.cc"
|
"${LIBRARY_DIR}/compute/row/row_internal.cc"
|
||||||
|
"${LIBRARY_DIR}/compute/util.cc"
|
||||||
|
"${LIBRARY_DIR}/config.cc"
|
||||||
|
"${LIBRARY_DIR}/datum.cc"
|
||||||
|
"${LIBRARY_DIR}/device.cc"
|
||||||
|
"${LIBRARY_DIR}/extension_type.cc"
|
||||||
|
"${LIBRARY_DIR}/integration/c_data_integration_internal.cc"
|
||||||
|
"${LIBRARY_DIR}/io/buffered.cc"
|
||||||
|
"${LIBRARY_DIR}/io/caching.cc"
|
||||||
|
"${LIBRARY_DIR}/io/compressed.cc"
|
||||||
|
"${LIBRARY_DIR}/io/file.cc"
|
||||||
|
"${LIBRARY_DIR}/io/hdfs.cc"
|
||||||
|
"${LIBRARY_DIR}/io/hdfs_internal.cc"
|
||||||
|
"${LIBRARY_DIR}/io/interfaces.cc"
|
||||||
|
"${LIBRARY_DIR}/io/memory.cc"
|
||||||
|
"${LIBRARY_DIR}/io/slow.cc"
|
||||||
|
"${LIBRARY_DIR}/io/stdio.cc"
|
||||||
|
"${LIBRARY_DIR}/io/transform.cc"
|
||||||
"${LIBRARY_DIR}/ipc/dictionary.cc"
|
"${LIBRARY_DIR}/ipc/dictionary.cc"
|
||||||
"${LIBRARY_DIR}/ipc/feather.cc"
|
"${LIBRARY_DIR}/ipc/feather.cc"
|
||||||
|
"${LIBRARY_DIR}/ipc/file_to_stream.cc"
|
||||||
"${LIBRARY_DIR}/ipc/message.cc"
|
"${LIBRARY_DIR}/ipc/message.cc"
|
||||||
"${LIBRARY_DIR}/ipc/metadata_internal.cc"
|
"${LIBRARY_DIR}/ipc/metadata_internal.cc"
|
||||||
"${LIBRARY_DIR}/ipc/options.cc"
|
"${LIBRARY_DIR}/ipc/options.cc"
|
||||||
"${LIBRARY_DIR}/ipc/reader.cc"
|
"${LIBRARY_DIR}/ipc/reader.cc"
|
||||||
|
"${LIBRARY_DIR}/ipc/stream_to_file.cc"
|
||||||
"${LIBRARY_DIR}/ipc/writer.cc"
|
"${LIBRARY_DIR}/ipc/writer.cc"
|
||||||
|
"${LIBRARY_DIR}/memory_pool.cc"
|
||||||
|
"${LIBRARY_DIR}/pretty_print.cc"
|
||||||
|
"${LIBRARY_DIR}/record_batch.cc"
|
||||||
|
"${LIBRARY_DIR}/result.cc"
|
||||||
|
"${LIBRARY_DIR}/scalar.cc"
|
||||||
|
"${LIBRARY_DIR}/sparse_tensor.cc"
|
||||||
|
"${LIBRARY_DIR}/status.cc"
|
||||||
|
"${LIBRARY_DIR}/table.cc"
|
||||||
|
"${LIBRARY_DIR}/table_builder.cc"
|
||||||
|
"${LIBRARY_DIR}/tensor.cc"
|
||||||
|
"${LIBRARY_DIR}/tensor/coo_converter.cc"
|
||||||
|
"${LIBRARY_DIR}/tensor/csf_converter.cc"
|
||||||
|
"${LIBRARY_DIR}/tensor/csx_converter.cc"
|
||||||
|
"${LIBRARY_DIR}/type.cc"
|
||||||
|
"${LIBRARY_DIR}/type_traits.cc"
|
||||||
|
"${LIBRARY_DIR}/util/align_util.cc"
|
||||||
|
"${LIBRARY_DIR}/util/async_util.cc"
|
||||||
|
"${LIBRARY_DIR}/util/atfork_internal.cc"
|
||||||
|
"${LIBRARY_DIR}/util/basic_decimal.cc"
|
||||||
|
"${LIBRARY_DIR}/util/bit_block_counter.cc"
|
||||||
|
"${LIBRARY_DIR}/util/bit_run_reader.cc"
|
||||||
|
"${LIBRARY_DIR}/util/bit_util.cc"
|
||||||
|
"${LIBRARY_DIR}/util/bitmap.cc"
|
||||||
|
"${LIBRARY_DIR}/util/bitmap_builders.cc"
|
||||||
|
"${LIBRARY_DIR}/util/bitmap_ops.cc"
|
||||||
|
"${LIBRARY_DIR}/util/bpacking.cc"
|
||||||
|
"${LIBRARY_DIR}/util/byte_size.cc"
|
||||||
|
"${LIBRARY_DIR}/util/cancel.cc"
|
||||||
|
"${LIBRARY_DIR}/util/compression.cc"
|
||||||
|
"${LIBRARY_DIR}/util/counting_semaphore.cc"
|
||||||
|
"${LIBRARY_DIR}/util/cpu_info.cc"
|
||||||
|
"${LIBRARY_DIR}/util/crc32.cc"
|
||||||
|
"${LIBRARY_DIR}/util/debug.cc"
|
||||||
|
"${LIBRARY_DIR}/util/decimal.cc"
|
||||||
|
"${LIBRARY_DIR}/util/delimiting.cc"
|
||||||
|
"${LIBRARY_DIR}/util/dict_util.cc"
|
||||||
|
"${LIBRARY_DIR}/util/float16.cc"
|
||||||
|
"${LIBRARY_DIR}/util/formatting.cc"
|
||||||
|
"${LIBRARY_DIR}/util/future.cc"
|
||||||
|
"${LIBRARY_DIR}/util/hashing.cc"
|
||||||
|
"${LIBRARY_DIR}/util/int_util.cc"
|
||||||
|
"${LIBRARY_DIR}/util/io_util.cc"
|
||||||
|
"${LIBRARY_DIR}/util/key_value_metadata.cc"
|
||||||
|
"${LIBRARY_DIR}/util/list_util.cc"
|
||||||
|
"${LIBRARY_DIR}/util/logging.cc"
|
||||||
|
"${LIBRARY_DIR}/util/memory.cc"
|
||||||
|
"${LIBRARY_DIR}/util/mutex.cc"
|
||||||
|
"${LIBRARY_DIR}/util/ree_util.cc"
|
||||||
|
"${LIBRARY_DIR}/util/string.cc"
|
||||||
|
"${LIBRARY_DIR}/util/string_builder.cc"
|
||||||
|
"${LIBRARY_DIR}/util/task_group.cc"
|
||||||
|
"${LIBRARY_DIR}/util/tdigest.cc"
|
||||||
|
"${LIBRARY_DIR}/util/thread_pool.cc"
|
||||||
|
"${LIBRARY_DIR}/util/time.cc"
|
||||||
|
"${LIBRARY_DIR}/util/tracing.cc"
|
||||||
|
"${LIBRARY_DIR}/util/trie.cc"
|
||||||
|
"${LIBRARY_DIR}/util/union_util.cc"
|
||||||
|
"${LIBRARY_DIR}/util/unreachable.cc"
|
||||||
|
"${LIBRARY_DIR}/util/uri.cc"
|
||||||
|
"${LIBRARY_DIR}/util/utf8.cc"
|
||||||
|
"${LIBRARY_DIR}/util/value_parsing.cc"
|
||||||
|
"${LIBRARY_DIR}/vendored/base64.cpp"
|
||||||
|
"${LIBRARY_DIR}/vendored/datetime/tz.cpp"
|
||||||
|
"${LIBRARY_DIR}/vendored/double-conversion/bignum-dtoa.cc"
|
||||||
|
"${LIBRARY_DIR}/vendored/double-conversion/bignum.cc"
|
||||||
|
"${LIBRARY_DIR}/vendored/double-conversion/cached-powers.cc"
|
||||||
|
"${LIBRARY_DIR}/vendored/double-conversion/double-to-string.cc"
|
||||||
|
"${LIBRARY_DIR}/vendored/double-conversion/fast-dtoa.cc"
|
||||||
|
"${LIBRARY_DIR}/vendored/double-conversion/fixed-dtoa.cc"
|
||||||
|
"${LIBRARY_DIR}/vendored/double-conversion/string-to-double.cc"
|
||||||
|
"${LIBRARY_DIR}/vendored/double-conversion/strtod.cc"
|
||||||
|
"${LIBRARY_DIR}/vendored/musl/strptime.c"
|
||||||
|
"${LIBRARY_DIR}/vendored/uriparser/UriCommon.c"
|
||||||
|
"${LIBRARY_DIR}/vendored/uriparser/UriCompare.c"
|
||||||
|
"${LIBRARY_DIR}/vendored/uriparser/UriEscape.c"
|
||||||
|
"${LIBRARY_DIR}/vendored/uriparser/UriFile.c"
|
||||||
|
"${LIBRARY_DIR}/vendored/uriparser/UriIp4.c"
|
||||||
|
"${LIBRARY_DIR}/vendored/uriparser/UriIp4Base.c"
|
||||||
|
"${LIBRARY_DIR}/vendored/uriparser/UriMemory.c"
|
||||||
|
"${LIBRARY_DIR}/vendored/uriparser/UriNormalize.c"
|
||||||
|
"${LIBRARY_DIR}/vendored/uriparser/UriNormalizeBase.c"
|
||||||
|
"${LIBRARY_DIR}/vendored/uriparser/UriParse.c"
|
||||||
|
"${LIBRARY_DIR}/vendored/uriparser/UriParseBase.c"
|
||||||
|
"${LIBRARY_DIR}/vendored/uriparser/UriQuery.c"
|
||||||
|
"${LIBRARY_DIR}/vendored/uriparser/UriRecompose.c"
|
||||||
|
"${LIBRARY_DIR}/vendored/uriparser/UriResolve.c"
|
||||||
|
"${LIBRARY_DIR}/vendored/uriparser/UriShorten.c"
|
||||||
|
"${LIBRARY_DIR}/visitor.cc"
|
||||||
|
|
||||||
"${ARROW_SRC_DIR}/arrow/adapters/orc/adapter.cc"
|
"${ARROW_SRC_DIR}/arrow/adapters/orc/adapter.cc"
|
||||||
"${ARROW_SRC_DIR}/arrow/adapters/orc/util.cc"
|
"${ARROW_SRC_DIR}/arrow/adapters/orc/util.cc"
|
||||||
@ -465,22 +483,38 @@ set(PARQUET_SRCS
|
|||||||
"${LIBRARY_DIR}/arrow/schema.cc"
|
"${LIBRARY_DIR}/arrow/schema.cc"
|
||||||
"${LIBRARY_DIR}/arrow/schema_internal.cc"
|
"${LIBRARY_DIR}/arrow/schema_internal.cc"
|
||||||
"${LIBRARY_DIR}/arrow/writer.cc"
|
"${LIBRARY_DIR}/arrow/writer.cc"
|
||||||
|
"${LIBRARY_DIR}/benchmark_util.cc"
|
||||||
"${LIBRARY_DIR}/bloom_filter.cc"
|
"${LIBRARY_DIR}/bloom_filter.cc"
|
||||||
|
"${LIBRARY_DIR}/bloom_filter_reader.cc"
|
||||||
"${LIBRARY_DIR}/column_reader.cc"
|
"${LIBRARY_DIR}/column_reader.cc"
|
||||||
"${LIBRARY_DIR}/column_scanner.cc"
|
"${LIBRARY_DIR}/column_scanner.cc"
|
||||||
"${LIBRARY_DIR}/column_writer.cc"
|
"${LIBRARY_DIR}/column_writer.cc"
|
||||||
"${LIBRARY_DIR}/encoding.cc"
|
"${LIBRARY_DIR}/encoding.cc"
|
||||||
|
"${LIBRARY_DIR}/encryption/crypto_factory.cc"
|
||||||
"${LIBRARY_DIR}/encryption/encryption.cc"
|
"${LIBRARY_DIR}/encryption/encryption.cc"
|
||||||
"${LIBRARY_DIR}/encryption/encryption_internal.cc"
|
"${LIBRARY_DIR}/encryption/encryption_internal.cc"
|
||||||
|
"${LIBRARY_DIR}/encryption/encryption_internal_nossl.cc"
|
||||||
|
"${LIBRARY_DIR}/encryption/file_key_unwrapper.cc"
|
||||||
|
"${LIBRARY_DIR}/encryption/file_key_wrapper.cc"
|
||||||
|
"${LIBRARY_DIR}/encryption/file_system_key_material_store.cc"
|
||||||
"${LIBRARY_DIR}/encryption/internal_file_decryptor.cc"
|
"${LIBRARY_DIR}/encryption/internal_file_decryptor.cc"
|
||||||
"${LIBRARY_DIR}/encryption/internal_file_encryptor.cc"
|
"${LIBRARY_DIR}/encryption/internal_file_encryptor.cc"
|
||||||
|
"${LIBRARY_DIR}/encryption/key_material.cc"
|
||||||
|
"${LIBRARY_DIR}/encryption/key_metadata.cc"
|
||||||
|
"${LIBRARY_DIR}/encryption/key_toolkit.cc"
|
||||||
|
"${LIBRARY_DIR}/encryption/key_toolkit_internal.cc"
|
||||||
|
"${LIBRARY_DIR}/encryption/kms_client.cc"
|
||||||
|
"${LIBRARY_DIR}/encryption/local_wrap_kms_client.cc"
|
||||||
|
"${LIBRARY_DIR}/encryption/openssl_internal.cc"
|
||||||
"${LIBRARY_DIR}/exception.cc"
|
"${LIBRARY_DIR}/exception.cc"
|
||||||
"${LIBRARY_DIR}/file_reader.cc"
|
"${LIBRARY_DIR}/file_reader.cc"
|
||||||
"${LIBRARY_DIR}/file_writer.cc"
|
"${LIBRARY_DIR}/file_writer.cc"
|
||||||
"${LIBRARY_DIR}/page_index.cc"
|
|
||||||
"${LIBRARY_DIR}/level_conversion.cc"
|
|
||||||
"${LIBRARY_DIR}/level_comparison.cc"
|
"${LIBRARY_DIR}/level_comparison.cc"
|
||||||
|
"${LIBRARY_DIR}/level_comparison_avx2.cc"
|
||||||
|
"${LIBRARY_DIR}/level_conversion.cc"
|
||||||
|
"${LIBRARY_DIR}/level_conversion_bmi2.cc"
|
||||||
"${LIBRARY_DIR}/metadata.cc"
|
"${LIBRARY_DIR}/metadata.cc"
|
||||||
|
"${LIBRARY_DIR}/page_index.cc"
|
||||||
"${LIBRARY_DIR}/platform.cc"
|
"${LIBRARY_DIR}/platform.cc"
|
||||||
"${LIBRARY_DIR}/printer.cc"
|
"${LIBRARY_DIR}/printer.cc"
|
||||||
"${LIBRARY_DIR}/properties.cc"
|
"${LIBRARY_DIR}/properties.cc"
|
||||||
@ -489,7 +523,6 @@ set(PARQUET_SRCS
|
|||||||
"${LIBRARY_DIR}/stream_reader.cc"
|
"${LIBRARY_DIR}/stream_reader.cc"
|
||||||
"${LIBRARY_DIR}/stream_writer.cc"
|
"${LIBRARY_DIR}/stream_writer.cc"
|
||||||
"${LIBRARY_DIR}/types.cc"
|
"${LIBRARY_DIR}/types.cc"
|
||||||
"${LIBRARY_DIR}/bloom_filter_reader.cc"
|
|
||||||
"${LIBRARY_DIR}/xxhasher.cc"
|
"${LIBRARY_DIR}/xxhasher.cc"
|
||||||
|
|
||||||
"${GEN_LIBRARY_DIR}/parquet_constants.cpp"
|
"${GEN_LIBRARY_DIR}/parquet_constants.cpp"
|
||||||
@ -520,6 +553,9 @@ endif ()
|
|||||||
add_definitions(-DPARQUET_THRIFT_VERSION_MAJOR=0)
|
add_definitions(-DPARQUET_THRIFT_VERSION_MAJOR=0)
|
||||||
add_definitions(-DPARQUET_THRIFT_VERSION_MINOR=16)
|
add_definitions(-DPARQUET_THRIFT_VERSION_MINOR=16)
|
||||||
|
|
||||||
|
# As per https://github.com/apache/arrow/pull/35672 you need to enable it explicitly.
|
||||||
|
add_definitions(-DARROW_ENABLE_THREADING)
|
||||||
|
|
||||||
# === tools
|
# === tools
|
||||||
|
|
||||||
set(TOOLS_DIR "${ClickHouse_SOURCE_DIR}/contrib/arrow/cpp/tools/parquet")
|
set(TOOLS_DIR "${ClickHouse_SOURCE_DIR}/contrib/arrow/cpp/tools/parquet")
|
||||||
|
2
contrib/flatbuffers
vendored
2
contrib/flatbuffers
vendored
@ -1 +1 @@
|
|||||||
Subproject commit eb3f827948241ce0e701516f16cd67324802bce9
|
Subproject commit 0100f6a5779831fa7a651e4b67ef389a8752bd9b
|
2
contrib/usearch
vendored
2
contrib/usearch
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 1706420acafbd83d852c512dcf343af0a4059e48
|
Subproject commit 53799b84ca9ad708b060d0b1cfa5f039371721cd
|
@ -190,6 +190,7 @@ You can pass parameters to `clickhouse-client` (all parameters have a default va
|
|||||||
- `--config-file` – The name of the configuration file.
|
- `--config-file` – The name of the configuration file.
|
||||||
- `--secure` – If specified, will connect to server over secure connection (TLS). You might need to configure your CA certificates in the [configuration file](#configuration_files). The available configuration settings are the same as for [server-side TLS configuration](../operations/server-configuration-parameters/settings.md#openssl).
|
- `--secure` – If specified, will connect to server over secure connection (TLS). You might need to configure your CA certificates in the [configuration file](#configuration_files). The available configuration settings are the same as for [server-side TLS configuration](../operations/server-configuration-parameters/settings.md#openssl).
|
||||||
- `--history_file` — Path to a file containing command history.
|
- `--history_file` — Path to a file containing command history.
|
||||||
|
- `--history_max_entries` — Maximum number of entries in the history file. Default value: 1 000 000.
|
||||||
- `--param_<name>` — Value for a [query with parameters](#cli-queries-with-parameters).
|
- `--param_<name>` — Value for a [query with parameters](#cli-queries-with-parameters).
|
||||||
- `--hardware-utilization` — Print hardware utilization information in progress bar.
|
- `--hardware-utilization` — Print hardware utilization information in progress bar.
|
||||||
- `--print-profile-events` – Print `ProfileEvents` packets.
|
- `--print-profile-events` – Print `ProfileEvents` packets.
|
||||||
|
@ -17,7 +17,7 @@ anyLast(column) [RESPECT NULLS]
|
|||||||
- `column`: The column name.
|
- `column`: The column name.
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
Supports the `RESPECT NULLS` modifier after the function name. Using this modifier will ensure the function selects the first value passed, regardless of whether it is `NULL` or not.
|
Supports the `RESPECT NULLS` modifier after the function name. Using this modifier will ensure the function selects the last value passed, regardless of whether it is `NULL` or not.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
**Returned value**
|
**Returned value**
|
||||||
@ -40,4 +40,4 @@ SELECT anyLast(city) FROM any_last_nulls;
|
|||||||
┌─anyLast(city)─┐
|
┌─anyLast(city)─┐
|
||||||
│ Valencia │
|
│ Valencia │
|
||||||
└───────────────┘
|
└───────────────┘
|
||||||
```
|
```
|
||||||
|
@ -55,7 +55,7 @@ SELECT * FROM view(column1=value1, column2=value2 ...)
|
|||||||
## Materialized View
|
## Materialized View
|
||||||
|
|
||||||
``` sql
|
``` sql
|
||||||
CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER] [TO[db.]name] [ENGINE = engine] [POPULATE]
|
CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster_name] [TO[db.]name] [ENGINE = engine] [POPULATE]
|
||||||
[DEFINER = { user | CURRENT_USER }] [SQL SECURITY { DEFINER | INVOKER | NONE }]
|
[DEFINER = { user | CURRENT_USER }] [SQL SECURITY { DEFINER | INVOKER | NONE }]
|
||||||
AS SELECT ...
|
AS SELECT ...
|
||||||
[COMMENT 'comment']
|
[COMMENT 'comment']
|
||||||
|
@ -117,6 +117,7 @@ GRANT SELECT ON db*.* TO john -- correct
|
|||||||
GRANT SELECT ON *.my_table TO john -- wrong
|
GRANT SELECT ON *.my_table TO john -- wrong
|
||||||
GRANT SELECT ON foo*bar TO john -- wrong
|
GRANT SELECT ON foo*bar TO john -- wrong
|
||||||
GRANT SELECT ON *suffix TO john -- wrong
|
GRANT SELECT ON *suffix TO john -- wrong
|
||||||
|
GRANT SELECT(foo) ON db.table* TO john -- wrong
|
||||||
```
|
```
|
||||||
|
|
||||||
## Privileges
|
## Privileges
|
||||||
@ -242,10 +243,13 @@ Hierarchy of privileges:
|
|||||||
- `HDFS`
|
- `HDFS`
|
||||||
- `HIVE`
|
- `HIVE`
|
||||||
- `JDBC`
|
- `JDBC`
|
||||||
|
- `KAFKA`
|
||||||
- `MONGO`
|
- `MONGO`
|
||||||
- `MYSQL`
|
- `MYSQL`
|
||||||
|
- `NATS`
|
||||||
- `ODBC`
|
- `ODBC`
|
||||||
- `POSTGRES`
|
- `POSTGRES`
|
||||||
|
- `RABBITMQ`
|
||||||
- `REDIS`
|
- `REDIS`
|
||||||
- `REMOTE`
|
- `REMOTE`
|
||||||
- `S3`
|
- `S3`
|
||||||
@ -524,10 +528,13 @@ Allows using external data sources. Applies to [table engines](../../engines/tab
|
|||||||
- `HDFS`. Level: `GLOBAL`
|
- `HDFS`. Level: `GLOBAL`
|
||||||
- `HIVE`. Level: `GLOBAL`
|
- `HIVE`. Level: `GLOBAL`
|
||||||
- `JDBC`. Level: `GLOBAL`
|
- `JDBC`. Level: `GLOBAL`
|
||||||
|
- `KAFKA`. Level: `GLOBAL`
|
||||||
- `MONGO`. Level: `GLOBAL`
|
- `MONGO`. Level: `GLOBAL`
|
||||||
- `MYSQL`. Level: `GLOBAL`
|
- `MYSQL`. Level: `GLOBAL`
|
||||||
|
- `NATS`. Level: `GLOBAL`
|
||||||
- `ODBC`. Level: `GLOBAL`
|
- `ODBC`. Level: `GLOBAL`
|
||||||
- `POSTGRES`. Level: `GLOBAL`
|
- `POSTGRES`. Level: `GLOBAL`
|
||||||
|
- `RABBITMQ`. Level: `GLOBAL`
|
||||||
- `REDIS`. Level: `GLOBAL`
|
- `REDIS`. Level: `GLOBAL`
|
||||||
- `REMOTE`. Level: `GLOBAL`
|
- `REMOTE`. Level: `GLOBAL`
|
||||||
- `S3`. Level: `GLOBAL`
|
- `S3`. Level: `GLOBAL`
|
||||||
|
@ -95,7 +95,7 @@ sudo yum install -y clickhouse-server clickhouse-client
|
|||||||
sudo systemctl enable clickhouse-server
|
sudo systemctl enable clickhouse-server
|
||||||
sudo systemctl start clickhouse-server
|
sudo systemctl start clickhouse-server
|
||||||
sudo systemctl status clickhouse-server
|
sudo systemctl status clickhouse-server
|
||||||
clickhouse-client # илм "clickhouse-client --password" если установлен пароль
|
clickhouse-client # или "clickhouse-client --password" если установлен пароль
|
||||||
```
|
```
|
||||||
|
|
||||||
Для использования наиболее свежих версий нужно заменить `stable` на `testing` (рекомендуется для тестовых окружений). Также иногда доступен `prestable`.
|
Для использования наиболее свежих версий нужно заменить `stable` на `testing` (рекомендуется для тестовых окружений). Также иногда доступен `prestable`.
|
||||||
|
@ -39,7 +39,7 @@ SELECT a, b, c FROM (SELECT ...)
|
|||||||
## Материализованные представления {#materialized}
|
## Материализованные представления {#materialized}
|
||||||
|
|
||||||
``` sql
|
``` sql
|
||||||
CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER] [TO[db.]name] [ENGINE = engine] [POPULATE]
|
CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster_name] [TO[db.]name] [ENGINE = engine] [POPULATE]
|
||||||
[DEFINER = { user | CURRENT_USER }] [SQL SECURITY { DEFINER | INVOKER | NONE }]
|
[DEFINER = { user | CURRENT_USER }] [SQL SECURITY { DEFINER | INVOKER | NONE }]
|
||||||
AS SELECT ...
|
AS SELECT ...
|
||||||
```
|
```
|
||||||
|
@ -192,14 +192,23 @@ GRANT SELECT(x,y) ON db.table TO john WITH GRANT OPTION
|
|||||||
- `addressToSymbol`
|
- `addressToSymbol`
|
||||||
- `demangle`
|
- `demangle`
|
||||||
- [SOURCES](#grant-sources)
|
- [SOURCES](#grant-sources)
|
||||||
|
- `AZURE`
|
||||||
- `FILE`
|
- `FILE`
|
||||||
- `URL`
|
|
||||||
- `REMOTE`
|
|
||||||
- `MYSQL`
|
|
||||||
- `ODBC`
|
|
||||||
- `JDBC`
|
|
||||||
- `HDFS`
|
- `HDFS`
|
||||||
|
- `HIVE`
|
||||||
|
- `JDBC`
|
||||||
|
- `KAFKA`
|
||||||
|
- `MONGO`
|
||||||
|
- `MYSQL`
|
||||||
|
- `NATS`
|
||||||
|
- `ODBC`
|
||||||
|
- `POSTGRES`
|
||||||
|
- `RABBITMQ`
|
||||||
|
- `REDIS`
|
||||||
|
- `REMOTE`
|
||||||
- `S3`
|
- `S3`
|
||||||
|
- `SQLITE`
|
||||||
|
- `URL`
|
||||||
- [dictGet](#grant-dictget)
|
- [dictGet](#grant-dictget)
|
||||||
|
|
||||||
Примеры того, как трактуется данная иерархия:
|
Примеры того, как трактуется данная иерархия:
|
||||||
@ -461,14 +470,23 @@ GRANT INSERT(x,y) ON db.table TO john
|
|||||||
Разрешает использовать внешние источники данных. Применяется к [движкам таблиц](../../engines/table-engines/index.md) и [табличным функциям](../table-functions/index.md#table-functions).
|
Разрешает использовать внешние источники данных. Применяется к [движкам таблиц](../../engines/table-engines/index.md) и [табличным функциям](../table-functions/index.md#table-functions).
|
||||||
|
|
||||||
- `SOURCES`. Уровень: `GROUP`
|
- `SOURCES`. Уровень: `GROUP`
|
||||||
|
- `AZURE`. Уровень: `GLOBAL`
|
||||||
- `FILE`. Уровень: `GLOBAL`
|
- `FILE`. Уровень: `GLOBAL`
|
||||||
- `URL`. Уровень: `GLOBAL`
|
|
||||||
- `REMOTE`. Уровень: `GLOBAL`
|
|
||||||
- `MYSQL`. Уровень: `GLOBAL`
|
|
||||||
- `ODBC`. Уровень: `GLOBAL`
|
|
||||||
- `JDBC`. Уровень: `GLOBAL`
|
|
||||||
- `HDFS`. Уровень: `GLOBAL`
|
- `HDFS`. Уровень: `GLOBAL`
|
||||||
|
- `HIVE`. Уровень: `GLOBAL`
|
||||||
|
- `JDBC`. Уровень: `GLOBAL`
|
||||||
|
- `KAFKA`. Уровень: `GLOBAL`
|
||||||
|
- `MONGO`. Уровень: `GLOBAL`
|
||||||
|
- `MYSQL`. Уровень: `GLOBAL`
|
||||||
|
- `NATS`. Уровень: `GLOBAL`
|
||||||
|
- `ODBC`. Уровень: `GLOBAL`
|
||||||
|
- `POSTGRES`. Уровень: `GLOBAL`
|
||||||
|
- `RABBITMQ`. Уровень: `GLOBAL`
|
||||||
|
- `REDIS`. Уровень: `GLOBAL`
|
||||||
|
- `REMOTE`. Уровень: `GLOBAL`
|
||||||
- `S3`. Уровень: `GLOBAL`
|
- `S3`. Уровень: `GLOBAL`
|
||||||
|
- `SQLITE`. Уровень: `GLOBAL`
|
||||||
|
- `URL`. Уровень: `GLOBAL`
|
||||||
|
|
||||||
Привилегия `SOURCES` разрешает использование всех источников. Также вы можете присвоить привилегию для каждого источника отдельно. Для использования источников необходимы дополнительные привилегии.
|
Привилегия `SOURCES` разрешает использование всех источников. Также вы можете присвоить привилегию для каждого источника отдельно. Для использования источников необходимы дополнительные привилегии.
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ SELECT a, b, c FROM (SELECT ...)
|
|||||||
## Materialized {#materialized}
|
## Materialized {#materialized}
|
||||||
|
|
||||||
``` sql
|
``` sql
|
||||||
CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER] [TO[db.]name] [ENGINE = engine] [POPULATE] AS SELECT ...
|
CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster_name] [TO[db.]name] [ENGINE = engine] [POPULATE] AS SELECT ...
|
||||||
```
|
```
|
||||||
|
|
||||||
物化视图存储由相应的[SELECT](../../../sql-reference/statements/select/index.md)管理.
|
物化视图存储由相应的[SELECT](../../../sql-reference/statements/select/index.md)管理.
|
||||||
|
@ -170,14 +170,23 @@ GRANT SELECT(x,y) ON db.table TO john WITH GRANT OPTION
|
|||||||
- `addressToSymbol`
|
- `addressToSymbol`
|
||||||
- `demangle`
|
- `demangle`
|
||||||
- [SOURCES](#grant-sources)
|
- [SOURCES](#grant-sources)
|
||||||
|
- `AZURE`
|
||||||
- `FILE`
|
- `FILE`
|
||||||
- `URL`
|
|
||||||
- `REMOTE`
|
|
||||||
- `YSQL`
|
|
||||||
- `ODBC`
|
|
||||||
- `JDBC`
|
|
||||||
- `HDFS`
|
- `HDFS`
|
||||||
|
- `HIVE`
|
||||||
|
- `JDBC`
|
||||||
|
- `KAFKA`
|
||||||
|
- `MONGO`
|
||||||
|
- `MYSQL`
|
||||||
|
- `NATS`
|
||||||
|
- `ODBC`
|
||||||
|
- `POSTGRES`
|
||||||
|
- `RABBITMQ`
|
||||||
|
- `REDIS`
|
||||||
|
- `REMOTE`
|
||||||
- `S3`
|
- `S3`
|
||||||
|
- `SQLITE`
|
||||||
|
- `URL`
|
||||||
- [dictGet](#grant-dictget)
|
- [dictGet](#grant-dictget)
|
||||||
|
|
||||||
如何对待该层级的示例:
|
如何对待该层级的示例:
|
||||||
@ -428,14 +437,23 @@ GRANT INSERT(x,y) ON db.table TO john
|
|||||||
允许在 [table engines](../../engines/table-engines/index.md) 和 [table functions](../../sql-reference/table-functions/index.md#table-functions)中使用外部数据源。
|
允许在 [table engines](../../engines/table-engines/index.md) 和 [table functions](../../sql-reference/table-functions/index.md#table-functions)中使用外部数据源。
|
||||||
|
|
||||||
- `SOURCES`. 级别: `GROUP`
|
- `SOURCES`. 级别: `GROUP`
|
||||||
|
- `AZURE`. 级别: `GLOBAL`
|
||||||
- `FILE`. 级别: `GLOBAL`
|
- `FILE`. 级别: `GLOBAL`
|
||||||
- `URL`. 级别: `GLOBAL`
|
|
||||||
- `REMOTE`. 级别: `GLOBAL`
|
|
||||||
- `YSQL`. 级别: `GLOBAL`
|
|
||||||
- `ODBC`. 级别: `GLOBAL`
|
|
||||||
- `JDBC`. 级别: `GLOBAL`
|
|
||||||
- `HDFS`. 级别: `GLOBAL`
|
- `HDFS`. 级别: `GLOBAL`
|
||||||
|
- `HIVE`. 级别: `GLOBAL`
|
||||||
|
- `JDBC`. 级别: `GLOBAL`
|
||||||
|
- `KAFKA`. 级别: `GLOBAL`
|
||||||
|
- `MONGO`. 级别: `GLOBAL`
|
||||||
|
- `MYSQL`. 级别: `GLOBAL`
|
||||||
|
- `NATS`. 级别: `GLOBAL`
|
||||||
|
- `ODBC`. 级别: `GLOBAL`
|
||||||
|
- `POSTGRES`. 级别: `GLOBAL`
|
||||||
|
- `RABBITMQ`. 级别: `GLOBAL`
|
||||||
|
- `REDIS`. 级别: `GLOBAL`
|
||||||
|
- `REMOTE`. 级别: `GLOBAL`
|
||||||
- `S3`. 级别: `GLOBAL`
|
- `S3`. 级别: `GLOBAL`
|
||||||
|
- `SQLITE`. 级别: `GLOBAL`
|
||||||
|
- `URL`. 级别: `GLOBAL`
|
||||||
|
|
||||||
`SOURCES` 权限允许使用所有数据源。当然也可以单独对每个数据源进行授权。要使用数据源时,还需要额外的权限。
|
`SOURCES` 权限允许使用所有数据源。当然也可以单独对每个数据源进行授权。要使用数据源时,还需要额外的权限。
|
||||||
|
|
||||||
|
@ -192,6 +192,10 @@ void Client::parseConnectionsCredentials(Poco::Util::AbstractConfiguration & con
|
|||||||
history_file = home_path + "/" + history_file.substr(1);
|
history_file = home_path + "/" + history_file.substr(1);
|
||||||
config.setString("history_file", history_file);
|
config.setString("history_file", history_file);
|
||||||
}
|
}
|
||||||
|
if (config.has(prefix + ".history_max_entries"))
|
||||||
|
{
|
||||||
|
config.setUInt("history_max_entries", history_max_entries);
|
||||||
|
}
|
||||||
if (config.has(prefix + ".accept-invalid-certificate"))
|
if (config.has(prefix + ".accept-invalid-certificate"))
|
||||||
config.setBool("accept-invalid-certificate", config.getBool(prefix + ".accept-invalid-certificate"));
|
config.setBool("accept-invalid-certificate", config.getBool(prefix + ".accept-invalid-certificate"));
|
||||||
}
|
}
|
||||||
|
@ -236,6 +236,7 @@ void DisksApp::runInteractiveReplxx()
|
|||||||
ReplxxLineReader lr(
|
ReplxxLineReader lr(
|
||||||
suggest,
|
suggest,
|
||||||
history_file,
|
history_file,
|
||||||
|
history_max_entries,
|
||||||
/* multiline= */ false,
|
/* multiline= */ false,
|
||||||
/* ignore_shell_suspend= */ false,
|
/* ignore_shell_suspend= */ false,
|
||||||
query_extenders,
|
query_extenders,
|
||||||
@ -398,6 +399,8 @@ void DisksApp::initializeHistoryFile()
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
history_max_entries = config().getUInt("history-max-entries", 1000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisksApp::init(const std::vector<String> & common_arguments)
|
void DisksApp::init(const std::vector<String> & common_arguments)
|
||||||
|
@ -62,6 +62,8 @@ private:
|
|||||||
|
|
||||||
// Fields responsible for the REPL work
|
// Fields responsible for the REPL work
|
||||||
String history_file;
|
String history_file;
|
||||||
|
UInt32 history_max_entries = 0; /// Maximum number of entries in the history file. Needs to be initialized to 0 since we don't have a proper constructor. Worry not, actual value is set within the initializeHistoryFile method.
|
||||||
|
|
||||||
LineReader::Suggest suggest;
|
LineReader::Suggest suggest;
|
||||||
static LineReader::Patterns query_extenders;
|
static LineReader::Patterns query_extenders;
|
||||||
static LineReader::Patterns query_delimiters;
|
static LineReader::Patterns query_delimiters;
|
||||||
|
@ -243,6 +243,8 @@ void KeeperClient::initialize(Poco::Util::Application & /* self */)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
history_max_entries = config().getUInt("history-max-entries", 1000000);
|
||||||
|
|
||||||
String default_log_level;
|
String default_log_level;
|
||||||
if (config().has("query"))
|
if (config().has("query"))
|
||||||
/// We don't want to see any information log in query mode, unless it was set explicitly
|
/// We don't want to see any information log in query mode, unless it was set explicitly
|
||||||
@ -319,6 +321,7 @@ void KeeperClient::runInteractiveReplxx()
|
|||||||
ReplxxLineReader lr(
|
ReplxxLineReader lr(
|
||||||
suggest,
|
suggest,
|
||||||
history_file,
|
history_file,
|
||||||
|
history_max_entries,
|
||||||
/* multiline= */ false,
|
/* multiline= */ false,
|
||||||
/* ignore_shell_suspend= */ false,
|
/* ignore_shell_suspend= */ false,
|
||||||
query_extenders,
|
query_extenders,
|
||||||
|
@ -59,6 +59,8 @@ protected:
|
|||||||
std::vector<String> getCompletions(const String & prefix) const;
|
std::vector<String> getCompletions(const String & prefix) const;
|
||||||
|
|
||||||
String history_file;
|
String history_file;
|
||||||
|
UInt32 history_max_entries; /// Maximum number of entries in the history file.
|
||||||
|
|
||||||
LineReader::Suggest suggest;
|
LineReader::Suggest suggest;
|
||||||
|
|
||||||
zkutil::ZooKeeperArgs zk_args;
|
zkutil::ZooKeeperArgs zk_args;
|
||||||
|
@ -243,6 +243,9 @@ enum class AccessType : uint8_t
|
|||||||
M(S3, "", GLOBAL, SOURCES) \
|
M(S3, "", GLOBAL, SOURCES) \
|
||||||
M(HIVE, "", GLOBAL, SOURCES) \
|
M(HIVE, "", GLOBAL, SOURCES) \
|
||||||
M(AZURE, "", GLOBAL, SOURCES) \
|
M(AZURE, "", GLOBAL, SOURCES) \
|
||||||
|
M(KAFKA, "", GLOBAL, SOURCES) \
|
||||||
|
M(NATS, "", GLOBAL, SOURCES) \
|
||||||
|
M(RABBITMQ, "", GLOBAL, SOURCES) \
|
||||||
M(SOURCES, "", GROUP, ALL) \
|
M(SOURCES, "", GROUP, ALL) \
|
||||||
\
|
\
|
||||||
M(CLUSTER, "", GLOBAL, ALL) /* ON CLUSTER queries */ \
|
M(CLUSTER, "", GLOBAL, ALL) /* ON CLUSTER queries */ \
|
||||||
|
@ -52,7 +52,10 @@ namespace
|
|||||||
{AccessType::HDFS, "HDFS"},
|
{AccessType::HDFS, "HDFS"},
|
||||||
{AccessType::S3, "S3"},
|
{AccessType::S3, "S3"},
|
||||||
{AccessType::HIVE, "Hive"},
|
{AccessType::HIVE, "Hive"},
|
||||||
{AccessType::AZURE, "AzureBlobStorage"}
|
{AccessType::AZURE, "AzureBlobStorage"},
|
||||||
|
{AccessType::KAFKA, "Kafka"},
|
||||||
|
{AccessType::NATS, "NATS"},
|
||||||
|
{AccessType::RABBITMQ, "RabbitMQ"}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,6 +15,9 @@ public:
|
|||||||
explicit Credentials() = default;
|
explicit Credentials() = default;
|
||||||
explicit Credentials(const String & user_name_);
|
explicit Credentials(const String & user_name_);
|
||||||
|
|
||||||
|
Credentials(const Credentials &) = default;
|
||||||
|
Credentials(Credentials &&) = default;
|
||||||
|
|
||||||
virtual ~Credentials() = default;
|
virtual ~Credentials() = default;
|
||||||
|
|
||||||
const String & getUserName() const;
|
const String & getUserName() const;
|
||||||
|
135
src/Backups/BackupConcurrencyCheck.cpp
Normal file
135
src/Backups/BackupConcurrencyCheck.cpp
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
#include <Backups/BackupConcurrencyCheck.h>
|
||||||
|
|
||||||
|
#include <Common/Exception.h>
|
||||||
|
#include <Common/logger_useful.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace ErrorCodes
|
||||||
|
{
|
||||||
|
extern const int CONCURRENT_ACCESS_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BackupConcurrencyCheck::BackupConcurrencyCheck(
|
||||||
|
const UUID & backup_or_restore_uuid_,
|
||||||
|
bool is_restore_,
|
||||||
|
bool on_cluster_,
|
||||||
|
bool allow_concurrency_,
|
||||||
|
BackupConcurrencyCounters & counters_)
|
||||||
|
: is_restore(is_restore_), backup_or_restore_uuid(backup_or_restore_uuid_), on_cluster(on_cluster_), counters(counters_)
|
||||||
|
{
|
||||||
|
std::lock_guard lock{counters.mutex};
|
||||||
|
|
||||||
|
if (!allow_concurrency_)
|
||||||
|
{
|
||||||
|
bool found_concurrent_operation = false;
|
||||||
|
if (is_restore)
|
||||||
|
{
|
||||||
|
size_t num_local_restores = counters.local_restores;
|
||||||
|
size_t num_on_cluster_restores = counters.on_cluster_restores.size();
|
||||||
|
if (on_cluster)
|
||||||
|
{
|
||||||
|
if (!counters.on_cluster_restores.contains(backup_or_restore_uuid))
|
||||||
|
++num_on_cluster_restores;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++num_local_restores;
|
||||||
|
}
|
||||||
|
found_concurrent_operation = (num_local_restores + num_on_cluster_restores > 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size_t num_local_backups = counters.local_backups;
|
||||||
|
size_t num_on_cluster_backups = counters.on_cluster_backups.size();
|
||||||
|
if (on_cluster)
|
||||||
|
{
|
||||||
|
if (!counters.on_cluster_backups.contains(backup_or_restore_uuid))
|
||||||
|
++num_on_cluster_backups;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++num_local_backups;
|
||||||
|
}
|
||||||
|
found_concurrent_operation = (num_local_backups + num_on_cluster_backups > 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found_concurrent_operation)
|
||||||
|
throwConcurrentOperationNotAllowed(is_restore);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (on_cluster)
|
||||||
|
{
|
||||||
|
if (is_restore)
|
||||||
|
++counters.on_cluster_restores[backup_or_restore_uuid];
|
||||||
|
else
|
||||||
|
++counters.on_cluster_backups[backup_or_restore_uuid];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (is_restore)
|
||||||
|
++counters.local_restores;
|
||||||
|
else
|
||||||
|
++counters.local_backups;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BackupConcurrencyCheck::~BackupConcurrencyCheck()
|
||||||
|
{
|
||||||
|
std::lock_guard lock{counters.mutex};
|
||||||
|
|
||||||
|
if (on_cluster)
|
||||||
|
{
|
||||||
|
if (is_restore)
|
||||||
|
{
|
||||||
|
auto it = counters.on_cluster_restores.find(backup_or_restore_uuid);
|
||||||
|
if (it != counters.on_cluster_restores.end())
|
||||||
|
{
|
||||||
|
if (!--it->second)
|
||||||
|
counters.on_cluster_restores.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto it = counters.on_cluster_backups.find(backup_or_restore_uuid);
|
||||||
|
if (it != counters.on_cluster_backups.end())
|
||||||
|
{
|
||||||
|
if (!--it->second)
|
||||||
|
counters.on_cluster_backups.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (is_restore)
|
||||||
|
--counters.local_restores;
|
||||||
|
else
|
||||||
|
--counters.local_backups;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BackupConcurrencyCheck::throwConcurrentOperationNotAllowed(bool is_restore)
|
||||||
|
{
|
||||||
|
throw Exception(
|
||||||
|
ErrorCodes::CONCURRENT_ACCESS_NOT_SUPPORTED,
|
||||||
|
"Concurrent {} are not allowed, turn on setting '{}'",
|
||||||
|
is_restore ? "restores" : "backups",
|
||||||
|
is_restore ? "allow_concurrent_restores" : "allow_concurrent_backups");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BackupConcurrencyCounters::BackupConcurrencyCounters() = default;
|
||||||
|
|
||||||
|
|
||||||
|
BackupConcurrencyCounters::~BackupConcurrencyCounters()
|
||||||
|
{
|
||||||
|
if (local_backups > 0 || local_restores > 0 || !on_cluster_backups.empty() || !on_cluster_restores.empty())
|
||||||
|
LOG_ERROR(getLogger(__PRETTY_FUNCTION__), "Some backups or restores are processing");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
55
src/Backups/BackupConcurrencyCheck.h
Normal file
55
src/Backups/BackupConcurrencyCheck.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Core/UUID.h>
|
||||||
|
#include <base/scope_guard.h>
|
||||||
|
#include <mutex>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
class BackupConcurrencyCounters;
|
||||||
|
|
||||||
|
/// Local checker for concurrent BACKUP or RESTORE operations.
|
||||||
|
/// This class is used by implementations of IBackupCoordination and IRestoreCoordination
|
||||||
|
/// to throw an exception if concurrent backups or restores are not allowed.
|
||||||
|
class BackupConcurrencyCheck
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Checks concurrency of a BACKUP operation or a RESTORE operation.
|
||||||
|
/// Keep a constructed instance of BackupConcurrencyCheck until the operation is done.
|
||||||
|
BackupConcurrencyCheck(
|
||||||
|
const UUID & backup_or_restore_uuid_,
|
||||||
|
bool is_restore_,
|
||||||
|
bool on_cluster_,
|
||||||
|
bool allow_concurrency_,
|
||||||
|
BackupConcurrencyCounters & counters_);
|
||||||
|
|
||||||
|
~BackupConcurrencyCheck();
|
||||||
|
|
||||||
|
[[noreturn]] static void throwConcurrentOperationNotAllowed(bool is_restore);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const bool is_restore;
|
||||||
|
const UUID backup_or_restore_uuid;
|
||||||
|
const bool on_cluster;
|
||||||
|
BackupConcurrencyCounters & counters;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class BackupConcurrencyCounters
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BackupConcurrencyCounters();
|
||||||
|
~BackupConcurrencyCounters();
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class BackupConcurrencyCheck;
|
||||||
|
size_t local_backups TSA_GUARDED_BY(mutex) = 0;
|
||||||
|
size_t local_restores TSA_GUARDED_BY(mutex) = 0;
|
||||||
|
std::unordered_map<UUID /* backup_uuid */, size_t /* num_refs */> on_cluster_backups TSA_GUARDED_BY(mutex);
|
||||||
|
std::unordered_map<UUID /* restore_uuid */, size_t /* num_refs */> on_cluster_restores TSA_GUARDED_BY(mutex);
|
||||||
|
std::mutex mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
64
src/Backups/BackupCoordinationCleaner.cpp
Normal file
64
src/Backups/BackupCoordinationCleaner.cpp
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#include <Backups/BackupCoordinationCleaner.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
BackupCoordinationCleaner::BackupCoordinationCleaner(const String & zookeeper_path_, const WithRetries & with_retries_, LoggerPtr log_)
|
||||||
|
: zookeeper_path(zookeeper_path_), with_retries(with_retries_), log(log_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackupCoordinationCleaner::cleanup()
|
||||||
|
{
|
||||||
|
tryRemoveAllNodes(/* throw_if_error = */ true, /* retries_kind = */ WithRetries::kNormal);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BackupCoordinationCleaner::tryCleanupAfterError() noexcept
|
||||||
|
{
|
||||||
|
return tryRemoveAllNodes(/* throw_if_error = */ false, /* retries_kind = */ WithRetries::kNormal);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BackupCoordinationCleaner::tryRemoveAllNodes(bool throw_if_error, WithRetries::Kind retries_kind)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard lock{mutex};
|
||||||
|
if (cleanup_result.succeeded)
|
||||||
|
return true;
|
||||||
|
if (cleanup_result.exception)
|
||||||
|
{
|
||||||
|
if (throw_if_error)
|
||||||
|
std::rethrow_exception(cleanup_result.exception);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LOG_TRACE(log, "Removing nodes from ZooKeeper");
|
||||||
|
auto holder = with_retries.createRetriesControlHolder("removeAllNodes", retries_kind);
|
||||||
|
holder.retries_ctl.retryLoop([&, &zookeeper = holder.faulty_zookeeper]()
|
||||||
|
{
|
||||||
|
with_retries.renewZooKeeper(zookeeper);
|
||||||
|
zookeeper->removeRecursive(zookeeper_path);
|
||||||
|
});
|
||||||
|
|
||||||
|
std::lock_guard lock{mutex};
|
||||||
|
cleanup_result.succeeded = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
LOG_TRACE(log, "Caught exception while removing nodes from ZooKeeper for this restore: {}",
|
||||||
|
getCurrentExceptionMessage(/* with_stacktrace= */ false, /* check_embedded_stacktrace= */ true));
|
||||||
|
|
||||||
|
std::lock_guard lock{mutex};
|
||||||
|
cleanup_result.exception = std::current_exception();
|
||||||
|
|
||||||
|
if (throw_if_error)
|
||||||
|
throw;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
40
src/Backups/BackupCoordinationCleaner.h
Normal file
40
src/Backups/BackupCoordinationCleaner.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Backups/WithRetries.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Removes all the nodes from ZooKeeper used to coordinate a BACKUP ON CLUSTER operation or
|
||||||
|
/// a RESTORE ON CLUSTER operation (successful or not).
|
||||||
|
/// This class is used by BackupCoordinationOnCluster and RestoreCoordinationOnCluster to cleanup.
|
||||||
|
class BackupCoordinationCleaner
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BackupCoordinationCleaner(const String & zookeeper_path_, const WithRetries & with_retries_, LoggerPtr log_);
|
||||||
|
|
||||||
|
void cleanup();
|
||||||
|
bool tryCleanupAfterError() noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool tryRemoveAllNodes(bool throw_if_error, WithRetries::Kind retries_kind);
|
||||||
|
|
||||||
|
const String zookeeper_path;
|
||||||
|
|
||||||
|
/// A reference to a field of the parent object which is either BackupCoordinationOnCluster or RestoreCoordinationOnCluster.
|
||||||
|
const WithRetries & with_retries;
|
||||||
|
|
||||||
|
const LoggerPtr log;
|
||||||
|
|
||||||
|
struct CleanupResult
|
||||||
|
{
|
||||||
|
bool succeeded = false;
|
||||||
|
std::exception_ptr exception;
|
||||||
|
};
|
||||||
|
CleanupResult cleanup_result TSA_GUARDED_BY(mutex);
|
||||||
|
|
||||||
|
std::mutex mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
#include <Backups/BackupCoordinationLocal.h>
|
#include <Backups/BackupCoordinationLocal.h>
|
||||||
|
|
||||||
#include <Common/Exception.h>
|
#include <Common/Exception.h>
|
||||||
|
#include <Common/ZooKeeper/ZooKeeperRetries.h>
|
||||||
#include <Common/logger_useful.h>
|
#include <Common/logger_useful.h>
|
||||||
#include <Common/quoteString.h>
|
#include <Common/quoteString.h>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
@ -8,27 +10,20 @@
|
|||||||
namespace DB
|
namespace DB
|
||||||
{
|
{
|
||||||
|
|
||||||
BackupCoordinationLocal::BackupCoordinationLocal(bool plain_backup_)
|
BackupCoordinationLocal::BackupCoordinationLocal(
|
||||||
: log(getLogger("BackupCoordinationLocal")), file_infos(plain_backup_)
|
const UUID & backup_uuid_,
|
||||||
|
bool is_plain_backup_,
|
||||||
|
bool allow_concurrent_backup_,
|
||||||
|
BackupConcurrencyCounters & concurrency_counters_)
|
||||||
|
: log(getLogger("BackupCoordinationLocal"))
|
||||||
|
, concurrency_check(backup_uuid_, /* is_restore = */ false, /* on_cluster = */ false, allow_concurrent_backup_, concurrency_counters_)
|
||||||
|
, file_infos(is_plain_backup_)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
BackupCoordinationLocal::~BackupCoordinationLocal() = default;
|
BackupCoordinationLocal::~BackupCoordinationLocal() = default;
|
||||||
|
|
||||||
void BackupCoordinationLocal::setStage(const String &, const String &)
|
ZooKeeperRetriesInfo BackupCoordinationLocal::getOnClusterInitializationKeeperRetriesInfo() const
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void BackupCoordinationLocal::setError(const Exception &)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Strings BackupCoordinationLocal::waitForStage(const String &)
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Strings BackupCoordinationLocal::waitForStage(const String &, std::chrono::milliseconds)
|
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -135,15 +130,4 @@ bool BackupCoordinationLocal::startWritingFile(size_t data_file_index)
|
|||||||
return writing_files.emplace(data_file_index).second;
|
return writing_files.emplace(data_file_index).second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool BackupCoordinationLocal::hasConcurrentBackups(const std::atomic<size_t> & num_active_backups) const
|
|
||||||
{
|
|
||||||
if (num_active_backups > 1)
|
|
||||||
{
|
|
||||||
LOG_WARNING(log, "Found concurrent backups: num_active_backups={}", num_active_backups);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Backups/IBackupCoordination.h>
|
#include <Backups/IBackupCoordination.h>
|
||||||
|
#include <Backups/BackupConcurrencyCheck.h>
|
||||||
#include <Backups/BackupCoordinationFileInfos.h>
|
#include <Backups/BackupCoordinationFileInfos.h>
|
||||||
#include <Backups/BackupCoordinationReplicatedAccess.h>
|
#include <Backups/BackupCoordinationReplicatedAccess.h>
|
||||||
#include <Backups/BackupCoordinationReplicatedSQLObjects.h>
|
#include <Backups/BackupCoordinationReplicatedSQLObjects.h>
|
||||||
@ -21,13 +22,21 @@ namespace DB
|
|||||||
class BackupCoordinationLocal : public IBackupCoordination
|
class BackupCoordinationLocal : public IBackupCoordination
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit BackupCoordinationLocal(bool plain_backup_);
|
explicit BackupCoordinationLocal(
|
||||||
|
const UUID & backup_uuid_,
|
||||||
|
bool is_plain_backup_,
|
||||||
|
bool allow_concurrent_backup_,
|
||||||
|
BackupConcurrencyCounters & concurrency_counters_);
|
||||||
|
|
||||||
~BackupCoordinationLocal() override;
|
~BackupCoordinationLocal() override;
|
||||||
|
|
||||||
void setStage(const String & new_stage, const String & message) override;
|
Strings setStage(const String &, const String &, bool) override { return {}; }
|
||||||
void setError(const Exception & exception) override;
|
void setBackupQueryWasSentToOtherHosts() override {}
|
||||||
Strings waitForStage(const String & stage_to_wait) override;
|
bool trySetError(std::exception_ptr) override { return true; }
|
||||||
Strings waitForStage(const String & stage_to_wait, std::chrono::milliseconds timeout) override;
|
void finish() override {}
|
||||||
|
bool tryFinishAfterError() noexcept override { return true; }
|
||||||
|
void waitForOtherHostsToFinish() override {}
|
||||||
|
bool tryWaitForOtherHostsToFinishAfterError() noexcept override { return true; }
|
||||||
|
|
||||||
void addReplicatedPartNames(const String & table_zk_path, const String & table_name_for_logs, const String & replica_name,
|
void addReplicatedPartNames(const String & table_zk_path, const String & table_name_for_logs, const String & replica_name,
|
||||||
const std::vector<PartNameAndChecksum> & part_names_and_checksums) override;
|
const std::vector<PartNameAndChecksum> & part_names_and_checksums) override;
|
||||||
@ -54,17 +63,18 @@ public:
|
|||||||
BackupFileInfos getFileInfosForAllHosts() const override;
|
BackupFileInfos getFileInfosForAllHosts() const override;
|
||||||
bool startWritingFile(size_t data_file_index) override;
|
bool startWritingFile(size_t data_file_index) override;
|
||||||
|
|
||||||
bool hasConcurrentBackups(const std::atomic<size_t> & num_active_backups) const override;
|
ZooKeeperRetriesInfo getOnClusterInitializationKeeperRetriesInfo() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
LoggerPtr const log;
|
LoggerPtr const log;
|
||||||
|
BackupConcurrencyCheck concurrency_check;
|
||||||
|
|
||||||
BackupCoordinationReplicatedTables TSA_GUARDED_BY(replicated_tables_mutex) replicated_tables;
|
BackupCoordinationReplicatedTables replicated_tables TSA_GUARDED_BY(replicated_tables_mutex);
|
||||||
BackupCoordinationReplicatedAccess TSA_GUARDED_BY(replicated_access_mutex) replicated_access;
|
BackupCoordinationReplicatedAccess replicated_access TSA_GUARDED_BY(replicated_access_mutex);
|
||||||
BackupCoordinationReplicatedSQLObjects TSA_GUARDED_BY(replicated_sql_objects_mutex) replicated_sql_objects;
|
BackupCoordinationReplicatedSQLObjects replicated_sql_objects TSA_GUARDED_BY(replicated_sql_objects_mutex);
|
||||||
BackupCoordinationFileInfos TSA_GUARDED_BY(file_infos_mutex) file_infos;
|
BackupCoordinationFileInfos file_infos TSA_GUARDED_BY(file_infos_mutex);
|
||||||
BackupCoordinationKeeperMapTables keeper_map_tables TSA_GUARDED_BY(keeper_map_tables_mutex);
|
BackupCoordinationKeeperMapTables keeper_map_tables TSA_GUARDED_BY(keeper_map_tables_mutex);
|
||||||
std::unordered_set<size_t> TSA_GUARDED_BY(writing_files_mutex) writing_files;
|
std::unordered_set<size_t> writing_files TSA_GUARDED_BY(writing_files_mutex);
|
||||||
|
|
||||||
mutable std::mutex replicated_tables_mutex;
|
mutable std::mutex replicated_tables_mutex;
|
||||||
mutable std::mutex replicated_access_mutex;
|
mutable std::mutex replicated_access_mutex;
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
#include <Backups/BackupCoordinationRemote.h>
|
#include <Backups/BackupCoordinationOnCluster.h>
|
||||||
|
|
||||||
#include <base/hex.h>
|
|
||||||
#include <boost/algorithm/string/split.hpp>
|
|
||||||
|
|
||||||
#include <Access/Common/AccessEntityType.h>
|
#include <Access/Common/AccessEntityType.h>
|
||||||
#include <Backups/BackupCoordinationReplicatedAccess.h>
|
#include <Backups/BackupCoordinationReplicatedAccess.h>
|
||||||
@ -26,8 +23,6 @@ namespace ErrorCodes
|
|||||||
extern const int LOGICAL_ERROR;
|
extern const int LOGICAL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Stage = BackupCoordinationStage;
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
using PartNameAndChecksum = IBackupCoordination::PartNameAndChecksum;
|
using PartNameAndChecksum = IBackupCoordination::PartNameAndChecksum;
|
||||||
@ -149,144 +144,152 @@ namespace
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t BackupCoordinationRemote::findCurrentHostIndex(const Strings & all_hosts, const String & current_host)
|
Strings BackupCoordinationOnCluster::excludeInitiator(const Strings & all_hosts)
|
||||||
|
{
|
||||||
|
Strings all_hosts_without_initiator = all_hosts;
|
||||||
|
bool has_initiator = (std::erase(all_hosts_without_initiator, kInitiator) > 0);
|
||||||
|
chassert(has_initiator);
|
||||||
|
return all_hosts_without_initiator;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t BackupCoordinationOnCluster::findCurrentHostIndex(const String & current_host, const Strings & all_hosts)
|
||||||
{
|
{
|
||||||
auto it = std::find(all_hosts.begin(), all_hosts.end(), current_host);
|
auto it = std::find(all_hosts.begin(), all_hosts.end(), current_host);
|
||||||
if (it == all_hosts.end())
|
if (it == all_hosts.end())
|
||||||
return 0;
|
return all_hosts.size();
|
||||||
return it - all_hosts.begin();
|
return it - all_hosts.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
BackupCoordinationRemote::BackupCoordinationRemote(
|
|
||||||
zkutil::GetZooKeeper get_zookeeper_,
|
BackupCoordinationOnCluster::BackupCoordinationOnCluster(
|
||||||
|
const UUID & backup_uuid_,
|
||||||
|
bool is_plain_backup_,
|
||||||
const String & root_zookeeper_path_,
|
const String & root_zookeeper_path_,
|
||||||
|
zkutil::GetZooKeeper get_zookeeper_,
|
||||||
const BackupKeeperSettings & keeper_settings_,
|
const BackupKeeperSettings & keeper_settings_,
|
||||||
const String & backup_uuid_,
|
|
||||||
const Strings & all_hosts_,
|
|
||||||
const String & current_host_,
|
const String & current_host_,
|
||||||
bool plain_backup_,
|
const Strings & all_hosts_,
|
||||||
bool is_internal_,
|
bool allow_concurrent_backup_,
|
||||||
|
BackupConcurrencyCounters & concurrency_counters_,
|
||||||
|
ThreadPoolCallbackRunnerUnsafe<void> schedule_,
|
||||||
QueryStatusPtr process_list_element_)
|
QueryStatusPtr process_list_element_)
|
||||||
: root_zookeeper_path(root_zookeeper_path_)
|
: root_zookeeper_path(root_zookeeper_path_)
|
||||||
, zookeeper_path(root_zookeeper_path_ + "/backup-" + backup_uuid_)
|
, zookeeper_path(root_zookeeper_path_ + "/backup-" + toString(backup_uuid_))
|
||||||
, keeper_settings(keeper_settings_)
|
, keeper_settings(keeper_settings_)
|
||||||
, backup_uuid(backup_uuid_)
|
, backup_uuid(backup_uuid_)
|
||||||
, all_hosts(all_hosts_)
|
, all_hosts(all_hosts_)
|
||||||
|
, all_hosts_without_initiator(excludeInitiator(all_hosts))
|
||||||
, current_host(current_host_)
|
, current_host(current_host_)
|
||||||
, current_host_index(findCurrentHostIndex(all_hosts, current_host))
|
, current_host_index(findCurrentHostIndex(current_host, all_hosts))
|
||||||
, plain_backup(plain_backup_)
|
, plain_backup(is_plain_backup_)
|
||||||
, is_internal(is_internal_)
|
, log(getLogger("BackupCoordinationOnCluster"))
|
||||||
, log(getLogger("BackupCoordinationRemote"))
|
, with_retries(log, get_zookeeper_, keeper_settings, process_list_element_, [root_zookeeper_path_](Coordination::ZooKeeperWithFaultInjection::Ptr zk) { zk->sync(root_zookeeper_path_); })
|
||||||
, with_retries(
|
, concurrency_check(backup_uuid_, /* is_restore = */ false, /* on_cluster = */ true, allow_concurrent_backup_, concurrency_counters_)
|
||||||
log,
|
, stage_sync(/* is_restore = */ false, fs::path{zookeeper_path} / "stage", current_host, all_hosts, allow_concurrent_backup_, with_retries, schedule_, process_list_element_, log)
|
||||||
get_zookeeper_,
|
, cleaner(zookeeper_path, with_retries, log)
|
||||||
keeper_settings,
|
|
||||||
process_list_element_,
|
|
||||||
[my_zookeeper_path = zookeeper_path, my_current_host = current_host, my_is_internal = is_internal]
|
|
||||||
(WithRetries::FaultyKeeper & zk)
|
|
||||||
{
|
|
||||||
/// Recreate this ephemeral node to signal that we are alive.
|
|
||||||
if (my_is_internal)
|
|
||||||
{
|
|
||||||
String alive_node_path = my_zookeeper_path + "/stage/alive|" + my_current_host;
|
|
||||||
|
|
||||||
/// Delete the ephemeral node from the previous connection so we don't have to wait for keeper to do it automatically.
|
|
||||||
zk->tryRemove(alive_node_path);
|
|
||||||
|
|
||||||
zk->createAncestors(alive_node_path);
|
|
||||||
zk->create(alive_node_path, "", zkutil::CreateMode::Ephemeral);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
{
|
{
|
||||||
createRootNodes();
|
createRootNodes();
|
||||||
|
|
||||||
stage_sync.emplace(
|
|
||||||
zookeeper_path,
|
|
||||||
with_retries,
|
|
||||||
log);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BackupCoordinationRemote::~BackupCoordinationRemote()
|
BackupCoordinationOnCluster::~BackupCoordinationOnCluster()
|
||||||
{
|
{
|
||||||
try
|
tryFinishImpl();
|
||||||
{
|
|
||||||
if (!is_internal)
|
|
||||||
removeAllNodes();
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
tryLogCurrentException(__PRETTY_FUNCTION__);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackupCoordinationRemote::createRootNodes()
|
void BackupCoordinationOnCluster::createRootNodes()
|
||||||
{
|
{
|
||||||
auto holder = with_retries.createRetriesControlHolder("createRootNodes");
|
auto holder = with_retries.createRetriesControlHolder("createRootNodes", WithRetries::kInitialization);
|
||||||
holder.retries_ctl.retryLoop(
|
holder.retries_ctl.retryLoop(
|
||||||
[&, &zk = holder.faulty_zookeeper]()
|
[&, &zk = holder.faulty_zookeeper]()
|
||||||
{
|
{
|
||||||
with_retries.renewZooKeeper(zk);
|
with_retries.renewZooKeeper(zk);
|
||||||
|
|
||||||
zk->createAncestors(zookeeper_path);
|
zk->createAncestors(zookeeper_path);
|
||||||
|
zk->createIfNotExists(zookeeper_path, "");
|
||||||
Coordination::Requests ops;
|
zk->createIfNotExists(zookeeper_path + "/repl_part_names", "");
|
||||||
Coordination::Responses responses;
|
zk->createIfNotExists(zookeeper_path + "/repl_mutations", "");
|
||||||
ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path, "", zkutil::CreateMode::Persistent));
|
zk->createIfNotExists(zookeeper_path + "/repl_data_paths", "");
|
||||||
ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/repl_part_names", "", zkutil::CreateMode::Persistent));
|
zk->createIfNotExists(zookeeper_path + "/repl_access", "");
|
||||||
ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/repl_mutations", "", zkutil::CreateMode::Persistent));
|
zk->createIfNotExists(zookeeper_path + "/repl_sql_objects", "");
|
||||||
ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/repl_data_paths", "", zkutil::CreateMode::Persistent));
|
zk->createIfNotExists(zookeeper_path + "/keeper_map_tables", "");
|
||||||
ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/repl_access", "", zkutil::CreateMode::Persistent));
|
zk->createIfNotExists(zookeeper_path + "/file_infos", "");
|
||||||
ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/repl_sql_objects", "", zkutil::CreateMode::Persistent));
|
zk->createIfNotExists(zookeeper_path + "/writing_files", "");
|
||||||
ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/keeper_map_tables", "", zkutil::CreateMode::Persistent));
|
|
||||||
ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/file_infos", "", zkutil::CreateMode::Persistent));
|
|
||||||
ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/writing_files", "", zkutil::CreateMode::Persistent));
|
|
||||||
zk->tryMulti(ops, responses);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackupCoordinationRemote::removeAllNodes()
|
Strings BackupCoordinationOnCluster::setStage(const String & new_stage, const String & message, bool sync)
|
||||||
{
|
{
|
||||||
auto holder = with_retries.createRetriesControlHolder("removeAllNodes");
|
stage_sync.setStage(new_stage, message);
|
||||||
holder.retries_ctl.retryLoop(
|
|
||||||
[&, &zk = holder.faulty_zookeeper]()
|
if (!sync)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return stage_sync.waitForHostsToReachStage(new_stage, all_hosts_without_initiator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackupCoordinationOnCluster::setBackupQueryWasSentToOtherHosts()
|
||||||
|
{
|
||||||
|
backup_query_was_sent_to_other_hosts = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BackupCoordinationOnCluster::trySetError(std::exception_ptr exception)
|
||||||
|
{
|
||||||
|
return stage_sync.trySetError(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackupCoordinationOnCluster::finish()
|
||||||
|
{
|
||||||
|
bool other_hosts_also_finished = false;
|
||||||
|
stage_sync.finish(other_hosts_also_finished);
|
||||||
|
|
||||||
|
if ((current_host == kInitiator) && (other_hosts_also_finished || !backup_query_was_sent_to_other_hosts))
|
||||||
|
cleaner.cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BackupCoordinationOnCluster::tryFinishAfterError() noexcept
|
||||||
|
{
|
||||||
|
return tryFinishImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BackupCoordinationOnCluster::tryFinishImpl() noexcept
|
||||||
|
{
|
||||||
|
bool other_hosts_also_finished = false;
|
||||||
|
if (!stage_sync.tryFinishAfterError(other_hosts_also_finished))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ((current_host == kInitiator) && (other_hosts_also_finished || !backup_query_was_sent_to_other_hosts))
|
||||||
{
|
{
|
||||||
/// Usually this function is called by the initiator when a backup is complete so we don't need the coordination anymore.
|
if (!cleaner.tryCleanupAfterError())
|
||||||
///
|
return false;
|
||||||
/// However there can be a rare situation when this function is called after an error occurs on the initiator of a query
|
}
|
||||||
/// while some hosts are still making the backup. Removing all the nodes will remove the parent node of the backup coordination
|
|
||||||
/// at `zookeeper_path` which might cause such hosts to stop with exception "ZNONODE". Or such hosts might still do some useless part
|
return true;
|
||||||
/// of their backup work before that. Anyway in this case backup won't be finalized (because only an initiator can do that).
|
|
||||||
with_retries.renewZooKeeper(zk);
|
|
||||||
zk->removeRecursive(zookeeper_path);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BackupCoordinationOnCluster::waitForOtherHostsToFinish()
|
||||||
void BackupCoordinationRemote::setStage(const String & new_stage, const String & message)
|
|
||||||
{
|
{
|
||||||
if (is_internal)
|
if ((current_host != kInitiator) || !backup_query_was_sent_to_other_hosts)
|
||||||
stage_sync->set(current_host, new_stage, message);
|
return;
|
||||||
else
|
stage_sync.waitForOtherHostsToFinish();
|
||||||
stage_sync->set(current_host, new_stage, /* message */ "", /* all_hosts */ true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackupCoordinationRemote::setError(const Exception & exception)
|
bool BackupCoordinationOnCluster::tryWaitForOtherHostsToFinishAfterError() noexcept
|
||||||
{
|
{
|
||||||
stage_sync->setError(current_host, exception);
|
if (current_host != kInitiator)
|
||||||
|
return false;
|
||||||
|
if (!backup_query_was_sent_to_other_hosts)
|
||||||
|
return true;
|
||||||
|
return stage_sync.tryWaitForOtherHostsToFinishAfterError();
|
||||||
}
|
}
|
||||||
|
|
||||||
Strings BackupCoordinationRemote::waitForStage(const String & stage_to_wait)
|
ZooKeeperRetriesInfo BackupCoordinationOnCluster::getOnClusterInitializationKeeperRetriesInfo() const
|
||||||
{
|
{
|
||||||
return stage_sync->wait(all_hosts, stage_to_wait);
|
return ZooKeeperRetriesInfo{keeper_settings.max_retries_while_initializing,
|
||||||
|
static_cast<UInt64>(keeper_settings.retry_initial_backoff_ms.count()),
|
||||||
|
static_cast<UInt64>(keeper_settings.retry_max_backoff_ms.count())};
|
||||||
}
|
}
|
||||||
|
|
||||||
Strings BackupCoordinationRemote::waitForStage(const String & stage_to_wait, std::chrono::milliseconds timeout)
|
void BackupCoordinationOnCluster::serializeToMultipleZooKeeperNodes(const String & path, const String & value, const String & logging_name)
|
||||||
{
|
|
||||||
return stage_sync->waitFor(all_hosts, stage_to_wait, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void BackupCoordinationRemote::serializeToMultipleZooKeeperNodes(const String & path, const String & value, const String & logging_name)
|
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
auto holder = with_retries.createRetriesControlHolder(logging_name + "::create");
|
auto holder = with_retries.createRetriesControlHolder(logging_name + "::create");
|
||||||
@ -301,7 +304,7 @@ void BackupCoordinationRemote::serializeToMultipleZooKeeperNodes(const String &
|
|||||||
if (value.empty())
|
if (value.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
size_t max_part_size = keeper_settings.keeper_value_max_size;
|
size_t max_part_size = keeper_settings.value_max_size;
|
||||||
if (!max_part_size)
|
if (!max_part_size)
|
||||||
max_part_size = value.size();
|
max_part_size = value.size();
|
||||||
|
|
||||||
@ -324,7 +327,7 @@ void BackupCoordinationRemote::serializeToMultipleZooKeeperNodes(const String &
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String BackupCoordinationRemote::deserializeFromMultipleZooKeeperNodes(const String & path, const String & logging_name) const
|
String BackupCoordinationOnCluster::deserializeFromMultipleZooKeeperNodes(const String & path, const String & logging_name) const
|
||||||
{
|
{
|
||||||
Strings part_names;
|
Strings part_names;
|
||||||
|
|
||||||
@ -357,7 +360,7 @@ String BackupCoordinationRemote::deserializeFromMultipleZooKeeperNodes(const Str
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void BackupCoordinationRemote::addReplicatedPartNames(
|
void BackupCoordinationOnCluster::addReplicatedPartNames(
|
||||||
const String & table_zk_path,
|
const String & table_zk_path,
|
||||||
const String & table_name_for_logs,
|
const String & table_name_for_logs,
|
||||||
const String & replica_name,
|
const String & replica_name,
|
||||||
@ -381,14 +384,14 @@ void BackupCoordinationRemote::addReplicatedPartNames(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Strings BackupCoordinationRemote::getReplicatedPartNames(const String & table_zk_path, const String & replica_name) const
|
Strings BackupCoordinationOnCluster::getReplicatedPartNames(const String & table_zk_path, const String & replica_name) const
|
||||||
{
|
{
|
||||||
std::lock_guard lock{replicated_tables_mutex};
|
std::lock_guard lock{replicated_tables_mutex};
|
||||||
prepareReplicatedTables();
|
prepareReplicatedTables();
|
||||||
return replicated_tables->getPartNames(table_zk_path, replica_name);
|
return replicated_tables->getPartNames(table_zk_path, replica_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackupCoordinationRemote::addReplicatedMutations(
|
void BackupCoordinationOnCluster::addReplicatedMutations(
|
||||||
const String & table_zk_path,
|
const String & table_zk_path,
|
||||||
const String & table_name_for_logs,
|
const String & table_name_for_logs,
|
||||||
const String & replica_name,
|
const String & replica_name,
|
||||||
@ -412,7 +415,7 @@ void BackupCoordinationRemote::addReplicatedMutations(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<IBackupCoordination::MutationInfo> BackupCoordinationRemote::getReplicatedMutations(const String & table_zk_path, const String & replica_name) const
|
std::vector<IBackupCoordination::MutationInfo> BackupCoordinationOnCluster::getReplicatedMutations(const String & table_zk_path, const String & replica_name) const
|
||||||
{
|
{
|
||||||
std::lock_guard lock{replicated_tables_mutex};
|
std::lock_guard lock{replicated_tables_mutex};
|
||||||
prepareReplicatedTables();
|
prepareReplicatedTables();
|
||||||
@ -420,7 +423,7 @@ std::vector<IBackupCoordination::MutationInfo> BackupCoordinationRemote::getRepl
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void BackupCoordinationRemote::addReplicatedDataPath(
|
void BackupCoordinationOnCluster::addReplicatedDataPath(
|
||||||
const String & table_zk_path, const String & data_path)
|
const String & table_zk_path, const String & data_path)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
@ -441,7 +444,7 @@ void BackupCoordinationRemote::addReplicatedDataPath(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Strings BackupCoordinationRemote::getReplicatedDataPaths(const String & table_zk_path) const
|
Strings BackupCoordinationOnCluster::getReplicatedDataPaths(const String & table_zk_path) const
|
||||||
{
|
{
|
||||||
std::lock_guard lock{replicated_tables_mutex};
|
std::lock_guard lock{replicated_tables_mutex};
|
||||||
prepareReplicatedTables();
|
prepareReplicatedTables();
|
||||||
@ -449,7 +452,7 @@ Strings BackupCoordinationRemote::getReplicatedDataPaths(const String & table_zk
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void BackupCoordinationRemote::prepareReplicatedTables() const
|
void BackupCoordinationOnCluster::prepareReplicatedTables() const
|
||||||
{
|
{
|
||||||
if (replicated_tables)
|
if (replicated_tables)
|
||||||
return;
|
return;
|
||||||
@ -536,7 +539,7 @@ void BackupCoordinationRemote::prepareReplicatedTables() const
|
|||||||
replicated_tables->addDataPath(std::move(data_paths));
|
replicated_tables->addDataPath(std::move(data_paths));
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackupCoordinationRemote::addReplicatedAccessFilePath(const String & access_zk_path, AccessEntityType access_entity_type, const String & file_path)
|
void BackupCoordinationOnCluster::addReplicatedAccessFilePath(const String & access_zk_path, AccessEntityType access_entity_type, const String & file_path)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
std::lock_guard lock{replicated_access_mutex};
|
std::lock_guard lock{replicated_access_mutex};
|
||||||
@ -558,14 +561,14 @@ void BackupCoordinationRemote::addReplicatedAccessFilePath(const String & access
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Strings BackupCoordinationRemote::getReplicatedAccessFilePaths(const String & access_zk_path, AccessEntityType access_entity_type) const
|
Strings BackupCoordinationOnCluster::getReplicatedAccessFilePaths(const String & access_zk_path, AccessEntityType access_entity_type) const
|
||||||
{
|
{
|
||||||
std::lock_guard lock{replicated_access_mutex};
|
std::lock_guard lock{replicated_access_mutex};
|
||||||
prepareReplicatedAccess();
|
prepareReplicatedAccess();
|
||||||
return replicated_access->getFilePaths(access_zk_path, access_entity_type, current_host);
|
return replicated_access->getFilePaths(access_zk_path, access_entity_type, current_host);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackupCoordinationRemote::prepareReplicatedAccess() const
|
void BackupCoordinationOnCluster::prepareReplicatedAccess() const
|
||||||
{
|
{
|
||||||
if (replicated_access)
|
if (replicated_access)
|
||||||
return;
|
return;
|
||||||
@ -601,7 +604,7 @@ void BackupCoordinationRemote::prepareReplicatedAccess() const
|
|||||||
replicated_access->addFilePath(std::move(file_path));
|
replicated_access->addFilePath(std::move(file_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackupCoordinationRemote::addReplicatedSQLObjectsDir(const String & loader_zk_path, UserDefinedSQLObjectType object_type, const String & dir_path)
|
void BackupCoordinationOnCluster::addReplicatedSQLObjectsDir(const String & loader_zk_path, UserDefinedSQLObjectType object_type, const String & dir_path)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
std::lock_guard lock{replicated_sql_objects_mutex};
|
std::lock_guard lock{replicated_sql_objects_mutex};
|
||||||
@ -631,14 +634,14 @@ void BackupCoordinationRemote::addReplicatedSQLObjectsDir(const String & loader_
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Strings BackupCoordinationRemote::getReplicatedSQLObjectsDirs(const String & loader_zk_path, UserDefinedSQLObjectType object_type) const
|
Strings BackupCoordinationOnCluster::getReplicatedSQLObjectsDirs(const String & loader_zk_path, UserDefinedSQLObjectType object_type) const
|
||||||
{
|
{
|
||||||
std::lock_guard lock{replicated_sql_objects_mutex};
|
std::lock_guard lock{replicated_sql_objects_mutex};
|
||||||
prepareReplicatedSQLObjects();
|
prepareReplicatedSQLObjects();
|
||||||
return replicated_sql_objects->getDirectories(loader_zk_path, object_type, current_host);
|
return replicated_sql_objects->getDirectories(loader_zk_path, object_type, current_host);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackupCoordinationRemote::prepareReplicatedSQLObjects() const
|
void BackupCoordinationOnCluster::prepareReplicatedSQLObjects() const
|
||||||
{
|
{
|
||||||
if (replicated_sql_objects)
|
if (replicated_sql_objects)
|
||||||
return;
|
return;
|
||||||
@ -674,7 +677,7 @@ void BackupCoordinationRemote::prepareReplicatedSQLObjects() const
|
|||||||
replicated_sql_objects->addDirectory(std::move(directory));
|
replicated_sql_objects->addDirectory(std::move(directory));
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackupCoordinationRemote::addKeeperMapTable(const String & table_zookeeper_root_path, const String & table_id, const String & data_path_in_backup)
|
void BackupCoordinationOnCluster::addKeeperMapTable(const String & table_zookeeper_root_path, const String & table_id, const String & data_path_in_backup)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
std::lock_guard lock{keeper_map_tables_mutex};
|
std::lock_guard lock{keeper_map_tables_mutex};
|
||||||
@ -695,7 +698,7 @@ void BackupCoordinationRemote::addKeeperMapTable(const String & table_zookeeper_
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackupCoordinationRemote::prepareKeeperMapTables() const
|
void BackupCoordinationOnCluster::prepareKeeperMapTables() const
|
||||||
{
|
{
|
||||||
if (keeper_map_tables)
|
if (keeper_map_tables)
|
||||||
return;
|
return;
|
||||||
@ -740,7 +743,7 @@ void BackupCoordinationRemote::prepareKeeperMapTables() const
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String BackupCoordinationRemote::getKeeperMapDataPath(const String & table_zookeeper_root_path) const
|
String BackupCoordinationOnCluster::getKeeperMapDataPath(const String & table_zookeeper_root_path) const
|
||||||
{
|
{
|
||||||
std::lock_guard lock(keeper_map_tables_mutex);
|
std::lock_guard lock(keeper_map_tables_mutex);
|
||||||
prepareKeeperMapTables();
|
prepareKeeperMapTables();
|
||||||
@ -748,7 +751,7 @@ String BackupCoordinationRemote::getKeeperMapDataPath(const String & table_zooke
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void BackupCoordinationRemote::addFileInfos(BackupFileInfos && file_infos_)
|
void BackupCoordinationOnCluster::addFileInfos(BackupFileInfos && file_infos_)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
std::lock_guard lock{file_infos_mutex};
|
std::lock_guard lock{file_infos_mutex};
|
||||||
@ -761,21 +764,21 @@ void BackupCoordinationRemote::addFileInfos(BackupFileInfos && file_infos_)
|
|||||||
serializeToMultipleZooKeeperNodes(zookeeper_path + "/file_infos/" + current_host, file_infos_str, "addFileInfos");
|
serializeToMultipleZooKeeperNodes(zookeeper_path + "/file_infos/" + current_host, file_infos_str, "addFileInfos");
|
||||||
}
|
}
|
||||||
|
|
||||||
BackupFileInfos BackupCoordinationRemote::getFileInfos() const
|
BackupFileInfos BackupCoordinationOnCluster::getFileInfos() const
|
||||||
{
|
{
|
||||||
std::lock_guard lock{file_infos_mutex};
|
std::lock_guard lock{file_infos_mutex};
|
||||||
prepareFileInfos();
|
prepareFileInfos();
|
||||||
return file_infos->getFileInfos(current_host);
|
return file_infos->getFileInfos(current_host);
|
||||||
}
|
}
|
||||||
|
|
||||||
BackupFileInfos BackupCoordinationRemote::getFileInfosForAllHosts() const
|
BackupFileInfos BackupCoordinationOnCluster::getFileInfosForAllHosts() const
|
||||||
{
|
{
|
||||||
std::lock_guard lock{file_infos_mutex};
|
std::lock_guard lock{file_infos_mutex};
|
||||||
prepareFileInfos();
|
prepareFileInfos();
|
||||||
return file_infos->getFileInfosForAllHosts();
|
return file_infos->getFileInfosForAllHosts();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackupCoordinationRemote::prepareFileInfos() const
|
void BackupCoordinationOnCluster::prepareFileInfos() const
|
||||||
{
|
{
|
||||||
if (file_infos)
|
if (file_infos)
|
||||||
return;
|
return;
|
||||||
@ -801,7 +804,7 @@ void BackupCoordinationRemote::prepareFileInfos() const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BackupCoordinationRemote::startWritingFile(size_t data_file_index)
|
bool BackupCoordinationOnCluster::startWritingFile(size_t data_file_index)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
/// Check if this host is already writing this file.
|
/// Check if this host is already writing this file.
|
||||||
@ -842,66 +845,4 @@ bool BackupCoordinationRemote::startWritingFile(size_t data_file_index)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BackupCoordinationRemote::hasConcurrentBackups(const std::atomic<size_t> &) const
|
|
||||||
{
|
|
||||||
/// If its internal concurrency will be checked for the base backup
|
|
||||||
if (is_internal)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::string backup_stage_path = zookeeper_path + "/stage";
|
|
||||||
|
|
||||||
bool result = false;
|
|
||||||
|
|
||||||
auto holder = with_retries.createRetriesControlHolder("getAllArchiveSuffixes");
|
|
||||||
holder.retries_ctl.retryLoop(
|
|
||||||
[&, &zk = holder.faulty_zookeeper]()
|
|
||||||
{
|
|
||||||
with_retries.renewZooKeeper(zk);
|
|
||||||
|
|
||||||
if (!zk->exists(root_zookeeper_path))
|
|
||||||
zk->createAncestors(root_zookeeper_path);
|
|
||||||
|
|
||||||
for (size_t attempt = 0; attempt < MAX_ZOOKEEPER_ATTEMPTS; ++attempt)
|
|
||||||
{
|
|
||||||
Coordination::Stat stat;
|
|
||||||
zk->get(root_zookeeper_path, &stat);
|
|
||||||
Strings existing_backup_paths = zk->getChildren(root_zookeeper_path);
|
|
||||||
|
|
||||||
for (const auto & existing_backup_path : existing_backup_paths)
|
|
||||||
{
|
|
||||||
if (startsWith(existing_backup_path, "restore-"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
String existing_backup_uuid = existing_backup_path;
|
|
||||||
existing_backup_uuid.erase(0, String("backup-").size());
|
|
||||||
|
|
||||||
if (existing_backup_uuid == toString(backup_uuid))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
String status;
|
|
||||||
if (zk->tryGet(root_zookeeper_path + "/" + existing_backup_path + "/stage", status))
|
|
||||||
{
|
|
||||||
/// Check if some other backup is in progress
|
|
||||||
if (status == Stage::SCHEDULED_TO_START)
|
|
||||||
{
|
|
||||||
LOG_WARNING(log, "Found a concurrent backup: {}, current backup: {}", existing_backup_uuid, toString(backup_uuid));
|
|
||||||
result = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
zk->createIfNotExists(backup_stage_path, "");
|
|
||||||
auto code = zk->trySet(backup_stage_path, Stage::SCHEDULED_TO_START, stat.version);
|
|
||||||
if (code == Coordination::Error::ZOK)
|
|
||||||
break;
|
|
||||||
bool is_last_attempt = (attempt == MAX_ZOOKEEPER_ATTEMPTS - 1);
|
|
||||||
if ((code != Coordination::Error::ZBADVERSION) || is_last_attempt)
|
|
||||||
throw zkutil::KeeperException::fromPath(code, backup_stage_path);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -1,6 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Backups/IBackupCoordination.h>
|
#include <Backups/IBackupCoordination.h>
|
||||||
|
#include <Backups/BackupConcurrencyCheck.h>
|
||||||
|
#include <Backups/BackupCoordinationCleaner.h>
|
||||||
#include <Backups/BackupCoordinationFileInfos.h>
|
#include <Backups/BackupCoordinationFileInfos.h>
|
||||||
#include <Backups/BackupCoordinationReplicatedAccess.h>
|
#include <Backups/BackupCoordinationReplicatedAccess.h>
|
||||||
#include <Backups/BackupCoordinationReplicatedSQLObjects.h>
|
#include <Backups/BackupCoordinationReplicatedSQLObjects.h>
|
||||||
@ -13,32 +15,35 @@
|
|||||||
namespace DB
|
namespace DB
|
||||||
{
|
{
|
||||||
|
|
||||||
/// We try to store data to zookeeper several times due to possible version conflicts.
|
|
||||||
constexpr size_t MAX_ZOOKEEPER_ATTEMPTS = 10;
|
|
||||||
|
|
||||||
/// Implementation of the IBackupCoordination interface performing coordination via ZooKeeper. It's necessary for "BACKUP ON CLUSTER".
|
/// Implementation of the IBackupCoordination interface performing coordination via ZooKeeper. It's necessary for "BACKUP ON CLUSTER".
|
||||||
class BackupCoordinationRemote : public IBackupCoordination
|
class BackupCoordinationOnCluster : public IBackupCoordination
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using BackupKeeperSettings = WithRetries::KeeperSettings;
|
/// Empty string as the current host is used to mark the initiator of a BACKUP ON CLUSTER query.
|
||||||
|
static const constexpr std::string_view kInitiator;
|
||||||
|
|
||||||
BackupCoordinationRemote(
|
BackupCoordinationOnCluster(
|
||||||
zkutil::GetZooKeeper get_zookeeper_,
|
const UUID & backup_uuid_,
|
||||||
|
bool is_plain_backup_,
|
||||||
const String & root_zookeeper_path_,
|
const String & root_zookeeper_path_,
|
||||||
|
zkutil::GetZooKeeper get_zookeeper_,
|
||||||
const BackupKeeperSettings & keeper_settings_,
|
const BackupKeeperSettings & keeper_settings_,
|
||||||
const String & backup_uuid_,
|
|
||||||
const Strings & all_hosts_,
|
|
||||||
const String & current_host_,
|
const String & current_host_,
|
||||||
bool plain_backup_,
|
const Strings & all_hosts_,
|
||||||
bool is_internal_,
|
bool allow_concurrent_backup_,
|
||||||
|
BackupConcurrencyCounters & concurrency_counters_,
|
||||||
|
ThreadPoolCallbackRunnerUnsafe<void> schedule_,
|
||||||
QueryStatusPtr process_list_element_);
|
QueryStatusPtr process_list_element_);
|
||||||
|
|
||||||
~BackupCoordinationRemote() override;
|
~BackupCoordinationOnCluster() override;
|
||||||
|
|
||||||
void setStage(const String & new_stage, const String & message) override;
|
Strings setStage(const String & new_stage, const String & message, bool sync) override;
|
||||||
void setError(const Exception & exception) override;
|
void setBackupQueryWasSentToOtherHosts() override;
|
||||||
Strings waitForStage(const String & stage_to_wait) override;
|
bool trySetError(std::exception_ptr exception) override;
|
||||||
Strings waitForStage(const String & stage_to_wait, std::chrono::milliseconds timeout) override;
|
void finish() override;
|
||||||
|
bool tryFinishAfterError() noexcept override;
|
||||||
|
void waitForOtherHostsToFinish() override;
|
||||||
|
bool tryWaitForOtherHostsToFinishAfterError() noexcept override;
|
||||||
|
|
||||||
void addReplicatedPartNames(
|
void addReplicatedPartNames(
|
||||||
const String & table_zk_path,
|
const String & table_zk_path,
|
||||||
@ -73,13 +78,14 @@ public:
|
|||||||
BackupFileInfos getFileInfosForAllHosts() const override;
|
BackupFileInfos getFileInfosForAllHosts() const override;
|
||||||
bool startWritingFile(size_t data_file_index) override;
|
bool startWritingFile(size_t data_file_index) override;
|
||||||
|
|
||||||
bool hasConcurrentBackups(const std::atomic<size_t> & num_active_backups) const override;
|
ZooKeeperRetriesInfo getOnClusterInitializationKeeperRetriesInfo() const override;
|
||||||
|
|
||||||
static size_t findCurrentHostIndex(const Strings & all_hosts, const String & current_host);
|
static Strings excludeInitiator(const Strings & all_hosts);
|
||||||
|
static size_t findCurrentHostIndex(const String & current_host, const Strings & all_hosts);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void createRootNodes();
|
void createRootNodes();
|
||||||
void removeAllNodes();
|
bool tryFinishImpl() noexcept;
|
||||||
|
|
||||||
void serializeToMultipleZooKeeperNodes(const String & path, const String & value, const String & logging_name);
|
void serializeToMultipleZooKeeperNodes(const String & path, const String & value, const String & logging_name);
|
||||||
String deserializeFromMultipleZooKeeperNodes(const String & path, const String & logging_name) const;
|
String deserializeFromMultipleZooKeeperNodes(const String & path, const String & logging_name) const;
|
||||||
@ -96,26 +102,27 @@ private:
|
|||||||
const String root_zookeeper_path;
|
const String root_zookeeper_path;
|
||||||
const String zookeeper_path;
|
const String zookeeper_path;
|
||||||
const BackupKeeperSettings keeper_settings;
|
const BackupKeeperSettings keeper_settings;
|
||||||
const String backup_uuid;
|
const UUID backup_uuid;
|
||||||
const Strings all_hosts;
|
const Strings all_hosts;
|
||||||
|
const Strings all_hosts_without_initiator;
|
||||||
const String current_host;
|
const String current_host;
|
||||||
const size_t current_host_index;
|
const size_t current_host_index;
|
||||||
const bool plain_backup;
|
const bool plain_backup;
|
||||||
const bool is_internal;
|
|
||||||
LoggerPtr const log;
|
LoggerPtr const log;
|
||||||
|
|
||||||
/// The order of these two fields matters, because stage_sync holds a reference to with_retries object
|
const WithRetries with_retries;
|
||||||
mutable WithRetries with_retries;
|
BackupConcurrencyCheck concurrency_check;
|
||||||
std::optional<BackupCoordinationStageSync> stage_sync;
|
BackupCoordinationStageSync stage_sync;
|
||||||
|
BackupCoordinationCleaner cleaner;
|
||||||
|
std::atomic<bool> backup_query_was_sent_to_other_hosts = false;
|
||||||
|
|
||||||
mutable std::optional<BackupCoordinationReplicatedTables> TSA_GUARDED_BY(replicated_tables_mutex) replicated_tables;
|
mutable std::optional<BackupCoordinationReplicatedTables> replicated_tables TSA_GUARDED_BY(replicated_tables_mutex);
|
||||||
mutable std::optional<BackupCoordinationReplicatedAccess> TSA_GUARDED_BY(replicated_access_mutex) replicated_access;
|
mutable std::optional<BackupCoordinationReplicatedAccess> replicated_access TSA_GUARDED_BY(replicated_access_mutex);
|
||||||
mutable std::optional<BackupCoordinationReplicatedSQLObjects> TSA_GUARDED_BY(replicated_sql_objects_mutex) replicated_sql_objects;
|
mutable std::optional<BackupCoordinationReplicatedSQLObjects> replicated_sql_objects TSA_GUARDED_BY(replicated_sql_objects_mutex);
|
||||||
mutable std::optional<BackupCoordinationFileInfos> TSA_GUARDED_BY(file_infos_mutex) file_infos;
|
mutable std::optional<BackupCoordinationFileInfos> file_infos TSA_GUARDED_BY(file_infos_mutex);
|
||||||
mutable std::optional<BackupCoordinationKeeperMapTables> keeper_map_tables TSA_GUARDED_BY(keeper_map_tables_mutex);
|
mutable std::optional<BackupCoordinationKeeperMapTables> keeper_map_tables TSA_GUARDED_BY(keeper_map_tables_mutex);
|
||||||
std::unordered_set<size_t> TSA_GUARDED_BY(writing_files_mutex) writing_files;
|
std::unordered_set<size_t> writing_files TSA_GUARDED_BY(writing_files_mutex);
|
||||||
|
|
||||||
mutable std::mutex zookeeper_mutex;
|
|
||||||
mutable std::mutex replicated_tables_mutex;
|
mutable std::mutex replicated_tables_mutex;
|
||||||
mutable std::mutex replicated_access_mutex;
|
mutable std::mutex replicated_access_mutex;
|
||||||
mutable std::mutex replicated_sql_objects_mutex;
|
mutable std::mutex replicated_sql_objects_mutex;
|
@ -8,10 +8,6 @@ namespace DB
|
|||||||
|
|
||||||
namespace BackupCoordinationStage
|
namespace BackupCoordinationStage
|
||||||
{
|
{
|
||||||
/// This stage is set after concurrency check so ensure we dont start other backup/restores
|
|
||||||
/// when concurrent backup/restores are not allowed
|
|
||||||
constexpr const char * SCHEDULED_TO_START = "scheduled to start";
|
|
||||||
|
|
||||||
/// Finding all tables and databases which we're going to put to the backup and collecting their metadata.
|
/// Finding all tables and databases which we're going to put to the backup and collecting their metadata.
|
||||||
constexpr const char * GATHERING_METADATA = "gathering metadata";
|
constexpr const char * GATHERING_METADATA = "gathering metadata";
|
||||||
|
|
||||||
@ -46,10 +42,6 @@ namespace BackupCoordinationStage
|
|||||||
|
|
||||||
/// Coordination stage meaning that a host finished its work.
|
/// Coordination stage meaning that a host finished its work.
|
||||||
constexpr const char * COMPLETED = "completed";
|
constexpr const char * COMPLETED = "completed";
|
||||||
|
|
||||||
/// Coordination stage meaning that backup/restore has failed due to an error
|
|
||||||
/// Check '/error' for the error message
|
|
||||||
constexpr const char * ERROR = "error";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -10,33 +10,193 @@ class BackupCoordinationStageSync
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BackupCoordinationStageSync(
|
BackupCoordinationStageSync(
|
||||||
const String & root_zookeeper_path_,
|
bool is_restore_, /// true if this is a RESTORE ON CLUSTER command, false if this is a BACKUP ON CLUSTER command
|
||||||
WithRetries & with_retries_,
|
const String & zookeeper_path_, /// path to the "stage" folder in ZooKeeper
|
||||||
|
const String & current_host_, /// the current host, or an empty string if it's the initiator of the BACKUP/RESTORE ON CLUSTER command
|
||||||
|
const Strings & all_hosts_, /// all the hosts (including the initiator and the current host) performing the BACKUP/RESTORE ON CLUSTER command
|
||||||
|
bool allow_concurrency_, /// whether it's allowed to have concurrent backups or restores.
|
||||||
|
const WithRetries & with_retries_,
|
||||||
|
ThreadPoolCallbackRunnerUnsafe<void> schedule_,
|
||||||
|
QueryStatusPtr process_list_element_,
|
||||||
LoggerPtr log_);
|
LoggerPtr log_);
|
||||||
|
|
||||||
|
~BackupCoordinationStageSync();
|
||||||
|
|
||||||
/// Sets the stage of the current host and signal other hosts if there were other hosts waiting for that.
|
/// Sets the stage of the current host and signal other hosts if there were other hosts waiting for that.
|
||||||
void set(const String & current_host, const String & new_stage, const String & message, const bool & all_hosts = false);
|
void setStage(const String & stage, const String & stage_result = {});
|
||||||
void setError(const String & current_host, const Exception & exception);
|
|
||||||
|
|
||||||
/// Sets the stage of the current host and waits until all hosts come to the same stage.
|
/// Waits until all the specified hosts come to the specified stage.
|
||||||
/// The function returns the messages all hosts set when they come to the required stage.
|
/// The function returns the results which specified hosts set when they came to the required stage.
|
||||||
Strings wait(const Strings & all_hosts, const String & stage_to_wait);
|
/// If it doesn't happen before the timeout then the function will stop waiting and throw an exception.
|
||||||
|
Strings waitForHostsToReachStage(const String & stage_to_wait, const Strings & hosts, std::optional<std::chrono::milliseconds> timeout = {}) const;
|
||||||
|
|
||||||
/// Almost the same as setAndWait() but this one stops waiting and throws an exception after a specific amount of time.
|
/// Waits until all the other hosts finish their work.
|
||||||
Strings waitFor(const Strings & all_hosts, const String & stage_to_wait, std::chrono::milliseconds timeout);
|
/// Stops waiting and throws an exception if another host encounters an error or if some host gets cancelled.
|
||||||
|
void waitForOtherHostsToFinish() const;
|
||||||
|
|
||||||
|
/// Lets other host know that the current host has finished its work.
|
||||||
|
void finish(bool & other_hosts_also_finished);
|
||||||
|
|
||||||
|
/// Lets other hosts know that the current host has encountered an error.
|
||||||
|
bool trySetError(std::exception_ptr exception) noexcept;
|
||||||
|
|
||||||
|
/// Waits until all the other hosts finish their work (as a part of error-handling process).
|
||||||
|
/// Doesn't stops waiting if some host encounters an error or gets cancelled.
|
||||||
|
bool tryWaitForOtherHostsToFinishAfterError() const noexcept;
|
||||||
|
|
||||||
|
/// Lets other host know that the current host has finished its work (as a part of error-handling process).
|
||||||
|
bool tryFinishAfterError(bool & other_hosts_also_finished) noexcept;
|
||||||
|
|
||||||
|
/// Returns a printable name of a specific host. For empty host the function returns "initiator".
|
||||||
|
static String getHostDesc(const String & host);
|
||||||
|
static String getHostsDesc(const Strings & hosts);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/// Initializes the original state. It will be updated then with readCurrentState().
|
||||||
|
void initializeState();
|
||||||
|
|
||||||
|
/// Creates the root node in ZooKeeper.
|
||||||
void createRootNodes();
|
void createRootNodes();
|
||||||
|
|
||||||
struct State;
|
/// Atomically creates both 'start' and 'alive' nodes and also checks that there is no concurrent backup or restore if `allow_concurrency` is false.
|
||||||
State readCurrentState(WithRetries::RetriesControlHolder & retries_control_holder, const Strings & zk_nodes, const Strings & all_hosts, const String & stage_to_wait) const;
|
void createStartAndAliveNodes();
|
||||||
|
void createStartAndAliveNodes(Coordination::ZooKeeperWithFaultInjection::Ptr zookeeper);
|
||||||
|
|
||||||
Strings waitImpl(const Strings & all_hosts, const String & stage_to_wait, std::optional<std::chrono::milliseconds> timeout) const;
|
/// Deserialize the version of a node stored in the 'start' node.
|
||||||
|
int parseStartNode(const String & start_node_contents, const String & host) const;
|
||||||
|
|
||||||
String zookeeper_path;
|
/// Recreates the 'alive' node if it doesn't exist. It's an ephemeral node so it's removed automatically after disconnections.
|
||||||
/// A reference to the field of parent object - BackupCoordinationRemote or RestoreCoordinationRemote
|
void createAliveNode(Coordination::ZooKeeperWithFaultInjection::Ptr zookeeper);
|
||||||
WithRetries & with_retries;
|
|
||||||
LoggerPtr log;
|
/// Checks that there is no concurrent backup or restore if `allow_concurrency` is false.
|
||||||
|
void checkConcurrency(Coordination::ZooKeeperWithFaultInjection::Ptr zookeeper);
|
||||||
|
|
||||||
|
/// Watching thread periodically reads the current state from ZooKeeper and recreates the 'alive' node.
|
||||||
|
void startWatchingThread();
|
||||||
|
void stopWatchingThread();
|
||||||
|
void watchingThread();
|
||||||
|
|
||||||
|
/// Reads the current state from ZooKeeper without throwing exceptions.
|
||||||
|
void readCurrentState(Coordination::ZooKeeperWithFaultInjection::Ptr zookeeper);
|
||||||
|
String getStageNodePath(const String & stage) const;
|
||||||
|
|
||||||
|
/// Lets other hosts know that the current host has encountered an error.
|
||||||
|
bool trySetError(const Exception & exception);
|
||||||
|
void setError(const Exception & exception);
|
||||||
|
|
||||||
|
/// Deserializes an error stored in the error node.
|
||||||
|
static std::pair<std::exception_ptr, String> parseErrorNode(const String & error_node_contents);
|
||||||
|
|
||||||
|
/// Reset the `connected` flag for each host.
|
||||||
|
void resetConnectedFlag();
|
||||||
|
|
||||||
|
/// Checks if the current query is cancelled, and if so then the function sets the `cancelled` flag in the current state.
|
||||||
|
void checkIfQueryCancelled();
|
||||||
|
|
||||||
|
/// Checks if the current state contains an error, and if so then the function passes this error to the query status
|
||||||
|
/// to cancel the current BACKUP or RESTORE command.
|
||||||
|
void cancelQueryIfError();
|
||||||
|
|
||||||
|
/// Checks if some host was disconnected for too long, and if so then the function generates an error and pass it to the query status
|
||||||
|
/// to cancel the current BACKUP or RESTORE command.
|
||||||
|
void cancelQueryIfDisconnectedTooLong();
|
||||||
|
|
||||||
|
/// Used by waitForHostsToReachStage() to check if everything is ready to return.
|
||||||
|
bool checkIfHostsReachStage(const Strings & hosts, const String & stage_to_wait, bool time_is_out, std::optional<std::chrono::milliseconds> timeout, Strings & results) const TSA_REQUIRES(mutex);
|
||||||
|
|
||||||
|
/// Creates the 'finish' node.
|
||||||
|
bool tryFinishImpl();
|
||||||
|
bool tryFinishImpl(bool & other_hosts_also_finished, bool throw_if_error, WithRetries::Kind retries_kind);
|
||||||
|
void createFinishNodeAndRemoveAliveNode(Coordination::ZooKeeperWithFaultInjection::Ptr zookeeper);
|
||||||
|
|
||||||
|
/// Returns the version used by the initiator.
|
||||||
|
int getInitiatorVersion() const;
|
||||||
|
|
||||||
|
/// Waits until all the other hosts finish their work.
|
||||||
|
bool tryWaitForOtherHostsToFinishImpl(const String & reason, bool throw_if_error, std::optional<std::chrono::seconds> timeout) const;
|
||||||
|
bool checkIfOtherHostsFinish(const String & reason, bool throw_if_error, bool time_is_out, std::optional<std::chrono::milliseconds> timeout) const TSA_REQUIRES(mutex);
|
||||||
|
|
||||||
|
const bool is_restore;
|
||||||
|
const String operation_name;
|
||||||
|
const String current_host;
|
||||||
|
const String current_host_desc;
|
||||||
|
const Strings all_hosts;
|
||||||
|
const bool allow_concurrency;
|
||||||
|
|
||||||
|
/// A reference to a field of the parent object which is either BackupCoordinationOnCluster or RestoreCoordinationOnCluster.
|
||||||
|
const WithRetries & with_retries;
|
||||||
|
|
||||||
|
const ThreadPoolCallbackRunnerUnsafe<void> schedule;
|
||||||
|
const QueryStatusPtr process_list_element;
|
||||||
|
const LoggerPtr log;
|
||||||
|
|
||||||
|
const std::chrono::seconds failure_after_host_disconnected_for_seconds;
|
||||||
|
const std::chrono::seconds finish_timeout_after_error;
|
||||||
|
const std::chrono::milliseconds sync_period_ms;
|
||||||
|
const size_t max_attempts_after_bad_version;
|
||||||
|
|
||||||
|
/// Paths in ZooKeeper.
|
||||||
|
const std::filesystem::path zookeeper_path;
|
||||||
|
const String root_zookeeper_path;
|
||||||
|
const String operation_node_path;
|
||||||
|
const String operation_node_name;
|
||||||
|
const String stage_node_path;
|
||||||
|
const String start_node_path;
|
||||||
|
const String finish_node_path;
|
||||||
|
const String num_hosts_node_path;
|
||||||
|
const String alive_node_path;
|
||||||
|
const String alive_tracker_node_path;
|
||||||
|
const String error_node_path;
|
||||||
|
|
||||||
|
std::shared_ptr<Poco::Event> zk_nodes_changed;
|
||||||
|
|
||||||
|
/// We store list of previously found ZooKeeper nodes to show better logging messages.
|
||||||
|
Strings zk_nodes;
|
||||||
|
|
||||||
|
/// Information about one host read from ZooKeeper.
|
||||||
|
struct HostInfo
|
||||||
|
{
|
||||||
|
String host;
|
||||||
|
bool started = false;
|
||||||
|
bool connected = false;
|
||||||
|
bool finished = false;
|
||||||
|
int version = 1;
|
||||||
|
std::map<String /* stage */, String /* result */> stages = {}; /// std::map because we need to compare states
|
||||||
|
std::exception_ptr exception = nullptr;
|
||||||
|
|
||||||
|
std::chrono::time_point<std::chrono::system_clock> last_connection_time = {};
|
||||||
|
std::chrono::time_point<std::chrono::steady_clock> last_connection_time_monotonic = {};
|
||||||
|
|
||||||
|
bool operator ==(const HostInfo & other) const;
|
||||||
|
bool operator !=(const HostInfo & other) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Information about all the host participating in the current BACKUP or RESTORE operation.
|
||||||
|
struct State
|
||||||
|
{
|
||||||
|
std::map<String /* host */, HostInfo> hosts; /// std::map because we need to compare states
|
||||||
|
std::optional<String> host_with_error;
|
||||||
|
bool cancelled = false;
|
||||||
|
|
||||||
|
bool operator ==(const State & other) const;
|
||||||
|
bool operator !=(const State & other) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
State state TSA_GUARDED_BY(mutex);
|
||||||
|
mutable std::condition_variable state_changed;
|
||||||
|
|
||||||
|
std::future<void> watching_thread_future;
|
||||||
|
std::atomic<bool> should_stop_watching_thread = false;
|
||||||
|
|
||||||
|
struct FinishResult
|
||||||
|
{
|
||||||
|
bool succeeded = false;
|
||||||
|
std::exception_ptr exception;
|
||||||
|
bool other_hosts_also_finished = false;
|
||||||
|
};
|
||||||
|
FinishResult finish_result TSA_GUARDED_BY(mutex);
|
||||||
|
|
||||||
|
mutable std::mutex mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,6 @@ BackupEntriesCollector::BackupEntriesCollector(
|
|||||||
, read_settings(read_settings_)
|
, read_settings(read_settings_)
|
||||||
, context(context_)
|
, context(context_)
|
||||||
, process_list_element(context->getProcessListElement())
|
, process_list_element(context->getProcessListElement())
|
||||||
, on_cluster_first_sync_timeout(context->getConfigRef().getUInt64("backups.on_cluster_first_sync_timeout", 180000))
|
|
||||||
, collect_metadata_timeout(context->getConfigRef().getUInt64(
|
, collect_metadata_timeout(context->getConfigRef().getUInt64(
|
||||||
"backups.collect_metadata_timeout", context->getConfigRef().getUInt64("backups.consistent_metadata_snapshot_timeout", 600000)))
|
"backups.collect_metadata_timeout", context->getConfigRef().getUInt64("backups.consistent_metadata_snapshot_timeout", 600000)))
|
||||||
, attempts_to_collect_metadata_before_sleep(context->getConfigRef().getUInt("backups.attempts_to_collect_metadata_before_sleep", 2))
|
, attempts_to_collect_metadata_before_sleep(context->getConfigRef().getUInt("backups.attempts_to_collect_metadata_before_sleep", 2))
|
||||||
@ -176,21 +175,7 @@ Strings BackupEntriesCollector::setStage(const String & new_stage, const String
|
|||||||
checkIsQueryCancelled();
|
checkIsQueryCancelled();
|
||||||
|
|
||||||
current_stage = new_stage;
|
current_stage = new_stage;
|
||||||
backup_coordination->setStage(new_stage, message);
|
return backup_coordination->setStage(new_stage, message, /* sync = */ true);
|
||||||
|
|
||||||
if (new_stage == Stage::formatGatheringMetadata(0))
|
|
||||||
{
|
|
||||||
return backup_coordination->waitForStage(new_stage, on_cluster_first_sync_timeout);
|
|
||||||
}
|
|
||||||
if (new_stage.starts_with(Stage::GATHERING_METADATA))
|
|
||||||
{
|
|
||||||
auto current_time = std::chrono::steady_clock::now();
|
|
||||||
auto end_of_timeout = std::max(current_time, collect_metadata_end_time);
|
|
||||||
return backup_coordination->waitForStage(
|
|
||||||
new_stage, std::chrono::duration_cast<std::chrono::milliseconds>(end_of_timeout - current_time));
|
|
||||||
}
|
|
||||||
|
|
||||||
return backup_coordination->waitForStage(new_stage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackupEntriesCollector::checkIsQueryCancelled() const
|
void BackupEntriesCollector::checkIsQueryCancelled() const
|
||||||
|
@ -111,10 +111,6 @@ private:
|
|||||||
ContextPtr context;
|
ContextPtr context;
|
||||||
QueryStatusPtr process_list_element;
|
QueryStatusPtr process_list_element;
|
||||||
|
|
||||||
/// The time a BACKUP ON CLUSTER or RESTORE ON CLUSTER command will wait until all the nodes receive the BACKUP (or RESTORE) query and start working.
|
|
||||||
/// This setting is similar to `distributed_ddl_task_timeout`.
|
|
||||||
const std::chrono::milliseconds on_cluster_first_sync_timeout;
|
|
||||||
|
|
||||||
/// The time a BACKUP command will try to collect the metadata of tables & databases.
|
/// The time a BACKUP command will try to collect the metadata of tables & databases.
|
||||||
const std::chrono::milliseconds collect_metadata_timeout;
|
const std::chrono::milliseconds collect_metadata_timeout;
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
{
|
{
|
||||||
|
|
||||||
class IDisk;
|
class IDisk;
|
||||||
using DiskPtr = std::shared_ptr<IDisk>;
|
using DiskPtr = std::shared_ptr<IDisk>;
|
||||||
class SeekableReadBuffer;
|
class SeekableReadBuffer;
|
||||||
@ -63,9 +64,13 @@ public:
|
|||||||
|
|
||||||
virtual void copyFile(const String & destination, const String & source, size_t size) = 0;
|
virtual void copyFile(const String & destination, const String & source, size_t size) = 0;
|
||||||
|
|
||||||
|
/// Removes a file written to the backup, if it still exists.
|
||||||
virtual void removeFile(const String & file_name) = 0;
|
virtual void removeFile(const String & file_name) = 0;
|
||||||
virtual void removeFiles(const Strings & file_names) = 0;
|
virtual void removeFiles(const Strings & file_names) = 0;
|
||||||
|
|
||||||
|
/// Removes the backup folder if it's empty or contains empty subfolders.
|
||||||
|
virtual void removeEmptyDirectories() = 0;
|
||||||
|
|
||||||
virtual const ReadSettings & getReadSettings() const = 0;
|
virtual const ReadSettings & getReadSettings() const = 0;
|
||||||
virtual const WriteSettings & getWriteSettings() const = 0;
|
virtual const WriteSettings & getWriteSettings() const = 0;
|
||||||
virtual size_t getWriteBufferSize() const = 0;
|
virtual size_t getWriteBufferSize() const = 0;
|
||||||
|
@ -81,6 +81,7 @@ public:
|
|||||||
|
|
||||||
void removeFile(const String & file_name) override;
|
void removeFile(const String & file_name) override;
|
||||||
void removeFiles(const Strings & file_names) override;
|
void removeFiles(const Strings & file_names) override;
|
||||||
|
void removeEmptyDirectories() override {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<ReadBuffer> readFile(const String & file_name, size_t expected_file_size) override;
|
std::unique_ptr<ReadBuffer> readFile(const String & file_name, size_t expected_file_size) override;
|
||||||
|
@ -91,16 +91,36 @@ std::unique_ptr<WriteBuffer> BackupWriterDisk::writeFile(const String & file_nam
|
|||||||
void BackupWriterDisk::removeFile(const String & file_name)
|
void BackupWriterDisk::removeFile(const String & file_name)
|
||||||
{
|
{
|
||||||
disk->removeFileIfExists(root_path / file_name);
|
disk->removeFileIfExists(root_path / file_name);
|
||||||
if (disk->existsDirectory(root_path) && disk->isDirectoryEmpty(root_path))
|
|
||||||
disk->removeDirectory(root_path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackupWriterDisk::removeFiles(const Strings & file_names)
|
void BackupWriterDisk::removeFiles(const Strings & file_names)
|
||||||
{
|
{
|
||||||
for (const auto & file_name : file_names)
|
for (const auto & file_name : file_names)
|
||||||
disk->removeFileIfExists(root_path / file_name);
|
disk->removeFileIfExists(root_path / file_name);
|
||||||
if (disk->existsDirectory(root_path) && disk->isDirectoryEmpty(root_path))
|
}
|
||||||
disk->removeDirectory(root_path);
|
|
||||||
|
void BackupWriterDisk::removeEmptyDirectories()
|
||||||
|
{
|
||||||
|
removeEmptyDirectoriesImpl(root_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackupWriterDisk::removeEmptyDirectoriesImpl(const fs::path & current_dir)
|
||||||
|
{
|
||||||
|
if (!disk->existsDirectory(current_dir))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (disk->isDirectoryEmpty(current_dir))
|
||||||
|
{
|
||||||
|
disk->removeDirectory(current_dir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Backups are not too deep, so recursion is good enough here.
|
||||||
|
for (auto it = disk->iterateDirectory(current_dir); it->isValid(); it->next())
|
||||||
|
removeEmptyDirectoriesImpl(current_dir / it->name());
|
||||||
|
|
||||||
|
if (disk->isDirectoryEmpty(current_dir))
|
||||||
|
disk->removeDirectory(current_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackupWriterDisk::copyFileFromDisk(const String & path_in_backup, DiskPtr src_disk, const String & src_path,
|
void BackupWriterDisk::copyFileFromDisk(const String & path_in_backup, DiskPtr src_disk, const String & src_path,
|
||||||
|
@ -50,9 +50,11 @@ public:
|
|||||||
|
|
||||||
void removeFile(const String & file_name) override;
|
void removeFile(const String & file_name) override;
|
||||||
void removeFiles(const Strings & file_names) override;
|
void removeFiles(const Strings & file_names) override;
|
||||||
|
void removeEmptyDirectories() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<ReadBuffer> readFile(const String & file_name, size_t expected_file_size) override;
|
std::unique_ptr<ReadBuffer> readFile(const String & file_name, size_t expected_file_size) override;
|
||||||
|
void removeEmptyDirectoriesImpl(const std::filesystem::path & current_dir);
|
||||||
|
|
||||||
const DiskPtr disk;
|
const DiskPtr disk;
|
||||||
const std::filesystem::path root_path;
|
const std::filesystem::path root_path;
|
||||||
|
@ -106,16 +106,36 @@ std::unique_ptr<WriteBuffer> BackupWriterFile::writeFile(const String & file_nam
|
|||||||
void BackupWriterFile::removeFile(const String & file_name)
|
void BackupWriterFile::removeFile(const String & file_name)
|
||||||
{
|
{
|
||||||
(void)fs::remove(root_path / file_name);
|
(void)fs::remove(root_path / file_name);
|
||||||
if (fs::is_directory(root_path) && fs::is_empty(root_path))
|
|
||||||
(void)fs::remove(root_path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackupWriterFile::removeFiles(const Strings & file_names)
|
void BackupWriterFile::removeFiles(const Strings & file_names)
|
||||||
{
|
{
|
||||||
for (const auto & file_name : file_names)
|
for (const auto & file_name : file_names)
|
||||||
(void)fs::remove(root_path / file_name);
|
(void)fs::remove(root_path / file_name);
|
||||||
if (fs::is_directory(root_path) && fs::is_empty(root_path))
|
}
|
||||||
(void)fs::remove(root_path);
|
|
||||||
|
void BackupWriterFile::removeEmptyDirectories()
|
||||||
|
{
|
||||||
|
removeEmptyDirectoriesImpl(root_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackupWriterFile::removeEmptyDirectoriesImpl(const fs::path & current_dir)
|
||||||
|
{
|
||||||
|
if (!fs::is_directory(current_dir))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (fs::is_empty(current_dir))
|
||||||
|
{
|
||||||
|
(void)fs::remove(current_dir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Backups are not too deep, so recursion is good enough here.
|
||||||
|
for (const auto & it : std::filesystem::directory_iterator{current_dir})
|
||||||
|
removeEmptyDirectoriesImpl(it.path());
|
||||||
|
|
||||||
|
if (fs::is_empty(current_dir))
|
||||||
|
(void)fs::remove(current_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackupWriterFile::copyFileFromDisk(const String & path_in_backup, DiskPtr src_disk, const String & src_path,
|
void BackupWriterFile::copyFileFromDisk(const String & path_in_backup, DiskPtr src_disk, const String & src_path,
|
||||||
|
@ -42,9 +42,11 @@ public:
|
|||||||
|
|
||||||
void removeFile(const String & file_name) override;
|
void removeFile(const String & file_name) override;
|
||||||
void removeFiles(const Strings & file_names) override;
|
void removeFiles(const Strings & file_names) override;
|
||||||
|
void removeEmptyDirectories() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<ReadBuffer> readFile(const String & file_name, size_t expected_file_size) override;
|
std::unique_ptr<ReadBuffer> readFile(const String & file_name, size_t expected_file_size) override;
|
||||||
|
void removeEmptyDirectoriesImpl(const std::filesystem::path & current_dir);
|
||||||
|
|
||||||
const std::filesystem::path root_path;
|
const std::filesystem::path root_path;
|
||||||
const DataSourceDescription data_source_description;
|
const DataSourceDescription data_source_description;
|
||||||
|
@ -74,6 +74,7 @@ public:
|
|||||||
|
|
||||||
void removeFile(const String & file_name) override;
|
void removeFile(const String & file_name) override;
|
||||||
void removeFiles(const Strings & file_names) override;
|
void removeFiles(const Strings & file_names) override;
|
||||||
|
void removeEmptyDirectories() override {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<ReadBuffer> readFile(const String & file_name, size_t expected_file_size) override;
|
std::unique_ptr<ReadBuffer> readFile(const String & file_name, size_t expected_file_size) override;
|
||||||
|
@ -147,11 +147,11 @@ BackupImpl::BackupImpl(
|
|||||||
|
|
||||||
BackupImpl::~BackupImpl()
|
BackupImpl::~BackupImpl()
|
||||||
{
|
{
|
||||||
if ((open_mode == OpenMode::WRITE) && !is_internal_backup && !writing_finalized && !std::uncaught_exceptions() && !std::current_exception())
|
if ((open_mode == OpenMode::WRITE) && !writing_finalized && !corrupted)
|
||||||
{
|
{
|
||||||
/// It is suspicious to destroy BackupImpl without finalization while writing a backup when there is no exception.
|
/// It is suspicious to destroy BackupImpl without finalization while writing a backup when there is no exception.
|
||||||
LOG_ERROR(log, "BackupImpl is not finalized when destructor is called. Stack trace: {}", StackTrace().toString());
|
LOG_ERROR(log, "BackupImpl is not finalized or marked as corrupted when destructor is called. Stack trace: {}", StackTrace().toString());
|
||||||
chassert(false && "BackupImpl is not finalized when destructor is called.");
|
chassert(false, "BackupImpl is not finalized or marked as corrupted when destructor is called.");
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -196,9 +196,6 @@ void BackupImpl::open()
|
|||||||
|
|
||||||
if (open_mode == OpenMode::READ)
|
if (open_mode == OpenMode::READ)
|
||||||
readBackupMetadata();
|
readBackupMetadata();
|
||||||
|
|
||||||
if ((open_mode == OpenMode::WRITE) && base_backup_info)
|
|
||||||
base_backup_uuid = getBaseBackupUnlocked()->getUUID();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackupImpl::close()
|
void BackupImpl::close()
|
||||||
@ -280,6 +277,8 @@ std::shared_ptr<const IBackup> BackupImpl::getBaseBackupUnlocked() const
|
|||||||
toString(base_backup->getUUID()),
|
toString(base_backup->getUUID()),
|
||||||
(base_backup_uuid ? toString(*base_backup_uuid) : ""));
|
(base_backup_uuid ? toString(*base_backup_uuid) : ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
base_backup_uuid = base_backup->getUUID();
|
||||||
}
|
}
|
||||||
return base_backup;
|
return base_backup;
|
||||||
}
|
}
|
||||||
@ -369,7 +368,7 @@ void BackupImpl::writeBackupMetadata()
|
|||||||
if (base_backup_in_use)
|
if (base_backup_in_use)
|
||||||
{
|
{
|
||||||
*out << "<base_backup>" << xml << base_backup_info->toString() << "</base_backup>";
|
*out << "<base_backup>" << xml << base_backup_info->toString() << "</base_backup>";
|
||||||
*out << "<base_backup_uuid>" << toString(*base_backup_uuid) << "</base_backup_uuid>";
|
*out << "<base_backup_uuid>" << getBaseBackupUnlocked()->getUUID() << "</base_backup_uuid>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -594,9 +593,6 @@ bool BackupImpl::checkLockFile(bool throw_if_failed) const
|
|||||||
|
|
||||||
void BackupImpl::removeLockFile()
|
void BackupImpl::removeLockFile()
|
||||||
{
|
{
|
||||||
if (is_internal_backup)
|
|
||||||
return; /// Internal backup must not remove the lock file (it's still used by the initiator).
|
|
||||||
|
|
||||||
if (checkLockFile(false))
|
if (checkLockFile(false))
|
||||||
writer->removeFile(lock_file_name);
|
writer->removeFile(lock_file_name);
|
||||||
}
|
}
|
||||||
@ -989,8 +985,11 @@ void BackupImpl::finalizeWriting()
|
|||||||
if (open_mode != OpenMode::WRITE)
|
if (open_mode != OpenMode::WRITE)
|
||||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Backup is not opened for writing");
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Backup is not opened for writing");
|
||||||
|
|
||||||
|
if (corrupted)
|
||||||
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Backup can't be finalized after an error happened");
|
||||||
|
|
||||||
if (writing_finalized)
|
if (writing_finalized)
|
||||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Backup is already finalized");
|
return;
|
||||||
|
|
||||||
if (!is_internal_backup)
|
if (!is_internal_backup)
|
||||||
{
|
{
|
||||||
@ -1015,20 +1014,58 @@ void BackupImpl::setCompressedSize()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void BackupImpl::tryRemoveAllFiles()
|
bool BackupImpl::setIsCorrupted() noexcept
|
||||||
{
|
{
|
||||||
if (open_mode != OpenMode::WRITE)
|
|
||||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Backup is not opened for writing");
|
|
||||||
|
|
||||||
if (is_internal_backup)
|
|
||||||
return;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
LOG_INFO(log, "Removing all files of backup {}", backup_name_for_logging);
|
std::lock_guard lock{mutex};
|
||||||
|
if (open_mode != OpenMode::WRITE)
|
||||||
|
{
|
||||||
|
LOG_ERROR(log, "Backup is not opened for writing. Stack trace: {}", StackTrace().toString());
|
||||||
|
chassert(false, "Backup is not opened for writing when setIsCorrupted() is called");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (writing_finalized)
|
||||||
|
{
|
||||||
|
LOG_WARNING(log, "An error happened after the backup was completed successfully, the backup must be correct!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (corrupted)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
LOG_WARNING(log, "An error happened, the backup won't be completed");
|
||||||
|
|
||||||
closeArchive(/* finalize= */ false);
|
closeArchive(/* finalize= */ false);
|
||||||
|
|
||||||
|
corrupted = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
DB::tryLogCurrentException(log, "Caught exception while setting that the backup was corrupted");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool BackupImpl::tryRemoveAllFiles() noexcept
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::lock_guard lock{mutex};
|
||||||
|
if (!corrupted)
|
||||||
|
{
|
||||||
|
LOG_ERROR(log, "Backup is not set as corrupted. Stack trace: {}", StackTrace().toString());
|
||||||
|
chassert(false, "Backup is not set as corrupted when tryRemoveAllFiles() is called");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO(log, "Removing all files of backup {}", backup_name_for_logging);
|
||||||
|
|
||||||
Strings files_to_remove;
|
Strings files_to_remove;
|
||||||
|
|
||||||
if (use_archive)
|
if (use_archive)
|
||||||
{
|
{
|
||||||
files_to_remove.push_back(archive_params.archive_name);
|
files_to_remove.push_back(archive_params.archive_name);
|
||||||
@ -1041,14 +1078,17 @@ void BackupImpl::tryRemoveAllFiles()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!checkLockFile(false))
|
if (!checkLockFile(false))
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
writer->removeFiles(files_to_remove);
|
writer->removeFiles(files_to_remove);
|
||||||
removeLockFile();
|
removeLockFile();
|
||||||
|
writer->removeEmptyDirectories();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
DB::tryLogCurrentException(__PRETTY_FUNCTION__);
|
DB::tryLogCurrentException(log, "Caught exception while removing files of a corrupted backup");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +86,8 @@ public:
|
|||||||
void writeFile(const BackupFileInfo & info, BackupEntryPtr entry) override;
|
void writeFile(const BackupFileInfo & info, BackupEntryPtr entry) override;
|
||||||
bool supportsWritingInMultipleThreads() const override { return !use_archive; }
|
bool supportsWritingInMultipleThreads() const override { return !use_archive; }
|
||||||
void finalizeWriting() override;
|
void finalizeWriting() override;
|
||||||
void tryRemoveAllFiles() override;
|
bool setIsCorrupted() noexcept override;
|
||||||
|
bool tryRemoveAllFiles() noexcept override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void open();
|
void open();
|
||||||
@ -146,13 +147,14 @@ private:
|
|||||||
int version;
|
int version;
|
||||||
mutable std::optional<BackupInfo> base_backup_info;
|
mutable std::optional<BackupInfo> base_backup_info;
|
||||||
mutable std::shared_ptr<const IBackup> base_backup;
|
mutable std::shared_ptr<const IBackup> base_backup;
|
||||||
std::optional<UUID> base_backup_uuid;
|
mutable std::optional<UUID> base_backup_uuid;
|
||||||
std::shared_ptr<IArchiveReader> archive_reader;
|
std::shared_ptr<IArchiveReader> archive_reader;
|
||||||
std::shared_ptr<IArchiveWriter> archive_writer;
|
std::shared_ptr<IArchiveWriter> archive_writer;
|
||||||
String lock_file_name;
|
String lock_file_name;
|
||||||
std::atomic<bool> lock_file_before_first_file_checked = false;
|
std::atomic<bool> lock_file_before_first_file_checked = false;
|
||||||
|
|
||||||
bool writing_finalized = false;
|
bool writing_finalized = false;
|
||||||
|
bool corrupted = false;
|
||||||
bool deduplicate_files = true;
|
bool deduplicate_files = true;
|
||||||
bool use_same_s3_credentials_for_base_backup = false;
|
bool use_same_s3_credentials_for_base_backup = false;
|
||||||
bool use_same_password_for_base_backup = false;
|
bool use_same_password_for_base_backup = false;
|
||||||
|
58
src/Backups/BackupKeeperSettings.cpp
Normal file
58
src/Backups/BackupKeeperSettings.cpp
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#include <Backups/BackupKeeperSettings.h>
|
||||||
|
|
||||||
|
#include <Core/Settings.h>
|
||||||
|
#include <Interpreters/Context.h>
|
||||||
|
#include <Poco/Util/AbstractConfiguration.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace Setting
|
||||||
|
{
|
||||||
|
extern const SettingsUInt64 backup_restore_keeper_max_retries;
|
||||||
|
extern const SettingsUInt64 backup_restore_keeper_retry_initial_backoff_ms;
|
||||||
|
extern const SettingsUInt64 backup_restore_keeper_retry_max_backoff_ms;
|
||||||
|
extern const SettingsUInt64 backup_restore_failure_after_host_disconnected_for_seconds;
|
||||||
|
extern const SettingsUInt64 backup_restore_keeper_max_retries_while_initializing;
|
||||||
|
extern const SettingsUInt64 backup_restore_keeper_max_retries_while_handling_error;
|
||||||
|
extern const SettingsUInt64 backup_restore_finish_timeout_after_error_sec;
|
||||||
|
extern const SettingsUInt64 backup_restore_keeper_value_max_size;
|
||||||
|
extern const SettingsUInt64 backup_restore_batch_size_for_keeper_multi;
|
||||||
|
extern const SettingsUInt64 backup_restore_batch_size_for_keeper_multiread;
|
||||||
|
extern const SettingsFloat backup_restore_keeper_fault_injection_probability;
|
||||||
|
extern const SettingsUInt64 backup_restore_keeper_fault_injection_seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
BackupKeeperSettings BackupKeeperSettings::fromContext(const ContextPtr & context)
|
||||||
|
{
|
||||||
|
BackupKeeperSettings keeper_settings;
|
||||||
|
|
||||||
|
const auto & settings = context->getSettingsRef();
|
||||||
|
const auto & config = context->getConfigRef();
|
||||||
|
|
||||||
|
keeper_settings.max_retries = settings[Setting::backup_restore_keeper_max_retries];
|
||||||
|
keeper_settings.retry_initial_backoff_ms = std::chrono::milliseconds{settings[Setting::backup_restore_keeper_retry_initial_backoff_ms]};
|
||||||
|
keeper_settings.retry_max_backoff_ms = std::chrono::milliseconds{settings[Setting::backup_restore_keeper_retry_max_backoff_ms]};
|
||||||
|
|
||||||
|
keeper_settings.failure_after_host_disconnected_for_seconds = std::chrono::seconds{settings[Setting::backup_restore_failure_after_host_disconnected_for_seconds]};
|
||||||
|
keeper_settings.max_retries_while_initializing = settings[Setting::backup_restore_keeper_max_retries_while_initializing];
|
||||||
|
keeper_settings.max_retries_while_handling_error = settings[Setting::backup_restore_keeper_max_retries_while_handling_error];
|
||||||
|
keeper_settings.finish_timeout_after_error = std::chrono::seconds(settings[Setting::backup_restore_finish_timeout_after_error_sec]);
|
||||||
|
|
||||||
|
if (config.has("backups.sync_period_ms"))
|
||||||
|
keeper_settings.sync_period_ms = std::chrono::milliseconds{config.getUInt64("backups.sync_period_ms")};
|
||||||
|
|
||||||
|
if (config.has("backups.max_attempts_after_bad_version"))
|
||||||
|
keeper_settings.max_attempts_after_bad_version = config.getUInt64("backups.max_attempts_after_bad_version");
|
||||||
|
|
||||||
|
keeper_settings.value_max_size = settings[Setting::backup_restore_keeper_value_max_size];
|
||||||
|
keeper_settings.batch_size_for_multi = settings[Setting::backup_restore_batch_size_for_keeper_multi];
|
||||||
|
keeper_settings.batch_size_for_multiread = settings[Setting::backup_restore_batch_size_for_keeper_multiread];
|
||||||
|
keeper_settings.fault_injection_probability = settings[Setting::backup_restore_keeper_fault_injection_probability];
|
||||||
|
keeper_settings.fault_injection_seed = settings[Setting::backup_restore_keeper_fault_injection_seed];
|
||||||
|
|
||||||
|
return keeper_settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
64
src/Backups/BackupKeeperSettings.h
Normal file
64
src/Backups/BackupKeeperSettings.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Interpreters/Context_fwd.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Settings for [Zoo]Keeper-related works during BACKUP or RESTORE.
|
||||||
|
struct BackupKeeperSettings
|
||||||
|
{
|
||||||
|
/// Maximum number of retries in the middle of a BACKUP ON CLUSTER or RESTORE ON CLUSTER operation.
|
||||||
|
/// Should be big enough so the whole operation won't be cancelled in the middle of it because of a temporary ZooKeeper failure.
|
||||||
|
UInt64 max_retries{1000};
|
||||||
|
|
||||||
|
/// Initial backoff timeout for ZooKeeper operations during backup or restore.
|
||||||
|
std::chrono::milliseconds retry_initial_backoff_ms{100};
|
||||||
|
|
||||||
|
/// Max backoff timeout for ZooKeeper operations during backup or restore.
|
||||||
|
std::chrono::milliseconds retry_max_backoff_ms{5000};
|
||||||
|
|
||||||
|
/// If a host during BACKUP ON CLUSTER or RESTORE ON CLUSTER doesn't recreate its 'alive' node in ZooKeeper
|
||||||
|
/// for this amount of time then the whole backup or restore is considered as failed.
|
||||||
|
/// Should be bigger than any reasonable time for a host to reconnect to ZooKeeper after a failure.
|
||||||
|
/// Set to zero to disable (if it's zero and some host crashed then BACKUP ON CLUSTER or RESTORE ON CLUSTER will be waiting
|
||||||
|
/// for the crashed host forever until the operation is explicitly cancelled with KILL QUERY).
|
||||||
|
std::chrono::seconds failure_after_host_disconnected_for_seconds{3600};
|
||||||
|
|
||||||
|
/// Maximum number of retries during the initialization of a BACKUP ON CLUSTER or RESTORE ON CLUSTER operation.
|
||||||
|
/// Shouldn't be too big because if the operation is going to fail then it's better if it fails faster.
|
||||||
|
UInt64 max_retries_while_initializing{20};
|
||||||
|
|
||||||
|
/// Maximum number of retries while handling an error of a BACKUP ON CLUSTER or RESTORE ON CLUSTER operation.
|
||||||
|
/// Shouldn't be too big because those retries are just for cleanup after the operation has failed already.
|
||||||
|
UInt64 max_retries_while_handling_error{20};
|
||||||
|
|
||||||
|
/// How long the initiator should wait for other host to handle the 'error' node and finish their work.
|
||||||
|
std::chrono::seconds finish_timeout_after_error{180};
|
||||||
|
|
||||||
|
/// How often the "stage" folder in ZooKeeper must be scanned in a background thread to track changes done by other hosts.
|
||||||
|
std::chrono::milliseconds sync_period_ms{5000};
|
||||||
|
|
||||||
|
/// Number of attempts after getting error ZBADVERSION from ZooKeeper.
|
||||||
|
size_t max_attempts_after_bad_version{10};
|
||||||
|
|
||||||
|
/// Maximum size of data of a ZooKeeper's node during backup.
|
||||||
|
UInt64 value_max_size{1048576};
|
||||||
|
|
||||||
|
/// Maximum size of a batch for a multi request.
|
||||||
|
UInt64 batch_size_for_multi{1000};
|
||||||
|
|
||||||
|
/// Maximum size of a batch for a multiread request.
|
||||||
|
UInt64 batch_size_for_multiread{10000};
|
||||||
|
|
||||||
|
/// Approximate probability of failure for a keeper request during backup or restore. Valid value is in interval [0.0f, 1.0f].
|
||||||
|
Float64 fault_injection_probability{0};
|
||||||
|
|
||||||
|
/// Seed for `fault_injection_probability`: 0 - random seed, otherwise the setting value.
|
||||||
|
UInt64 fault_injection_seed{0};
|
||||||
|
|
||||||
|
static BackupKeeperSettings fromContext(const ContextPtr & context);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -74,6 +74,17 @@ BackupSettings BackupSettings::fromBackupQuery(const ASTBackupQuery & query)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BackupSettings::isAsync(const ASTBackupQuery & query)
|
||||||
|
{
|
||||||
|
if (query.settings)
|
||||||
|
{
|
||||||
|
const auto * field = query.settings->as<const ASTSetQuery &>().changes.tryGet("async");
|
||||||
|
if (field)
|
||||||
|
return field->safeGet<bool>();
|
||||||
|
}
|
||||||
|
return false; /// `async` is false by default.
|
||||||
|
}
|
||||||
|
|
||||||
void BackupSettings::copySettingsToQuery(ASTBackupQuery & query) const
|
void BackupSettings::copySettingsToQuery(ASTBackupQuery & query) const
|
||||||
{
|
{
|
||||||
auto query_settings = std::make_shared<ASTSetQuery>();
|
auto query_settings = std::make_shared<ASTSetQuery>();
|
||||||
|
@ -101,6 +101,8 @@ struct BackupSettings
|
|||||||
static BackupSettings fromBackupQuery(const ASTBackupQuery & query);
|
static BackupSettings fromBackupQuery(const ASTBackupQuery & query);
|
||||||
void copySettingsToQuery(ASTBackupQuery & query) const;
|
void copySettingsToQuery(ASTBackupQuery & query) const;
|
||||||
|
|
||||||
|
static bool isAsync(const ASTBackupQuery & query);
|
||||||
|
|
||||||
struct Util
|
struct Util
|
||||||
{
|
{
|
||||||
static std::vector<Strings> clusterHostIDsFromAST(const IAST & ast);
|
static std::vector<Strings> clusterHostIDsFromAST(const IAST & ast);
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -23,6 +23,7 @@ using BackupMutablePtr = std::shared_ptr<IBackup>;
|
|||||||
using BackupPtr = std::shared_ptr<const IBackup>;
|
using BackupPtr = std::shared_ptr<const IBackup>;
|
||||||
class IBackupEntry;
|
class IBackupEntry;
|
||||||
using BackupEntries = std::vector<std::pair<String, std::shared_ptr<const IBackupEntry>>>;
|
using BackupEntries = std::vector<std::pair<String, std::shared_ptr<const IBackupEntry>>>;
|
||||||
|
class BackupConcurrencyCounters;
|
||||||
using DataRestoreTasks = std::vector<std::function<void()>>;
|
using DataRestoreTasks = std::vector<std::function<void()>>;
|
||||||
struct ReadSettings;
|
struct ReadSettings;
|
||||||
class BackupLog;
|
class BackupLog;
|
||||||
@ -31,6 +32,10 @@ using ThreadGroupPtr = std::shared_ptr<ThreadGroup>;
|
|||||||
class QueryStatus;
|
class QueryStatus;
|
||||||
using QueryStatusPtr = std::shared_ptr<QueryStatus>;
|
using QueryStatusPtr = std::shared_ptr<QueryStatus>;
|
||||||
class ProcessList;
|
class ProcessList;
|
||||||
|
class Cluster;
|
||||||
|
using ClusterPtr = std::shared_ptr<Cluster>;
|
||||||
|
class AccessRightsElements;
|
||||||
|
struct ZooKeeperRetriesInfo;
|
||||||
|
|
||||||
|
|
||||||
/// Manager of backups and restores: executes backups and restores' threads in the background.
|
/// Manager of backups and restores: executes backups and restores' threads in the background.
|
||||||
@ -47,18 +52,18 @@ public:
|
|||||||
/// Starts executing a BACKUP or RESTORE query. Returns ID of the operation.
|
/// Starts executing a BACKUP or RESTORE query. Returns ID of the operation.
|
||||||
/// For asynchronous operations the function throws no exceptions on failure usually,
|
/// For asynchronous operations the function throws no exceptions on failure usually,
|
||||||
/// call getInfo() on a returned operation id to check for errors.
|
/// call getInfo() on a returned operation id to check for errors.
|
||||||
BackupOperationID start(const ASTPtr & backup_or_restore_query, ContextMutablePtr context);
|
std::pair<BackupOperationID, BackupStatus> start(const ASTPtr & backup_or_restore_query, ContextMutablePtr context);
|
||||||
|
|
||||||
/// Waits until the specified backup or restore operation finishes or stops.
|
/// Waits until the specified backup or restore operation finishes or stops.
|
||||||
/// The function returns immediately if the operation is already finished.
|
/// The function returns immediately if the operation is already finished.
|
||||||
void wait(const BackupOperationID & backup_or_restore_id, bool rethrow_exception = true);
|
BackupStatus wait(const BackupOperationID & backup_or_restore_id, bool rethrow_exception = true);
|
||||||
|
|
||||||
/// Waits until all running backup and restore operations finish or stop.
|
/// Waits until all running backup and restore operations finish or stop.
|
||||||
void waitAll();
|
void waitAll();
|
||||||
|
|
||||||
/// Cancels the specified backup or restore operation.
|
/// Cancels the specified backup or restore operation.
|
||||||
/// The function does nothing if this operation has already finished.
|
/// The function does nothing if this operation has already finished.
|
||||||
void cancel(const BackupOperationID & backup_or_restore_id, bool wait_ = true);
|
BackupStatus cancel(const BackupOperationID & backup_or_restore_id, bool wait_ = true);
|
||||||
|
|
||||||
/// Cancels all running backup and restore operations.
|
/// Cancels all running backup and restore operations.
|
||||||
void cancelAll(bool wait_ = true);
|
void cancelAll(bool wait_ = true);
|
||||||
@ -67,26 +72,32 @@ public:
|
|||||||
std::vector<BackupOperationInfo> getAllInfos() const;
|
std::vector<BackupOperationInfo> getAllInfos() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BackupOperationID startMakingBackup(const ASTPtr & query, const ContextPtr & context);
|
std::pair<BackupOperationID, BackupStatus> startMakingBackup(const ASTPtr & query, const ContextPtr & context);
|
||||||
|
struct BackupStarter;
|
||||||
|
|
||||||
|
BackupMutablePtr openBackupForWriting(const BackupInfo & backup_info, const BackupSettings & backup_settings, std::shared_ptr<IBackupCoordination> backup_coordination, const ContextPtr & context) const;
|
||||||
|
|
||||||
void doBackup(
|
void doBackup(
|
||||||
BackupMutablePtr & backup,
|
BackupMutablePtr backup,
|
||||||
const std::shared_ptr<ASTBackupQuery> & backup_query,
|
const std::shared_ptr<ASTBackupQuery> & backup_query,
|
||||||
const BackupOperationID & backup_id,
|
const BackupOperationID & backup_id,
|
||||||
const String & backup_name_for_logging,
|
const String & backup_name_for_logging,
|
||||||
const BackupInfo & backup_info,
|
const BackupSettings & backup_settings,
|
||||||
BackupSettings backup_settings,
|
|
||||||
std::shared_ptr<IBackupCoordination> backup_coordination,
|
std::shared_ptr<IBackupCoordination> backup_coordination,
|
||||||
const ContextPtr & context,
|
ContextMutablePtr context,
|
||||||
ContextMutablePtr mutable_context);
|
bool on_cluster,
|
||||||
|
const ClusterPtr & cluster);
|
||||||
|
|
||||||
/// Builds file infos for specified backup entries.
|
/// Builds file infos for specified backup entries.
|
||||||
void buildFileInfosForBackupEntries(const BackupPtr & backup, const BackupEntries & backup_entries, const ReadSettings & read_settings, std::shared_ptr<IBackupCoordination> backup_coordination, QueryStatusPtr process_list_element);
|
void buildFileInfosForBackupEntries(const BackupPtr & backup, const BackupEntries & backup_entries, const ReadSettings & read_settings, std::shared_ptr<IBackupCoordination> backup_coordination, QueryStatusPtr process_list_element);
|
||||||
|
|
||||||
/// Write backup entries to an opened backup.
|
/// Write backup entries to an opened backup.
|
||||||
void writeBackupEntries(BackupMutablePtr backup, BackupEntries && backup_entries, const BackupOperationID & backup_id, std::shared_ptr<IBackupCoordination> backup_coordination, bool internal, QueryStatusPtr process_list_element);
|
void writeBackupEntries(BackupMutablePtr backup, BackupEntries && backup_entries, const BackupOperationID & backup_id, std::shared_ptr<IBackupCoordination> backup_coordination, bool is_internal_backup, QueryStatusPtr process_list_element);
|
||||||
|
|
||||||
BackupOperationID startRestoring(const ASTPtr & query, ContextMutablePtr context);
|
std::pair<BackupOperationID, BackupStatus> startRestoring(const ASTPtr & query, ContextMutablePtr context);
|
||||||
|
struct RestoreStarter;
|
||||||
|
|
||||||
|
BackupPtr openBackupForReading(const BackupInfo & backup_info, const RestoreSettings & restore_settings, const ContextPtr & context) const;
|
||||||
|
|
||||||
void doRestore(
|
void doRestore(
|
||||||
const std::shared_ptr<ASTBackupQuery> & restore_query,
|
const std::shared_ptr<ASTBackupQuery> & restore_query,
|
||||||
@ -95,7 +106,17 @@ private:
|
|||||||
const BackupInfo & backup_info,
|
const BackupInfo & backup_info,
|
||||||
RestoreSettings restore_settings,
|
RestoreSettings restore_settings,
|
||||||
std::shared_ptr<IRestoreCoordination> restore_coordination,
|
std::shared_ptr<IRestoreCoordination> restore_coordination,
|
||||||
ContextMutablePtr context);
|
ContextMutablePtr context,
|
||||||
|
bool on_cluster,
|
||||||
|
const ClusterPtr & cluster);
|
||||||
|
|
||||||
|
std::shared_ptr<IBackupCoordination> makeBackupCoordination(bool on_cluster, const BackupSettings & backup_settings, const ContextPtr & context) const;
|
||||||
|
std::shared_ptr<IRestoreCoordination> makeRestoreCoordination(bool on_cluster, const RestoreSettings & restore_settings, const ContextPtr & context) const;
|
||||||
|
|
||||||
|
/// Sends a BACKUP or RESTORE query to other hosts.
|
||||||
|
void sendQueryToOtherHosts(const ASTBackupQuery & backup_or_restore_query, const ClusterPtr & cluster,
|
||||||
|
size_t only_shard_num, size_t only_replica_num, ContextMutablePtr context, const AccessRightsElements & access_to_check,
|
||||||
|
const ZooKeeperRetriesInfo & retries_info) const;
|
||||||
|
|
||||||
/// Run data restoring tasks which insert data to tables.
|
/// Run data restoring tasks which insert data to tables.
|
||||||
void restoreTablesData(const BackupOperationID & restore_id, BackupPtr backup, DataRestoreTasks && tasks, ThreadPool & thread_pool, QueryStatusPtr process_list_element);
|
void restoreTablesData(const BackupOperationID & restore_id, BackupPtr backup, DataRestoreTasks && tasks, ThreadPool & thread_pool, QueryStatusPtr process_list_element);
|
||||||
@ -139,6 +160,8 @@ private:
|
|||||||
|
|
||||||
std::shared_ptr<BackupLog> backup_log;
|
std::shared_ptr<BackupLog> backup_log;
|
||||||
ProcessList & process_list;
|
ProcessList & process_list;
|
||||||
|
|
||||||
|
std::unique_ptr<BackupConcurrencyCounters> concurrency_counters;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -121,8 +121,13 @@ public:
|
|||||||
/// Finalizes writing the backup, should be called after all entries have been successfully written.
|
/// Finalizes writing the backup, should be called after all entries have been successfully written.
|
||||||
virtual void finalizeWriting() = 0;
|
virtual void finalizeWriting() = 0;
|
||||||
|
|
||||||
/// Try to remove all files copied to the backup. Used after an exception or it the backup was cancelled.
|
/// Sets that a non-retriable error happened while the backup was being written which means that
|
||||||
virtual void tryRemoveAllFiles() = 0;
|
/// the backup is most likely corrupted and it can't be finalized.
|
||||||
|
/// This function is called while handling an exception or if the backup was cancelled.
|
||||||
|
virtual bool setIsCorrupted() noexcept = 0;
|
||||||
|
|
||||||
|
/// Try to remove all files copied to the backup. Could be used after setIsCorrupted().
|
||||||
|
virtual bool tryRemoveAllFiles() noexcept = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
using BackupPtr = std::shared_ptr<const IBackup>;
|
using BackupPtr = std::shared_ptr<const IBackup>;
|
||||||
|
@ -5,26 +5,44 @@
|
|||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
{
|
{
|
||||||
class Exception;
|
|
||||||
struct BackupFileInfo;
|
struct BackupFileInfo;
|
||||||
using BackupFileInfos = std::vector<BackupFileInfo>;
|
using BackupFileInfos = std::vector<BackupFileInfo>;
|
||||||
enum class AccessEntityType : uint8_t;
|
enum class AccessEntityType : uint8_t;
|
||||||
enum class UserDefinedSQLObjectType : uint8_t;
|
enum class UserDefinedSQLObjectType : uint8_t;
|
||||||
|
struct ZooKeeperRetriesInfo;
|
||||||
|
|
||||||
/// Replicas use this class to coordinate what they're writing to a backup while executing BACKUP ON CLUSTER.
|
/// Replicas use this class to coordinate what they're writing to a backup while executing BACKUP ON CLUSTER.
|
||||||
/// There are two implementation of this interface: BackupCoordinationLocal and BackupCoordinationRemote.
|
/// There are two implementation of this interface: BackupCoordinationLocal and BackupCoordinationOnCluster.
|
||||||
/// BackupCoordinationLocal is used while executing BACKUP without ON CLUSTER and performs coordination in memory.
|
/// BackupCoordinationLocal is used while executing BACKUP without ON CLUSTER and performs coordination in memory.
|
||||||
/// BackupCoordinationRemote is used while executing BACKUP with ON CLUSTER and performs coordination via ZooKeeper.
|
/// BackupCoordinationOnCluster is used while executing BACKUP with ON CLUSTER and performs coordination via ZooKeeper.
|
||||||
class IBackupCoordination
|
class IBackupCoordination
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~IBackupCoordination() = default;
|
virtual ~IBackupCoordination() = default;
|
||||||
|
|
||||||
/// Sets the current stage and waits for other hosts to come to this stage too.
|
/// Sets the current stage and waits for other hosts to come to this stage too.
|
||||||
virtual void setStage(const String & new_stage, const String & message) = 0;
|
virtual Strings setStage(const String & new_stage, const String & message, bool sync) = 0;
|
||||||
virtual void setError(const Exception & exception) = 0;
|
|
||||||
virtual Strings waitForStage(const String & stage_to_wait) = 0;
|
/// Sets that the backup query was sent to other hosts.
|
||||||
virtual Strings waitForStage(const String & stage_to_wait, std::chrono::milliseconds timeout) = 0;
|
/// Function waitForOtherHostsToFinish() will check that to find out if it should really wait or not.
|
||||||
|
virtual void setBackupQueryWasSentToOtherHosts() = 0;
|
||||||
|
|
||||||
|
/// Lets other hosts know that the current host has encountered an error.
|
||||||
|
virtual bool trySetError(std::exception_ptr exception) = 0;
|
||||||
|
|
||||||
|
/// Lets other hosts know that the current host has finished its work.
|
||||||
|
virtual void finish() = 0;
|
||||||
|
|
||||||
|
/// Lets other hosts know that the current host has finished its work (as a part of error-handling process).
|
||||||
|
virtual bool tryFinishAfterError() noexcept = 0;
|
||||||
|
|
||||||
|
/// Waits until all the other hosts finish their work.
|
||||||
|
/// Stops waiting and throws an exception if another host encounters an error or if some host gets cancelled.
|
||||||
|
virtual void waitForOtherHostsToFinish() = 0;
|
||||||
|
|
||||||
|
/// Waits until all the other hosts finish their work (as a part of error-handling process).
|
||||||
|
/// Doesn't stops waiting if some host encounters an error or gets cancelled.
|
||||||
|
virtual bool tryWaitForOtherHostsToFinishAfterError() noexcept = 0;
|
||||||
|
|
||||||
struct PartNameAndChecksum
|
struct PartNameAndChecksum
|
||||||
{
|
{
|
||||||
@ -87,9 +105,7 @@ public:
|
|||||||
/// Starts writing a specified file, the function returns false if that file is already being written concurrently.
|
/// Starts writing a specified file, the function returns false if that file is already being written concurrently.
|
||||||
virtual bool startWritingFile(size_t data_file_index) = 0;
|
virtual bool startWritingFile(size_t data_file_index) = 0;
|
||||||
|
|
||||||
/// This function is used to check if concurrent backups are running
|
virtual ZooKeeperRetriesInfo getOnClusterInitializationKeeperRetriesInfo() const = 0;
|
||||||
/// other than the backup passed to the function
|
|
||||||
virtual bool hasConcurrentBackups(const std::atomic<size_t> & num_active_backups) const = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,26 +5,42 @@
|
|||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
{
|
{
|
||||||
class Exception;
|
|
||||||
enum class UserDefinedSQLObjectType : uint8_t;
|
enum class UserDefinedSQLObjectType : uint8_t;
|
||||||
class ASTCreateQuery;
|
class ASTCreateQuery;
|
||||||
|
struct ZooKeeperRetriesInfo;
|
||||||
|
|
||||||
/// Replicas use this class to coordinate what they're reading from a backup while executing RESTORE ON CLUSTER.
|
/// Replicas use this class to coordinate what they're reading from a backup while executing RESTORE ON CLUSTER.
|
||||||
/// There are two implementation of this interface: RestoreCoordinationLocal and RestoreCoordinationRemote.
|
/// There are two implementation of this interface: RestoreCoordinationLocal and RestoreCoordinationOnCluster.
|
||||||
/// RestoreCoordinationLocal is used while executing RESTORE without ON CLUSTER and performs coordination in memory.
|
/// RestoreCoordinationLocal is used while executing RESTORE without ON CLUSTER and performs coordination in memory.
|
||||||
/// RestoreCoordinationRemote is used while executing RESTORE with ON CLUSTER and performs coordination via ZooKeeper.
|
/// RestoreCoordinationOnCluster is used while executing RESTORE with ON CLUSTER and performs coordination via ZooKeeper.
|
||||||
class IRestoreCoordination
|
class IRestoreCoordination
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~IRestoreCoordination() = default;
|
virtual ~IRestoreCoordination() = default;
|
||||||
|
|
||||||
/// Sets the current stage and waits for other hosts to come to this stage too.
|
/// Sets the current stage and waits for other hosts to come to this stage too.
|
||||||
virtual void setStage(const String & new_stage, const String & message) = 0;
|
virtual Strings setStage(const String & new_stage, const String & message, bool sync) = 0;
|
||||||
virtual void setError(const Exception & exception) = 0;
|
|
||||||
virtual Strings waitForStage(const String & stage_to_wait) = 0;
|
|
||||||
virtual Strings waitForStage(const String & stage_to_wait, std::chrono::milliseconds timeout) = 0;
|
|
||||||
|
|
||||||
static constexpr const char * kErrorStatus = "error";
|
/// Sets that the restore query was sent to other hosts.
|
||||||
|
/// Function waitForOtherHostsToFinish() will check that to find out if it should really wait or not.
|
||||||
|
virtual void setRestoreQueryWasSentToOtherHosts() = 0;
|
||||||
|
|
||||||
|
/// Lets other hosts know that the current host has encountered an error.
|
||||||
|
virtual bool trySetError(std::exception_ptr exception) = 0;
|
||||||
|
|
||||||
|
/// Lets other hosts know that the current host has finished its work.
|
||||||
|
virtual void finish() = 0;
|
||||||
|
|
||||||
|
/// Lets other hosts know that the current host has finished its work (as a part of error-handling process).
|
||||||
|
virtual bool tryFinishAfterError() noexcept = 0;
|
||||||
|
|
||||||
|
/// Waits until all the other hosts finish their work.
|
||||||
|
/// Stops waiting and throws an exception if another host encounters an error or if some host gets cancelled.
|
||||||
|
virtual void waitForOtherHostsToFinish() = 0;
|
||||||
|
|
||||||
|
/// Waits until all the other hosts finish their work (as a part of error-handling process).
|
||||||
|
/// Doesn't stops waiting if some host encounters an error or gets cancelled.
|
||||||
|
virtual bool tryWaitForOtherHostsToFinishAfterError() noexcept = 0;
|
||||||
|
|
||||||
/// Starts creating a table in a replicated database. Returns false if there is another host which is already creating this table.
|
/// Starts creating a table in a replicated database. Returns false if there is another host which is already creating this table.
|
||||||
virtual bool acquireCreatingTableInReplicatedDatabase(const String & database_zk_path, const String & table_name) = 0;
|
virtual bool acquireCreatingTableInReplicatedDatabase(const String & database_zk_path, const String & table_name) = 0;
|
||||||
@ -49,9 +65,7 @@ public:
|
|||||||
/// (because otherwise the macro "{uuid}" in the ZooKeeper path will not work correctly).
|
/// (because otherwise the macro "{uuid}" in the ZooKeeper path will not work correctly).
|
||||||
virtual void generateUUIDForTable(ASTCreateQuery & create_query) = 0;
|
virtual void generateUUIDForTable(ASTCreateQuery & create_query) = 0;
|
||||||
|
|
||||||
/// This function is used to check if concurrent restores are running
|
virtual ZooKeeperRetriesInfo getOnClusterInitializationKeeperRetriesInfo() const = 0;
|
||||||
/// other than the restore passed to the function
|
|
||||||
virtual bool hasConcurrentRestores(const std::atomic<size_t> & num_active_restores) const = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,24 @@
|
|||||||
#include <Backups/RestoreCoordinationLocal.h>
|
#include <Backups/RestoreCoordinationLocal.h>
|
||||||
|
|
||||||
#include <Parsers/ASTCreateQuery.h>
|
#include <Parsers/ASTCreateQuery.h>
|
||||||
#include <Parsers/formatAST.h>
|
#include <Parsers/formatAST.h>
|
||||||
|
#include <Common/ZooKeeper/ZooKeeperRetries.h>
|
||||||
#include <Common/logger_useful.h>
|
#include <Common/logger_useful.h>
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
{
|
{
|
||||||
|
|
||||||
RestoreCoordinationLocal::RestoreCoordinationLocal() : log(getLogger("RestoreCoordinationLocal"))
|
RestoreCoordinationLocal::RestoreCoordinationLocal(
|
||||||
|
const UUID & restore_uuid, bool allow_concurrent_restore_, BackupConcurrencyCounters & concurrency_counters_)
|
||||||
|
: log(getLogger("RestoreCoordinationLocal"))
|
||||||
|
, concurrency_check(restore_uuid, /* is_restore = */ true, /* on_cluster = */ false, allow_concurrent_restore_, concurrency_counters_)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
RestoreCoordinationLocal::~RestoreCoordinationLocal() = default;
|
RestoreCoordinationLocal::~RestoreCoordinationLocal() = default;
|
||||||
|
|
||||||
void RestoreCoordinationLocal::setStage(const String &, const String &)
|
ZooKeeperRetriesInfo RestoreCoordinationLocal::getOnClusterInitializationKeeperRetriesInfo() const
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void RestoreCoordinationLocal::setError(const Exception &)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Strings RestoreCoordinationLocal::waitForStage(const String &)
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Strings RestoreCoordinationLocal::waitForStage(const String &, std::chrono::milliseconds)
|
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -63,7 +55,7 @@ void RestoreCoordinationLocal::generateUUIDForTable(ASTCreateQuery & create_quer
|
|||||||
{
|
{
|
||||||
String query_str = serializeAST(create_query);
|
String query_str = serializeAST(create_query);
|
||||||
|
|
||||||
auto find_in_map = [&]
|
auto find_in_map = [&]() TSA_REQUIRES(mutex)
|
||||||
{
|
{
|
||||||
auto it = create_query_uuids.find(query_str);
|
auto it = create_query_uuids.find(query_str);
|
||||||
if (it != create_query_uuids.end())
|
if (it != create_query_uuids.end())
|
||||||
@ -91,14 +83,4 @@ void RestoreCoordinationLocal::generateUUIDForTable(ASTCreateQuery & create_quer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RestoreCoordinationLocal::hasConcurrentRestores(const std::atomic<size_t> & num_active_restores) const
|
|
||||||
{
|
|
||||||
if (num_active_restores > 1)
|
|
||||||
{
|
|
||||||
LOG_WARNING(log, "Found concurrent backups: num_active_restores={}", num_active_restores);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Backups/IRestoreCoordination.h>
|
#include <Backups/IRestoreCoordination.h>
|
||||||
|
#include <Backups/BackupConcurrencyCheck.h>
|
||||||
#include <Parsers/CreateQueryUUIDs.h>
|
#include <Parsers/CreateQueryUUIDs.h>
|
||||||
#include <Common/Logger.h>
|
#include <Common/Logger.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
@ -12,19 +13,20 @@ namespace DB
|
|||||||
{
|
{
|
||||||
class ASTCreateQuery;
|
class ASTCreateQuery;
|
||||||
|
|
||||||
|
|
||||||
/// Implementation of the IRestoreCoordination interface performing coordination in memory.
|
/// Implementation of the IRestoreCoordination interface performing coordination in memory.
|
||||||
class RestoreCoordinationLocal : public IRestoreCoordination
|
class RestoreCoordinationLocal : public IRestoreCoordination
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RestoreCoordinationLocal();
|
RestoreCoordinationLocal(const UUID & restore_uuid_, bool allow_concurrent_restore_, BackupConcurrencyCounters & concurrency_counters_);
|
||||||
~RestoreCoordinationLocal() override;
|
~RestoreCoordinationLocal() override;
|
||||||
|
|
||||||
/// Sets the current stage and waits for other hosts to come to this stage too.
|
Strings setStage(const String &, const String &, bool) override { return {}; }
|
||||||
void setStage(const String & new_stage, const String & message) override;
|
void setRestoreQueryWasSentToOtherHosts() override {}
|
||||||
void setError(const Exception & exception) override;
|
bool trySetError(std::exception_ptr) override { return true; }
|
||||||
Strings waitForStage(const String & stage_to_wait) override;
|
void finish() override {}
|
||||||
Strings waitForStage(const String & stage_to_wait, std::chrono::milliseconds timeout) override;
|
bool tryFinishAfterError() noexcept override { return true; }
|
||||||
|
void waitForOtherHostsToFinish() override {}
|
||||||
|
bool tryWaitForOtherHostsToFinishAfterError() noexcept override { return true; }
|
||||||
|
|
||||||
/// Starts creating a table in a replicated database. Returns false if there is another host which is already creating this table.
|
/// Starts creating a table in a replicated database. Returns false if there is another host which is already creating this table.
|
||||||
bool acquireCreatingTableInReplicatedDatabase(const String & database_zk_path, const String & table_name) override;
|
bool acquireCreatingTableInReplicatedDatabase(const String & database_zk_path, const String & table_name) override;
|
||||||
@ -49,15 +51,16 @@ public:
|
|||||||
/// (because otherwise the macro "{uuid}" in the ZooKeeper path will not work correctly).
|
/// (because otherwise the macro "{uuid}" in the ZooKeeper path will not work correctly).
|
||||||
void generateUUIDForTable(ASTCreateQuery & create_query) override;
|
void generateUUIDForTable(ASTCreateQuery & create_query) override;
|
||||||
|
|
||||||
bool hasConcurrentRestores(const std::atomic<size_t> & num_active_restores) const override;
|
ZooKeeperRetriesInfo getOnClusterInitializationKeeperRetriesInfo() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
LoggerPtr const log;
|
LoggerPtr const log;
|
||||||
|
BackupConcurrencyCheck concurrency_check;
|
||||||
|
|
||||||
std::set<std::pair<String /* database_zk_path */, String /* table_name */>> acquired_tables_in_replicated_databases;
|
std::set<std::pair<String /* database_zk_path */, String /* table_name */>> acquired_tables_in_replicated_databases TSA_GUARDED_BY(mutex);
|
||||||
std::unordered_set<String /* table_zk_path */> acquired_data_in_replicated_tables;
|
std::unordered_set<String /* table_zk_path */> acquired_data_in_replicated_tables TSA_GUARDED_BY(mutex);
|
||||||
std::unordered_map<String, CreateQueryUUIDs> create_query_uuids;
|
std::unordered_map<String, CreateQueryUUIDs> create_query_uuids TSA_GUARDED_BY(mutex);
|
||||||
std::unordered_set<String /* root_zk_path */> acquired_data_in_keeper_map_tables;
|
std::unordered_set<String /* root_zk_path */> acquired_data_in_keeper_map_tables TSA_GUARDED_BY(mutex);
|
||||||
|
|
||||||
mutable std::mutex mutex;
|
mutable std::mutex mutex;
|
||||||
};
|
};
|
||||||
|
318
src/Backups/RestoreCoordinationOnCluster.cpp
Normal file
318
src/Backups/RestoreCoordinationOnCluster.cpp
Normal file
@ -0,0 +1,318 @@
|
|||||||
|
#include <Backups/BackupCoordinationOnCluster.h>
|
||||||
|
|
||||||
|
#include <Backups/BackupCoordinationStage.h>
|
||||||
|
#include <Backups/BackupCoordinationStageSync.h>
|
||||||
|
#include <Backups/RestoreCoordinationOnCluster.h>
|
||||||
|
#include <Parsers/ASTCreateQuery.h>
|
||||||
|
#include <Parsers/CreateQueryUUIDs.h>
|
||||||
|
#include <Parsers/formatAST.h>
|
||||||
|
#include <Functions/UserDefined/UserDefinedSQLObjectType.h>
|
||||||
|
#include <Common/ZooKeeper/KeeperException.h>
|
||||||
|
#include <Common/escapeForFileName.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
RestoreCoordinationOnCluster::RestoreCoordinationOnCluster(
|
||||||
|
const UUID & restore_uuid_,
|
||||||
|
const String & root_zookeeper_path_,
|
||||||
|
zkutil::GetZooKeeper get_zookeeper_,
|
||||||
|
const BackupKeeperSettings & keeper_settings_,
|
||||||
|
const String & current_host_,
|
||||||
|
const Strings & all_hosts_,
|
||||||
|
bool allow_concurrent_restore_,
|
||||||
|
BackupConcurrencyCounters & concurrency_counters_,
|
||||||
|
ThreadPoolCallbackRunnerUnsafe<void> schedule_,
|
||||||
|
QueryStatusPtr process_list_element_)
|
||||||
|
: root_zookeeper_path(root_zookeeper_path_)
|
||||||
|
, keeper_settings(keeper_settings_)
|
||||||
|
, restore_uuid(restore_uuid_)
|
||||||
|
, zookeeper_path(root_zookeeper_path_ + "/restore-" + toString(restore_uuid_))
|
||||||
|
, all_hosts(all_hosts_)
|
||||||
|
, all_hosts_without_initiator(BackupCoordinationOnCluster::excludeInitiator(all_hosts))
|
||||||
|
, current_host(current_host_)
|
||||||
|
, current_host_index(BackupCoordinationOnCluster::findCurrentHostIndex(current_host, all_hosts))
|
||||||
|
, log(getLogger("RestoreCoordinationOnCluster"))
|
||||||
|
, with_retries(log, get_zookeeper_, keeper_settings, process_list_element_, [root_zookeeper_path_](Coordination::ZooKeeperWithFaultInjection::Ptr zk) { zk->sync(root_zookeeper_path_); })
|
||||||
|
, concurrency_check(restore_uuid_, /* is_restore = */ true, /* on_cluster = */ true, allow_concurrent_restore_, concurrency_counters_)
|
||||||
|
, stage_sync(/* is_restore = */ true, fs::path{zookeeper_path} / "stage", current_host, all_hosts, allow_concurrent_restore_, with_retries, schedule_, process_list_element_, log)
|
||||||
|
, cleaner(zookeeper_path, with_retries, log)
|
||||||
|
{
|
||||||
|
createRootNodes();
|
||||||
|
}
|
||||||
|
|
||||||
|
RestoreCoordinationOnCluster::~RestoreCoordinationOnCluster()
|
||||||
|
{
|
||||||
|
tryFinishImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestoreCoordinationOnCluster::createRootNodes()
|
||||||
|
{
|
||||||
|
auto holder = with_retries.createRetriesControlHolder("createRootNodes", WithRetries::kInitialization);
|
||||||
|
holder.retries_ctl.retryLoop(
|
||||||
|
[&, &zk = holder.faulty_zookeeper]()
|
||||||
|
{
|
||||||
|
with_retries.renewZooKeeper(zk);
|
||||||
|
|
||||||
|
zk->createAncestors(zookeeper_path);
|
||||||
|
zk->createIfNotExists(zookeeper_path, "");
|
||||||
|
zk->createIfNotExists(zookeeper_path + "/repl_databases_tables_acquired", "");
|
||||||
|
zk->createIfNotExists(zookeeper_path + "/repl_tables_data_acquired", "");
|
||||||
|
zk->createIfNotExists(zookeeper_path + "/repl_access_storages_acquired", "");
|
||||||
|
zk->createIfNotExists(zookeeper_path + "/repl_sql_objects_acquired", "");
|
||||||
|
zk->createIfNotExists(zookeeper_path + "/keeper_map_tables", "");
|
||||||
|
zk->createIfNotExists(zookeeper_path + "/table_uuids", "");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Strings RestoreCoordinationOnCluster::setStage(const String & new_stage, const String & message, bool sync)
|
||||||
|
{
|
||||||
|
stage_sync.setStage(new_stage, message);
|
||||||
|
|
||||||
|
if (!sync)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return stage_sync.waitForHostsToReachStage(new_stage, all_hosts_without_initiator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestoreCoordinationOnCluster::setRestoreQueryWasSentToOtherHosts()
|
||||||
|
{
|
||||||
|
restore_query_was_sent_to_other_hosts = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RestoreCoordinationOnCluster::trySetError(std::exception_ptr exception)
|
||||||
|
{
|
||||||
|
return stage_sync.trySetError(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestoreCoordinationOnCluster::finish()
|
||||||
|
{
|
||||||
|
bool other_hosts_also_finished = false;
|
||||||
|
stage_sync.finish(other_hosts_also_finished);
|
||||||
|
|
||||||
|
if ((current_host == kInitiator) && (other_hosts_also_finished || !restore_query_was_sent_to_other_hosts))
|
||||||
|
cleaner.cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RestoreCoordinationOnCluster::tryFinishAfterError() noexcept
|
||||||
|
{
|
||||||
|
return tryFinishImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RestoreCoordinationOnCluster::tryFinishImpl() noexcept
|
||||||
|
{
|
||||||
|
bool other_hosts_also_finished = false;
|
||||||
|
if (!stage_sync.tryFinishAfterError(other_hosts_also_finished))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ((current_host == kInitiator) && (other_hosts_also_finished || !restore_query_was_sent_to_other_hosts))
|
||||||
|
{
|
||||||
|
if (!cleaner.tryCleanupAfterError())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestoreCoordinationOnCluster::waitForOtherHostsToFinish()
|
||||||
|
{
|
||||||
|
if ((current_host != kInitiator) || !restore_query_was_sent_to_other_hosts)
|
||||||
|
return;
|
||||||
|
stage_sync.waitForOtherHostsToFinish();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RestoreCoordinationOnCluster::tryWaitForOtherHostsToFinishAfterError() noexcept
|
||||||
|
{
|
||||||
|
if (current_host != kInitiator)
|
||||||
|
return false;
|
||||||
|
if (!restore_query_was_sent_to_other_hosts)
|
||||||
|
return true;
|
||||||
|
return stage_sync.tryWaitForOtherHostsToFinishAfterError();
|
||||||
|
}
|
||||||
|
|
||||||
|
ZooKeeperRetriesInfo RestoreCoordinationOnCluster::getOnClusterInitializationKeeperRetriesInfo() const
|
||||||
|
{
|
||||||
|
return ZooKeeperRetriesInfo{keeper_settings.max_retries_while_initializing,
|
||||||
|
static_cast<UInt64>(keeper_settings.retry_initial_backoff_ms.count()),
|
||||||
|
static_cast<UInt64>(keeper_settings.retry_max_backoff_ms.count())};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RestoreCoordinationOnCluster::acquireCreatingTableInReplicatedDatabase(const String & database_zk_path, const String & table_name)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
auto holder = with_retries.createRetriesControlHolder("acquireCreatingTableInReplicatedDatabase");
|
||||||
|
holder.retries_ctl.retryLoop(
|
||||||
|
[&, &zk = holder.faulty_zookeeper]()
|
||||||
|
{
|
||||||
|
with_retries.renewZooKeeper(zk);
|
||||||
|
|
||||||
|
String path = zookeeper_path + "/repl_databases_tables_acquired/" + escapeForFileName(database_zk_path);
|
||||||
|
zk->createIfNotExists(path, "");
|
||||||
|
|
||||||
|
path += "/" + escapeForFileName(table_name);
|
||||||
|
auto code = zk->tryCreate(path, toString(current_host_index), zkutil::CreateMode::Persistent);
|
||||||
|
if ((code != Coordination::Error::ZOK) && (code != Coordination::Error::ZNODEEXISTS))
|
||||||
|
throw zkutil::KeeperException::fromPath(code, path);
|
||||||
|
|
||||||
|
if (code == Coordination::Error::ZOK)
|
||||||
|
{
|
||||||
|
result = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// We need to check who created that node
|
||||||
|
result = zk->get(path) == toString(current_host_index);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RestoreCoordinationOnCluster::acquireInsertingDataIntoReplicatedTable(const String & table_zk_path)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
auto holder = with_retries.createRetriesControlHolder("acquireInsertingDataIntoReplicatedTable");
|
||||||
|
holder.retries_ctl.retryLoop(
|
||||||
|
[&, &zk = holder.faulty_zookeeper]()
|
||||||
|
{
|
||||||
|
with_retries.renewZooKeeper(zk);
|
||||||
|
|
||||||
|
String path = zookeeper_path + "/repl_tables_data_acquired/" + escapeForFileName(table_zk_path);
|
||||||
|
auto code = zk->tryCreate(path, toString(current_host_index), zkutil::CreateMode::Persistent);
|
||||||
|
if ((code != Coordination::Error::ZOK) && (code != Coordination::Error::ZNODEEXISTS))
|
||||||
|
throw zkutil::KeeperException::fromPath(code, path);
|
||||||
|
|
||||||
|
if (code == Coordination::Error::ZOK)
|
||||||
|
{
|
||||||
|
result = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// We need to check who created that node
|
||||||
|
result = zk->get(path) == toString(current_host_index);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RestoreCoordinationOnCluster::acquireReplicatedAccessStorage(const String & access_storage_zk_path)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
auto holder = with_retries.createRetriesControlHolder("acquireReplicatedAccessStorage");
|
||||||
|
holder.retries_ctl.retryLoop(
|
||||||
|
[&, &zk = holder.faulty_zookeeper]()
|
||||||
|
{
|
||||||
|
with_retries.renewZooKeeper(zk);
|
||||||
|
|
||||||
|
String path = zookeeper_path + "/repl_access_storages_acquired/" + escapeForFileName(access_storage_zk_path);
|
||||||
|
auto code = zk->tryCreate(path, toString(current_host_index), zkutil::CreateMode::Persistent);
|
||||||
|
if ((code != Coordination::Error::ZOK) && (code != Coordination::Error::ZNODEEXISTS))
|
||||||
|
throw zkutil::KeeperException::fromPath(code, path);
|
||||||
|
|
||||||
|
if (code == Coordination::Error::ZOK)
|
||||||
|
{
|
||||||
|
result = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// We need to check who created that node
|
||||||
|
result = zk->get(path) == toString(current_host_index);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RestoreCoordinationOnCluster::acquireReplicatedSQLObjects(const String & loader_zk_path, UserDefinedSQLObjectType object_type)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
auto holder = with_retries.createRetriesControlHolder("acquireReplicatedSQLObjects");
|
||||||
|
holder.retries_ctl.retryLoop(
|
||||||
|
[&, &zk = holder.faulty_zookeeper]()
|
||||||
|
{
|
||||||
|
with_retries.renewZooKeeper(zk);
|
||||||
|
|
||||||
|
String path = zookeeper_path + "/repl_sql_objects_acquired/" + escapeForFileName(loader_zk_path);
|
||||||
|
zk->createIfNotExists(path, "");
|
||||||
|
|
||||||
|
path += "/";
|
||||||
|
switch (object_type)
|
||||||
|
{
|
||||||
|
case UserDefinedSQLObjectType::Function:
|
||||||
|
path += "functions";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto code = zk->tryCreate(path, "", zkutil::CreateMode::Persistent);
|
||||||
|
if ((code != Coordination::Error::ZOK) && (code != Coordination::Error::ZNODEEXISTS))
|
||||||
|
throw zkutil::KeeperException::fromPath(code, path);
|
||||||
|
|
||||||
|
if (code == Coordination::Error::ZOK)
|
||||||
|
{
|
||||||
|
result = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// We need to check who created that node
|
||||||
|
result = zk->get(path) == toString(current_host_index);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RestoreCoordinationOnCluster::acquireInsertingDataForKeeperMap(const String & root_zk_path, const String & table_unique_id)
|
||||||
|
{
|
||||||
|
bool lock_acquired = false;
|
||||||
|
auto holder = with_retries.createRetriesControlHolder("acquireInsertingDataForKeeperMap");
|
||||||
|
holder.retries_ctl.retryLoop(
|
||||||
|
[&, &zk = holder.faulty_zookeeper]()
|
||||||
|
{
|
||||||
|
with_retries.renewZooKeeper(zk);
|
||||||
|
|
||||||
|
/// we need to remove leading '/' from root_zk_path
|
||||||
|
auto normalized_root_zk_path = root_zk_path.substr(1);
|
||||||
|
std::string restore_lock_path = fs::path(zookeeper_path) / "keeper_map_tables" / escapeForFileName(normalized_root_zk_path);
|
||||||
|
zk->createAncestors(restore_lock_path);
|
||||||
|
auto code = zk->tryCreate(restore_lock_path, table_unique_id, zkutil::CreateMode::Persistent);
|
||||||
|
|
||||||
|
if (code == Coordination::Error::ZOK)
|
||||||
|
{
|
||||||
|
lock_acquired = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code == Coordination::Error::ZNODEEXISTS)
|
||||||
|
lock_acquired = table_unique_id == zk->get(restore_lock_path);
|
||||||
|
else
|
||||||
|
zkutil::KeeperException::fromPath(code, restore_lock_path);
|
||||||
|
});
|
||||||
|
return lock_acquired;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestoreCoordinationOnCluster::generateUUIDForTable(ASTCreateQuery & create_query)
|
||||||
|
{
|
||||||
|
String query_str = serializeAST(create_query);
|
||||||
|
CreateQueryUUIDs new_uuids{create_query, /* generate_random= */ true, /* force_random= */ true};
|
||||||
|
String new_uuids_str = new_uuids.toString();
|
||||||
|
|
||||||
|
auto holder = with_retries.createRetriesControlHolder("generateUUIDForTable");
|
||||||
|
holder.retries_ctl.retryLoop(
|
||||||
|
[&, &zk = holder.faulty_zookeeper]()
|
||||||
|
{
|
||||||
|
with_retries.renewZooKeeper(zk);
|
||||||
|
|
||||||
|
String path = zookeeper_path + "/table_uuids/" + escapeForFileName(query_str);
|
||||||
|
Coordination::Error res = zk->tryCreate(path, new_uuids_str, zkutil::CreateMode::Persistent);
|
||||||
|
|
||||||
|
if (res == Coordination::Error::ZOK)
|
||||||
|
{
|
||||||
|
new_uuids.copyToQuery(create_query);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res == Coordination::Error::ZNODEEXISTS)
|
||||||
|
{
|
||||||
|
CreateQueryUUIDs::fromString(zk->get(path)).copyToQuery(create_query);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
zkutil::KeeperException::fromPath(res, path);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Backups/IRestoreCoordination.h>
|
#include <Backups/IRestoreCoordination.h>
|
||||||
|
#include <Backups/BackupConcurrencyCheck.h>
|
||||||
|
#include <Backups/BackupCoordinationCleaner.h>
|
||||||
#include <Backups/BackupCoordinationStageSync.h>
|
#include <Backups/BackupCoordinationStageSync.h>
|
||||||
#include <Backups/WithRetries.h>
|
#include <Backups/WithRetries.h>
|
||||||
|
|
||||||
@ -9,28 +11,33 @@ namespace DB
|
|||||||
{
|
{
|
||||||
|
|
||||||
/// Implementation of the IRestoreCoordination interface performing coordination via ZooKeeper. It's necessary for "RESTORE ON CLUSTER".
|
/// Implementation of the IRestoreCoordination interface performing coordination via ZooKeeper. It's necessary for "RESTORE ON CLUSTER".
|
||||||
class RestoreCoordinationRemote : public IRestoreCoordination
|
class RestoreCoordinationOnCluster : public IRestoreCoordination
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using RestoreKeeperSettings = WithRetries::KeeperSettings;
|
/// Empty string as the current host is used to mark the initiator of a RESTORE ON CLUSTER query.
|
||||||
|
static const constexpr std::string_view kInitiator;
|
||||||
|
|
||||||
RestoreCoordinationRemote(
|
RestoreCoordinationOnCluster(
|
||||||
zkutil::GetZooKeeper get_zookeeper_,
|
const UUID & restore_uuid_,
|
||||||
const String & root_zookeeper_path_,
|
const String & root_zookeeper_path_,
|
||||||
const RestoreKeeperSettings & keeper_settings_,
|
zkutil::GetZooKeeper get_zookeeper_,
|
||||||
const String & restore_uuid_,
|
const BackupKeeperSettings & keeper_settings_,
|
||||||
const Strings & all_hosts_,
|
|
||||||
const String & current_host_,
|
const String & current_host_,
|
||||||
bool is_internal_,
|
const Strings & all_hosts_,
|
||||||
|
bool allow_concurrent_restore_,
|
||||||
|
BackupConcurrencyCounters & concurrency_counters_,
|
||||||
|
ThreadPoolCallbackRunnerUnsafe<void> schedule_,
|
||||||
QueryStatusPtr process_list_element_);
|
QueryStatusPtr process_list_element_);
|
||||||
|
|
||||||
~RestoreCoordinationRemote() override;
|
~RestoreCoordinationOnCluster() override;
|
||||||
|
|
||||||
/// Sets the current stage and waits for other hosts to come to this stage too.
|
Strings setStage(const String & new_stage, const String & message, bool sync) override;
|
||||||
void setStage(const String & new_stage, const String & message) override;
|
void setRestoreQueryWasSentToOtherHosts() override;
|
||||||
void setError(const Exception & exception) override;
|
bool trySetError(std::exception_ptr exception) override;
|
||||||
Strings waitForStage(const String & stage_to_wait) override;
|
void finish() override;
|
||||||
Strings waitForStage(const String & stage_to_wait, std::chrono::milliseconds timeout) override;
|
bool tryFinishAfterError() noexcept override;
|
||||||
|
void waitForOtherHostsToFinish() override;
|
||||||
|
bool tryWaitForOtherHostsToFinishAfterError() noexcept override;
|
||||||
|
|
||||||
/// Starts creating a table in a replicated database. Returns false if there is another host which is already creating this table.
|
/// Starts creating a table in a replicated database. Returns false if there is another host which is already creating this table.
|
||||||
bool acquireCreatingTableInReplicatedDatabase(const String & database_zk_path, const String & table_name) override;
|
bool acquireCreatingTableInReplicatedDatabase(const String & database_zk_path, const String & table_name) override;
|
||||||
@ -55,27 +62,27 @@ public:
|
|||||||
/// (because otherwise the macro "{uuid}" in the ZooKeeper path will not work correctly).
|
/// (because otherwise the macro "{uuid}" in the ZooKeeper path will not work correctly).
|
||||||
void generateUUIDForTable(ASTCreateQuery & create_query) override;
|
void generateUUIDForTable(ASTCreateQuery & create_query) override;
|
||||||
|
|
||||||
bool hasConcurrentRestores(const std::atomic<size_t> & num_active_restores) const override;
|
ZooKeeperRetriesInfo getOnClusterInitializationKeeperRetriesInfo() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void createRootNodes();
|
void createRootNodes();
|
||||||
void removeAllNodes();
|
bool tryFinishImpl() noexcept;
|
||||||
|
|
||||||
/// get_zookeeper will provide a zookeeper client without any fault injection
|
|
||||||
const zkutil::GetZooKeeper get_zookeeper;
|
|
||||||
const String root_zookeeper_path;
|
const String root_zookeeper_path;
|
||||||
const RestoreKeeperSettings keeper_settings;
|
const BackupKeeperSettings keeper_settings;
|
||||||
const String restore_uuid;
|
const UUID restore_uuid;
|
||||||
const String zookeeper_path;
|
const String zookeeper_path;
|
||||||
const Strings all_hosts;
|
const Strings all_hosts;
|
||||||
|
const Strings all_hosts_without_initiator;
|
||||||
const String current_host;
|
const String current_host;
|
||||||
const size_t current_host_index;
|
const size_t current_host_index;
|
||||||
const bool is_internal;
|
|
||||||
LoggerPtr const log;
|
LoggerPtr const log;
|
||||||
|
|
||||||
mutable WithRetries with_retries;
|
const WithRetries with_retries;
|
||||||
std::optional<BackupCoordinationStageSync> stage_sync;
|
BackupConcurrencyCheck concurrency_check;
|
||||||
mutable std::mutex mutex;
|
BackupCoordinationStageSync stage_sync;
|
||||||
|
BackupCoordinationCleaner cleaner;
|
||||||
|
std::atomic<bool> restore_query_was_sent_to_other_hosts = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
@ -1,379 +0,0 @@
|
|||||||
#include <Backups/BackupCoordinationRemote.h>
|
|
||||||
#include <Backups/BackupCoordinationStage.h>
|
|
||||||
#include <Backups/RestoreCoordinationRemote.h>
|
|
||||||
#include <Backups/BackupCoordinationStageSync.h>
|
|
||||||
#include <Parsers/ASTCreateQuery.h>
|
|
||||||
#include <Parsers/CreateQueryUUIDs.h>
|
|
||||||
#include <Parsers/formatAST.h>
|
|
||||||
#include <Functions/UserDefined/UserDefinedSQLObjectType.h>
|
|
||||||
#include <Common/ZooKeeper/KeeperException.h>
|
|
||||||
#include <Common/escapeForFileName.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
|
||||||
{
|
|
||||||
|
|
||||||
namespace Stage = BackupCoordinationStage;
|
|
||||||
|
|
||||||
RestoreCoordinationRemote::RestoreCoordinationRemote(
|
|
||||||
zkutil::GetZooKeeper get_zookeeper_,
|
|
||||||
const String & root_zookeeper_path_,
|
|
||||||
const RestoreKeeperSettings & keeper_settings_,
|
|
||||||
const String & restore_uuid_,
|
|
||||||
const Strings & all_hosts_,
|
|
||||||
const String & current_host_,
|
|
||||||
bool is_internal_,
|
|
||||||
QueryStatusPtr process_list_element_)
|
|
||||||
: get_zookeeper(get_zookeeper_)
|
|
||||||
, root_zookeeper_path(root_zookeeper_path_)
|
|
||||||
, keeper_settings(keeper_settings_)
|
|
||||||
, restore_uuid(restore_uuid_)
|
|
||||||
, zookeeper_path(root_zookeeper_path_ + "/restore-" + restore_uuid_)
|
|
||||||
, all_hosts(all_hosts_)
|
|
||||||
, current_host(current_host_)
|
|
||||||
, current_host_index(BackupCoordinationRemote::findCurrentHostIndex(all_hosts, current_host))
|
|
||||||
, is_internal(is_internal_)
|
|
||||||
, log(getLogger("RestoreCoordinationRemote"))
|
|
||||||
, with_retries(
|
|
||||||
log,
|
|
||||||
get_zookeeper_,
|
|
||||||
keeper_settings,
|
|
||||||
process_list_element_,
|
|
||||||
[my_zookeeper_path = zookeeper_path, my_current_host = current_host, my_is_internal = is_internal]
|
|
||||||
(WithRetries::FaultyKeeper & zk)
|
|
||||||
{
|
|
||||||
/// Recreate this ephemeral node to signal that we are alive.
|
|
||||||
if (my_is_internal)
|
|
||||||
{
|
|
||||||
String alive_node_path = my_zookeeper_path + "/stage/alive|" + my_current_host;
|
|
||||||
|
|
||||||
/// Delete the ephemeral node from the previous connection so we don't have to wait for keeper to do it automatically.
|
|
||||||
zk->tryRemove(alive_node_path);
|
|
||||||
|
|
||||||
zk->createAncestors(alive_node_path);
|
|
||||||
zk->create(alive_node_path, "", zkutil::CreateMode::Ephemeral);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
{
|
|
||||||
createRootNodes();
|
|
||||||
|
|
||||||
stage_sync.emplace(
|
|
||||||
zookeeper_path,
|
|
||||||
with_retries,
|
|
||||||
log);
|
|
||||||
}
|
|
||||||
|
|
||||||
RestoreCoordinationRemote::~RestoreCoordinationRemote()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!is_internal)
|
|
||||||
removeAllNodes();
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
tryLogCurrentException(__PRETTY_FUNCTION__);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RestoreCoordinationRemote::createRootNodes()
|
|
||||||
{
|
|
||||||
auto holder = with_retries.createRetriesControlHolder("createRootNodes");
|
|
||||||
holder.retries_ctl.retryLoop(
|
|
||||||
[&, &zk = holder.faulty_zookeeper]()
|
|
||||||
{
|
|
||||||
with_retries.renewZooKeeper(zk);
|
|
||||||
zk->createAncestors(zookeeper_path);
|
|
||||||
|
|
||||||
Coordination::Requests ops;
|
|
||||||
Coordination::Responses responses;
|
|
||||||
ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path, "", zkutil::CreateMode::Persistent));
|
|
||||||
ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/repl_databases_tables_acquired", "", zkutil::CreateMode::Persistent));
|
|
||||||
ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/repl_tables_data_acquired", "", zkutil::CreateMode::Persistent));
|
|
||||||
ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/repl_access_storages_acquired", "", zkutil::CreateMode::Persistent));
|
|
||||||
ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/repl_sql_objects_acquired", "", zkutil::CreateMode::Persistent));
|
|
||||||
ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/keeper_map_tables", "", zkutil::CreateMode::Persistent));
|
|
||||||
ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/table_uuids", "", zkutil::CreateMode::Persistent));
|
|
||||||
zk->tryMulti(ops, responses);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void RestoreCoordinationRemote::setStage(const String & new_stage, const String & message)
|
|
||||||
{
|
|
||||||
if (is_internal)
|
|
||||||
stage_sync->set(current_host, new_stage, message);
|
|
||||||
else
|
|
||||||
stage_sync->set(current_host, new_stage, /* message */ "", /* all_hosts */ true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RestoreCoordinationRemote::setError(const Exception & exception)
|
|
||||||
{
|
|
||||||
stage_sync->setError(current_host, exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
Strings RestoreCoordinationRemote::waitForStage(const String & stage_to_wait)
|
|
||||||
{
|
|
||||||
return stage_sync->wait(all_hosts, stage_to_wait);
|
|
||||||
}
|
|
||||||
|
|
||||||
Strings RestoreCoordinationRemote::waitForStage(const String & stage_to_wait, std::chrono::milliseconds timeout)
|
|
||||||
{
|
|
||||||
return stage_sync->waitFor(all_hosts, stage_to_wait, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RestoreCoordinationRemote::acquireCreatingTableInReplicatedDatabase(const String & database_zk_path, const String & table_name)
|
|
||||||
{
|
|
||||||
bool result = false;
|
|
||||||
auto holder = with_retries.createRetriesControlHolder("acquireCreatingTableInReplicatedDatabase");
|
|
||||||
holder.retries_ctl.retryLoop(
|
|
||||||
[&, &zk = holder.faulty_zookeeper]()
|
|
||||||
{
|
|
||||||
with_retries.renewZooKeeper(zk);
|
|
||||||
|
|
||||||
String path = zookeeper_path + "/repl_databases_tables_acquired/" + escapeForFileName(database_zk_path);
|
|
||||||
zk->createIfNotExists(path, "");
|
|
||||||
|
|
||||||
path += "/" + escapeForFileName(table_name);
|
|
||||||
auto code = zk->tryCreate(path, toString(current_host_index), zkutil::CreateMode::Persistent);
|
|
||||||
if ((code != Coordination::Error::ZOK) && (code != Coordination::Error::ZNODEEXISTS))
|
|
||||||
throw zkutil::KeeperException::fromPath(code, path);
|
|
||||||
|
|
||||||
if (code == Coordination::Error::ZOK)
|
|
||||||
{
|
|
||||||
result = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// We need to check who created that node
|
|
||||||
result = zk->get(path) == toString(current_host_index);
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RestoreCoordinationRemote::acquireInsertingDataIntoReplicatedTable(const String & table_zk_path)
|
|
||||||
{
|
|
||||||
bool result = false;
|
|
||||||
auto holder = with_retries.createRetriesControlHolder("acquireInsertingDataIntoReplicatedTable");
|
|
||||||
holder.retries_ctl.retryLoop(
|
|
||||||
[&, &zk = holder.faulty_zookeeper]()
|
|
||||||
{
|
|
||||||
with_retries.renewZooKeeper(zk);
|
|
||||||
|
|
||||||
String path = zookeeper_path + "/repl_tables_data_acquired/" + escapeForFileName(table_zk_path);
|
|
||||||
auto code = zk->tryCreate(path, toString(current_host_index), zkutil::CreateMode::Persistent);
|
|
||||||
if ((code != Coordination::Error::ZOK) && (code != Coordination::Error::ZNODEEXISTS))
|
|
||||||
throw zkutil::KeeperException::fromPath(code, path);
|
|
||||||
|
|
||||||
if (code == Coordination::Error::ZOK)
|
|
||||||
{
|
|
||||||
result = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// We need to check who created that node
|
|
||||||
result = zk->get(path) == toString(current_host_index);
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RestoreCoordinationRemote::acquireReplicatedAccessStorage(const String & access_storage_zk_path)
|
|
||||||
{
|
|
||||||
bool result = false;
|
|
||||||
auto holder = with_retries.createRetriesControlHolder("acquireReplicatedAccessStorage");
|
|
||||||
holder.retries_ctl.retryLoop(
|
|
||||||
[&, &zk = holder.faulty_zookeeper]()
|
|
||||||
{
|
|
||||||
with_retries.renewZooKeeper(zk);
|
|
||||||
|
|
||||||
String path = zookeeper_path + "/repl_access_storages_acquired/" + escapeForFileName(access_storage_zk_path);
|
|
||||||
auto code = zk->tryCreate(path, toString(current_host_index), zkutil::CreateMode::Persistent);
|
|
||||||
if ((code != Coordination::Error::ZOK) && (code != Coordination::Error::ZNODEEXISTS))
|
|
||||||
throw zkutil::KeeperException::fromPath(code, path);
|
|
||||||
|
|
||||||
if (code == Coordination::Error::ZOK)
|
|
||||||
{
|
|
||||||
result = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// We need to check who created that node
|
|
||||||
result = zk->get(path) == toString(current_host_index);
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RestoreCoordinationRemote::acquireReplicatedSQLObjects(const String & loader_zk_path, UserDefinedSQLObjectType object_type)
|
|
||||||
{
|
|
||||||
bool result = false;
|
|
||||||
auto holder = with_retries.createRetriesControlHolder("acquireReplicatedSQLObjects");
|
|
||||||
holder.retries_ctl.retryLoop(
|
|
||||||
[&, &zk = holder.faulty_zookeeper]()
|
|
||||||
{
|
|
||||||
with_retries.renewZooKeeper(zk);
|
|
||||||
|
|
||||||
String path = zookeeper_path + "/repl_sql_objects_acquired/" + escapeForFileName(loader_zk_path);
|
|
||||||
zk->createIfNotExists(path, "");
|
|
||||||
|
|
||||||
path += "/";
|
|
||||||
switch (object_type)
|
|
||||||
{
|
|
||||||
case UserDefinedSQLObjectType::Function:
|
|
||||||
path += "functions";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto code = zk->tryCreate(path, "", zkutil::CreateMode::Persistent);
|
|
||||||
if ((code != Coordination::Error::ZOK) && (code != Coordination::Error::ZNODEEXISTS))
|
|
||||||
throw zkutil::KeeperException::fromPath(code, path);
|
|
||||||
|
|
||||||
if (code == Coordination::Error::ZOK)
|
|
||||||
{
|
|
||||||
result = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// We need to check who created that node
|
|
||||||
result = zk->get(path) == toString(current_host_index);
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RestoreCoordinationRemote::acquireInsertingDataForKeeperMap(const String & root_zk_path, const String & table_unique_id)
|
|
||||||
{
|
|
||||||
bool lock_acquired = false;
|
|
||||||
auto holder = with_retries.createRetriesControlHolder("acquireInsertingDataForKeeperMap");
|
|
||||||
holder.retries_ctl.retryLoop(
|
|
||||||
[&, &zk = holder.faulty_zookeeper]()
|
|
||||||
{
|
|
||||||
with_retries.renewZooKeeper(zk);
|
|
||||||
|
|
||||||
/// we need to remove leading '/' from root_zk_path
|
|
||||||
auto normalized_root_zk_path = root_zk_path.substr(1);
|
|
||||||
std::string restore_lock_path = fs::path(zookeeper_path) / "keeper_map_tables" / escapeForFileName(normalized_root_zk_path);
|
|
||||||
zk->createAncestors(restore_lock_path);
|
|
||||||
auto code = zk->tryCreate(restore_lock_path, table_unique_id, zkutil::CreateMode::Persistent);
|
|
||||||
|
|
||||||
if (code == Coordination::Error::ZOK)
|
|
||||||
{
|
|
||||||
lock_acquired = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (code == Coordination::Error::ZNODEEXISTS)
|
|
||||||
lock_acquired = table_unique_id == zk->get(restore_lock_path);
|
|
||||||
else
|
|
||||||
zkutil::KeeperException::fromPath(code, restore_lock_path);
|
|
||||||
});
|
|
||||||
return lock_acquired;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RestoreCoordinationRemote::generateUUIDForTable(ASTCreateQuery & create_query)
|
|
||||||
{
|
|
||||||
String query_str = serializeAST(create_query);
|
|
||||||
CreateQueryUUIDs new_uuids{create_query, /* generate_random= */ true, /* force_random= */ true};
|
|
||||||
String new_uuids_str = new_uuids.toString();
|
|
||||||
|
|
||||||
auto holder = with_retries.createRetriesControlHolder("generateUUIDForTable");
|
|
||||||
holder.retries_ctl.retryLoop(
|
|
||||||
[&, &zk = holder.faulty_zookeeper]()
|
|
||||||
{
|
|
||||||
with_retries.renewZooKeeper(zk);
|
|
||||||
|
|
||||||
String path = zookeeper_path + "/table_uuids/" + escapeForFileName(query_str);
|
|
||||||
Coordination::Error res = zk->tryCreate(path, new_uuids_str, zkutil::CreateMode::Persistent);
|
|
||||||
|
|
||||||
if (res == Coordination::Error::ZOK)
|
|
||||||
{
|
|
||||||
new_uuids.copyToQuery(create_query);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res == Coordination::Error::ZNODEEXISTS)
|
|
||||||
{
|
|
||||||
CreateQueryUUIDs::fromString(zk->get(path)).copyToQuery(create_query);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
zkutil::KeeperException::fromPath(res, path);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void RestoreCoordinationRemote::removeAllNodes()
|
|
||||||
{
|
|
||||||
/// Usually this function is called by the initiator when a restore operation is complete so we don't need the coordination anymore.
|
|
||||||
///
|
|
||||||
/// However there can be a rare situation when this function is called after an error occurs on the initiator of a query
|
|
||||||
/// while some hosts are still restoring something. Removing all the nodes will remove the parent node of the restore coordination
|
|
||||||
/// at `zookeeper_path` which might cause such hosts to stop with exception "ZNONODE". Or such hosts might still do some part
|
|
||||||
/// of their restore work before that.
|
|
||||||
|
|
||||||
auto holder = with_retries.createRetriesControlHolder("removeAllNodes");
|
|
||||||
holder.retries_ctl.retryLoop(
|
|
||||||
[&, &zk = holder.faulty_zookeeper]()
|
|
||||||
{
|
|
||||||
with_retries.renewZooKeeper(zk);
|
|
||||||
zk->removeRecursive(zookeeper_path);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RestoreCoordinationRemote::hasConcurrentRestores(const std::atomic<size_t> &) const
|
|
||||||
{
|
|
||||||
/// If its internal concurrency will be checked for the base restore
|
|
||||||
if (is_internal)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
bool result = false;
|
|
||||||
std::string path = zookeeper_path + "/stage";
|
|
||||||
|
|
||||||
auto holder = with_retries.createRetriesControlHolder("createRootNodes");
|
|
||||||
holder.retries_ctl.retryLoop(
|
|
||||||
[&, &zk = holder.faulty_zookeeper]()
|
|
||||||
{
|
|
||||||
with_retries.renewZooKeeper(zk);
|
|
||||||
|
|
||||||
if (! zk->exists(root_zookeeper_path))
|
|
||||||
zk->createAncestors(root_zookeeper_path);
|
|
||||||
|
|
||||||
for (size_t attempt = 0; attempt < MAX_ZOOKEEPER_ATTEMPTS; ++attempt)
|
|
||||||
{
|
|
||||||
Coordination::Stat stat;
|
|
||||||
zk->get(root_zookeeper_path, &stat);
|
|
||||||
Strings existing_restore_paths = zk->getChildren(root_zookeeper_path);
|
|
||||||
for (const auto & existing_restore_path : existing_restore_paths)
|
|
||||||
{
|
|
||||||
if (startsWith(existing_restore_path, "backup-"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
String existing_restore_uuid = existing_restore_path;
|
|
||||||
existing_restore_uuid.erase(0, String("restore-").size());
|
|
||||||
|
|
||||||
if (existing_restore_uuid == toString(restore_uuid))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
String status;
|
|
||||||
if (zk->tryGet(root_zookeeper_path + "/" + existing_restore_path + "/stage", status))
|
|
||||||
{
|
|
||||||
/// Check if some other restore is in progress
|
|
||||||
if (status == Stage::SCHEDULED_TO_START)
|
|
||||||
{
|
|
||||||
LOG_WARNING(log, "Found a concurrent restore: {}, current restore: {}", existing_restore_uuid, toString(restore_uuid));
|
|
||||||
result = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
zk->createIfNotExists(path, "");
|
|
||||||
auto code = zk->trySet(path, Stage::SCHEDULED_TO_START, stat.version);
|
|
||||||
if (code == Coordination::Error::ZOK)
|
|
||||||
break;
|
|
||||||
bool is_last_attempt = (attempt == MAX_ZOOKEEPER_ATTEMPTS - 1);
|
|
||||||
if ((code != Coordination::Error::ZBADVERSION) || is_last_attempt)
|
|
||||||
throw zkutil::KeeperException::fromPath(code, path);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -100,7 +100,6 @@ RestorerFromBackup::RestorerFromBackup(
|
|||||||
, context(context_)
|
, context(context_)
|
||||||
, process_list_element(context->getProcessListElement())
|
, process_list_element(context->getProcessListElement())
|
||||||
, after_task_callback(after_task_callback_)
|
, after_task_callback(after_task_callback_)
|
||||||
, on_cluster_first_sync_timeout(context->getConfigRef().getUInt64("backups.on_cluster_first_sync_timeout", 180000))
|
|
||||||
, create_table_timeout(context->getConfigRef().getUInt64("backups.create_table_timeout", 300000))
|
, create_table_timeout(context->getConfigRef().getUInt64("backups.create_table_timeout", 300000))
|
||||||
, log(getLogger("RestorerFromBackup"))
|
, log(getLogger("RestorerFromBackup"))
|
||||||
, tables_dependencies("RestorerFromBackup")
|
, tables_dependencies("RestorerFromBackup")
|
||||||
@ -119,12 +118,14 @@ RestorerFromBackup::~RestorerFromBackup()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RestorerFromBackup::run(Mode mode)
|
void RestorerFromBackup::run(Mode mode_)
|
||||||
{
|
{
|
||||||
/// run() can be called onle once.
|
/// run() can be called onle once.
|
||||||
if (!current_stage.empty())
|
if (!current_stage.empty())
|
||||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Already restoring");
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Already restoring");
|
||||||
|
|
||||||
|
mode = mode_;
|
||||||
|
|
||||||
/// Find other hosts working along with us to execute this ON CLUSTER query.
|
/// Find other hosts working along with us to execute this ON CLUSTER query.
|
||||||
all_hosts = BackupSettings::Util::filterHostIDs(
|
all_hosts = BackupSettings::Util::filterHostIDs(
|
||||||
restore_settings.cluster_host_ids, restore_settings.shard_num, restore_settings.replica_num);
|
restore_settings.cluster_host_ids, restore_settings.shard_num, restore_settings.replica_num);
|
||||||
@ -139,6 +140,7 @@ void RestorerFromBackup::run(Mode mode)
|
|||||||
setStage(Stage::FINDING_TABLES_IN_BACKUP);
|
setStage(Stage::FINDING_TABLES_IN_BACKUP);
|
||||||
findDatabasesAndTablesInBackup();
|
findDatabasesAndTablesInBackup();
|
||||||
waitFutures();
|
waitFutures();
|
||||||
|
logNumberOfDatabasesAndTablesToRestore();
|
||||||
|
|
||||||
/// Check access rights.
|
/// Check access rights.
|
||||||
setStage(Stage::CHECKING_ACCESS_RIGHTS);
|
setStage(Stage::CHECKING_ACCESS_RIGHTS);
|
||||||
@ -228,20 +230,8 @@ void RestorerFromBackup::setStage(const String & new_stage, const String & messa
|
|||||||
|
|
||||||
if (restore_coordination)
|
if (restore_coordination)
|
||||||
{
|
{
|
||||||
restore_coordination->setStage(new_stage, message);
|
/// There is no need to sync Stage::COMPLETED with other hosts because it's the last stage.
|
||||||
|
restore_coordination->setStage(new_stage, message, /* sync = */ (new_stage != Stage::COMPLETED));
|
||||||
/// The initiator of a RESTORE ON CLUSTER query waits for other hosts to complete their work (see waitForStage(Stage::COMPLETED) in BackupsWorker::doRestore),
|
|
||||||
/// but other hosts shouldn't wait for each others' completion. (That's simply unnecessary and also
|
|
||||||
/// the initiator may start cleaning up (e.g. removing restore-coordination ZooKeeper nodes) once all other hosts are in Stage::COMPLETED.)
|
|
||||||
bool need_wait = (new_stage != Stage::COMPLETED);
|
|
||||||
|
|
||||||
if (need_wait)
|
|
||||||
{
|
|
||||||
if (new_stage == Stage::FINDING_TABLES_IN_BACKUP)
|
|
||||||
restore_coordination->waitForStage(new_stage, on_cluster_first_sync_timeout);
|
|
||||||
else
|
|
||||||
restore_coordination->waitForStage(new_stage);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,8 +374,12 @@ void RestorerFromBackup::findDatabasesAndTablesInBackup()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LOG_INFO(log, "Will restore {} databases and {} tables", getNumDatabases(), getNumTables());
|
void RestorerFromBackup::logNumberOfDatabasesAndTablesToRestore() const
|
||||||
|
{
|
||||||
|
std::string_view action = (mode == CHECK_ACCESS_ONLY) ? "check access rights for restoring" : "restore";
|
||||||
|
LOG_INFO(log, "Will {} {} databases and {} tables", action, getNumDatabases(), getNumTables());
|
||||||
}
|
}
|
||||||
|
|
||||||
void RestorerFromBackup::findTableInBackup(const QualifiedTableName & table_name_in_backup, bool skip_if_inner_table, const std::optional<ASTs> & partitions)
|
void RestorerFromBackup::findTableInBackup(const QualifiedTableName & table_name_in_backup, bool skip_if_inner_table, const std::optional<ASTs> & partitions)
|
||||||
|
@ -53,7 +53,7 @@ public:
|
|||||||
using DataRestoreTasks = std::vector<DataRestoreTask>;
|
using DataRestoreTasks = std::vector<DataRestoreTask>;
|
||||||
|
|
||||||
/// Restores the metadata of databases and tables and returns tasks to restore the data of tables.
|
/// Restores the metadata of databases and tables and returns tasks to restore the data of tables.
|
||||||
void run(Mode mode);
|
void run(Mode mode_);
|
||||||
|
|
||||||
BackupPtr getBackup() const { return backup; }
|
BackupPtr getBackup() const { return backup; }
|
||||||
const RestoreSettings & getRestoreSettings() const { return restore_settings; }
|
const RestoreSettings & getRestoreSettings() const { return restore_settings; }
|
||||||
@ -80,10 +80,10 @@ private:
|
|||||||
ContextMutablePtr context;
|
ContextMutablePtr context;
|
||||||
QueryStatusPtr process_list_element;
|
QueryStatusPtr process_list_element;
|
||||||
std::function<void()> after_task_callback;
|
std::function<void()> after_task_callback;
|
||||||
std::chrono::milliseconds on_cluster_first_sync_timeout;
|
|
||||||
std::chrono::milliseconds create_table_timeout;
|
std::chrono::milliseconds create_table_timeout;
|
||||||
LoggerPtr log;
|
LoggerPtr log;
|
||||||
|
|
||||||
|
Mode mode = Mode::RESTORE;
|
||||||
Strings all_hosts;
|
Strings all_hosts;
|
||||||
DDLRenamingMap renaming_map;
|
DDLRenamingMap renaming_map;
|
||||||
std::vector<std::filesystem::path> root_paths_in_backup;
|
std::vector<std::filesystem::path> root_paths_in_backup;
|
||||||
@ -97,6 +97,7 @@ private:
|
|||||||
void findDatabaseInBackupImpl(const String & database_name_in_backup, const std::set<DatabaseAndTableName> & except_table_names);
|
void findDatabaseInBackupImpl(const String & database_name_in_backup, const std::set<DatabaseAndTableName> & except_table_names);
|
||||||
void findEverythingInBackup(const std::set<String> & except_database_names, const std::set<DatabaseAndTableName> & except_table_names);
|
void findEverythingInBackup(const std::set<String> & except_database_names, const std::set<DatabaseAndTableName> & except_table_names);
|
||||||
|
|
||||||
|
void logNumberOfDatabasesAndTablesToRestore() const;
|
||||||
size_t getNumDatabases() const;
|
size_t getNumDatabases() const;
|
||||||
size_t getNumTables() const;
|
size_t getNumTables() const;
|
||||||
|
|
||||||
|
@ -1,57 +1,34 @@
|
|||||||
#include <Backups/WithRetries.h>
|
#include <Backups/WithRetries.h>
|
||||||
#include <Core/Settings.h>
|
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
{
|
{
|
||||||
namespace Setting
|
|
||||||
{
|
|
||||||
extern const SettingsUInt64 backup_restore_keeper_max_retries;
|
|
||||||
extern const SettingsUInt64 backup_restore_keeper_retry_initial_backoff_ms;
|
|
||||||
extern const SettingsUInt64 backup_restore_keeper_retry_max_backoff_ms;
|
|
||||||
extern const SettingsUInt64 backup_restore_batch_size_for_keeper_multiread;
|
|
||||||
extern const SettingsFloat backup_restore_keeper_fault_injection_probability;
|
|
||||||
extern const SettingsUInt64 backup_restore_keeper_fault_injection_seed;
|
|
||||||
extern const SettingsUInt64 backup_restore_keeper_value_max_size;
|
|
||||||
extern const SettingsUInt64 backup_restore_batch_size_for_keeper_multi;
|
|
||||||
}
|
|
||||||
|
|
||||||
WithRetries::KeeperSettings WithRetries::KeeperSettings::fromContext(ContextPtr context)
|
|
||||||
{
|
|
||||||
return
|
|
||||||
{
|
|
||||||
.keeper_max_retries = context->getSettingsRef()[Setting::backup_restore_keeper_max_retries],
|
|
||||||
.keeper_retry_initial_backoff_ms = context->getSettingsRef()[Setting::backup_restore_keeper_retry_initial_backoff_ms],
|
|
||||||
.keeper_retry_max_backoff_ms = context->getSettingsRef()[Setting::backup_restore_keeper_retry_max_backoff_ms],
|
|
||||||
.batch_size_for_keeper_multiread = context->getSettingsRef()[Setting::backup_restore_batch_size_for_keeper_multiread],
|
|
||||||
.keeper_fault_injection_probability = context->getSettingsRef()[Setting::backup_restore_keeper_fault_injection_probability],
|
|
||||||
.keeper_fault_injection_seed = context->getSettingsRef()[Setting::backup_restore_keeper_fault_injection_seed],
|
|
||||||
.keeper_value_max_size = context->getSettingsRef()[Setting::backup_restore_keeper_value_max_size],
|
|
||||||
.batch_size_for_keeper_multi = context->getSettingsRef()[Setting::backup_restore_batch_size_for_keeper_multi],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
WithRetries::WithRetries(
|
WithRetries::WithRetries(
|
||||||
LoggerPtr log_, zkutil::GetZooKeeper get_zookeeper_, const KeeperSettings & settings_, QueryStatusPtr process_list_element_, RenewerCallback callback_)
|
LoggerPtr log_, zkutil::GetZooKeeper get_zookeeper_, const BackupKeeperSettings & settings_, QueryStatusPtr process_list_element_, RenewerCallback callback_)
|
||||||
: log(log_)
|
: log(log_)
|
||||||
, get_zookeeper(get_zookeeper_)
|
, get_zookeeper(get_zookeeper_)
|
||||||
, settings(settings_)
|
, settings(settings_)
|
||||||
, process_list_element(process_list_element_)
|
, process_list_element(process_list_element_)
|
||||||
, callback(callback_)
|
, callback(callback_)
|
||||||
, global_zookeeper_retries_info(
|
|
||||||
settings.keeper_max_retries, settings.keeper_retry_initial_backoff_ms, settings.keeper_retry_max_backoff_ms)
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
WithRetries::RetriesControlHolder::RetriesControlHolder(const WithRetries * parent, const String & name)
|
WithRetries::RetriesControlHolder::RetriesControlHolder(const WithRetries * parent, const String & name, Kind kind)
|
||||||
: info(parent->global_zookeeper_retries_info)
|
: info( (kind == kInitialization) ? parent->settings.max_retries_while_initializing
|
||||||
, retries_ctl(name, parent->log, info, parent->process_list_element)
|
: (kind == kErrorHandling) ? parent->settings.max_retries_while_handling_error
|
||||||
|
: parent->settings.max_retries,
|
||||||
|
parent->settings.retry_initial_backoff_ms.count(),
|
||||||
|
parent->settings.retry_max_backoff_ms.count())
|
||||||
|
/// We don't use process_list_element while handling an error because the error handling can't be cancellable.
|
||||||
|
, retries_ctl(name, parent->log, info, (kind == kErrorHandling) ? nullptr : parent->process_list_element)
|
||||||
, faulty_zookeeper(parent->getFaultyZooKeeper())
|
, faulty_zookeeper(parent->getFaultyZooKeeper())
|
||||||
{}
|
{}
|
||||||
|
|
||||||
WithRetries::RetriesControlHolder WithRetries::createRetriesControlHolder(const String & name)
|
WithRetries::RetriesControlHolder WithRetries::createRetriesControlHolder(const String & name, Kind kind) const
|
||||||
{
|
{
|
||||||
return RetriesControlHolder(this, name);
|
return RetriesControlHolder(this, name, kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WithRetries::renewZooKeeper(FaultyKeeper my_faulty_zookeeper) const
|
void WithRetries::renewZooKeeper(FaultyKeeper my_faulty_zookeeper) const
|
||||||
@ -62,8 +39,8 @@ void WithRetries::renewZooKeeper(FaultyKeeper my_faulty_zookeeper) const
|
|||||||
{
|
{
|
||||||
zookeeper = get_zookeeper();
|
zookeeper = get_zookeeper();
|
||||||
my_faulty_zookeeper->setKeeper(zookeeper);
|
my_faulty_zookeeper->setKeeper(zookeeper);
|
||||||
|
if (callback)
|
||||||
callback(my_faulty_zookeeper);
|
callback(my_faulty_zookeeper);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -71,7 +48,7 @@ void WithRetries::renewZooKeeper(FaultyKeeper my_faulty_zookeeper) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const WithRetries::KeeperSettings & WithRetries::getKeeperSettings() const
|
const BackupKeeperSettings & WithRetries::getKeeperSettings() const
|
||||||
{
|
{
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
@ -88,8 +65,8 @@ WithRetries::FaultyKeeper WithRetries::getFaultyZooKeeper() const
|
|||||||
/// The reason is that ZooKeeperWithFaultInjection may reset the underlying pointer and there could be a race condition
|
/// The reason is that ZooKeeperWithFaultInjection may reset the underlying pointer and there could be a race condition
|
||||||
/// when the same object is used from multiple threads.
|
/// when the same object is used from multiple threads.
|
||||||
auto faulty_zookeeper = ZooKeeperWithFaultInjection::createInstance(
|
auto faulty_zookeeper = ZooKeeperWithFaultInjection::createInstance(
|
||||||
settings.keeper_fault_injection_probability,
|
settings.fault_injection_probability,
|
||||||
settings.keeper_fault_injection_seed,
|
settings.fault_injection_seed,
|
||||||
current_zookeeper,
|
current_zookeeper,
|
||||||
log->name(),
|
log->name(),
|
||||||
log);
|
log);
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Common/ZooKeeper/ZooKeeperRetries.h>
|
#include <Backups/BackupKeeperSettings.h>
|
||||||
#include <Common/ZooKeeper/Common.h>
|
#include <Common/ZooKeeper/Common.h>
|
||||||
|
#include <Common/ZooKeeper/ZooKeeperRetries.h>
|
||||||
#include <Common/ZooKeeper/ZooKeeperWithFaultInjection.h>
|
#include <Common/ZooKeeper/ZooKeeperWithFaultInjection.h>
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -15,20 +17,13 @@ class WithRetries
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using FaultyKeeper = Coordination::ZooKeeperWithFaultInjection::Ptr;
|
using FaultyKeeper = Coordination::ZooKeeperWithFaultInjection::Ptr;
|
||||||
using RenewerCallback = std::function<void(FaultyKeeper &)>;
|
using RenewerCallback = std::function<void(FaultyKeeper)>;
|
||||||
|
|
||||||
struct KeeperSettings
|
enum Kind
|
||||||
{
|
{
|
||||||
UInt64 keeper_max_retries{0};
|
kNormal,
|
||||||
UInt64 keeper_retry_initial_backoff_ms{0};
|
kInitialization,
|
||||||
UInt64 keeper_retry_max_backoff_ms{0};
|
kErrorHandling,
|
||||||
UInt64 batch_size_for_keeper_multiread{10000};
|
|
||||||
Float64 keeper_fault_injection_probability{0};
|
|
||||||
UInt64 keeper_fault_injection_seed{42};
|
|
||||||
UInt64 keeper_value_max_size{1048576};
|
|
||||||
UInt64 batch_size_for_keeper_multi{1000};
|
|
||||||
|
|
||||||
static KeeperSettings fromContext(ContextPtr context);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// For simplicity a separate ZooKeeperRetriesInfo and a faulty [Zoo]Keeper client
|
/// For simplicity a separate ZooKeeperRetriesInfo and a faulty [Zoo]Keeper client
|
||||||
@ -48,23 +43,23 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
friend class WithRetries;
|
friend class WithRetries;
|
||||||
RetriesControlHolder(const WithRetries * parent, const String & name);
|
RetriesControlHolder(const WithRetries * parent, const String & name, Kind kind);
|
||||||
};
|
};
|
||||||
|
|
||||||
RetriesControlHolder createRetriesControlHolder(const String & name);
|
RetriesControlHolder createRetriesControlHolder(const String & name, Kind kind = Kind::kNormal) const;
|
||||||
WithRetries(LoggerPtr log, zkutil::GetZooKeeper get_zookeeper_, const KeeperSettings & settings, QueryStatusPtr process_list_element_, RenewerCallback callback);
|
WithRetries(LoggerPtr log, zkutil::GetZooKeeper get_zookeeper_, const BackupKeeperSettings & settings, QueryStatusPtr process_list_element_, RenewerCallback callback = {});
|
||||||
|
|
||||||
/// Used to re-establish new connection inside a retry loop.
|
/// Used to re-establish new connection inside a retry loop.
|
||||||
void renewZooKeeper(FaultyKeeper my_faulty_zookeeper) const;
|
void renewZooKeeper(FaultyKeeper my_faulty_zookeeper) const;
|
||||||
|
|
||||||
const KeeperSettings & getKeeperSettings() const;
|
const BackupKeeperSettings & getKeeperSettings() const;
|
||||||
private:
|
private:
|
||||||
/// This will provide a special wrapper which is useful for testing
|
/// This will provide a special wrapper which is useful for testing
|
||||||
FaultyKeeper getFaultyZooKeeper() const;
|
FaultyKeeper getFaultyZooKeeper() const;
|
||||||
|
|
||||||
LoggerPtr log;
|
LoggerPtr log;
|
||||||
zkutil::GetZooKeeper get_zookeeper;
|
zkutil::GetZooKeeper get_zookeeper;
|
||||||
KeeperSettings settings;
|
BackupKeeperSettings settings;
|
||||||
QueryStatusPtr process_list_element;
|
QueryStatusPtr process_list_element;
|
||||||
|
|
||||||
/// This callback is called each time when a new [Zoo]Keeper session is created.
|
/// This callback is called each time when a new [Zoo]Keeper session is created.
|
||||||
@ -76,7 +71,6 @@ private:
|
|||||||
/// it could lead just to a failed backup which could possibly be successful
|
/// it could lead just to a failed backup which could possibly be successful
|
||||||
/// if there were a little bit more retries.
|
/// if there were a little bit more retries.
|
||||||
RenewerCallback callback;
|
RenewerCallback callback;
|
||||||
ZooKeeperRetriesInfo global_zookeeper_retries_info;
|
|
||||||
|
|
||||||
/// This is needed only to protect zookeeper object
|
/// This is needed only to protect zookeeper object
|
||||||
mutable std::mutex zookeeper_mutex;
|
mutable std::mutex zookeeper_mutex;
|
||||||
|
@ -167,7 +167,8 @@ void ClientApplicationBase::init(int argc, char ** argv)
|
|||||||
("query_kind", po::value<std::string>()->default_value("initial_query"), "One of initial_query/secondary_query/no_query")
|
("query_kind", po::value<std::string>()->default_value("initial_query"), "One of initial_query/secondary_query/no_query")
|
||||||
("query_id", po::value<std::string>(), "query_id")
|
("query_id", po::value<std::string>(), "query_id")
|
||||||
|
|
||||||
("history_file", po::value<std::string>(), "path to history file")
|
("history_file", po::value<std::string>(), "Path to a file containing command history.")
|
||||||
|
("history_max_entries", po::value<UInt32>()->default_value(1000000), "Maximum number of entries in the history file.")
|
||||||
|
|
||||||
("stage", po::value<std::string>()->default_value("complete"), "Request query processing up to specified stage: complete,fetch_columns,with_mergeable_state,with_mergeable_state_after_aggregation,with_mergeable_state_after_aggregation_and_limit")
|
("stage", po::value<std::string>()->default_value("complete"), "Request query processing up to specified stage: complete,fetch_columns,with_mergeable_state,with_mergeable_state_after_aggregation,with_mergeable_state_after_aggregation_and_limit")
|
||||||
("progress", po::value<ProgressOption>()->implicit_value(ProgressOption::TTY, "tty")->default_value(ProgressOption::DEFAULT, "default"), "Print progress of queries execution - to TTY: tty|on|1|true|yes; to STDERR non-interactive mode: err; OFF: off|0|false|no; DEFAULT - interactive to TTY, non-interactive is off")
|
("progress", po::value<ProgressOption>()->implicit_value(ProgressOption::TTY, "tty")->default_value(ProgressOption::DEFAULT, "default"), "Print progress of queries execution - to TTY: tty|on|1|true|yes; to STDERR non-interactive mode: err; OFF: off|0|false|no; DEFAULT - interactive to TTY, non-interactive is off")
|
||||||
@ -350,6 +351,8 @@ void ClientApplicationBase::init(int argc, char ** argv)
|
|||||||
getClientConfiguration().setBool("highlight", options["highlight"].as<bool>());
|
getClientConfiguration().setBool("highlight", options["highlight"].as<bool>());
|
||||||
if (options.count("history_file"))
|
if (options.count("history_file"))
|
||||||
getClientConfiguration().setString("history_file", options["history_file"].as<std::string>());
|
getClientConfiguration().setString("history_file", options["history_file"].as<std::string>());
|
||||||
|
if (options.count("history_max_entries"))
|
||||||
|
getClientConfiguration().setUInt("history_max_entries", options["history_max_entries"].as<UInt32>());
|
||||||
if (options.count("interactive"))
|
if (options.count("interactive"))
|
||||||
getClientConfiguration().setBool("interactive", true);
|
getClientConfiguration().setBool("interactive", true);
|
||||||
if (options.count("pager"))
|
if (options.count("pager"))
|
||||||
|
@ -2665,6 +2665,8 @@ void ClientBase::runInteractive()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
history_max_entries = getClientConfiguration().getUInt("history_max_entries");
|
||||||
|
|
||||||
LineReader::Patterns query_extenders = {"\\"};
|
LineReader::Patterns query_extenders = {"\\"};
|
||||||
LineReader::Patterns query_delimiters = {";", "\\G", "\\G;"};
|
LineReader::Patterns query_delimiters = {";", "\\G", "\\G;"};
|
||||||
char word_break_characters[] = " \t\v\f\a\b\r\n`~!@#$%^&*()-=+[{]}\\|;:'\",<.>/?";
|
char word_break_characters[] = " \t\v\f\a\b\r\n`~!@#$%^&*()-=+[{]}\\|;:'\",<.>/?";
|
||||||
@ -2677,6 +2679,7 @@ void ClientBase::runInteractive()
|
|||||||
ReplxxLineReader lr(
|
ReplxxLineReader lr(
|
||||||
*suggest,
|
*suggest,
|
||||||
history_file,
|
history_file,
|
||||||
|
history_max_entries,
|
||||||
getClientConfiguration().has("multiline"),
|
getClientConfiguration().has("multiline"),
|
||||||
getClientConfiguration().getBool("ignore_shell_suspend", true),
|
getClientConfiguration().getBool("ignore_shell_suspend", true),
|
||||||
query_extenders,
|
query_extenders,
|
||||||
|
@ -328,6 +328,7 @@ protected:
|
|||||||
|
|
||||||
String home_path;
|
String home_path;
|
||||||
String history_file; /// Path to a file containing command history.
|
String history_file; /// Path to a file containing command history.
|
||||||
|
UInt32 history_max_entries; /// Maximum number of entries in the history file.
|
||||||
|
|
||||||
String current_profile;
|
String current_profile;
|
||||||
|
|
||||||
|
@ -293,6 +293,7 @@ void ReplxxLineReader::setLastIsDelimiter(bool flag)
|
|||||||
ReplxxLineReader::ReplxxLineReader(
|
ReplxxLineReader::ReplxxLineReader(
|
||||||
Suggest & suggest,
|
Suggest & suggest,
|
||||||
const String & history_file_path_,
|
const String & history_file_path_,
|
||||||
|
UInt32 history_max_entries_,
|
||||||
bool multiline_,
|
bool multiline_,
|
||||||
bool ignore_shell_suspend,
|
bool ignore_shell_suspend,
|
||||||
Patterns extenders_,
|
Patterns extenders_,
|
||||||
@ -313,6 +314,8 @@ ReplxxLineReader::ReplxxLineReader(
|
|||||||
{
|
{
|
||||||
using Replxx = replxx::Replxx;
|
using Replxx = replxx::Replxx;
|
||||||
|
|
||||||
|
rx.set_max_history_size(static_cast<int>(history_max_entries_));
|
||||||
|
|
||||||
if (!history_file_path.empty())
|
if (!history_file_path.empty())
|
||||||
{
|
{
|
||||||
history_file_fd = open(history_file_path.c_str(), O_RDWR);
|
history_file_fd = open(history_file_path.c_str(), O_RDWR);
|
||||||
|
@ -14,6 +14,7 @@ public:
|
|||||||
(
|
(
|
||||||
Suggest & suggest,
|
Suggest & suggest,
|
||||||
const String & history_file_path,
|
const String & history_file_path,
|
||||||
|
UInt32 history_max_entries,
|
||||||
bool multiline,
|
bool multiline,
|
||||||
bool ignore_shell_suspend,
|
bool ignore_shell_suspend,
|
||||||
Patterns extenders_,
|
Patterns extenders_,
|
||||||
|
@ -627,7 +627,7 @@ PreformattedMessage getExceptionMessageAndPattern(const Exception & e, bool with
|
|||||||
return PreformattedMessage{stream.str(), e.tryGetMessageFormatString(), e.getMessageFormatStringArgs()};
|
return PreformattedMessage{stream.str(), e.tryGetMessageFormatString(), e.getMessageFormatStringArgs()};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getExceptionMessage(std::exception_ptr e, bool with_stacktrace)
|
std::string getExceptionMessage(std::exception_ptr e, bool with_stacktrace, bool check_embedded_stacktrace)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -635,7 +635,7 @@ std::string getExceptionMessage(std::exception_ptr e, bool with_stacktrace)
|
|||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
return getCurrentExceptionMessage(with_stacktrace);
|
return getCurrentExceptionMessage(with_stacktrace, check_embedded_stacktrace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,7 +329,7 @@ void tryLogException(std::exception_ptr e, const AtomicLogger & logger, const st
|
|||||||
|
|
||||||
std::string getExceptionMessage(const Exception & e, bool with_stacktrace, bool check_embedded_stacktrace = false);
|
std::string getExceptionMessage(const Exception & e, bool with_stacktrace, bool check_embedded_stacktrace = false);
|
||||||
PreformattedMessage getExceptionMessageAndPattern(const Exception & e, bool with_stacktrace, bool check_embedded_stacktrace = false);
|
PreformattedMessage getExceptionMessageAndPattern(const Exception & e, bool with_stacktrace, bool check_embedded_stacktrace = false);
|
||||||
std::string getExceptionMessage(std::exception_ptr e, bool with_stacktrace);
|
std::string getExceptionMessage(std::exception_ptr e, bool with_stacktrace, bool check_embedded_stacktrace = false);
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -492,9 +492,9 @@ public:
|
|||||||
nodes.push_back(impl.semaphore);
|
nodes.push_back(impl.semaphore);
|
||||||
if (impl.branch.queue)
|
if (impl.branch.queue)
|
||||||
nodes.push_back(impl.branch.queue);
|
nodes.push_back(impl.branch.queue);
|
||||||
for (auto & [_, branch] : impl.branch.branch.branches)
|
for (auto & [_0, branch] : impl.branch.branch.branches)
|
||||||
{
|
{
|
||||||
for (auto & [_, child] : branch.children)
|
for (auto & [_1, child] : branch.children)
|
||||||
child->addRawPointerNodes(nodes);
|
child->addRawPointerNodes(nodes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,9 +48,9 @@ ASTPtr normalizeCreateWorkloadEntityQuery(const IAST & create_query)
|
|||||||
/// Returns a type of a workload entity `ptr`
|
/// Returns a type of a workload entity `ptr`
|
||||||
WorkloadEntityType getEntityType(const ASTPtr & ptr)
|
WorkloadEntityType getEntityType(const ASTPtr & ptr)
|
||||||
{
|
{
|
||||||
if (auto * res = typeid_cast<ASTCreateWorkloadQuery *>(ptr.get()))
|
if (auto * res = typeid_cast<ASTCreateWorkloadQuery *>(ptr.get()); res)
|
||||||
return WorkloadEntityType::Workload;
|
return WorkloadEntityType::Workload;
|
||||||
if (auto * res = typeid_cast<ASTCreateResourceQuery *>(ptr.get()))
|
if (auto * res = typeid_cast<ASTCreateResourceQuery *>(ptr.get()); res)
|
||||||
return WorkloadEntityType::Resource;
|
return WorkloadEntityType::Resource;
|
||||||
chassert(false);
|
chassert(false);
|
||||||
return WorkloadEntityType::MAX;
|
return WorkloadEntityType::MAX;
|
||||||
@ -106,7 +106,7 @@ void forEachReference(
|
|||||||
for (const String & resource : resources)
|
for (const String & resource : resources)
|
||||||
func(resource, res->getWorkloadName(), ReferenceType::ForResource);
|
func(resource, res->getWorkloadName(), ReferenceType::ForResource);
|
||||||
}
|
}
|
||||||
if (auto * res = typeid_cast<ASTCreateResourceQuery *>(source_entity.get()))
|
if (auto * res = typeid_cast<ASTCreateResourceQuery *>(source_entity.get()); res)
|
||||||
{
|
{
|
||||||
// RESOURCE has no references to be validated, we allow mentioned disks to be created later
|
// RESOURCE has no references to be validated, we allow mentioned disks to be created later
|
||||||
}
|
}
|
||||||
@ -578,15 +578,15 @@ void WorkloadEntityStorageBase::setAllEntities(const std::vector<std::pair<Strin
|
|||||||
if (!entityEquals(entity, it->second))
|
if (!entityEquals(entity, it->second))
|
||||||
{
|
{
|
||||||
changes.emplace_back(entity_name, entity, it->second); // Update entities that are present in both `new_entities` and `entities`
|
changes.emplace_back(entity_name, entity, it->second); // Update entities that are present in both `new_entities` and `entities`
|
||||||
LOG_TRACE(log, "Entity {} was updated", entity_name);
|
LOG_TRACE(log, "Workload entity {} was updated", entity_name);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
LOG_TRACE(log, "Entity {} is the same", entity_name);
|
LOG_TRACE(log, "Workload entity {} is the same", entity_name);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
changes.emplace_back(entity_name, entity, ASTPtr{}); // Remove entities that are not present in `new_entities`
|
changes.emplace_back(entity_name, entity, ASTPtr{}); // Remove entities that are not present in `new_entities`
|
||||||
LOG_TRACE(log, "Entity {} was dropped", entity_name);
|
LOG_TRACE(log, "Workload entity {} was dropped", entity_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const auto & [entity_name, entity] : new_entities)
|
for (const auto & [entity_name, entity] : new_entities)
|
||||||
@ -594,7 +594,7 @@ void WorkloadEntityStorageBase::setAllEntities(const std::vector<std::pair<Strin
|
|||||||
if (!entities.contains(entity_name))
|
if (!entities.contains(entity_name))
|
||||||
{
|
{
|
||||||
changes.emplace_back(entity_name, ASTPtr{}, entity); // Create entities that are only present in `new_entities`
|
changes.emplace_back(entity_name, ASTPtr{}, entity); // Create entities that are only present in `new_entities`
|
||||||
LOG_TRACE(log, "Entity {} was created", entity_name);
|
LOG_TRACE(log, "Workload entity {} was created", entity_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,6 +176,10 @@ void ZooKeeperArgs::initFromKeeperSection(const Poco::Util::AbstractConfiguratio
|
|||||||
{
|
{
|
||||||
connection_timeout_ms = config.getInt(config_name + "." + key);
|
connection_timeout_ms = config.getInt(config_name + "." + key);
|
||||||
}
|
}
|
||||||
|
else if (key == "num_connection_retries")
|
||||||
|
{
|
||||||
|
num_connection_retries = config.getInt(config_name + "." + key);
|
||||||
|
}
|
||||||
else if (key == "enable_fault_injections_during_startup")
|
else if (key == "enable_fault_injections_during_startup")
|
||||||
{
|
{
|
||||||
enable_fault_injections_during_startup = config.getBool(config_name + "." + key);
|
enable_fault_injections_during_startup = config.getBool(config_name + "." + key);
|
||||||
|
@ -39,6 +39,7 @@ struct ZooKeeperArgs
|
|||||||
String sessions_path = "/clickhouse/sessions";
|
String sessions_path = "/clickhouse/sessions";
|
||||||
String client_availability_zone;
|
String client_availability_zone;
|
||||||
int32_t connection_timeout_ms = Coordination::DEFAULT_CONNECTION_TIMEOUT_MS;
|
int32_t connection_timeout_ms = Coordination::DEFAULT_CONNECTION_TIMEOUT_MS;
|
||||||
|
UInt64 num_connection_retries = 2;
|
||||||
int32_t session_timeout_ms = Coordination::DEFAULT_SESSION_TIMEOUT_MS;
|
int32_t session_timeout_ms = Coordination::DEFAULT_SESSION_TIMEOUT_MS;
|
||||||
int32_t operation_timeout_ms = Coordination::DEFAULT_OPERATION_TIMEOUT_MS;
|
int32_t operation_timeout_ms = Coordination::DEFAULT_OPERATION_TIMEOUT_MS;
|
||||||
bool enable_fault_injections_during_startup = false;
|
bool enable_fault_injections_during_startup = false;
|
||||||
|
@ -440,7 +440,9 @@ void ZooKeeper::connect(
|
|||||||
if (nodes.empty())
|
if (nodes.empty())
|
||||||
throw Exception::fromMessage(Error::ZBADARGUMENTS, "No nodes passed to ZooKeeper constructor");
|
throw Exception::fromMessage(Error::ZBADARGUMENTS, "No nodes passed to ZooKeeper constructor");
|
||||||
|
|
||||||
static constexpr size_t num_tries = 3;
|
/// We always have at least one attempt to connect.
|
||||||
|
size_t num_tries = args.num_connection_retries + 1;
|
||||||
|
|
||||||
bool connected = false;
|
bool connected = false;
|
||||||
bool dns_error = false;
|
bool dns_error = false;
|
||||||
|
|
||||||
|
@ -15,14 +15,15 @@ namespace ErrorCodes
|
|||||||
|
|
||||||
struct ZooKeeperRetriesInfo
|
struct ZooKeeperRetriesInfo
|
||||||
{
|
{
|
||||||
|
ZooKeeperRetriesInfo() = default;
|
||||||
ZooKeeperRetriesInfo(UInt64 max_retries_, UInt64 initial_backoff_ms_, UInt64 max_backoff_ms_)
|
ZooKeeperRetriesInfo(UInt64 max_retries_, UInt64 initial_backoff_ms_, UInt64 max_backoff_ms_)
|
||||||
: max_retries(max_retries_), initial_backoff_ms(std::min(initial_backoff_ms_, max_backoff_ms_)), max_backoff_ms(max_backoff_ms_)
|
: max_retries(max_retries_), initial_backoff_ms(std::min(initial_backoff_ms_, max_backoff_ms_)), max_backoff_ms(max_backoff_ms_)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
UInt64 max_retries;
|
UInt64 max_retries = 0; /// "max_retries = 0" means only one attempt.
|
||||||
UInt64 initial_backoff_ms;
|
UInt64 initial_backoff_ms = 100;
|
||||||
UInt64 max_backoff_ms;
|
UInt64 max_backoff_ms = 5000;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ZooKeeperRetriesControl
|
class ZooKeeperRetriesControl
|
||||||
@ -220,6 +221,7 @@ private:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if the query was cancelled.
|
||||||
if (process_list_element)
|
if (process_list_element)
|
||||||
process_list_element->checkTimeLimit();
|
process_list_element->checkTimeLimit();
|
||||||
|
|
||||||
@ -228,6 +230,10 @@ private:
|
|||||||
sleepForMilliseconds(current_backoff_ms);
|
sleepForMilliseconds(current_backoff_ms);
|
||||||
current_backoff_ms = std::min(current_backoff_ms * 2, retries_info.max_backoff_ms);
|
current_backoff_ms = std::min(current_backoff_ms * 2, retries_info.max_backoff_ms);
|
||||||
|
|
||||||
|
/// Check if the query was cancelled again after sleeping.
|
||||||
|
if (process_list_element)
|
||||||
|
process_list_element->checkTimeLimit();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2665,29 +2665,44 @@ The maximum amount of data consumed by temporary files on disk in bytes for all
|
|||||||
The maximum amount of data consumed by temporary files on disk in bytes for all concurrently running queries. Zero means unlimited.
|
The maximum amount of data consumed by temporary files on disk in bytes for all concurrently running queries. Zero means unlimited.
|
||||||
)", 0)\
|
)", 0)\
|
||||||
\
|
\
|
||||||
DECLARE(UInt64, backup_restore_keeper_max_retries, 20, R"(
|
DECLARE(UInt64, backup_restore_keeper_max_retries, 1000, R"(
|
||||||
Max retries for keeper operations during backup or restore
|
Max retries for [Zoo]Keeper operations in the middle of a BACKUP or RESTORE operation.
|
||||||
|
Should be big enough so the whole operation won't fail because of a temporary [Zoo]Keeper failure.
|
||||||
)", 0) \
|
)", 0) \
|
||||||
DECLARE(UInt64, backup_restore_keeper_retry_initial_backoff_ms, 100, R"(
|
DECLARE(UInt64, backup_restore_keeper_retry_initial_backoff_ms, 100, R"(
|
||||||
Initial backoff timeout for [Zoo]Keeper operations during backup or restore
|
Initial backoff timeout for [Zoo]Keeper operations during backup or restore
|
||||||
)", 0) \
|
)", 0) \
|
||||||
DECLARE(UInt64, backup_restore_keeper_retry_max_backoff_ms, 5000, R"(
|
DECLARE(UInt64, backup_restore_keeper_retry_max_backoff_ms, 5000, R"(
|
||||||
Max backoff timeout for [Zoo]Keeper operations during backup or restore
|
Max backoff timeout for [Zoo]Keeper operations during backup or restore
|
||||||
|
)", 0) \
|
||||||
|
DECLARE(UInt64, backup_restore_failure_after_host_disconnected_for_seconds, 3600, R"(
|
||||||
|
If a host during a BACKUP ON CLUSTER or RESTORE ON CLUSTER operation doesn't recreate its ephemeral 'alive' node in ZooKeeper for this amount of time then the whole backup or restore is considered as failed.
|
||||||
|
This value should be bigger than any reasonable time for a host to reconnect to ZooKeeper after a failure.
|
||||||
|
Zero means unlimited.
|
||||||
|
)", 0) \
|
||||||
|
DECLARE(UInt64, backup_restore_keeper_max_retries_while_initializing, 20, R"(
|
||||||
|
Max retries for [Zoo]Keeper operations during the initialization of a BACKUP ON CLUSTER or RESTORE ON CLUSTER operation.
|
||||||
|
)", 0) \
|
||||||
|
DECLARE(UInt64, backup_restore_keeper_max_retries_while_handling_error, 20, R"(
|
||||||
|
Max retries for [Zoo]Keeper operations while handling an error of a BACKUP ON CLUSTER or RESTORE ON CLUSTER operation.
|
||||||
|
)", 0) \
|
||||||
|
DECLARE(UInt64, backup_restore_finish_timeout_after_error_sec, 180, R"(
|
||||||
|
How long the initiator should wait for other host to react to the 'error' node and stop their work on the current BACKUP ON CLUSTER or RESTORE ON CLUSTER operation.
|
||||||
|
)", 0) \
|
||||||
|
DECLARE(UInt64, backup_restore_keeper_value_max_size, 1048576, R"(
|
||||||
|
Maximum size of data of a [Zoo]Keeper's node during backup
|
||||||
|
)", 0) \
|
||||||
|
DECLARE(UInt64, backup_restore_batch_size_for_keeper_multi, 1000, R"(
|
||||||
|
Maximum size of batch for multi request to [Zoo]Keeper during backup or restore
|
||||||
|
)", 0) \
|
||||||
|
DECLARE(UInt64, backup_restore_batch_size_for_keeper_multiread, 10000, R"(
|
||||||
|
Maximum size of batch for multiread request to [Zoo]Keeper during backup or restore
|
||||||
)", 0) \
|
)", 0) \
|
||||||
DECLARE(Float, backup_restore_keeper_fault_injection_probability, 0.0f, R"(
|
DECLARE(Float, backup_restore_keeper_fault_injection_probability, 0.0f, R"(
|
||||||
Approximate probability of failure for a keeper request during backup or restore. Valid value is in interval [0.0f, 1.0f]
|
Approximate probability of failure for a keeper request during backup or restore. Valid value is in interval [0.0f, 1.0f]
|
||||||
)", 0) \
|
)", 0) \
|
||||||
DECLARE(UInt64, backup_restore_keeper_fault_injection_seed, 0, R"(
|
DECLARE(UInt64, backup_restore_keeper_fault_injection_seed, 0, R"(
|
||||||
0 - random seed, otherwise the setting value
|
0 - random seed, otherwise the setting value
|
||||||
)", 0) \
|
|
||||||
DECLARE(UInt64, backup_restore_keeper_value_max_size, 1048576, R"(
|
|
||||||
Maximum size of data of a [Zoo]Keeper's node during backup
|
|
||||||
)", 0) \
|
|
||||||
DECLARE(UInt64, backup_restore_batch_size_for_keeper_multiread, 10000, R"(
|
|
||||||
Maximum size of batch for multiread request to [Zoo]Keeper during backup or restore
|
|
||||||
)", 0) \
|
|
||||||
DECLARE(UInt64, backup_restore_batch_size_for_keeper_multi, 1000, R"(
|
|
||||||
Maximum size of batch for multi request to [Zoo]Keeper during backup or restore
|
|
||||||
)", 0) \
|
)", 0) \
|
||||||
DECLARE(UInt64, backup_restore_s3_retry_attempts, 1000, R"(
|
DECLARE(UInt64, backup_restore_s3_retry_attempts, 1000, R"(
|
||||||
Setting for Aws::Client::RetryStrategy, Aws::Client does retries itself, 0 means no retries. It takes place only for backup/restore.
|
Setting for Aws::Client::RetryStrategy, Aws::Client does retries itself, 0 means no retries. It takes place only for backup/restore.
|
||||||
|
@ -65,6 +65,12 @@ static std::initializer_list<std::pair<ClickHouseVersion, SettingsChangesHistory
|
|||||||
{"24.11",
|
{"24.11",
|
||||||
{
|
{
|
||||||
{"distributed_cache_discard_connection_if_unread_data", true, true, "New setting"},
|
{"distributed_cache_discard_connection_if_unread_data", true, true, "New setting"},
|
||||||
|
{"azure_check_objects_after_upload", false, false, "Check each uploaded object in azure blob storage to be sure that upload was successful"},
|
||||||
|
{"backup_restore_keeper_max_retries", 20, 1000, "Should be big enough so the whole operation BACKUP or RESTORE operation won't fail because of a temporary [Zoo]Keeper failure in the middle of it."},
|
||||||
|
{"backup_restore_failure_after_host_disconnected_for_seconds", 0, 3600, "New setting."},
|
||||||
|
{"backup_restore_keeper_max_retries_while_initializing", 0, 20, "New setting."},
|
||||||
|
{"backup_restore_keeper_max_retries_while_handling_error", 0, 20, "New setting."},
|
||||||
|
{"backup_restore_finish_timeout_after_error_sec", 0, 180, "New setting."},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{"24.10",
|
{"24.10",
|
||||||
@ -113,7 +119,6 @@ static std::initializer_list<std::pair<ClickHouseVersion, SettingsChangesHistory
|
|||||||
{"allow_reorder_prewhere_conditions", false, true, "New setting"},
|
{"allow_reorder_prewhere_conditions", false, true, "New setting"},
|
||||||
{"input_format_parquet_bloom_filter_push_down", false, true, "When reading Parquet files, skip whole row groups based on the WHERE/PREWHERE expressions and bloom filter in the Parquet metadata."},
|
{"input_format_parquet_bloom_filter_push_down", false, true, "When reading Parquet files, skip whole row groups based on the WHERE/PREWHERE expressions and bloom filter in the Parquet metadata."},
|
||||||
{"date_time_64_output_format_cut_trailing_zeros_align_to_groups_of_thousands", false, false, "Dynamically trim the trailing zeros of datetime64 values to adjust the output scale to (0, 3, 6), corresponding to 'seconds', 'milliseconds', and 'microseconds'."},
|
{"date_time_64_output_format_cut_trailing_zeros_align_to_groups_of_thousands", false, false, "Dynamically trim the trailing zeros of datetime64 values to adjust the output scale to (0, 3, 6), corresponding to 'seconds', 'milliseconds', and 'microseconds'."},
|
||||||
{"azure_check_objects_after_upload", false, false, "Check each uploaded object in azure blob storage to be sure that upload was successful"},
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{"24.9",
|
{"24.9",
|
||||||
|
@ -195,6 +195,15 @@ struct SortCursorHelper
|
|||||||
/// The last row of this cursor is no larger than the first row of the another cursor.
|
/// The last row of this cursor is no larger than the first row of the another cursor.
|
||||||
return !derived().greaterAt(rhs.derived(), impl->rows - 1, 0);
|
return !derived().greaterAt(rhs.derived(), impl->rows - 1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ALWAYS_INLINE totallyLess(const SortCursorHelper & rhs) const
|
||||||
|
{
|
||||||
|
if (impl->rows == 0 || rhs.impl->rows == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/// The last row of this cursor is less than the first row of the another cursor.
|
||||||
|
return rhs.derived().template greaterAt<false>(derived(), 0, impl->rows - 1);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -203,6 +212,7 @@ struct SortCursor : SortCursorHelper<SortCursor>
|
|||||||
using SortCursorHelper<SortCursor>::SortCursorHelper;
|
using SortCursorHelper<SortCursor>::SortCursorHelper;
|
||||||
|
|
||||||
/// The specified row of this cursor is greater than the specified row of another cursor.
|
/// The specified row of this cursor is greater than the specified row of another cursor.
|
||||||
|
template <bool consider_order = true>
|
||||||
bool ALWAYS_INLINE greaterAt(const SortCursor & rhs, size_t lhs_pos, size_t rhs_pos) const
|
bool ALWAYS_INLINE greaterAt(const SortCursor & rhs, size_t lhs_pos, size_t rhs_pos) const
|
||||||
{
|
{
|
||||||
#if USE_EMBEDDED_COMPILER
|
#if USE_EMBEDDED_COMPILER
|
||||||
@ -218,7 +228,10 @@ struct SortCursor : SortCursorHelper<SortCursor>
|
|||||||
if (res < 0)
|
if (res < 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return impl->order > rhs.impl->order;
|
if constexpr (consider_order)
|
||||||
|
return impl->order > rhs.impl->order;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -235,7 +248,10 @@ struct SortCursor : SortCursorHelper<SortCursor>
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return impl->order > rhs.impl->order;
|
if constexpr (consider_order)
|
||||||
|
return impl->order > rhs.impl->order;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -245,6 +261,7 @@ struct SimpleSortCursor : SortCursorHelper<SimpleSortCursor>
|
|||||||
{
|
{
|
||||||
using SortCursorHelper<SimpleSortCursor>::SortCursorHelper;
|
using SortCursorHelper<SimpleSortCursor>::SortCursorHelper;
|
||||||
|
|
||||||
|
template <bool consider_order = true>
|
||||||
bool ALWAYS_INLINE greaterAt(const SimpleSortCursor & rhs, size_t lhs_pos, size_t rhs_pos) const
|
bool ALWAYS_INLINE greaterAt(const SimpleSortCursor & rhs, size_t lhs_pos, size_t rhs_pos) const
|
||||||
{
|
{
|
||||||
int res = 0;
|
int res = 0;
|
||||||
@ -271,7 +288,10 @@ struct SimpleSortCursor : SortCursorHelper<SimpleSortCursor>
|
|||||||
if (res < 0)
|
if (res < 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return impl->order > rhs.impl->order;
|
if constexpr (consider_order)
|
||||||
|
return impl->order > rhs.impl->order;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -280,6 +300,7 @@ struct SpecializedSingleColumnSortCursor : SortCursorHelper<SpecializedSingleCol
|
|||||||
{
|
{
|
||||||
using SortCursorHelper<SpecializedSingleColumnSortCursor>::SortCursorHelper;
|
using SortCursorHelper<SpecializedSingleColumnSortCursor>::SortCursorHelper;
|
||||||
|
|
||||||
|
template <bool consider_order = true>
|
||||||
bool ALWAYS_INLINE greaterAt(const SortCursorHelper<SpecializedSingleColumnSortCursor> & rhs, size_t lhs_pos, size_t rhs_pos) const
|
bool ALWAYS_INLINE greaterAt(const SortCursorHelper<SpecializedSingleColumnSortCursor> & rhs, size_t lhs_pos, size_t rhs_pos) const
|
||||||
{
|
{
|
||||||
auto & this_impl = this->impl;
|
auto & this_impl = this->impl;
|
||||||
@ -302,7 +323,10 @@ struct SpecializedSingleColumnSortCursor : SortCursorHelper<SpecializedSingleCol
|
|||||||
if (res < 0)
|
if (res < 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return this_impl->order > rhs.impl->order;
|
if constexpr (consider_order)
|
||||||
|
return this_impl->order > rhs.impl->order;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -311,6 +335,7 @@ struct SortCursorWithCollation : SortCursorHelper<SortCursorWithCollation>
|
|||||||
{
|
{
|
||||||
using SortCursorHelper<SortCursorWithCollation>::SortCursorHelper;
|
using SortCursorHelper<SortCursorWithCollation>::SortCursorHelper;
|
||||||
|
|
||||||
|
template <bool consider_order = true>
|
||||||
bool ALWAYS_INLINE greaterAt(const SortCursorWithCollation & rhs, size_t lhs_pos, size_t rhs_pos) const
|
bool ALWAYS_INLINE greaterAt(const SortCursorWithCollation & rhs, size_t lhs_pos, size_t rhs_pos) const
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < impl->sort_columns_size; ++i)
|
for (size_t i = 0; i < impl->sort_columns_size; ++i)
|
||||||
@ -330,7 +355,10 @@ struct SortCursorWithCollation : SortCursorHelper<SortCursorWithCollation>
|
|||||||
if (res < 0)
|
if (res < 0)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return impl->order > rhs.impl->order;
|
if constexpr (consider_order)
|
||||||
|
return impl->order > rhs.impl->order;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -199,13 +199,12 @@ void DatabaseReplicatedDDLWorker::initializeReplication()
|
|||||||
active_node_holder = zkutil::EphemeralNodeHolder::existing(active_path, *active_node_holder_zookeeper);
|
active_node_holder = zkutil::EphemeralNodeHolder::existing(active_path, *active_node_holder_zookeeper);
|
||||||
}
|
}
|
||||||
|
|
||||||
String DatabaseReplicatedDDLWorker::enqueueQuery(DDLLogEntry & entry)
|
String DatabaseReplicatedDDLWorker::enqueueQuery(DDLLogEntry & entry, const ZooKeeperRetriesInfo &, QueryStatusPtr)
|
||||||
{
|
{
|
||||||
auto zookeeper = getAndSetZooKeeper();
|
auto zookeeper = getAndSetZooKeeper();
|
||||||
return enqueueQueryImpl(zookeeper, entry, database);
|
return enqueueQueryImpl(zookeeper, entry, database);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool DatabaseReplicatedDDLWorker::waitForReplicaToProcessAllEntries(UInt64 timeout_ms)
|
bool DatabaseReplicatedDDLWorker::waitForReplicaToProcessAllEntries(UInt64 timeout_ms)
|
||||||
{
|
{
|
||||||
auto zookeeper = getAndSetZooKeeper();
|
auto zookeeper = getAndSetZooKeeper();
|
||||||
|
@ -24,7 +24,7 @@ class DatabaseReplicatedDDLWorker : public DDLWorker
|
|||||||
public:
|
public:
|
||||||
DatabaseReplicatedDDLWorker(DatabaseReplicated * db, ContextPtr context_);
|
DatabaseReplicatedDDLWorker(DatabaseReplicated * db, ContextPtr context_);
|
||||||
|
|
||||||
String enqueueQuery(DDLLogEntry & entry) override;
|
String enqueueQuery(DDLLogEntry & entry, const ZooKeeperRetriesInfo &, QueryStatusPtr) override;
|
||||||
|
|
||||||
String tryEnqueueAndExecuteEntry(DDLLogEntry & entry, ContextPtr query_context);
|
String tryEnqueueAndExecuteEntry(DDLLogEntry & entry, ContextPtr query_context);
|
||||||
|
|
||||||
|
@ -89,11 +89,40 @@ struct HasSystemTablesMatcher
|
|||||||
{
|
{
|
||||||
database_table = identifier->name();
|
database_table = identifier->name();
|
||||||
}
|
}
|
||||||
/// Handle SELECT [...] FROM clusterAllReplicas(<cluster>, '<table>')
|
/// SELECT [...] FROM clusterAllReplicas(<cluster>, '<table>')
|
||||||
else if (const auto * literal = node->as<ASTLiteral>())
|
/// This SQL syntax is quite common but we need to be careful. A naive attempt to cast 'node' to an ASTLiteral will be too general
|
||||||
|
/// and introduce false positives in queries like
|
||||||
|
/// 'SELECT * FROM users WHERE name = 'system.metrics' SETTINGS use_query_cache = true;'
|
||||||
|
/// Therefore, make sure we are really in `clusterAllReplicas`. EXPLAIN AST for
|
||||||
|
/// 'SELECT * FROM clusterAllReplicas('default', system.one) SETTINGS use_query_cache = 1'
|
||||||
|
/// returns:
|
||||||
|
/// [...]
|
||||||
|
/// Function clusterAllReplicas (children 1)
|
||||||
|
/// ExpressionList (children 2)
|
||||||
|
/// Literal 'test_shard_localhost'
|
||||||
|
/// Literal 'system.one'
|
||||||
|
/// [...]
|
||||||
|
else if (const auto * function = node->as<ASTFunction>())
|
||||||
{
|
{
|
||||||
const auto & value = literal->value;
|
if (function->name == "clusterAllReplicas")
|
||||||
database_table = toString(value);
|
{
|
||||||
|
const ASTs & function_children = function->children;
|
||||||
|
if (!function_children.empty())
|
||||||
|
{
|
||||||
|
if (const auto * expression_list = function_children[0]->as<ASTExpressionList>())
|
||||||
|
{
|
||||||
|
const ASTs & expression_list_children = expression_list->children;
|
||||||
|
if (expression_list_children.size() >= 2)
|
||||||
|
{
|
||||||
|
if (const auto * literal = expression_list_children[1]->as<ASTLiteral>())
|
||||||
|
{
|
||||||
|
const auto & value = literal->value;
|
||||||
|
database_table = toString(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Tokens tokens(database_table.c_str(), database_table.c_str() + database_table.size(), /*max_query_size*/ 2048, /*skip_insignificant*/ true);
|
Tokens tokens(database_table.c_str(), database_table.c_str() + database_table.size(), /*max_query_size*/ 2048, /*skip_insignificant*/ true);
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include <Common/ZooKeeper/KeeperException.h>
|
#include <Common/ZooKeeper/KeeperException.h>
|
||||||
#include <Common/ZooKeeper/ZooKeeper.h>
|
#include <Common/ZooKeeper/ZooKeeper.h>
|
||||||
#include <Common/ZooKeeper/ZooKeeperLock.h>
|
#include <Common/ZooKeeper/ZooKeeperLock.h>
|
||||||
|
#include <Common/ZooKeeper/ZooKeeperRetries.h>
|
||||||
#include <Common/isLocalAddress.h>
|
#include <Common/isLocalAddress.h>
|
||||||
#include <Common/logger_useful.h>
|
#include <Common/logger_useful.h>
|
||||||
#include <Common/randomSeed.h>
|
#include <Common/randomSeed.h>
|
||||||
@ -1053,7 +1054,25 @@ void DDLWorker::createStatusDirs(const std::string & node_path, const ZooKeeperP
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
String DDLWorker::enqueueQuery(DDLLogEntry & entry)
|
String DDLWorker::enqueueQuery(DDLLogEntry & entry, const ZooKeeperRetriesInfo & retries_info, QueryStatusPtr process_list_element)
|
||||||
|
{
|
||||||
|
String node_path;
|
||||||
|
if (retries_info.max_retries > 0)
|
||||||
|
{
|
||||||
|
ZooKeeperRetriesControl retries_ctl{"DDLWorker::enqueueQuery", log, retries_info, process_list_element};
|
||||||
|
retries_ctl.retryLoop([&]{
|
||||||
|
node_path = enqueueQueryAttempt(entry);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
node_path = enqueueQueryAttempt(entry);
|
||||||
|
}
|
||||||
|
return node_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String DDLWorker::enqueueQueryAttempt(DDLLogEntry & entry)
|
||||||
{
|
{
|
||||||
if (entry.hosts.empty())
|
if (entry.hosts.empty())
|
||||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Empty host list in a distributed DDL task");
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Empty host list in a distributed DDL task");
|
||||||
|
@ -48,6 +48,9 @@ struct DDLTaskBase;
|
|||||||
using DDLTaskPtr = std::unique_ptr<DDLTaskBase>;
|
using DDLTaskPtr = std::unique_ptr<DDLTaskBase>;
|
||||||
using ZooKeeperPtr = std::shared_ptr<zkutil::ZooKeeper>;
|
using ZooKeeperPtr = std::shared_ptr<zkutil::ZooKeeper>;
|
||||||
class AccessRightsElements;
|
class AccessRightsElements;
|
||||||
|
struct ZooKeeperRetriesInfo;
|
||||||
|
class QueryStatus;
|
||||||
|
using QueryStatusPtr = std::shared_ptr<QueryStatus>;
|
||||||
|
|
||||||
class DDLWorker
|
class DDLWorker
|
||||||
{
|
{
|
||||||
@ -65,7 +68,7 @@ public:
|
|||||||
virtual ~DDLWorker();
|
virtual ~DDLWorker();
|
||||||
|
|
||||||
/// Pushes query into DDL queue, returns path to created node
|
/// Pushes query into DDL queue, returns path to created node
|
||||||
virtual String enqueueQuery(DDLLogEntry & entry);
|
virtual String enqueueQuery(DDLLogEntry & entry, const ZooKeeperRetriesInfo & retries_info, QueryStatusPtr process_list_element);
|
||||||
|
|
||||||
/// Host ID (name:port) for logging purposes
|
/// Host ID (name:port) for logging purposes
|
||||||
/// Note that in each task hosts are identified individually by name:port from initiator server cluster config
|
/// Note that in each task hosts are identified individually by name:port from initiator server cluster config
|
||||||
@ -120,6 +123,9 @@ protected:
|
|||||||
mutable std::shared_mutex mtx;
|
mutable std::shared_mutex mtx;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Pushes query into DDL queue, returns path to created node
|
||||||
|
String enqueueQueryAttempt(DDLLogEntry & entry);
|
||||||
|
|
||||||
/// Iterates through queue tasks in ZooKeeper, runs execution of new tasks
|
/// Iterates through queue tasks in ZooKeeper, runs execution of new tasks
|
||||||
void scheduleTasks(bool reinitialized);
|
void scheduleTasks(bool reinitialized);
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
#include <Interpreters/InterpreterBackupQuery.h>
|
#include <Interpreters/InterpreterBackupQuery.h>
|
||||||
|
|
||||||
#include <Backups/BackupsWorker.h>
|
#include <Backups/BackupsWorker.h>
|
||||||
|
#include <Backups/BackupSettings.h>
|
||||||
|
#include <Parsers/ASTBackupQuery.h>
|
||||||
#include <Columns/ColumnString.h>
|
#include <Columns/ColumnString.h>
|
||||||
#include <Columns/ColumnsNumber.h>
|
#include <Columns/ColumnsNumber.h>
|
||||||
#include <DataTypes/DataTypeEnum.h>
|
#include <DataTypes/DataTypeEnum.h>
|
||||||
@ -18,13 +20,13 @@ namespace DB
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
Block getResultRow(const BackupOperationInfo & info)
|
Block getResultRow(const String & id, BackupStatus status)
|
||||||
{
|
{
|
||||||
auto column_id = ColumnString::create();
|
auto column_id = ColumnString::create();
|
||||||
auto column_status = ColumnInt8::create();
|
auto column_status = ColumnInt8::create();
|
||||||
|
|
||||||
column_id->insert(info.id);
|
column_id->insert(id);
|
||||||
column_status->insert(static_cast<Int8>(info.status));
|
column_status->insert(static_cast<Int8>(status));
|
||||||
|
|
||||||
Block res_columns;
|
Block res_columns;
|
||||||
res_columns.insert(0, {std::move(column_id), std::make_shared<DataTypeString>(), "id"});
|
res_columns.insert(0, {std::move(column_id), std::make_shared<DataTypeString>(), "id"});
|
||||||
@ -36,15 +38,18 @@ namespace
|
|||||||
|
|
||||||
BlockIO InterpreterBackupQuery::execute()
|
BlockIO InterpreterBackupQuery::execute()
|
||||||
{
|
{
|
||||||
|
const ASTBackupQuery & backup_query = query_ptr->as<const ASTBackupQuery &>();
|
||||||
auto & backups_worker = context->getBackupsWorker();
|
auto & backups_worker = context->getBackupsWorker();
|
||||||
auto id = backups_worker.start(query_ptr, context);
|
|
||||||
|
|
||||||
auto info = backups_worker.getInfo(id);
|
auto [id, status] = backups_worker.start(query_ptr, context);
|
||||||
if (info.exception)
|
|
||||||
std::rethrow_exception(info.exception);
|
/// Wait if it's a synchronous operation.
|
||||||
|
bool async = BackupSettings::isAsync(backup_query);
|
||||||
|
if (!async)
|
||||||
|
status = backups_worker.wait(id);
|
||||||
|
|
||||||
BlockIO res_io;
|
BlockIO res_io;
|
||||||
res_io.pipeline = QueryPipeline(std::make_shared<SourceFromSingleChunk>(getResultRow(info)));
|
res_io.pipeline = QueryPipeline(std::make_shared<SourceFromSingleChunk>(getResultRow(id, status)));
|
||||||
return res_io;
|
return res_io;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,12 +447,16 @@ void QueryStatus::ExecutorHolder::remove()
|
|||||||
executor = nullptr;
|
executor = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
CancellationCode QueryStatus::cancelQuery(bool)
|
CancellationCode QueryStatus::cancelQuery(bool /* kill */, std::exception_ptr exception)
|
||||||
{
|
{
|
||||||
if (is_killed.load())
|
if (is_killed.exchange(true))
|
||||||
return CancellationCode::CancelSent;
|
return CancellationCode::CancelSent;
|
||||||
|
|
||||||
is_killed.store(true);
|
{
|
||||||
|
std::lock_guard lock{cancellation_exception_mutex};
|
||||||
|
if (!cancellation_exception)
|
||||||
|
cancellation_exception = exception;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<ExecutorHolderPtr> executors_snapshot;
|
std::vector<ExecutorHolderPtr> executors_snapshot;
|
||||||
|
|
||||||
@ -486,7 +490,7 @@ void QueryStatus::addPipelineExecutor(PipelineExecutor * e)
|
|||||||
/// addPipelineExecutor() from the cancelQuery() context, and this will
|
/// addPipelineExecutor() from the cancelQuery() context, and this will
|
||||||
/// lead to deadlock.
|
/// lead to deadlock.
|
||||||
if (is_killed.load())
|
if (is_killed.load())
|
||||||
throw Exception(ErrorCodes::QUERY_WAS_CANCELLED, "Query was cancelled");
|
throwQueryWasCancelled();
|
||||||
|
|
||||||
std::lock_guard lock(executors_mutex);
|
std::lock_guard lock(executors_mutex);
|
||||||
assert(!executors.contains(e));
|
assert(!executors.contains(e));
|
||||||
@ -512,11 +516,20 @@ void QueryStatus::removePipelineExecutor(PipelineExecutor * e)
|
|||||||
bool QueryStatus::checkTimeLimit()
|
bool QueryStatus::checkTimeLimit()
|
||||||
{
|
{
|
||||||
if (is_killed.load())
|
if (is_killed.load())
|
||||||
throw Exception(ErrorCodes::QUERY_WAS_CANCELLED, "Query was cancelled");
|
throwQueryWasCancelled();
|
||||||
|
|
||||||
return limits.checkTimeLimit(watch, overflow_mode);
|
return limits.checkTimeLimit(watch, overflow_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QueryStatus::throwQueryWasCancelled() const
|
||||||
|
{
|
||||||
|
std::lock_guard lock{cancellation_exception_mutex};
|
||||||
|
if (cancellation_exception)
|
||||||
|
std::rethrow_exception(cancellation_exception);
|
||||||
|
else
|
||||||
|
throw Exception(ErrorCodes::QUERY_WAS_CANCELLED, "Query was cancelled");
|
||||||
|
}
|
||||||
|
|
||||||
bool QueryStatus::checkTimeLimitSoft()
|
bool QueryStatus::checkTimeLimitSoft()
|
||||||
{
|
{
|
||||||
if (is_killed.load())
|
if (is_killed.load())
|
||||||
|
@ -109,6 +109,9 @@ protected:
|
|||||||
/// KILL was send to the query
|
/// KILL was send to the query
|
||||||
std::atomic<bool> is_killed { false };
|
std::atomic<bool> is_killed { false };
|
||||||
|
|
||||||
|
std::exception_ptr cancellation_exception TSA_GUARDED_BY(cancellation_exception_mutex);
|
||||||
|
mutable std::mutex cancellation_exception_mutex;
|
||||||
|
|
||||||
/// All data to the client already had been sent.
|
/// All data to the client already had been sent.
|
||||||
/// Including EndOfStream or Exception.
|
/// Including EndOfStream or Exception.
|
||||||
std::atomic<bool> is_all_data_sent { false };
|
std::atomic<bool> is_all_data_sent { false };
|
||||||
@ -127,6 +130,8 @@ protected:
|
|||||||
/// A weak pointer is used here because it's a ProcessListEntry which owns this QueryStatus, and not vice versa.
|
/// A weak pointer is used here because it's a ProcessListEntry which owns this QueryStatus, and not vice versa.
|
||||||
void setProcessListEntry(std::weak_ptr<ProcessListEntry> process_list_entry_);
|
void setProcessListEntry(std::weak_ptr<ProcessListEntry> process_list_entry_);
|
||||||
|
|
||||||
|
[[noreturn]] void throwQueryWasCancelled() const;
|
||||||
|
|
||||||
mutable std::mutex executors_mutex;
|
mutable std::mutex executors_mutex;
|
||||||
|
|
||||||
struct ExecutorHolder
|
struct ExecutorHolder
|
||||||
@ -225,7 +230,9 @@ public:
|
|||||||
|
|
||||||
QueryStatusInfo getInfo(bool get_thread_list = false, bool get_profile_events = false, bool get_settings = false) const;
|
QueryStatusInfo getInfo(bool get_thread_list = false, bool get_profile_events = false, bool get_settings = false) const;
|
||||||
|
|
||||||
CancellationCode cancelQuery(bool kill);
|
/// Cancels the current query.
|
||||||
|
/// Optional argument `exception` allows to set an exception which checkTimeLimit() will throw instead of "QUERY_WAS_CANCELLED".
|
||||||
|
CancellationCode cancelQuery(bool kill, std::exception_ptr exception = nullptr);
|
||||||
|
|
||||||
bool isKilled() const { return is_killed; }
|
bool isKilled() const { return is_killed; }
|
||||||
|
|
||||||
|
@ -189,7 +189,7 @@ BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr_, ContextPtr context,
|
|||||||
entry.setSettingsIfRequired(context);
|
entry.setSettingsIfRequired(context);
|
||||||
entry.tracing_context = OpenTelemetry::CurrentContext();
|
entry.tracing_context = OpenTelemetry::CurrentContext();
|
||||||
entry.initial_query_id = context->getClientInfo().initial_query_id;
|
entry.initial_query_id = context->getClientInfo().initial_query_id;
|
||||||
String node_path = ddl_worker.enqueueQuery(entry);
|
String node_path = ddl_worker.enqueueQuery(entry, params.retries_info, context->getProcessListElement());
|
||||||
|
|
||||||
return getDDLOnClusterStatus(node_path, ddl_worker.getReplicasDir(), entry, context);
|
return getDDLOnClusterStatus(node_path, ddl_worker.getReplicasDir(), entry, context);
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,9 @@ struct DDLQueryOnClusterParams
|
|||||||
|
|
||||||
/// Privileges which the current user should have to execute a query.
|
/// Privileges which the current user should have to execute a query.
|
||||||
AccessRightsElements access_to_check;
|
AccessRightsElements access_to_check;
|
||||||
|
|
||||||
|
/// Use retries when creating nodes "query-0000000000", "query-0000000001", "query-0000000002" in ZooKeeper.
|
||||||
|
ZooKeeperRetriesInfo retries_info;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Pushes distributed DDL query to the queue.
|
/// Pushes distributed DDL query to the queue.
|
||||||
|
@ -155,6 +155,9 @@ namespace
|
|||||||
|
|
||||||
for (auto & [access_flags, columns] : access_and_columns)
|
for (auto & [access_flags, columns] : access_and_columns)
|
||||||
{
|
{
|
||||||
|
if (wildcard && !columns.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
AccessRightsElement element;
|
AccessRightsElement element;
|
||||||
element.access_flags = access_flags;
|
element.access_flags = access_flags;
|
||||||
element.columns = std::move(columns);
|
element.columns = std::move(columns);
|
||||||
|
@ -46,11 +46,28 @@ ReplacingSortedAlgorithm::ReplacingSortedAlgorithm(
|
|||||||
{
|
{
|
||||||
if (!is_deleted_column.empty())
|
if (!is_deleted_column.empty())
|
||||||
is_deleted_column_number = header_.getPositionByName(is_deleted_column);
|
is_deleted_column_number = header_.getPositionByName(is_deleted_column);
|
||||||
|
|
||||||
if (!version_column.empty())
|
if (!version_column.empty())
|
||||||
version_column_number = header_.getPositionByName(version_column);
|
version_column_number = header_.getPositionByName(version_column);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReplacingSortedAlgorithm::insertRow()
|
void ReplacingSortedAlgorithm::insertRow()
|
||||||
|
{
|
||||||
|
if (is_deleted_column_number != -1)
|
||||||
|
{
|
||||||
|
if (!(cleanup && assert_cast<const ColumnUInt8 &>(*(*selected_row.all_columns)[is_deleted_column_number]).getData()[selected_row.row_num]))
|
||||||
|
insertRowImpl();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
insertRowImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// insertRowImpl() may has not been called
|
||||||
|
saveChunkForSkippingFinalFromSelectedRow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReplacingSortedAlgorithm::insertRowImpl()
|
||||||
{
|
{
|
||||||
if (out_row_sources_buf)
|
if (out_row_sources_buf)
|
||||||
{
|
{
|
||||||
@ -67,6 +84,7 @@ void ReplacingSortedAlgorithm::insertRow()
|
|||||||
/// We just record the position to be selected in the chunk
|
/// We just record the position to be selected in the chunk
|
||||||
if (!selected_row.owned_chunk->replace_final_selection)
|
if (!selected_row.owned_chunk->replace_final_selection)
|
||||||
selected_row.owned_chunk->replace_final_selection = ColumnUInt64::create();
|
selected_row.owned_chunk->replace_final_selection = ColumnUInt64::create();
|
||||||
|
|
||||||
selected_row.owned_chunk->replace_final_selection->insert(selected_row.row_num);
|
selected_row.owned_chunk->replace_final_selection->insert(selected_row.row_num);
|
||||||
|
|
||||||
/// This is the last row we can select from `selected_row.owned_chunk`, keep it to emit later
|
/// This is the last row we can select from `selected_row.owned_chunk`, keep it to emit later
|
||||||
@ -74,7 +92,9 @@ void ReplacingSortedAlgorithm::insertRow()
|
|||||||
to_be_emitted.push(std::move(selected_row.owned_chunk));
|
to_be_emitted.push(std::move(selected_row.owned_chunk));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
merged_data->insertRow(*selected_row.all_columns, selected_row.row_num, selected_row.owned_chunk->getNumRows());
|
merged_data->insertRow(*selected_row.all_columns, selected_row.row_num, selected_row.owned_chunk->getNumRows());
|
||||||
|
}
|
||||||
|
|
||||||
selected_row.clear();
|
selected_row.clear();
|
||||||
}
|
}
|
||||||
@ -113,30 +133,68 @@ IMergingAlgorithm::Status ReplacingSortedAlgorithm::merge()
|
|||||||
|
|
||||||
/// Write the data for the previous primary key.
|
/// Write the data for the previous primary key.
|
||||||
if (!selected_row.empty())
|
if (!selected_row.empty())
|
||||||
{
|
insertRow();
|
||||||
if (is_deleted_column_number!=-1)
|
|
||||||
{
|
|
||||||
if (!(cleanup && assert_cast<const ColumnUInt8 &>(*(*selected_row.all_columns)[is_deleted_column_number]).getData()[selected_row.row_num]))
|
|
||||||
insertRow();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
insertRow();
|
|
||||||
/// insertRow() may has not been called
|
|
||||||
saveChunkForSkippingFinalFromSelectedRow();
|
|
||||||
}
|
|
||||||
|
|
||||||
selected_row.clear();
|
selected_row.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (current->isFirst()
|
||||||
|
&& key_differs
|
||||||
|
&& is_deleted_column_number == -1 /// Ignore optimization if we need to filter deleted rows.
|
||||||
|
&& sources_origin_merge_tree_part_level[current->order] > 0
|
||||||
|
&& !skipLastRowFor(current->order) /// Ignore optimization if last row should be skipped.
|
||||||
|
&& (queue.size() == 1 || (queue.size() >= 2 && current.totallyLess(queue.nextChild()))))
|
||||||
|
{
|
||||||
|
/// This is special optimization if current cursor is totally less than next cursor
|
||||||
|
/// and current chunk has no duplicates (we assume that parts with non-zero level have no duplicates)
|
||||||
|
/// We want to insert current cursor chunk directly in merged data.
|
||||||
|
|
||||||
|
/// First if merged_data is not empty we need to flush it.
|
||||||
|
/// We will get into the same condition on next merge call.
|
||||||
|
if (merged_data->mergedRows() != 0)
|
||||||
|
return Status(merged_data->pull());
|
||||||
|
|
||||||
|
size_t source_num = current->order;
|
||||||
|
auto current_chunk = std::move(*sources[source_num].chunk);
|
||||||
|
size_t chunk_num_rows = current_chunk.getNumRows();
|
||||||
|
|
||||||
|
/// We will get the next block from the corresponding source, if there is one.
|
||||||
|
queue.removeTop();
|
||||||
|
|
||||||
|
if (enable_vertical_final)
|
||||||
|
{
|
||||||
|
current_chunk.getChunkInfos().add(std::make_shared<ChunkSelectFinalAllRows>());
|
||||||
|
Status status(std::move(current_chunk));
|
||||||
|
status.required_source = source_num;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
merged_data->insertChunk(std::move(current_chunk), chunk_num_rows);
|
||||||
|
sources[source_num].chunk = {};
|
||||||
|
|
||||||
|
/// Write order of rows for other columns this data will be used in gather stream
|
||||||
|
if (out_row_sources_buf)
|
||||||
|
{
|
||||||
|
/// All rows are not skipped.
|
||||||
|
RowSourcePart row_source(source_num);
|
||||||
|
for (size_t i = 0; i < chunk_num_rows; ++i)
|
||||||
|
out_row_sources_buf->write(row_source.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
Status status(merged_data->pull());
|
||||||
|
status.required_source = source_num;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
/// Initially, skip all rows. Unskip last on insert.
|
/// Initially, skip all rows. Unskip last on insert.
|
||||||
size_t current_pos = current_row_sources.size();
|
size_t current_pos = current_row_sources.size();
|
||||||
if (out_row_sources_buf)
|
if (out_row_sources_buf)
|
||||||
current_row_sources.emplace_back(current.impl->order, true);
|
current_row_sources.emplace_back(current.impl->order, true);
|
||||||
|
|
||||||
if ((is_deleted_column_number!=-1))
|
if (is_deleted_column_number != -1)
|
||||||
{
|
{
|
||||||
const UInt8 is_deleted = assert_cast<const ColumnUInt8 &>(*current->all_columns[is_deleted_column_number]).getData()[current->getRow()];
|
const UInt8 is_deleted = assert_cast<const ColumnUInt8 &>(*current->all_columns[is_deleted_column_number]).getData()[current->getRow()];
|
||||||
if ((is_deleted != 1) && (is_deleted != 0))
|
if (is_deleted > 1)
|
||||||
throw Exception(ErrorCodes::INCORRECT_DATA, "Incorrect data: is_deleted = {} (must be 1 or 0).", toString(is_deleted));
|
throw Exception(ErrorCodes::INCORRECT_DATA, "Incorrect data: is_deleted = {} (must be 1 or 0).", toString(is_deleted));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,17 +230,7 @@ IMergingAlgorithm::Status ReplacingSortedAlgorithm::merge()
|
|||||||
|
|
||||||
/// We will write the data for the last primary key.
|
/// We will write the data for the last primary key.
|
||||||
if (!selected_row.empty())
|
if (!selected_row.empty())
|
||||||
{
|
insertRow();
|
||||||
if (is_deleted_column_number!=-1)
|
|
||||||
{
|
|
||||||
if (!(cleanup && assert_cast<const ColumnUInt8 &>(*(*selected_row.all_columns)[is_deleted_column_number]).getData()[selected_row.row_num]))
|
|
||||||
insertRow();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
insertRow();
|
|
||||||
/// insertRow() may has not been called
|
|
||||||
saveChunkForSkippingFinalFromSelectedRow();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Skipping final: emit the remaining chunks
|
/// Skipping final: emit the remaining chunks
|
||||||
if (!to_be_emitted.empty())
|
if (!to_be_emitted.empty())
|
||||||
|
@ -13,8 +13,7 @@ class Logger;
|
|||||||
namespace DB
|
namespace DB
|
||||||
{
|
{
|
||||||
|
|
||||||
/** Use in skipping final to keep list of indices of selected row after merging final
|
//// Used in skipping final to keep the list of indices of selected rows after merging.
|
||||||
*/
|
|
||||||
struct ChunkSelectFinalIndices : public ChunkInfoCloneable<ChunkSelectFinalIndices>
|
struct ChunkSelectFinalIndices : public ChunkInfoCloneable<ChunkSelectFinalIndices>
|
||||||
{
|
{
|
||||||
explicit ChunkSelectFinalIndices(MutableColumnPtr select_final_indices_);
|
explicit ChunkSelectFinalIndices(MutableColumnPtr select_final_indices_);
|
||||||
@ -24,6 +23,11 @@ struct ChunkSelectFinalIndices : public ChunkInfoCloneable<ChunkSelectFinalIndic
|
|||||||
const ColumnUInt64 * select_final_indices = nullptr;
|
const ColumnUInt64 * select_final_indices = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//// Used in skipping final to keep all rows in chunk after merging.
|
||||||
|
struct ChunkSelectFinalAllRows : public ChunkInfoCloneable<ChunkSelectFinalAllRows>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
/** Merges several sorted inputs into one.
|
/** Merges several sorted inputs into one.
|
||||||
* For each group of consecutive identical values of the primary key (the columns by which the data is sorted),
|
* For each group of consecutive identical values of the primary key (the columns by which the data is sorted),
|
||||||
* keeps row with max `version` value.
|
* keeps row with max `version` value.
|
||||||
@ -63,6 +67,7 @@ private:
|
|||||||
PODArray<RowSourcePart> current_row_sources;
|
PODArray<RowSourcePart> current_row_sources;
|
||||||
|
|
||||||
void insertRow();
|
void insertRow();
|
||||||
|
void insertRowImpl();
|
||||||
|
|
||||||
/// Method for using in skipping FINAL logic
|
/// Method for using in skipping FINAL logic
|
||||||
/// Skipping FINAL doesn't merge rows to new chunks but marks selected rows in input chunks and emit them
|
/// Skipping FINAL doesn't merge rows to new chunks but marks selected rows in input chunks and emit them
|
||||||
|
@ -26,8 +26,12 @@ public:
|
|||||||
void transform(Chunk & chunk) override
|
void transform(Chunk & chunk) override
|
||||||
{
|
{
|
||||||
size_t num_rows = chunk.getNumRows();
|
size_t num_rows = chunk.getNumRows();
|
||||||
auto select_final_indices_info = chunk.getChunkInfos().extract<ChunkSelectFinalIndices>();
|
|
||||||
|
|
||||||
|
auto select_all_rows_info = chunk.getChunkInfos().extract<ChunkSelectFinalAllRows>();
|
||||||
|
if (select_all_rows_info)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto select_final_indices_info = chunk.getChunkInfos().extract<ChunkSelectFinalIndices>();
|
||||||
if (!select_final_indices_info || !select_final_indices_info->select_final_indices)
|
if (!select_final_indices_info || !select_final_indices_info->select_final_indices)
|
||||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Chunk passed to SelectByIndicesTransform without indices column");
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Chunk passed to SelectByIndicesTransform without indices column");
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include <Access/ExternalAuthenticators.h>
|
#include <Access/ExternalAuthenticators.h>
|
||||||
#include <Common/Base64.h>
|
#include <Common/Base64.h>
|
||||||
#include <Common/HTTPHeaderFilter.h>
|
#include <Common/HTTPHeaderFilter.h>
|
||||||
|
#include <Server/HTTPHandler.h>
|
||||||
#include <Server/HTTP/HTTPServerRequest.h>
|
#include <Server/HTTP/HTTPServerRequest.h>
|
||||||
#include <Server/HTTP/HTMLForm.h>
|
#include <Server/HTTP/HTMLForm.h>
|
||||||
#include <Server/HTTP/HTTPServerResponse.h>
|
#include <Server/HTTP/HTTPServerResponse.h>
|
||||||
@ -54,11 +55,13 @@ bool authenticateUserByHTTP(
|
|||||||
HTTPServerResponse & response,
|
HTTPServerResponse & response,
|
||||||
Session & session,
|
Session & session,
|
||||||
std::unique_ptr<Credentials> & request_credentials,
|
std::unique_ptr<Credentials> & request_credentials,
|
||||||
|
const HTTPHandlerConnectionConfig & connection_config,
|
||||||
ContextPtr global_context,
|
ContextPtr global_context,
|
||||||
LoggerPtr log)
|
LoggerPtr log)
|
||||||
{
|
{
|
||||||
/// Get the credentials created by the previous call of authenticateUserByHTTP() while handling the previous HTTP request.
|
/// Get the credentials created by the previous call of authenticateUserByHTTP() while handling the previous HTTP request.
|
||||||
auto current_credentials = std::move(request_credentials);
|
auto current_credentials = std::move(request_credentials);
|
||||||
|
const auto & config_credentials = connection_config.credentials;
|
||||||
|
|
||||||
/// The user and password can be passed by headers (similar to X-Auth-*),
|
/// The user and password can be passed by headers (similar to X-Auth-*),
|
||||||
/// which is used by load balancers to pass authentication information.
|
/// which is used by load balancers to pass authentication information.
|
||||||
@ -70,6 +73,7 @@ bool authenticateUserByHTTP(
|
|||||||
/// The header 'X-ClickHouse-SSL-Certificate-Auth: on' enables checking the common name
|
/// The header 'X-ClickHouse-SSL-Certificate-Auth: on' enables checking the common name
|
||||||
/// extracted from the SSL certificate used for this connection instead of checking password.
|
/// extracted from the SSL certificate used for this connection instead of checking password.
|
||||||
bool has_ssl_certificate_auth = (request.get("X-ClickHouse-SSL-Certificate-Auth", "") == "on");
|
bool has_ssl_certificate_auth = (request.get("X-ClickHouse-SSL-Certificate-Auth", "") == "on");
|
||||||
|
bool has_config_credentials = config_credentials.has_value();
|
||||||
|
|
||||||
/// User name and password can be passed using HTTP Basic auth or query parameters
|
/// User name and password can be passed using HTTP Basic auth or query parameters
|
||||||
/// (both methods are insecure).
|
/// (both methods are insecure).
|
||||||
@ -79,6 +83,10 @@ bool authenticateUserByHTTP(
|
|||||||
std::string spnego_challenge;
|
std::string spnego_challenge;
|
||||||
SSLCertificateSubjects certificate_subjects;
|
SSLCertificateSubjects certificate_subjects;
|
||||||
|
|
||||||
|
if (config_credentials)
|
||||||
|
{
|
||||||
|
checkUserNameNotEmpty(config_credentials->getUserName(), "config authentication");
|
||||||
|
}
|
||||||
if (has_ssl_certificate_auth)
|
if (has_ssl_certificate_auth)
|
||||||
{
|
{
|
||||||
#if USE_SSL
|
#if USE_SSL
|
||||||
@ -86,6 +94,8 @@ bool authenticateUserByHTTP(
|
|||||||
checkUserNameNotEmpty(user, "X-ClickHouse HTTP headers");
|
checkUserNameNotEmpty(user, "X-ClickHouse HTTP headers");
|
||||||
|
|
||||||
/// It is prohibited to mix different authorization schemes.
|
/// It is prohibited to mix different authorization schemes.
|
||||||
|
if (has_config_credentials)
|
||||||
|
throwMultipleAuthenticationMethods("SSL certificate authentication", "authentication set in config");
|
||||||
if (!password.empty())
|
if (!password.empty())
|
||||||
throwMultipleAuthenticationMethods("SSL certificate authentication", "authentication via password");
|
throwMultipleAuthenticationMethods("SSL certificate authentication", "authentication via password");
|
||||||
if (has_http_credentials)
|
if (has_http_credentials)
|
||||||
@ -109,6 +119,8 @@ bool authenticateUserByHTTP(
|
|||||||
checkUserNameNotEmpty(user, "X-ClickHouse HTTP headers");
|
checkUserNameNotEmpty(user, "X-ClickHouse HTTP headers");
|
||||||
|
|
||||||
/// It is prohibited to mix different authorization schemes.
|
/// It is prohibited to mix different authorization schemes.
|
||||||
|
if (has_config_credentials)
|
||||||
|
throwMultipleAuthenticationMethods("X-ClickHouse HTTP headers", "authentication set in config");
|
||||||
if (has_http_credentials)
|
if (has_http_credentials)
|
||||||
throwMultipleAuthenticationMethods("X-ClickHouse HTTP headers", "Authorization HTTP header");
|
throwMultipleAuthenticationMethods("X-ClickHouse HTTP headers", "Authorization HTTP header");
|
||||||
if (has_credentials_in_query_params)
|
if (has_credentials_in_query_params)
|
||||||
@ -117,6 +129,8 @@ bool authenticateUserByHTTP(
|
|||||||
else if (has_http_credentials)
|
else if (has_http_credentials)
|
||||||
{
|
{
|
||||||
/// It is prohibited to mix different authorization schemes.
|
/// It is prohibited to mix different authorization schemes.
|
||||||
|
if (has_config_credentials)
|
||||||
|
throwMultipleAuthenticationMethods("Authorization HTTP header", "authentication set in config");
|
||||||
if (has_credentials_in_query_params)
|
if (has_credentials_in_query_params)
|
||||||
throwMultipleAuthenticationMethods("Authorization HTTP header", "authentication via parameters");
|
throwMultipleAuthenticationMethods("Authorization HTTP header", "authentication via parameters");
|
||||||
|
|
||||||
@ -190,6 +204,10 @@ bool authenticateUserByHTTP(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (has_config_credentials)
|
||||||
|
{
|
||||||
|
current_credentials = std::make_unique<BasicCredentials>(*config_credentials);
|
||||||
|
}
|
||||||
else // I.e., now using user name and password strings ("Basic").
|
else // I.e., now using user name and password strings ("Basic").
|
||||||
{
|
{
|
||||||
if (!current_credentials)
|
if (!current_credentials)
|
||||||
|
@ -11,13 +11,22 @@ class HTMLForm;
|
|||||||
class HTTPServerResponse;
|
class HTTPServerResponse;
|
||||||
class Session;
|
class Session;
|
||||||
class Credentials;
|
class Credentials;
|
||||||
|
class BasicCredentials;
|
||||||
|
struct HTTPHandlerConnectionConfig;
|
||||||
|
|
||||||
/// Authenticates a user via HTTP protocol and initializes a session.
|
/// Authenticates a user via HTTP protocol and initializes a session.
|
||||||
|
///
|
||||||
/// Usually retrieves the name and the password for that user from either the request's headers or from the query parameters.
|
/// Usually retrieves the name and the password for that user from either the request's headers or from the query parameters.
|
||||||
/// Returns true when the user successfully authenticated,
|
/// You can also pass user/password explicitly via `config_credentials`.
|
||||||
/// the session instance will be configured accordingly, and the request_credentials instance will be dropped.
|
///
|
||||||
/// Returns false when the user is not authenticated yet, and the HTTP_UNAUTHORIZED response is sent with the "WWW-Authenticate" header,
|
/// Returns true when the user successfully authenticated:
|
||||||
/// in this case the `request_credentials` instance must be preserved until the next request or until any exception.
|
/// - the session instance will be configured accordingly
|
||||||
|
/// - and the request_credentials instance will be dropped.
|
||||||
|
///
|
||||||
|
/// Returns false when the user is not authenticated yet:
|
||||||
|
/// - the HTTP_UNAUTHORIZED response is sent with the "WWW-Authenticate" header
|
||||||
|
/// - the `request_credentials` instance must be preserved until the next request or until any exception.
|
||||||
|
///
|
||||||
/// Throws an exception if authentication failed.
|
/// Throws an exception if authentication failed.
|
||||||
bool authenticateUserByHTTP(
|
bool authenticateUserByHTTP(
|
||||||
const HTTPServerRequest & request,
|
const HTTPServerRequest & request,
|
||||||
@ -25,6 +34,7 @@ bool authenticateUserByHTTP(
|
|||||||
HTTPServerResponse & response,
|
HTTPServerResponse & response,
|
||||||
Session & session,
|
Session & session,
|
||||||
std::unique_ptr<Credentials> & request_credentials,
|
std::unique_ptr<Credentials> & request_credentials,
|
||||||
|
const HTTPHandlerConnectionConfig & connection_config,
|
||||||
ContextPtr global_context,
|
ContextPtr global_context,
|
||||||
LoggerPtr log);
|
LoggerPtr log);
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#include <Server/HTTPHandler.h>
|
#include <Server/HTTPHandler.h>
|
||||||
|
|
||||||
#include <Access/Credentials.h>
|
|
||||||
#include <Compression/CompressedReadBuffer.h>
|
#include <Compression/CompressedReadBuffer.h>
|
||||||
#include <Compression/CompressedWriteBuffer.h>
|
#include <Compression/CompressedWriteBuffer.h>
|
||||||
#include <Core/ExternalTable.h>
|
#include <Core/ExternalTable.h>
|
||||||
@ -145,6 +144,15 @@ static std::chrono::steady_clock::duration parseSessionTimeout(
|
|||||||
return std::chrono::seconds(session_timeout);
|
return std::chrono::seconds(session_timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HTTPHandlerConnectionConfig::HTTPHandlerConnectionConfig(const Poco::Util::AbstractConfiguration & config, const std::string & config_prefix)
|
||||||
|
{
|
||||||
|
if (config.has(config_prefix + ".handler.user") || config.has(config_prefix + ".handler.password"))
|
||||||
|
{
|
||||||
|
credentials.emplace(
|
||||||
|
config.getString(config_prefix + ".handler.user", "default"),
|
||||||
|
config.getString(config_prefix + ".handler.password", ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void HTTPHandler::pushDelayedResults(Output & used_output)
|
void HTTPHandler::pushDelayedResults(Output & used_output)
|
||||||
{
|
{
|
||||||
@ -182,11 +190,12 @@ void HTTPHandler::pushDelayedResults(Output & used_output)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
HTTPHandler::HTTPHandler(IServer & server_, const std::string & name, const HTTPResponseHeaderSetup & http_response_headers_override_)
|
HTTPHandler::HTTPHandler(IServer & server_, const HTTPHandlerConnectionConfig & connection_config_, const std::string & name, const HTTPResponseHeaderSetup & http_response_headers_override_)
|
||||||
: server(server_)
|
: server(server_)
|
||||||
, log(getLogger(name))
|
, log(getLogger(name))
|
||||||
, default_settings(server.context()->getSettingsRef())
|
, default_settings(server.context()->getSettingsRef())
|
||||||
, http_response_headers_override(http_response_headers_override_)
|
, http_response_headers_override(http_response_headers_override_)
|
||||||
|
, connection_config(connection_config_)
|
||||||
{
|
{
|
||||||
server_display_name = server.config().getString("display_name", getFQDNOrHostName());
|
server_display_name = server.config().getString("display_name", getFQDNOrHostName());
|
||||||
}
|
}
|
||||||
@ -199,7 +208,7 @@ HTTPHandler::~HTTPHandler() = default;
|
|||||||
|
|
||||||
bool HTTPHandler::authenticateUser(HTTPServerRequest & request, HTMLForm & params, HTTPServerResponse & response)
|
bool HTTPHandler::authenticateUser(HTTPServerRequest & request, HTMLForm & params, HTTPServerResponse & response)
|
||||||
{
|
{
|
||||||
return authenticateUserByHTTP(request, params, response, *session, request_credentials, server.context(), log);
|
return authenticateUserByHTTP(request, params, response, *session, request_credentials, connection_config, server.context(), log);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -768,8 +777,12 @@ void HTTPHandler::handleRequest(HTTPServerRequest & request, HTTPServerResponse
|
|||||||
}
|
}
|
||||||
|
|
||||||
DynamicQueryHandler::DynamicQueryHandler(
|
DynamicQueryHandler::DynamicQueryHandler(
|
||||||
IServer & server_, const std::string & param_name_, const HTTPResponseHeaderSetup & http_response_headers_override_)
|
IServer & server_,
|
||||||
: HTTPHandler(server_, "DynamicQueryHandler", http_response_headers_override_), param_name(param_name_)
|
const HTTPHandlerConnectionConfig & connection_config,
|
||||||
|
const std::string & param_name_,
|
||||||
|
const HTTPResponseHeaderSetup & http_response_headers_override_)
|
||||||
|
: HTTPHandler(server_, connection_config, "DynamicQueryHandler", http_response_headers_override_)
|
||||||
|
, param_name(param_name_)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -826,12 +839,13 @@ std::string DynamicQueryHandler::getQuery(HTTPServerRequest & request, HTMLForm
|
|||||||
|
|
||||||
PredefinedQueryHandler::PredefinedQueryHandler(
|
PredefinedQueryHandler::PredefinedQueryHandler(
|
||||||
IServer & server_,
|
IServer & server_,
|
||||||
|
const HTTPHandlerConnectionConfig & connection_config,
|
||||||
const NameSet & receive_params_,
|
const NameSet & receive_params_,
|
||||||
const std::string & predefined_query_,
|
const std::string & predefined_query_,
|
||||||
const CompiledRegexPtr & url_regex_,
|
const CompiledRegexPtr & url_regex_,
|
||||||
const std::unordered_map<String, CompiledRegexPtr> & header_name_with_regex_,
|
const std::unordered_map<String, CompiledRegexPtr> & header_name_with_regex_,
|
||||||
const HTTPResponseHeaderSetup & http_response_headers_override_)
|
const HTTPResponseHeaderSetup & http_response_headers_override_)
|
||||||
: HTTPHandler(server_, "PredefinedQueryHandler", http_response_headers_override_)
|
: HTTPHandler(server_, connection_config, "PredefinedQueryHandler", http_response_headers_override_)
|
||||||
, receive_params(receive_params_)
|
, receive_params(receive_params_)
|
||||||
, predefined_query(predefined_query_)
|
, predefined_query(predefined_query_)
|
||||||
, url_regex(url_regex_)
|
, url_regex(url_regex_)
|
||||||
@ -923,10 +937,11 @@ HTTPRequestHandlerFactoryPtr createDynamicHandlerFactory(IServer & server,
|
|||||||
{
|
{
|
||||||
auto query_param_name = config.getString(config_prefix + ".handler.query_param_name", "query");
|
auto query_param_name = config.getString(config_prefix + ".handler.query_param_name", "query");
|
||||||
|
|
||||||
|
HTTPHandlerConnectionConfig connection_config(config, config_prefix);
|
||||||
HTTPResponseHeaderSetup http_response_headers_override = parseHTTPResponseHeaders(config, config_prefix);
|
HTTPResponseHeaderSetup http_response_headers_override = parseHTTPResponseHeaders(config, config_prefix);
|
||||||
|
|
||||||
auto creator = [&server, query_param_name, http_response_headers_override]() -> std::unique_ptr<DynamicQueryHandler>
|
auto creator = [&server, query_param_name, http_response_headers_override, connection_config]() -> std::unique_ptr<DynamicQueryHandler>
|
||||||
{ return std::make_unique<DynamicQueryHandler>(server, query_param_name, http_response_headers_override); };
|
{ return std::make_unique<DynamicQueryHandler>(server, connection_config, query_param_name, http_response_headers_override); };
|
||||||
|
|
||||||
auto factory = std::make_shared<HandlingRuleHTTPHandlerFactory<DynamicQueryHandler>>(std::move(creator));
|
auto factory = std::make_shared<HandlingRuleHTTPHandlerFactory<DynamicQueryHandler>>(std::move(creator));
|
||||||
factory->addFiltersFromConfig(config, config_prefix);
|
factory->addFiltersFromConfig(config, config_prefix);
|
||||||
@ -968,6 +983,8 @@ HTTPRequestHandlerFactoryPtr createPredefinedHandlerFactory(IServer & server,
|
|||||||
Poco::Util::AbstractConfiguration::Keys headers_name;
|
Poco::Util::AbstractConfiguration::Keys headers_name;
|
||||||
config.keys(config_prefix + ".headers", headers_name);
|
config.keys(config_prefix + ".headers", headers_name);
|
||||||
|
|
||||||
|
HTTPHandlerConnectionConfig connection_config(config, config_prefix);
|
||||||
|
|
||||||
for (const auto & header_name : headers_name)
|
for (const auto & header_name : headers_name)
|
||||||
{
|
{
|
||||||
auto expression = config.getString(config_prefix + ".headers." + header_name);
|
auto expression = config.getString(config_prefix + ".headers." + header_name);
|
||||||
@ -1001,12 +1018,18 @@ HTTPRequestHandlerFactoryPtr createPredefinedHandlerFactory(IServer & server,
|
|||||||
predefined_query,
|
predefined_query,
|
||||||
regex,
|
regex,
|
||||||
headers_name_with_regex,
|
headers_name_with_regex,
|
||||||
http_response_headers_override]
|
http_response_headers_override,
|
||||||
|
connection_config]
|
||||||
-> std::unique_ptr<PredefinedQueryHandler>
|
-> std::unique_ptr<PredefinedQueryHandler>
|
||||||
{
|
{
|
||||||
return std::make_unique<PredefinedQueryHandler>(
|
return std::make_unique<PredefinedQueryHandler>(
|
||||||
server, analyze_receive_params, predefined_query, regex,
|
server,
|
||||||
headers_name_with_regex, http_response_headers_override);
|
connection_config,
|
||||||
|
analyze_receive_params,
|
||||||
|
predefined_query,
|
||||||
|
regex,
|
||||||
|
headers_name_with_regex,
|
||||||
|
http_response_headers_override);
|
||||||
};
|
};
|
||||||
factory = std::make_shared<HandlingRuleHTTPHandlerFactory<PredefinedQueryHandler>>(std::move(creator));
|
factory = std::make_shared<HandlingRuleHTTPHandlerFactory<PredefinedQueryHandler>>(std::move(creator));
|
||||||
factory->addFiltersFromConfig(config, config_prefix);
|
factory->addFiltersFromConfig(config, config_prefix);
|
||||||
@ -1019,18 +1042,21 @@ HTTPRequestHandlerFactoryPtr createPredefinedHandlerFactory(IServer & server,
|
|||||||
analyze_receive_params,
|
analyze_receive_params,
|
||||||
predefined_query,
|
predefined_query,
|
||||||
headers_name_with_regex,
|
headers_name_with_regex,
|
||||||
http_response_headers_override]
|
http_response_headers_override,
|
||||||
|
connection_config]
|
||||||
-> std::unique_ptr<PredefinedQueryHandler>
|
-> std::unique_ptr<PredefinedQueryHandler>
|
||||||
{
|
{
|
||||||
return std::make_unique<PredefinedQueryHandler>(
|
return std::make_unique<PredefinedQueryHandler>(
|
||||||
server, analyze_receive_params, predefined_query, CompiledRegexPtr{},
|
server,
|
||||||
headers_name_with_regex, http_response_headers_override);
|
connection_config,
|
||||||
|
analyze_receive_params,
|
||||||
|
predefined_query,
|
||||||
|
CompiledRegexPtr{},
|
||||||
|
headers_name_with_regex,
|
||||||
|
http_response_headers_override);
|
||||||
};
|
};
|
||||||
|
|
||||||
factory = std::make_shared<HandlingRuleHTTPHandlerFactory<PredefinedQueryHandler>>(std::move(creator));
|
factory = std::make_shared<HandlingRuleHTTPHandlerFactory<PredefinedQueryHandler>>(std::move(creator));
|
||||||
|
|
||||||
factory->addFiltersFromConfig(config, config_prefix);
|
factory->addFiltersFromConfig(config, config_prefix);
|
||||||
|
|
||||||
return factory;
|
return factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include <IO/CascadeWriteBuffer.h>
|
#include <IO/CascadeWriteBuffer.h>
|
||||||
#include <Compression/CompressedWriteBuffer.h>
|
#include <Compression/CompressedWriteBuffer.h>
|
||||||
#include <Common/re2.h>
|
#include <Common/re2.h>
|
||||||
|
#include <Access/Credentials.h>
|
||||||
|
|
||||||
#include "HTTPResponseHeaderWriter.h"
|
#include "HTTPResponseHeaderWriter.h"
|
||||||
|
|
||||||
@ -26,17 +27,28 @@ namespace DB
|
|||||||
{
|
{
|
||||||
|
|
||||||
class Session;
|
class Session;
|
||||||
class Credentials;
|
|
||||||
class IServer;
|
class IServer;
|
||||||
struct Settings;
|
struct Settings;
|
||||||
class WriteBufferFromHTTPServerResponse;
|
class WriteBufferFromHTTPServerResponse;
|
||||||
|
|
||||||
using CompiledRegexPtr = std::shared_ptr<const re2::RE2>;
|
using CompiledRegexPtr = std::shared_ptr<const re2::RE2>;
|
||||||
|
|
||||||
|
struct HTTPHandlerConnectionConfig
|
||||||
|
{
|
||||||
|
std::optional<BasicCredentials> credentials;
|
||||||
|
|
||||||
|
/// TODO:
|
||||||
|
/// String quota;
|
||||||
|
/// String default_database;
|
||||||
|
|
||||||
|
HTTPHandlerConnectionConfig() = default;
|
||||||
|
HTTPHandlerConnectionConfig(const Poco::Util::AbstractConfiguration & config, const std::string & config_prefix);
|
||||||
|
};
|
||||||
|
|
||||||
class HTTPHandler : public HTTPRequestHandler
|
class HTTPHandler : public HTTPRequestHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
HTTPHandler(IServer & server_, const std::string & name, const HTTPResponseHeaderSetup & http_response_headers_override_);
|
HTTPHandler(IServer & server_, const HTTPHandlerConnectionConfig & connection_config_, const std::string & name, const HTTPResponseHeaderSetup & http_response_headers_override_);
|
||||||
~HTTPHandler() override;
|
~HTTPHandler() override;
|
||||||
|
|
||||||
void handleRequest(HTTPServerRequest & request, HTTPServerResponse & response, const ProfileEvents::Event & write_event) override;
|
void handleRequest(HTTPServerRequest & request, HTTPServerResponse & response, const ProfileEvents::Event & write_event) override;
|
||||||
@ -146,16 +158,7 @@ private:
|
|||||||
// The request_credential instance may outlive a single request/response loop.
|
// The request_credential instance may outlive a single request/response loop.
|
||||||
// This happens only when the authentication mechanism requires more than a single request/response exchange (e.g., SPNEGO).
|
// This happens only when the authentication mechanism requires more than a single request/response exchange (e.g., SPNEGO).
|
||||||
std::unique_ptr<Credentials> request_credentials;
|
std::unique_ptr<Credentials> request_credentials;
|
||||||
|
HTTPHandlerConnectionConfig connection_config;
|
||||||
// Returns true when the user successfully authenticated,
|
|
||||||
// the session instance will be configured accordingly, and the request_credentials instance will be dropped.
|
|
||||||
// Returns false when the user is not authenticated yet, and the 'Negotiate' response is sent,
|
|
||||||
// the session and request_credentials instances are preserved.
|
|
||||||
// Throws an exception if authentication failed.
|
|
||||||
bool authenticateUser(
|
|
||||||
HTTPServerRequest & request,
|
|
||||||
HTMLForm & params,
|
|
||||||
HTTPServerResponse & response);
|
|
||||||
|
|
||||||
/// Also initializes 'used_output'.
|
/// Also initializes 'used_output'.
|
||||||
void processQuery(
|
void processQuery(
|
||||||
@ -174,6 +177,13 @@ private:
|
|||||||
Output & used_output);
|
Output & used_output);
|
||||||
|
|
||||||
static void pushDelayedResults(Output & used_output);
|
static void pushDelayedResults(Output & used_output);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// @see authenticateUserByHTTP()
|
||||||
|
virtual bool authenticateUser(
|
||||||
|
HTTPServerRequest & request,
|
||||||
|
HTMLForm & params,
|
||||||
|
HTTPServerResponse & response);
|
||||||
};
|
};
|
||||||
|
|
||||||
class DynamicQueryHandler : public HTTPHandler
|
class DynamicQueryHandler : public HTTPHandler
|
||||||
@ -184,6 +194,7 @@ private:
|
|||||||
public:
|
public:
|
||||||
explicit DynamicQueryHandler(
|
explicit DynamicQueryHandler(
|
||||||
IServer & server_,
|
IServer & server_,
|
||||||
|
const HTTPHandlerConnectionConfig & connection_config,
|
||||||
const std::string & param_name_ = "query",
|
const std::string & param_name_ = "query",
|
||||||
const HTTPResponseHeaderSetup & http_response_headers_override_ = std::nullopt);
|
const HTTPResponseHeaderSetup & http_response_headers_override_ = std::nullopt);
|
||||||
|
|
||||||
@ -203,6 +214,7 @@ private:
|
|||||||
public:
|
public:
|
||||||
PredefinedQueryHandler(
|
PredefinedQueryHandler(
|
||||||
IServer & server_,
|
IServer & server_,
|
||||||
|
const HTTPHandlerConnectionConfig & connection_config,
|
||||||
const NameSet & receive_params_,
|
const NameSet & receive_params_,
|
||||||
const std::string & predefined_query_,
|
const std::string & predefined_query_,
|
||||||
const CompiledRegexPtr & url_regex_,
|
const CompiledRegexPtr & url_regex_,
|
||||||
|
@ -275,7 +275,7 @@ void addDefaultHandlersFactory(
|
|||||||
|
|
||||||
auto dynamic_creator = [&server] () -> std::unique_ptr<DynamicQueryHandler>
|
auto dynamic_creator = [&server] () -> std::unique_ptr<DynamicQueryHandler>
|
||||||
{
|
{
|
||||||
return std::make_unique<DynamicQueryHandler>(server, "query");
|
return std::make_unique<DynamicQueryHandler>(server, HTTPHandlerConnectionConfig{}, "query");
|
||||||
};
|
};
|
||||||
auto query_handler = std::make_shared<HandlingRuleHTTPHandlerFactory<DynamicQueryHandler>>(std::move(dynamic_creator));
|
auto query_handler = std::make_shared<HandlingRuleHTTPHandlerFactory<DynamicQueryHandler>>(std::move(dynamic_creator));
|
||||||
query_handler->addFilter([](const auto & request)
|
query_handler->addFilter([](const auto & request)
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <Server/HTTP/sendExceptionToHTTPClient.h>
|
#include <Server/HTTP/sendExceptionToHTTPClient.h>
|
||||||
#include <Server/IServer.h>
|
#include <Server/IServer.h>
|
||||||
#include <Server/PrometheusMetricsWriter.h>
|
#include <Server/PrometheusMetricsWriter.h>
|
||||||
|
#include <Server/HTTPHandler.h>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <Access/Credentials.h>
|
#include <Access/Credentials.h>
|
||||||
@ -137,7 +138,7 @@ protected:
|
|||||||
|
|
||||||
bool authenticateUser(HTTPServerRequest & request, HTTPServerResponse & response)
|
bool authenticateUser(HTTPServerRequest & request, HTTPServerResponse & response)
|
||||||
{
|
{
|
||||||
return authenticateUserByHTTP(request, *params, response, *session, request_credentials, server().context(), log());
|
return authenticateUserByHTTP(request, *params, response, *session, request_credentials, HTTPHandlerConnectionConfig{}, server().context(), log());
|
||||||
}
|
}
|
||||||
|
|
||||||
void makeContext(HTTPServerRequest & request)
|
void makeContext(HTTPServerRequest & request)
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user