Merge branch 'master' of github.com:yandex/ClickHouse

This commit is contained in:
Alexey Milovidov 2016-10-23 07:02:27 +03:00
commit 45b6e0b38b
71 changed files with 1173 additions and 865 deletions

View File

@ -28,11 +28,6 @@ set_target_properties( "${LIBNAME}"
target_link_libraries( "${LIBNAME}" PocoFoundation) target_link_libraries( "${LIBNAME}" PocoFoundation)
if(ENABLE_DATA_SQLITE)
# SQlite3 is built in any case
add_subdirectory( SQLite )
endif(ENABLE_DATA_SQLITE)
if(ENABLE_DATA_MYSQL) if(ENABLE_DATA_MYSQL)
find_package(MySQL) find_package(MySQL)
if(MYSQL_FOUND) if(MYSQL_FOUND)

View File

@ -1,4 +1,4 @@
https://github.com/gperftools/gperftools/commit/7852eeb75b9375cf52a7da01be044da6e915dd08 https://github.com/gperftools/gperftools/commit/dde32f8bbc95312379f9f5a651799815bb6327c5
Several modifications: Several modifications:
1. Disabled TCMALLOC_AGGRESSIVE_DECOMMIT by default. It is important. 1. Disabled TCMALLOC_AGGRESSIVE_DECOMMIT by default. It is important.

View File

@ -102,8 +102,14 @@
+ __GNUC_MINOR__ * 100 \ + __GNUC_MINOR__ * 100 \
+ __GNUC_PATCHLEVEL__) + __GNUC_PATCHLEVEL__)
#define CLANG_VERSION (__clang_major__ * 10000 \
+ __clang_minor__ * 100 \
+ __clang_patchlevel__)
#if defined(TCMALLOC_PREFER_GCC_ATOMICS) && defined(__GNUC__) && GCC_VERSION >= 40700 #if defined(TCMALLOC_PREFER_GCC_ATOMICS) && defined(__GNUC__) && GCC_VERSION >= 40700
#include "base/atomicops-internals-gcc.h" #include "base/atomicops-internals-gcc.h"
#elif defined(TCMALLOC_PREFER_GCC_ATOMICS) && defined(__clang__) && CLANG_VERSION >= 30400
#include "base/atomicops-internals-gcc.h"
#elif defined(__MACH__) && defined(__APPLE__) #elif defined(__MACH__) && defined(__APPLE__)
#include "base/atomicops-internals-macosx.h" #include "base/atomicops-internals-macosx.h"
#elif defined(__GNUC__) && defined(ARMV6) #elif defined(__GNUC__) && defined(ARMV6)
@ -120,6 +126,8 @@
#include "base/atomicops-internals-mips.h" #include "base/atomicops-internals-mips.h"
#elif defined(__GNUC__) && GCC_VERSION >= 40700 #elif defined(__GNUC__) && GCC_VERSION >= 40700
#include "base/atomicops-internals-gcc.h" #include "base/atomicops-internals-gcc.h"
#elif defined(__clang__) && CLANG_VERSION >= 30400
#include "base/atomicops-internals-gcc.h"
#else #else
#error You need to implement atomic operations for this architecture #error You need to implement atomic operations for this architecture
#endif #endif

View File

@ -229,6 +229,16 @@ inline Dest bit_cast(const Source& source) {
return dest; return dest;
} }
// bit_store<Dest,Source> implements the equivalent of
// "dest = *reinterpret_cast<Dest*>(&source)".
//
// This prevents undefined behavior when the dest pointer is unaligned.
template <class Dest, class Source>
inline void bit_store(Dest *dest, const Source *source) {
COMPILE_ASSERT(sizeof(Dest) == sizeof(Source), bitcasting_unequal_sizes);
memcpy(dest, source, sizeof(Dest));
}
#ifdef HAVE___ATTRIBUTE__ #ifdef HAVE___ATTRIBUTE__
# define ATTRIBUTE_WEAK __attribute__((weak)) # define ATTRIBUTE_WEAK __attribute__((weak))
# define ATTRIBUTE_NOINLINE __attribute__((noinline)) # define ATTRIBUTE_NOINLINE __attribute__((noinline))
@ -363,7 +373,7 @@ class AssignAttributeStartEnd {
# elif (defined(__aarch64__)) # elif (defined(__aarch64__))
# define CACHELINE_ALIGNED __attribute__((aligned(64))) # define CACHELINE_ALIGNED __attribute__((aligned(64)))
// implementation specific, Cortex-A53 and 57 should have 64 bytes // implementation specific, Cortex-A53 and 57 should have 64 bytes
# elif (defined(__s390x__)) # elif (defined(__s390__))
# define CACHELINE_ALIGNED __attribute__((aligned(256))) # define CACHELINE_ALIGNED __attribute__((aligned(256)))
# else # else
# error Could not determine cache line length - unknown architecture # error Could not determine cache line length - unknown architecture

View File

@ -130,12 +130,13 @@
#ifndef SYS_LINUX_SYSCALL_SUPPORT_H #ifndef SYS_LINUX_SYSCALL_SUPPORT_H
#define SYS_LINUX_SYSCALL_SUPPORT_H #define SYS_LINUX_SYSCALL_SUPPORT_H
/* We currently only support x86-32, x86-64, ARM, MIPS, PPC/PPC64, Aarch64 and s390x on Linux. /* We currently only support x86-32, x86-64, ARM, MIPS, PPC/PPC64, Aarch64, s390 and s390x
* on Linux.
* Porting to other related platforms should not be difficult. * Porting to other related platforms should not be difficult.
*/ */
#if (defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \ #if (defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \
defined(__mips__) || defined(__PPC__) || \ defined(__mips__) || defined(__PPC__) || \
defined(__aarch64__) || defined(__s390x__)) \ defined(__aarch64__) || defined(__s390__)) \
&& (defined(__linux)) && (defined(__linux))
#ifndef SYS_CPLUSPLUS #ifndef SYS_CPLUSPLUS
@ -247,7 +248,8 @@ struct kernel_rusage {
long ru_nivcsw; long ru_nivcsw;
}; };
#if defined(__i386__) || defined(__arm__) || defined(__PPC__) #if defined(__i386__) || defined(__arm__) \
|| defined(__PPC__) || (defined(__s390__) && !defined(__s390x__))
/* include/asm-{arm,i386,mips,ppc}/signal.h */ /* include/asm-{arm,i386,mips,ppc}/signal.h */
struct kernel_old_sigaction { struct kernel_old_sigaction {
@ -261,8 +263,8 @@ struct kernel_old_sigaction {
} __attribute__((packed,aligned(4))); } __attribute__((packed,aligned(4)));
#elif (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) #elif (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32)
#define kernel_old_sigaction kernel_sigaction #define kernel_old_sigaction kernel_sigaction
#elif defined(__aarch64__) || defined(__s390x__) #elif defined(__aarch64__)
// No kernel_old_sigaction defined for arm64 or s390x. // No kernel_old_sigaction defined for arm64.
#endif #endif
/* Some kernel functions (e.g. sigaction() in 2.6.23) require that the /* Some kernel functions (e.g. sigaction() in 2.6.23) require that the
@ -306,7 +308,7 @@ struct kernel_sigaction {
#endif #endif
}; };
/* include/asm-{arm,i386,mips,ppc}/stat.h */ /* include/asm-{arm,i386,mips,ppc,s390}/stat.h */
#ifdef __mips__ #ifdef __mips__
#if _MIPS_SIM == _MIPS_SIM_ABI64 #if _MIPS_SIM == _MIPS_SIM_ABI64
struct kernel_stat { struct kernel_stat {
@ -514,6 +516,29 @@ struct kernel_stat {
long st_blocks; long st_blocks;
unsigned long __unused[3]; unsigned long __unused[3];
}; };
#elif defined(__s390__)
struct kernel_stat {
unsigned short st_dev;
unsigned short __pad1;
unsigned long st_ino;
unsigned short st_mode;
unsigned short st_nlink;
unsigned short st_uid;
unsigned short st_gid;
unsigned short st_rdev;
unsigned short __pad2;
unsigned long st_size;
unsigned long st_blksize;
unsigned long st_blocks;
unsigned long st_atime_;
unsigned long st_atime_nsec_;
unsigned long st_mtime_;
unsigned long st_mtime_nsec_;
unsigned long st_ctime_;
unsigned long st_ctime_nsec_;
unsigned long __unused4;
unsigned long __unused5;
};
#endif #endif
@ -728,7 +753,7 @@ struct kernel_stat {
#define __NR_fstatat 79 #define __NR_fstatat 79
#endif #endif
/* End of aarch64 defininitions */ /* End of aarch64 defininitions */
#elif defined(__s390x__) #elif defined(__s390__)
#ifndef __NR_quotactl #ifndef __NR_quotactl
#define __NR_quotactl 131 #define __NR_quotactl 131
#endif #endif
@ -753,27 +778,6 @@ struct kernel_stat {
#ifndef __NR_pwrite64 #ifndef __NR_pwrite64
#define __NR_pwrite64 181 #define __NR_pwrite64 181
#endif #endif
#ifndef __NR_getrlimit
#define __NR_getrlimit 191
#endif
#ifndef __NR_setresuid
#define __NR_setresuid 208
#endif
#ifndef __NR_getresuid
#define __NR_getresuid 209
#endif
#ifndef __NR_setresgid
#define __NR_setresgid 210
#endif
#ifndef __NR_getresgid
#define __NR_getresgid 211
#endif
#ifndef __NR_setfsuid
#define __NR_setfsuid 215
#endif
#ifndef __NR_setfsgid
#define __NR_setfsgid 216
#endif
#ifndef __NR_getdents64 #ifndef __NR_getdents64
#define __NR_getdents64 220 #define __NR_getdents64 220
#endif #endif
@ -816,9 +820,6 @@ struct kernel_stat {
#ifndef __NR_set_tid_address #ifndef __NR_set_tid_address
#define __NR_set_tid_address 252 #define __NR_set_tid_address 252
#endif #endif
#ifndef __NR_fadvise64
#define __NR_fadvise64 253
#endif
#ifndef __NR_clock_gettime #ifndef __NR_clock_gettime
#define __NR_clock_gettime 260 #define __NR_clock_gettime 260
#endif #endif
@ -840,9 +841,6 @@ struct kernel_stat {
#ifndef __NR_openat #ifndef __NR_openat
#define __NR_openat 288 #define __NR_openat 288
#endif #endif
#ifndef __NR_newfstatat
#define __NR_newfstatat 293
#endif
#ifndef __NR_unlinkat #ifndef __NR_unlinkat
#define __NR_unlinkat 294 #define __NR_unlinkat 294
#endif #endif
@ -855,7 +853,89 @@ struct kernel_stat {
#ifndef __NR_fallocate #ifndef __NR_fallocate
#define __NR_fallocate 314 #define __NR_fallocate 314
#endif #endif
/* End of s390x definitions */ /* Some syscalls are named/numbered differently between s390 and s390x. */
#ifdef __s390x__
# ifndef __NR_getrlimit
# define __NR_getrlimit 191
# endif
# ifndef __NR_setresuid
# define __NR_setresuid 208
# endif
# ifndef __NR_getresuid
# define __NR_getresuid 209
# endif
# ifndef __NR_setresgid
# define __NR_setresgid 210
# endif
# ifndef __NR_getresgid
# define __NR_getresgid 211
# endif
# ifndef __NR_setfsuid
# define __NR_setfsuid 215
# endif
# ifndef __NR_setfsgid
# define __NR_setfsgid 216
# endif
# ifndef __NR_fadvise64
# define __NR_fadvise64 253
# endif
# ifndef __NR_newfstatat
# define __NR_newfstatat 293
# endif
#else /* __s390x__ */
# ifndef __NR_getrlimit
# define __NR_getrlimit 76
# endif
# ifndef __NR_setfsuid
# define __NR_setfsuid 138
# endif
# ifndef __NR_setfsgid
# define __NR_setfsgid 139
# endif
# ifndef __NR_setresuid
# define __NR_setresuid 164
# endif
# ifndef __NR_getresuid
# define __NR_getresuid 165
# endif
# ifndef __NR_setresgid
# define __NR_setresgid 170
# endif
# ifndef __NR_getresgid
# define __NR_getresgid 171
# endif
# ifndef __NR_ugetrlimit
# define __NR_ugetrlimit 191
# endif
# ifndef __NR_mmap2
# define __NR_mmap2 192
# endif
# ifndef __NR_setresuid32
# define __NR_setresuid32 208
# endif
# ifndef __NR_getresuid32
# define __NR_getresuid32 209
# endif
# ifndef __NR_setresgid32
# define __NR_setresgid32 210
# endif
# ifndef __NR_getresgid32
# define __NR_getresgid32 211
# endif
# ifndef __NR_setfsuid32
# define __NR_setfsuid32 215
# endif
# ifndef __NR_setfsgid32
# define __NR_setfsgid32 216
# endif
# ifndef __NR_fadvise64_64
# define __NR_fadvise64_64 264
# endif
# ifndef __NR_fstatat64
# define __NR_fstatat64 293
# endif
#endif /* __s390__ */
/* End of s390/s390x definitions */
#endif #endif
@ -919,7 +999,7 @@ struct kernel_stat {
#undef LSS_RETURN #undef LSS_RETURN
#if (defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \ #if (defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \
defined(__aarch64__) || defined(__s390x__)) defined(__aarch64__) || defined(__s390__))
/* Failing system calls return a negative result in the range of /* Failing system calls return a negative result in the range of
* -1..-4095. These are "errno" values with the sign inverted. * -1..-4095. These are "errno" values with the sign inverted.
*/ */
@ -2236,19 +2316,20 @@ struct kernel_stat {
} }
LSS_RETURN(int, __res); LSS_RETURN(int, __res);
} }
#elif defined(__s390x__) #elif defined(__s390__)
#undef LSS_REG #undef LSS_REG
#define LSS_REG(r, a) register unsigned long __r##r __asm__("r"#r) = (unsigned long) a #define LSS_REG(r, a) register unsigned long __r##r __asm__("r"#r) = (unsigned long) a
#undef LSS_BODY #undef LSS_BODY
#define LSS_BODY(type, name, args...) \ #define LSS_BODY(type, name, args...) \
register unsigned long __nr __asm__("r1") \
= (unsigned long)(__NR_##name); \
register long __res_r2 __asm__("r2"); \ register long __res_r2 __asm__("r2"); \
long __res; \ long __res; \
__asm__ __volatile__ \ __asm__ __volatile__ \
("lgfi %%r1, %1\n\t" \ ("svc 0\n\t" \
"svc 0\n\t" \ : "=d"(__res_r2) \
: "=&r"(__res_r2) \ : "d"(__nr), ## args \
: "i"(__NR_##name), ## args \ : "memory"); \
: "r1", "memory"); \
__res = __res_r2; \ __res = __res_r2; \
LSS_RETURN(type, __res) LSS_RETURN(type, __res)
#undef _syscall0 #undef _syscall0
@ -2266,13 +2347,13 @@ struct kernel_stat {
#define _syscall2(type, name, type1, arg1, type2, arg2) \ #define _syscall2(type, name, type1, arg1, type2, arg2) \
type LSS_NAME(name)(type1 arg1, type2 arg2) { \ type LSS_NAME(name)(type1 arg1, type2 arg2) { \
LSS_REG(2, arg1); LSS_REG(3, arg2); \ LSS_REG(2, arg1); LSS_REG(3, arg2); \
LSS_BODY(type, name, "0"(__r2), "r"(__r3)); \ LSS_BODY(type, name, "0"(__r2), "d"(__r3)); \
} }
#undef _syscall3 #undef _syscall3
#define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \
type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \
LSS_REG(2, arg1); LSS_REG(3, arg2); LSS_REG(4, arg3); \ LSS_REG(2, arg1); LSS_REG(3, arg2); LSS_REG(4, arg3); \
LSS_BODY(type, name, "0"(__r2), "r"(__r3), "r"(__r4)); \ LSS_BODY(type, name, "0"(__r2), "d"(__r3), "d"(__r4)); \
} }
#undef _syscall4 #undef _syscall4
#define _syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, \ #define _syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, \
@ -2281,8 +2362,8 @@ struct kernel_stat {
type4 arg4) { \ type4 arg4) { \
LSS_REG(2, arg1); LSS_REG(3, arg2); LSS_REG(4, arg3); \ LSS_REG(2, arg1); LSS_REG(3, arg2); LSS_REG(4, arg3); \
LSS_REG(5, arg4); \ LSS_REG(5, arg4); \
LSS_BODY(type, name, "0"(__r2), "r"(__r3), "r"(__r4), \ LSS_BODY(type, name, "0"(__r2), "d"(__r3), "d"(__r4), \
"r"(__r5)); \ "d"(__r5)); \
} }
#undef _syscall5 #undef _syscall5
#define _syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, \ #define _syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, \
@ -2291,8 +2372,8 @@ struct kernel_stat {
type4 arg4, type5 arg5) { \ type4 arg4, type5 arg5) { \
LSS_REG(2, arg1); LSS_REG(3, arg2); LSS_REG(4, arg3); \ LSS_REG(2, arg1); LSS_REG(3, arg2); LSS_REG(4, arg3); \
LSS_REG(5, arg4); LSS_REG(6, arg5); \ LSS_REG(5, arg4); LSS_REG(6, arg5); \
LSS_BODY(type, name, "0"(__r2), "r"(__r3), "r"(__r4), \ LSS_BODY(type, name, "0"(__r2), "d"(__r3), "d"(__r4), \
"r"(__r5), "r"(__r6)); \ "d"(__r5), "d"(__r6)); \
} }
#undef _syscall6 #undef _syscall6
#define _syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, \ #define _syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, \
@ -2301,8 +2382,8 @@ struct kernel_stat {
type4 arg4, type5 arg5, type6 arg6) { \ type4 arg4, type5 arg5, type6 arg6) { \
LSS_REG(2, arg1); LSS_REG(3, arg2); LSS_REG(4, arg3); \ LSS_REG(2, arg1); LSS_REG(3, arg2); LSS_REG(4, arg3); \
LSS_REG(5, arg4); LSS_REG(6, arg5); LSS_REG(7, arg6); \ LSS_REG(5, arg4); LSS_REG(6, arg5); LSS_REG(7, arg6); \
LSS_BODY(type, name, "0"(__r2), "r"(__r3), "r"(__r4), \ LSS_BODY(type, name, "0"(__r2), "d"(__r3), "d"(__r4), \
"r"(__r5), "r"(__r6), "r"(__r7)); \ "d"(__r5), "d"(__r6), "d"(__r7)); \
} }
LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack,
int flags, void *arg, int *parent_tidptr, int flags, void *arg, int *parent_tidptr,
@ -2317,6 +2398,27 @@ struct kernel_stat {
register void *__newtls __asm__ ("r6") = newtls; register void *__newtls __asm__ ("r6") = newtls;
register int *__ctidptr __asm__ ("r5") = child_tidptr; register int *__ctidptr __asm__ ("r5") = child_tidptr;
__asm__ __volatile__ ( __asm__ __volatile__ (
#ifndef __s390x__
/* arg already in r0 */
"ltr %4, %4\n\t" /* check fn, which is already in r1 */
"jz 1f\n\t" /* NULL function pointer, return -EINVAL */
"ltr %5, %5\n\t" /* check child_stack, which is already in r2 */
"jz 1f\n\t" /* NULL stack pointer, return -EINVAL */
/* flags already in r3 */
/* parent_tidptr already in r4 */
/* child_tidptr already in r5 */
/* newtls already in r6 */
"svc %2\n\t" /* invoke clone syscall */
"ltr %0,%%r2\n\t" /* load return code into __ret and test */
"jnz 1f\n\t" /* return to parent if non-zero */
/* start child thread */
"lr %%r2, %7\n\t" /* set first parameter to void *arg */
"ahi %%r15, -96\n\t" /* make room on the stack for the save area */
"xc 0(4,%%r15), 0(%%r15)\n\t"
"basr %%r14, %4\n\t" /* jump to fn */
"svc %3\n" /* invoke exit syscall */
"1:\n"
#else
/* arg already in r0 */ /* arg already in r0 */
"ltgr %4, %4\n\t" /* check fn, which is already in r1 */ "ltgr %4, %4\n\t" /* check fn, which is already in r1 */
"jz 1f\n\t" /* NULL function pointer, return -EINVAL */ "jz 1f\n\t" /* NULL function pointer, return -EINVAL */
@ -2335,12 +2437,12 @@ struct kernel_stat {
"xc 0(8,%%r15), 0(%%r15)\n\t" "xc 0(8,%%r15), 0(%%r15)\n\t"
"basr %%r14, %4\n\t" /* jump to fn */ "basr %%r14, %4\n\t" /* jump to fn */
"svc %3\n" /* invoke exit syscall */ "svc %3\n" /* invoke exit syscall */
"1:\n" "1:\n"
#endif
: "=r" (__ret) : "=r" (__ret)
: "0" (-EINVAL), "i" (__NR_clone), "i" (__NR_exit), : "0" (-EINVAL), "i" (__NR_clone), "i" (__NR_exit),
"r" (__fn), "r" (__cstack), "r" (__flags), "r" (__arg), "d" (__fn), "d" (__cstack), "d" (__flags), "d" (__arg),
"r" (__ptidptr), "r" (__newtls), "r" (__ctidptr) "d" (__ptidptr), "d" (__newtls), "d" (__ctidptr)
: "cc", "r14", "memory" : "cc", "r14", "memory"
); );
} }
@ -2429,18 +2531,6 @@ struct kernel_stat {
int, t, int, p) int, t, int, p)
#endif #endif
#if defined(__x86_64__) || defined(__s390x__) #if defined(__x86_64__) || defined(__s390x__)
#if defined(__s390x__)
LSS_INLINE _syscall1(void*, mmap, void*, a)
#else
/* Need to make sure __off64_t isn't truncated to 32-bits under x32. */
LSS_INLINE void* LSS_NAME(mmap)(void *s, size_t l, int p, int f, int d,
__off64_t o) {
LSS_BODY(6, void*, mmap, LSS_SYSCALL_ARG(s), LSS_SYSCALL_ARG(l),
LSS_SYSCALL_ARG(p), LSS_SYSCALL_ARG(f),
LSS_SYSCALL_ARG(d), (uint64_t)(o));
}
#endif
LSS_INLINE int LSS_NAME(sigaction)(int signum, LSS_INLINE int LSS_NAME(sigaction)(int signum,
const struct kernel_sigaction *act, const struct kernel_sigaction *act,
struct kernel_sigaction *oldact) { struct kernel_sigaction *oldact) {
@ -2458,10 +2548,8 @@ struct kernel_stat {
(KERNEL_NSIG+7)/8); (KERNEL_NSIG+7)/8);
} else } else
#endif #endif
{
return LSS_NAME(rt_sigaction)(signum, act, oldact, return LSS_NAME(rt_sigaction)(signum, act, oldact,
(KERNEL_NSIG+7)/8); (KERNEL_NSIG+7)/8);
}
} }
LSS_INLINE int LSS_NAME(sigprocmask)(int how, LSS_INLINE int LSS_NAME(sigprocmask)(int how,
@ -2472,10 +2560,6 @@ struct kernel_stat {
#endif #endif
#if (defined(__aarch64__)) || \ #if (defined(__aarch64__)) || \
(defined(__mips__) && (_MIPS_ISA == _MIPS_ISA_MIPS64)) (defined(__mips__) && (_MIPS_ISA == _MIPS_ISA_MIPS64))
LSS_INLINE _syscall6(void*, mmap, void*, s,
size_t, l, int, p,
int, f, int, d,
__off64_t, o)
LSS_INLINE int LSS_NAME(sigaction)(int signum, LSS_INLINE int LSS_NAME(sigaction)(int signum,
const struct kernel_sigaction *act, const struct kernel_sigaction *act,
struct kernel_sigaction *oldact) { struct kernel_sigaction *oldact) {
@ -2542,26 +2626,30 @@ struct kernel_stat {
} }
} }
#if defined(__i386__) || \ #if defined(__i386__) || \
defined(__arm__) || \ defined(__arm__) || \
(defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || defined(__PPC__) (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || \
defined(__PPC__) || \
(defined(__s390__) && !defined(__s390x__))
#define __NR__sigaction __NR_sigaction #define __NR__sigaction __NR_sigaction
#define __NR__sigprocmask __NR_sigprocmask #define __NR__sigprocmask __NR_sigprocmask
LSS_INLINE _syscall2(int, fstat64, int, f, LSS_INLINE _syscall2(int, fstat64, int, f,
struct kernel_stat64 *, b) struct kernel_stat64 *, b)
LSS_INLINE _syscall5(int, _llseek, uint, fd, ulong, hi, ulong, lo, LSS_INLINE _syscall5(int, _llseek, uint, fd, ulong, hi, ulong, lo,
loff_t *, res, uint, wh) loff_t *, res, uint, wh)
#ifdef __PPC64__ #if defined(__s390__) && !defined(__s390x__)
LSS_INLINE _syscall6(void*, mmap, void*, s, /* On s390, mmap2() arguments are passed in memory. */
size_t, l, int, p, LSS_INLINE void* LSS_NAME(_mmap2)(void *s, size_t l, int p, int f, int d,
int, f, int, d, off_t o) {
off_t, o) unsigned long buf[6] = { (unsigned long) s, (unsigned long) l,
#else (unsigned long) p, (unsigned long) f,
#ifndef __ARM_EABI__ (unsigned long) d, (unsigned long) o };
/* Not available on ARM EABI Linux. */ LSS_REG(2, buf);
LSS_INLINE _syscall1(void*, mmap, void*, a) LSS_BODY(void*, mmap2, "0"(__r2));
#endif }
LSS_INLINE _syscall6(void*, mmap2, void*, s, #elif !defined(__PPC64__)
#define __NR__mmap2 __NR_mmap2
LSS_INLINE _syscall6(void*, _mmap2, void*, s,
size_t, l, int, p, size_t, l, int, p,
int, f, int, d, int, f, int, d,
off_t, o) off_t, o)
@ -2654,11 +2742,48 @@ struct kernel_stat {
return rc; return rc;
} }
#endif #endif
#if defined(__i386__) || \
defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) || \
(defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || \
(defined(__PPC__) && !defined(__PPC64__)) || \
(defined(__s390__) && !defined(__s390x__))
/* On these architectures, implement mmap() with mmap2(). */
LSS_INLINE void* LSS_NAME(mmap)(void *s, size_t l, int p, int f, int d,
int64_t o) {
if (o % 4096) {
LSS_ERRNO = EINVAL;
return (void *) -1;
}
return LSS_NAME(_mmap2)(s, l, p, f, d, (o / 4096));
}
#elif defined(__s390x__)
/* On s390x, mmap() arguments are passed in memory. */
LSS_INLINE void* LSS_NAME(mmap)(void *s, size_t l, int p, int f, int d,
int64_t o) {
unsigned long buf[6] = { (unsigned long) s, (unsigned long) l,
(unsigned long) p, (unsigned long) f,
(unsigned long) d, (unsigned long) o };
LSS_REG(2, buf);
LSS_BODY(void*, mmap, "0"(__r2));
}
#elif defined(__x86_64__)
/* Need to make sure __off64_t isn't truncated to 32-bits under x32. */
LSS_INLINE void* LSS_NAME(mmap)(void *s, size_t l, int p, int f, int d,
int64_t o) {
LSS_BODY(6, void*, mmap, LSS_SYSCALL_ARG(s), LSS_SYSCALL_ARG(l),
LSS_SYSCALL_ARG(p), LSS_SYSCALL_ARG(f),
LSS_SYSCALL_ARG(d), (uint64_t)(o));
}
#else
/* Remaining 64-bit architectures. */
LSS_INLINE _syscall6(void*, mmap, void*, addr, size_t, length, int, prot,
int, flags, int, fd, int64_t, offset)
#endif
#if defined(__i386__) || \ #if defined(__i386__) || \
defined(__PPC__) || \ defined(__PPC__) || \
(defined(__arm__) && !defined(__ARM_EABI__)) || \ (defined(__arm__) && !defined(__ARM_EABI__)) || \
(defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || \ (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || \
defined(__s390x__) defined(__s390__)
/* See sys_socketcall in net/socket.c in kernel source. /* See sys_socketcall in net/socket.c in kernel source.
* It de-multiplexes on its first arg and unpacks the arglist * It de-multiplexes on its first arg and unpacks the arglist

View File

@ -42,7 +42,7 @@
*/ */
#if (defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \ #if (defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \
defined(__mips__) || defined(__PPC__) || defined(__aarch64__) || \ defined(__mips__) || defined(__PPC__) || defined(__aarch64__) || \
defined(__s390x__)) && defined(__linux) defined(__s390__)) && defined(__linux)
/* Define the THREADS symbol to make sure that there is exactly one core dumper /* Define the THREADS symbol to make sure that there is exactly one core dumper
* built into the library. * built into the library.

View File

@ -218,9 +218,6 @@
*/ */
#define LT_OBJDIR ".libs/" #define LT_OBJDIR ".libs/"
/* Define to 'volatile' if __malloc_hook is declared volatile */
#define MALLOC_HOOK_MAYBE_VOLATILE volatile
/* Name of package */ /* Name of package */
#define PACKAGE "gperftools" #define PACKAGE "gperftools"

View File

@ -278,8 +278,8 @@ class MallocBlock {
// We use either do_malloc or mmap to make the actual allocation. In // We use either do_malloc or mmap to make the actual allocation. In
// order to remember which one of the two was used for any block, we store an // order to remember which one of the two was used for any block, we store an
// appropriate magic word next to the block. // appropriate magic word next to the block.
static const int kMagicMalloc = 0xDEADBEEF; static const size_t kMagicMalloc = 0xDEADBEEF;
static const int kMagicMMap = 0xABCDEFAB; static const size_t kMagicMMap = 0xABCDEFAB;
// This array will be filled with 0xCD, for use with memcmp. // This array will be filled with 0xCD, for use with memcmp.
static unsigned char kMagicDeletedBuffer[1024]; static unsigned char kMagicDeletedBuffer[1024];
@ -305,7 +305,7 @@ class MallocBlock {
// then come the size2_ and magic2_, or a full page of mprotect-ed memory // then come the size2_ and magic2_, or a full page of mprotect-ed memory
// if the malloc_page_fence feature is enabled. // if the malloc_page_fence feature is enabled.
size_t size2_; size_t size2_;
int magic2_; size_t magic2_;
private: // static data and helpers private: // static data and helpers
@ -348,7 +348,7 @@ class MallocBlock {
bool IsMMapped() const { return kMagicMMap == magic1_; } bool IsMMapped() const { return kMagicMMap == magic1_; }
bool IsValidMagicValue(int value) const { bool IsValidMagicValue(size_t value) const {
return kMagicMMap == value || kMagicMalloc == value; return kMagicMMap == value || kMagicMalloc == value;
} }
@ -381,8 +381,8 @@ class MallocBlock {
return (const size_t*)((char*)&size2_ + size1_); return (const size_t*)((char*)&size2_ + size1_);
} }
int* magic2_addr() { return (int*)(size2_addr() + 1); } size_t* magic2_addr() { return (size_t*)(size2_addr() + 1); }
const int* magic2_addr() const { return (const int*)(size2_addr() + 1); } const size_t* magic2_addr() const { return (const size_t*)(size2_addr() + 1); }
private: // other helpers private: // other helpers
@ -400,14 +400,14 @@ class MallocBlock {
offset_ = 0; offset_ = 0;
alloc_type_ = type; alloc_type_ = type;
if (!IsMMapped()) { if (!IsMMapped()) {
*magic2_addr() = magic1_; bit_store(magic2_addr(), &magic1_);
*size2_addr() = size; bit_store(size2_addr(), &size);
} }
alloc_map_lock_.Unlock(); alloc_map_lock_.Unlock();
memset(data_addr(), kMagicUninitializedByte, size); memset(data_addr(), kMagicUninitializedByte, size);
if (!IsMMapped()) { if (!IsMMapped()) {
RAW_CHECK(size1_ == *size2_addr(), "should hold"); RAW_CHECK(memcmp(&size1_, size2_addr(), sizeof(size1_)) == 0, "should hold");
RAW_CHECK(magic1_ == *magic2_addr(), "should hold"); RAW_CHECK(memcmp(&magic1_, magic2_addr(), sizeof(magic1_)) == 0, "should hold");
} }
} }
@ -415,7 +415,7 @@ class MallocBlock {
alloc_map_lock_.Lock(); alloc_map_lock_.Lock();
CheckLocked(type); CheckLocked(type);
if (!IsMMapped()) { if (!IsMMapped()) {
RAW_CHECK(size1_ == *size2_addr(), "should hold"); RAW_CHECK(memcmp(&size1_, size2_addr(), sizeof(size1_)) == 0, "should hold");
} }
// record us as deallocated in the map // record us as deallocated in the map
alloc_map_->Insert(data_addr(), type | kDeallocatedTypeBit); alloc_map_->Insert(data_addr(), type | kDeallocatedTypeBit);
@ -457,11 +457,13 @@ class MallocBlock {
data_addr()); data_addr());
} }
if (!IsMMapped()) { if (!IsMMapped()) {
if (size1_ != *size2_addr()) { if (memcmp(&size1_, size2_addr(), sizeof(size1_))) {
RAW_LOG(FATAL, "memory stomping bug: a word after object at %p " RAW_LOG(FATAL, "memory stomping bug: a word after object at %p "
"has been corrupted", data_addr()); "has been corrupted", data_addr());
} }
if (!IsValidMagicValue(*magic2_addr())) { size_t addr;
bit_store(&addr, magic2_addr());
if (!IsValidMagicValue(addr)) {
RAW_LOG(FATAL, "memory stomping bug: a word after object at %p " RAW_LOG(FATAL, "memory stomping bug: a word after object at %p "
"has been corrupted", data_addr()); "has been corrupted", data_addr());
} }
@ -845,8 +847,8 @@ void DanglingWriteChecker() {
// ========================================================================= // // ========================================================================= //
const int MallocBlock::kMagicMalloc; const size_t MallocBlock::kMagicMalloc;
const int MallocBlock::kMagicMMap; const size_t MallocBlock::kMagicMMap;
MallocBlock::AllocMap* MallocBlock::alloc_map_ = NULL; MallocBlock::AllocMap* MallocBlock::alloc_map_ = NULL;
SpinLock MallocBlock::alloc_map_lock_(SpinLock::LINKER_INITIALIZED); SpinLock MallocBlock::alloc_map_lock_(SpinLock::LINKER_INITIALIZED);

View File

@ -179,7 +179,12 @@ inline void* GetPC(const struct ucontext_t& signal_ucontext) {
// configure.ac (or set it manually in your config.h). // configure.ac (or set it manually in your config.h).
#else #else
inline void* GetPC(const ucontext_t& signal_ucontext) { inline void* GetPC(const ucontext_t& signal_ucontext) {
#if defined(__s390__) && !defined(__s390x__)
// Mask out the AMODE31 bit from the PC recorded in the context.
return (void*)((unsigned long)signal_ucontext.PC_FROM_UCONTEXT & 0x7fffffffUL);
#else
return (void*)signal_ucontext.PC_FROM_UCONTEXT; // defined in config.h return (void*)signal_ucontext.PC_FROM_UCONTEXT; // defined in config.h
#endif
} }
#endif #endif

View File

@ -86,60 +86,6 @@ extern "C" {
#endif // #if defined(__GNUC__) && !defined(__MACH__) #endif // #if defined(__GNUC__) && !defined(__MACH__)
// We also have to hook libc malloc. While our work with weak symbols
// should make sure libc malloc is never called in most situations, it
// can be worked around by shared libraries with the DEEPBIND
// environment variable set. The below hooks libc to call our malloc
// routines even in that situation. In other situations, this hook
// should never be called.
extern "C" {
static void* glibc_override_malloc(size_t size, const void *caller) {
return tc_malloc(size);
}
static void* glibc_override_realloc(void *ptr, size_t size,
const void *caller) {
return tc_realloc(ptr, size);
}
static void glibc_override_free(void *ptr, const void *caller) {
tc_free(ptr);
}
static void* glibc_override_memalign(size_t align, size_t size,
const void *caller) {
return tc_memalign(align, size);
}
// We should be using __malloc_initialize_hook here, like the #if 0
// code below. (See http://swoolley.org/man.cgi/3/malloc_hook.)
// However, this causes weird linker errors with programs that link
// with -static, so instead we just assign the vars directly at
// static-constructor time. That should serve the same effect of
// making sure the hooks are set before the first malloc call the
// program makes.
#if 0
#include <malloc.h> // for __malloc_hook, etc.
void glibc_override_malloc_init_hook(void) {
__malloc_hook = glibc_override_malloc;
__realloc_hook = glibc_override_realloc;
__free_hook = glibc_override_free;
__memalign_hook = glibc_override_memalign;
}
void (* MALLOC_HOOK_MAYBE_VOLATILE __malloc_initialize_hook)(void)
= &glibc_override_malloc_init_hook;
#endif
void* (* MALLOC_HOOK_MAYBE_VOLATILE __malloc_hook)(size_t, const void*)
= &glibc_override_malloc;
void* (* MALLOC_HOOK_MAYBE_VOLATILE __realloc_hook)(void*, size_t, const void*)
= &glibc_override_realloc;
void (* MALLOC_HOOK_MAYBE_VOLATILE __free_hook)(void*, const void*)
= &glibc_override_free;
void* (* MALLOC_HOOK_MAYBE_VOLATILE __memalign_hook)(size_t,size_t, const void*)
= &glibc_override_memalign;
} // extern "C"
// No need to write ReplaceSystemAlloc(); one of the #includes above // No need to write ReplaceSystemAlloc(); one of the #includes above
// did it for us. // did it for us.

View File

@ -211,6 +211,33 @@ extern "C" {
size_t malloc_usable_size(void* p) { return tc_malloc_size(p); } size_t malloc_usable_size(void* p) { return tc_malloc_size(p); }
} // extern "C" } // extern "C"
static malloc_zone_t *get_default_zone() {
malloc_zone_t **zones = NULL;
unsigned int num_zones = 0;
/*
* On OSX 10.12, malloc_default_zone returns a special zone that is not
* present in the list of registered zones. That zone uses a "lite zone"
* if one is present (apparently enabled when malloc stack logging is
* enabled), or the first registered zone otherwise. In practice this
* means unless malloc stack logging is enabled, the first registered
* zone is the default.
* So get the list of zones to get the first one, instead of relying on
* malloc_default_zone.
*/
if (KERN_SUCCESS != malloc_get_all_zones(0, NULL, (vm_address_t**) &zones,
&num_zones)) {
/* Reset the value in case the failure happened after it was set. */
num_zones = 0;
}
if (num_zones)
return zones[0];
return malloc_default_zone();
}
static void ReplaceSystemAlloc() { static void ReplaceSystemAlloc() {
static malloc_introspection_t tcmalloc_introspection; static malloc_introspection_t tcmalloc_introspection;
memset(&tcmalloc_introspection, 0, sizeof(tcmalloc_introspection)); memset(&tcmalloc_introspection, 0, sizeof(tcmalloc_introspection));
@ -273,7 +300,7 @@ static void ReplaceSystemAlloc() {
// zone. The default zone is then re-registered to ensure that // zone. The default zone is then re-registered to ensure that
// allocations made from it earlier will be handled correctly. // allocations made from it earlier will be handled correctly.
// Things are not guaranteed to work that way, but it's how they work now. // Things are not guaranteed to work that way, but it's how they work now.
malloc_zone_t *default_zone = malloc_default_zone(); malloc_zone_t *default_zone = get_default_zone();
malloc_zone_unregister(default_zone); malloc_zone_unregister(default_zone);
malloc_zone_register(default_zone); malloc_zone_register(default_zone);
} }

View File

@ -52,7 +52,7 @@
// I test for 64-bit first so I don't have to do things like // I test for 64-bit first so I don't have to do things like
// '#if (defined(__mips__) && !defined(__MIPS64__))' as a mips32 check. // '#if (defined(__mips__) && !defined(__MIPS64__))' as a mips32 check.
#if defined(__x86_64__) || defined(__PPC64__) || defined(__aarch64__) || (defined(_MIPS_SIM) && _MIPS_SIM == _ABI64) #if defined(__x86_64__) || defined(__PPC64__) || defined(__aarch64__) || (defined(_MIPS_SIM) && _MIPS_SIM == _ABI64) || defined(__s390__)
static inline void* do_mmap64(void *start, size_t length, static inline void* do_mmap64(void *start, size_t length,
int prot, int flags, int prot, int flags,
@ -119,20 +119,6 @@ static inline void* do_mmap64(void *start, size_t length,
#define MALLOC_HOOK_HAVE_DO_MMAP64 1 #define MALLOC_HOOK_HAVE_DO_MMAP64 1
#elif defined(__s390x__)
static inline void* do_mmap64(void *start, size_t length,
int prot, int flags,
int fd, __off64_t offset) __THROW {
// mmap on s390x uses the old syscall interface
unsigned long args[6] = { (unsigned long) start, (unsigned long) length,
(unsigned long) prot, (unsigned long) flags,
(unsigned long) fd, (unsigned long) offset };
return sys_mmap(args);
}
#define MALLOC_HOOK_HAVE_DO_MMAP64 1
#endif // #if defined(__x86_64__) #endif // #if defined(__x86_64__)

View File

@ -26,7 +26,7 @@ add_library (dbms
src/Server/TCPHandler.h src/Server/TCPHandler.h
src/Server/HTTPHandler.h src/Server/HTTPHandler.h
src/Server/MetricsTransmitter.h src/Server/MetricsTransmitter.h
src/Server/UsersConfigReloader.h src/Server/ConfigReloader.h
src/Server/StatusFile.h src/Server/StatusFile.h
src/Server/ReplicasStatusHandler.h src/Server/ReplicasStatusHandler.h
src/Client/InterruptListener.h src/Client/InterruptListener.h

View File

@ -44,6 +44,12 @@ public:
*/ */
ConfigurationPtr loadConfig(const std::string & path); ConfigurationPtr loadConfig(const std::string & path);
public:
using Files = std::list<std::string>;
static Files getConfigMergeFiles(const std::string & config_path);
private: private:
Logger * log; Logger * log;
Poco::AutoPtr<Poco::Channel> channel_ptr; Poco::AutoPtr<Poco::Channel> channel_ptr;

View File

@ -83,7 +83,6 @@
\ \
M(ReplicaYieldLeadership) \ M(ReplicaYieldLeadership) \
M(ReplicaPartialShutdown) \ M(ReplicaPartialShutdown) \
M(ReplicaPermanentlyReadonly) \
\ \
M(SelectedParts) \ M(SelectedParts) \
M(SelectedRanges) \ M(SelectedRanges) \

View File

@ -15,9 +15,6 @@
#define DBMS_DEFAULT_PING_TIMEOUT_SEC 5 #define DBMS_DEFAULT_PING_TIMEOUT_SEC 5
#define DBMS_DEFAULT_POLL_INTERVAL 10 #define DBMS_DEFAULT_POLL_INTERVAL 10
/// Насколько секунд можно максимально задерживать вставку в таблицу типа MergeTree, если в ней много недомердженных кусков.
#define DBMS_MAX_DELAY_OF_INSERT 200.0
/// Размер буфера ввода-вывода по-умолчанию. /// Размер буфера ввода-вывода по-умолчанию.
#define DBMS_DEFAULT_BUFFER_SIZE 1048576ULL #define DBMS_DEFAULT_BUFFER_SIZE 1048576ULL

View File

@ -7,6 +7,8 @@
#include <DB/Interpreters/Context.h> #include <DB/Interpreters/Context.h>
#include <DB/Client/ConnectionPool.h> #include <DB/Client/ConnectionPool.h>
#include <DB/Client/MultiplexedConnections.h> #include <DB/Client/MultiplexedConnections.h>
#include <DB/Interpreters/Cluster.h>
namespace DB namespace DB
{ {
@ -29,7 +31,7 @@ public:
const Context & context_ = getDefaultContext()); const Context & context_ = getDefaultContext());
/// Принимает пул, из которого нужно будет достать одно или несколько соединений. /// Принимает пул, из которого нужно будет достать одно или несколько соединений.
RemoteBlockInputStream(IConnectionPool * pool_, const String & query_, const Settings * settings_, RemoteBlockInputStream(ConnectionPoolPtr & pool_, const String & query_, const Settings * settings_,
ThrottlerPtr throttler_ = nullptr, const Tables & external_tables_ = Tables(), ThrottlerPtr throttler_ = nullptr, const Tables & external_tables_ = Tables(),
QueryProcessingStage::Enum stage_ = QueryProcessingStage::Complete, QueryProcessingStage::Enum stage_ = QueryProcessingStage::Complete,
const Context & context_ = getDefaultContext()); const Context & context_ = getDefaultContext());
@ -110,7 +112,7 @@ private:
Connection * connection = nullptr; Connection * connection = nullptr;
/// Пул соединений одного шарда. /// Пул соединений одного шарда.
IConnectionPool * pool = nullptr; ConnectionPoolPtr pool = nullptr;
/// Пулы соединений одного или нескольких шардов. /// Пулы соединений одного или нескольких шардов.
ConnectionPoolsPtr pools; ConnectionPoolsPtr pools;

View File

@ -10,6 +10,7 @@
#include <ext/range.hpp> #include <ext/range.hpp>
#include <Poco/Util/AbstractConfiguration.h> #include <Poco/Util/AbstractConfiguration.h>
#include "writeParenthesisedString.h" #include "writeParenthesisedString.h"
#include <memory>
namespace DB namespace DB
@ -45,7 +46,7 @@ public:
query_builder{dict_struct, db, table, where}, query_builder{dict_struct, db, table, where},
sample_block{sample_block}, context(context), sample_block{sample_block}, context(context),
is_local{isLocalAddress({ host, port })}, is_local{isLocalAddress({ host, port })},
pool{is_local ? nullptr : std::make_unique<ConnectionPool>( pool{is_local ? nullptr : std::make_shared<ConnectionPool>(
max_connections, host, port, db, user, password, max_connections, host, port, db, user, password,
"ClickHouseDictionarySource") "ClickHouseDictionarySource")
}, },
@ -61,7 +62,7 @@ public:
query_builder{dict_struct, db, table, where}, query_builder{dict_struct, db, table, where},
sample_block{other.sample_block}, context(other.context), sample_block{other.sample_block}, context(other.context),
is_local{other.is_local}, is_local{other.is_local},
pool{is_local ? nullptr : std::make_unique<ConnectionPool>( pool{is_local ? nullptr : std::make_shared<ConnectionPool>(
max_connections, host, port, db, user, password, max_connections, host, port, db, user, password,
"ClickHouseDictionarySource")}, "ClickHouseDictionarySource")},
load_all_query{other.load_all_query} load_all_query{other.load_all_query}
@ -74,7 +75,7 @@ public:
*/ */
if (is_local) if (is_local)
return executeQuery(load_all_query, context, true).in; return executeQuery(load_all_query, context, true).in;
return std::make_shared<RemoteBlockInputStream>(pool.get(), load_all_query, nullptr); return std::make_shared<RemoteBlockInputStream>(pool, load_all_query, nullptr);
} }
BlockInputStreamPtr loadIds(const std::vector<std::uint64_t> & ids) override BlockInputStreamPtr loadIds(const std::vector<std::uint64_t> & ids) override
@ -107,7 +108,7 @@ private:
{ {
if (is_local) if (is_local)
return executeQuery(query, context, true).in; return executeQuery(query, context, true).in;
return std::make_shared<RemoteBlockInputStream>(pool.get(), query, nullptr); return std::make_shared<RemoteBlockInputStream>(pool, query, nullptr);
} }
@ -123,7 +124,7 @@ private:
Block sample_block; Block sample_block;
Context & context; Context & context;
const bool is_local; const bool is_local;
std::unique_ptr<ConnectionPool> pool; ConnectionPoolPtr pool;
const std::string load_all_query; const std::string load_all_query;
}; };

View File

@ -11,11 +11,11 @@
namespace DB namespace DB
{ {
/** Функции работы с URL. /** URL processing functions.
* Все функции работают не по RFC - то есть, максимально упрощены ради производительности. * All functions are not strictly follow RFC, instead they are maximally simplified for performance reasons.
* *
* Функции, извлекающие часть URL-а. * Functions for extraction parts of URL.
* Если в URL-е нет ничего похожего, то возвращается пустая строка. * If URL has nothing like, then empty string is returned.
* *
* domain * domain
* domainWithoutWWW * domainWithoutWWW
@ -26,29 +26,29 @@ namespace DB
* fragment * fragment
* queryStringAndFragment * queryStringAndFragment
* *
* Функции, удаляющие часть из URL-а. * Functions, removing parts from URL.
* Если в URL-е нет ничего похожего, то URL остаётся без изменений. * If URL has nothing like, then it is retured unchanged.
* *
* cutWWW * cutWWW
* cutFragment * cutFragment
* cutQueryString * cutQueryString
* cutQueryStringAndFragment * cutQueryStringAndFragment
* *
* Извлечь значение параметра в URL, если он есть. Вернуть пустую строку, если его нет. * Extract value of parameter in query string or in fragment identifier. Return empty string, if URL has no such parameter.
* Если таких параметров много - вернуть значение первого. Значение не разэскейпливается. * If there are many parameters with same name - return value of first one. Value is not %-decoded.
* *
* extractURLParameter(URL, name) * extractURLParameter(URL, name)
* *
* Извлечь все параметры из URL в виде массива строк вида name=value. * Extract all parameters from URL in form of array of strings name=value.
* extractURLParameters(URL) * extractURLParameters(URL)
* *
* Извлечь все имена параметров из URL в виде массива строк * Extract names of all parameters from URL in form of array of strings.
* extractURLParameterNames(URL) * extractURLParameterNames(URL)
* *
* Убрать указанный параметр из URL. * Remove specified parameter from URL.
* cutURLParameter(URL, name) * cutURLParameter(URL, name)
* *
* Получить массив иерархии URL. См. функцию nextURLInHierarchy в URLParser. * Get array of URL 'hierarchy' as in Yandex.Metrica tree-like reports. See docs.
* URLHierarchy(URL) * URLHierarchy(URL)
*/ */
@ -164,10 +164,10 @@ struct ExtractFirstSignificantSubdomain
if (!last_3_periods[2]) if (!last_3_periods[2])
last_3_periods[2] = begin - 1; last_3_periods[2] = begin - 1;
if (!strncmp(last_3_periods[1] + 1, "com", 3) || if (!strncmp(last_3_periods[1] + 1, "com.", 4) /// Note that in ColumnString every value has zero byte after it.
!strncmp(last_3_periods[1] + 1, "net", 3) || || !strncmp(last_3_periods[1] + 1, "net.", 4)
!strncmp(last_3_periods[1] + 1, "org", 3) || || !strncmp(last_3_periods[1] + 1, "org.", 4)
!strncmp(last_3_periods[1] + 1, "co", 2)) || !strncmp(last_3_periods[1] + 1, "co.", 3))
{ {
res_data += last_3_periods[2] + 1 - begin; res_data += last_3_periods[2] + 1 - begin;
res_size = last_3_periods[1] - last_3_periods[2] - 1; res_size = last_3_periods[1] - last_3_periods[2] - 1;

View File

@ -36,7 +36,7 @@ namespace ErrorCodes
extern const int CANNOT_READ_ARRAY_FROM_TEXT; extern const int CANNOT_READ_ARRAY_FROM_TEXT;
} }
/// Функции-помошники для форматированного чтения /// Helper functions for formatted input.
inline char parseEscapeSequence(char c) inline char parseEscapeSequence(char c)
{ {
@ -79,7 +79,7 @@ inline char unhex(char c)
} }
/// Эти функции находятся в VarInt.h /// These functions are located in VarInt.h
/// inline void throwReadAfterEOF() /// inline void throwReadAfterEOF()
@ -95,7 +95,7 @@ inline void readChar(char & x, ReadBuffer & buf)
} }
/// Чтение POD-типа в native формате /// Read POD-type in native format
template <typename T> template <typename T>
inline void readPODBinary(T & x, ReadBuffer & buf) inline void readPODBinary(T & x, ReadBuffer & buf)
{ {
@ -250,11 +250,11 @@ bool tryReadIntText(T & x, ReadBuffer & buf)
return readIntTextImpl<T, bool>(x, buf); return readIntTextImpl<T, bool>(x, buf);
} }
/** Более оптимизированная версия (примерно в 1.5 раза на реальных данных). /** More efficient variant (about 1.5 times on real dataset).
* Отличается тем, что: * Differs in following:
* - из чисел, начинающихся на ноль, парсится только ноль; * - for numbers starting with zero, parsed only zero;
* - не поддерживается символ '+' перед числом; * - symbol '+' before number is not supported;
* - символы :;<=>? парсятся, как некоторые цифры. * - symbols :;<=>? are parsed as some numbers.
*/ */
template <typename T, bool throw_on_error = true> template <typename T, bool throw_on_error = true>
void readIntTextUnsafe(T & x, ReadBuffer & buf) void readIntTextUnsafe(T & x, ReadBuffer & buf)
@ -279,7 +279,7 @@ void readIntTextUnsafe(T & x, ReadBuffer & buf)
return on_error(); return on_error();
} }
if (*buf.position() == '0') /// В реальных данных много нулей. if (*buf.position() == '0') /// There are many zeros in real datasets.
{ {
++buf.position(); ++buf.position();
return; return;
@ -287,7 +287,7 @@ void readIntTextUnsafe(T & x, ReadBuffer & buf)
while (!buf.eof()) while (!buf.eof())
{ {
if ((*buf.position() & 0xF0) == 0x30) /// Имеет смысл, что это условие находится внутри цикла. if ((*buf.position() & 0xF0) == 0x30) /// It makes sense to have this condition inside loop.
{ {
x *= 10; x *= 10;
x += *buf.position() & 0x0F; x += *buf.position() & 0x0F;
@ -451,7 +451,7 @@ inline void readFloatText(T & x, ReadBuffer & buf)
readFloatTextImpl<T, void>(x, buf); readFloatTextImpl<T, void>(x, buf);
} }
/// грубо; всё до '\n' или '\t' /// rough; all until '\n' or '\t'
void readString(String & s, ReadBuffer & buf); void readString(String & s, ReadBuffer & buf);
void readEscapedString(String & s, ReadBuffer & buf); void readEscapedString(String & s, ReadBuffer & buf);
@ -467,21 +467,21 @@ void readBackQuotedString(String & s, ReadBuffer & buf);
void readStringUntilEOF(String & s, ReadBuffer & buf); void readStringUntilEOF(String & s, ReadBuffer & buf);
/** Прочитать строку в формате CSV. /** Read string in CSV format.
* Правила: * Parsing rules:
* - строка может быть заключена в кавычки; кавычки могут быть одинарными: ' или двойными: "; * - string could be placed in quotes; quotes could be single: ' or double: ";
* - а может быть не заключена в кавычки - это определяется по первому символу; * - or string could be unquoted - this is determined by first character;
* - если строка не заключена в кавычки, то она читается до следующего разделителя, * - if string is unquoted, then it is read until next delimiter,
* либо до перевода строки (CR или LF), * either until end of line (CR or LF),
* либо до конца потока; * or until end of stream;
* при этом, пробелы и табы на конце строки съедаются, но игнорируются. * but spaces and tabs at begin and end of unquoted string are consumed but ignored (note that this behaviour differs from RFC).
* - если строка заключена в кавычки, то она читается до закрывающей кавычки, * - if string is in quotes, then it will be read until closing quote,
* но при этом, последовательность двух подряд идущих кавычек, считается одной кавычкой внутри строки; * but sequences of two consecutive quotes are parsed as single quote inside string;
*/ */
void readCSVString(String & s, ReadBuffer & buf, const char delimiter = ','); void readCSVString(String & s, ReadBuffer & buf, const char delimiter = ',');
/// Добавляют прочитанный результат в массив символов. /// Read and append result to array of characters.
template <typename Vector> template <typename Vector>
void readStringInto(Vector & s, ReadBuffer & buf); void readStringInto(Vector & s, ReadBuffer & buf);
@ -506,7 +506,7 @@ void readCSVStringInto(Vector & s, ReadBuffer & buf, const char delimiter = ',')
template <typename Vector> template <typename Vector>
void readJSONStringInto(Vector & s, ReadBuffer & buf); void readJSONStringInto(Vector & s, ReadBuffer & buf);
/// Это можно использовать в качестве параметра шаблона для функций выше, если данные не надо никуда считывать, но нужно просто пропустить. /// This could be used as template parameter for functions above, if you want to just skip data.
struct NullSink struct NullSink
{ {
void append(const char *, size_t) {}; void append(const char *, size_t) {};
@ -514,7 +514,7 @@ struct NullSink
}; };
/// в формате YYYY-MM-DD /// In YYYY-MM-DD format
inline void readDateText(DayNum_t & date, ReadBuffer & buf) inline void readDateText(DayNum_t & date, ReadBuffer & buf)
{ {
char s[10]; char s[10];
@ -554,18 +554,18 @@ inline T parse(const char * data, size_t size);
void readDateTimeTextFallback(time_t & datetime, ReadBuffer & buf); void readDateTimeTextFallback(time_t & datetime, ReadBuffer & buf);
/** В формате YYYY-MM-DD hh:mm:ss, согласно текущему часовому поясу /** In YYYY-MM-DD hh:mm:ss format, according to current time zone.
* В качестве исключения, также поддерживается парсинг из десятичного числа - unix timestamp. * As an exception, also supported parsing of unix timestamp in form of decimal number.
*/ */
inline void readDateTimeText(time_t & datetime, ReadBuffer & buf) inline void readDateTimeText(time_t & datetime, ReadBuffer & buf)
{ {
/** Считываем 10 символов, которые могут быть unix timestamp. /** Read 10 characters, that could represent unix timestamp.
* При этом, поддерживается только unix timestamp из 5-10 символов. * Only unix timestamp of 5-10 characters is supported.
* Потом смотрим на пятый символ. Если это число - парсим unix timestamp. * Then look at 5th charater. If it is a number - treat whole as unix timestamp.
* Если это не число - парсим YYYY-MM-DD hh:mm:ss. * If it is not a number - then parse datetime in YYYY-MM-DD hh:mm:ss format.
*/ */
/// Оптимистичный вариант, когда всё значение точно лежит в буфере. /// Optimistic path, when whole value are in buffer.
const char * s = buf.position(); const char * s = buf.position();
if (s + 19 < buf.buffer().end()) if (s + 19 < buf.buffer().end())
{ {
@ -587,7 +587,7 @@ inline void readDateTimeText(time_t & datetime, ReadBuffer & buf)
buf.position() += 19; buf.position() += 19;
} }
else else
/// Почему не readIntTextUnsafe? Дело в том, что для нужд AdFox, поддерживается парсинг unix timestamp с отбивкой нулями: 000...NNNN. /// Why not readIntTextUnsafe? Because for needs of AdFox, parsing of unix timestamp with leading zeros is supported: 000...NNNN.
readIntText(datetime, buf); readIntText(datetime, buf);
} }
else else
@ -614,7 +614,7 @@ inline void readDateTimeText(LocalDateTime & datetime, ReadBuffer & buf)
} }
/// Общие методы для чтения значения в бинарном формате. /// Generic methods to read value in native binary format.
inline void readBinary(UInt8 & x, ReadBuffer & buf) { readPODBinary(x, buf); } inline void readBinary(UInt8 & x, ReadBuffer & buf) { readPODBinary(x, buf); }
inline void readBinary(UInt16 & x, ReadBuffer & buf) { readPODBinary(x, buf); } inline void readBinary(UInt16 & x, ReadBuffer & buf) { readPODBinary(x, buf); }
inline void readBinary(UInt32 & x, ReadBuffer & buf) { readPODBinary(x, buf); } inline void readBinary(UInt32 & x, ReadBuffer & buf) { readPODBinary(x, buf); }
@ -634,7 +634,7 @@ inline void readBinary(LocalDate & x, ReadBuffer & buf) { readPODBinary(x, buf
inline void readBinary(LocalDateTime & x, ReadBuffer & buf) { readPODBinary(x, buf); } inline void readBinary(LocalDateTime & x, ReadBuffer & buf) { readPODBinary(x, buf); }
/// Общие методы для чтения значения в текстовом виде из tab-separated формата. /// Generic methods to read value in text tab-separated format.
inline void readText(UInt8 & x, ReadBuffer & buf) { readIntText(x, buf); } inline void readText(UInt8 & x, ReadBuffer & buf) { readIntText(x, buf); }
inline void readText(UInt16 & x, ReadBuffer & buf) { readIntText(x, buf); } inline void readText(UInt16 & x, ReadBuffer & buf) { readIntText(x, buf); }
inline void readText(UInt32 & x, ReadBuffer & buf) { readIntText(x, buf); } inline void readText(UInt32 & x, ReadBuffer & buf) { readIntText(x, buf); }
@ -653,7 +653,8 @@ inline void readText(LocalDate & x, ReadBuffer & buf) { readDateText(x, buf); }
inline void readText(LocalDateTime & x, ReadBuffer & buf) { readDateTimeText(x, buf); } inline void readText(LocalDateTime & x, ReadBuffer & buf) { readDateTimeText(x, buf); }
/// Общие методы для чтения значения в текстовом виде, при необходимости, в кавычках. /// Generic methods to read value in text format,
/// possibly in single quotes (only for data types that use quotes in VALUES format of INSERT statement in SQL).
inline void readQuoted(UInt8 & x, ReadBuffer & buf) { readIntText(x, buf); } inline void readQuoted(UInt8 & x, ReadBuffer & buf) { readIntText(x, buf); }
inline void readQuoted(UInt16 & x, ReadBuffer & buf) { readIntText(x, buf); } inline void readQuoted(UInt16 & x, ReadBuffer & buf) { readIntText(x, buf); }
inline void readQuoted(UInt32 & x, ReadBuffer & buf) { readIntText(x, buf); } inline void readQuoted(UInt32 & x, ReadBuffer & buf) { readIntText(x, buf); }
@ -684,7 +685,7 @@ inline void readQuoted(LocalDateTime & x, ReadBuffer & buf)
} }
/// В двойных кавычках /// Same as above, but in double quotes.
inline void readDoubleQuoted(UInt8 & x, ReadBuffer & buf) { readIntText(x, buf); } inline void readDoubleQuoted(UInt8 & x, ReadBuffer & buf) { readIntText(x, buf); }
inline void readDoubleQuoted(UInt16 & x, ReadBuffer & buf) { readIntText(x, buf); } inline void readDoubleQuoted(UInt16 & x, ReadBuffer & buf) { readIntText(x, buf); }
inline void readDoubleQuoted(UInt32 & x, ReadBuffer & buf) { readIntText(x, buf); } inline void readDoubleQuoted(UInt32 & x, ReadBuffer & buf) { readIntText(x, buf); }
@ -715,7 +716,7 @@ inline void readDoubleQuoted(LocalDateTime & x, ReadBuffer & buf)
} }
/// CSV, для чисел, дат, дат-с-временем: кавычки не обязательны, специального эскейпинга нет. /// CSV, for numbers, dates, datetimes: quotes are optional, no special escaping rules.
template <typename T> template <typename T>
inline void readCSVSimple(T & x, ReadBuffer & buf) inline void readCSVSimple(T & x, ReadBuffer & buf)
{ {
@ -817,7 +818,7 @@ void readText(std::vector<T> & x, ReadBuffer & buf)
} }
/// Пропустить пробельные символы. /// Skip whitespace characters.
inline void skipWhitespaceIfAny(ReadBuffer & buf) inline void skipWhitespaceIfAny(ReadBuffer & buf)
{ {
while (!buf.eof() && isWhitespaceASCII(*buf.position())) while (!buf.eof() && isWhitespaceASCII(*buf.position()))
@ -828,17 +829,17 @@ inline void skipWhitespaceIfAny(ReadBuffer & buf)
void skipJSONFieldPlain(ReadBuffer & buf, const StringRef & name_of_filed); void skipJSONFieldPlain(ReadBuffer & buf, const StringRef & name_of_filed);
/** Прочитать сериализованный эксепшен. /** Read serialized exception.
* При сериализации/десериализации часть информации теряется * During serialization/deserialization some information is lost
* (тип обрезается до базового, message заменяется на displayText, и stack trace дописывается в message) * (type is cut to base class, 'message' replaced by 'displayText', and stack trace is appended to 'message')
* К нему может быть добавлено дополнительное сообщение (например, вы можете указать, откуда оно было прочитано). * Some additional message could be appended to exception (example: you could add information about from where it was received).
*/ */
void readException(Exception & e, ReadBuffer & buf, const String & additional_message = ""); void readException(Exception & e, ReadBuffer & buf, const String & additional_message = "");
void readAndThrowException(ReadBuffer & buf, const String & additional_message = ""); void readAndThrowException(ReadBuffer & buf, const String & additional_message = "");
/** Вспомогательная функция /** Helper function for implementation.
*/ */
template <typename T> template <typename T>
static inline const char * tryReadIntText(T & x, const char * pos, const char * end) static inline const char * tryReadIntText(T & x, const char * pos, const char * end)
{ {
@ -885,7 +886,7 @@ static inline const char * tryReadIntText(T & x, const char * pos, const char *
} }
/// Простые для использования методы чтения чего-либо из строки в текстовом виде. /// Convenient methods for reading something from string in text format.
template <typename T> template <typename T>
inline T parse(const char * data, size_t size) inline T parse(const char * data, size_t size)
{ {

View File

@ -16,7 +16,7 @@ namespace DB
class Cluster class Cluster
{ {
public: public:
Cluster(const Settings & settings, const String & cluster_name); Cluster(Poco::Util::AbstractConfiguration & config, const Settings & settings, const String & cluster_name);
/// Построить кластер по именам шардов и реплик. Локальные обрабатываются так же как удаленные. /// Построить кластер по именам шардов и реплик. Локальные обрабатываются так же как удаленные.
Cluster(const Settings & settings, const std::vector<std::vector<String>> & names, Cluster(const Settings & settings, const std::vector<std::vector<String>> & names,
@ -56,7 +56,7 @@ public:
String default_database; /// this database is selected when no database is specified for Distributed table String default_database; /// this database is selected when no database is specified for Distributed table
UInt32 replica_num; UInt32 replica_num;
Address(const String & config_prefix); Address(Poco::Util::AbstractConfiguration & config, const String & config_prefix);
Address(const String & host_port_, const String & user_, const String & password_); Address(const String & host_port_, const String & user_, const String & password_);
}; };
@ -142,20 +142,31 @@ private:
size_t local_shard_count = 0; size_t local_shard_count = 0;
}; };
using ClusterPtr = std::shared_ptr<Cluster>;
class Clusters class Clusters
{ {
public: public:
Clusters(const Settings & settings, const String & config_name = "remote_servers"); Clusters(Poco::Util::AbstractConfiguration & config, const Settings & settings, const String & config_name = "remote_servers");
Clusters(const Clusters &) = delete; Clusters(const Clusters &) = delete;
Clusters & operator=(const Clusters &) = delete; Clusters & operator=(const Clusters &) = delete;
public: ClusterPtr getCluster(const std::string & cluster_name) const;
using Impl = std::map<String, Cluster>;
void updateClusters(Poco::Util::AbstractConfiguration & config, const Settings & settings, const String & config_name = "remote_servers");
public: public:
using Impl = std::map<String, ClusterPtr>;
Impl getContainer() const;
protected:
Impl impl; Impl impl;
mutable std::mutex mutex;
}; };
using ClustersPtr = std::shared_ptr<Clusters>;
} }

View File

@ -14,7 +14,7 @@ public:
AlterQueryConstructor() = default; AlterQueryConstructor() = default;
BlockInputStreamPtr createLocal(ASTPtr query_ast, const Context & context, const Cluster::Address & address) override; BlockInputStreamPtr createLocal(ASTPtr query_ast, const Context & context, const Cluster::Address & address) override;
BlockInputStreamPtr createRemote(IConnectionPool * pool, const std::string & query, BlockInputStreamPtr createRemote(ConnectionPoolPtr & pool, const std::string & query,
const Settings & settings, ThrottlerPtr throttler, const Context & context) override; const Settings & settings, ThrottlerPtr throttler, const Context & context) override;
BlockInputStreamPtr createRemote(ConnectionPoolsPtr & pools, const std::string & query, BlockInputStreamPtr createRemote(ConnectionPoolsPtr & pools, const std::string & query,
const Settings & settings, ThrottlerPtr throttler, const Context & context) override; const Settings & settings, ThrottlerPtr throttler, const Context & context) override;

View File

@ -14,7 +14,7 @@ public:
DescribeQueryConstructor() = default; DescribeQueryConstructor() = default;
BlockInputStreamPtr createLocal(ASTPtr query_ast, const Context & context, const Cluster::Address & address) override; BlockInputStreamPtr createLocal(ASTPtr query_ast, const Context & context, const Cluster::Address & address) override;
BlockInputStreamPtr createRemote(IConnectionPool * pool, const std::string & query, BlockInputStreamPtr createRemote(ConnectionPoolPtr & pool, const std::string & query,
const Settings & settings, ThrottlerPtr throttler, const Context & context) override; const Settings & settings, ThrottlerPtr throttler, const Context & context) override;
BlockInputStreamPtr createRemote(ConnectionPoolsPtr & pools, const std::string & query, BlockInputStreamPtr createRemote(ConnectionPoolsPtr & pools, const std::string & query,
const Settings & settings, ThrottlerPtr throttler, const Context & context) override; const Settings & settings, ThrottlerPtr throttler, const Context & context) override;

View File

@ -26,7 +26,7 @@ public:
/// Create an input stream for local query execution. /// Create an input stream for local query execution.
virtual BlockInputStreamPtr createLocal(ASTPtr query_ast, const Context & context, const Cluster::Address & address) = 0; virtual BlockInputStreamPtr createLocal(ASTPtr query_ast, const Context & context, const Cluster::Address & address) = 0;
/// Create an input stream for remote query execution on one shard. /// Create an input stream for remote query execution on one shard.
virtual BlockInputStreamPtr createRemote(IConnectionPool * pool, const std::string & query, virtual BlockInputStreamPtr createRemote(ConnectionPoolPtr & pool, const std::string & query,
const Settings & settings, ThrottlerPtr throttler, const Context & context) = 0; const Settings & settings, ThrottlerPtr throttler, const Context & context) = 0;
/// Create an input stream for remote query execution on one or more shards. /// Create an input stream for remote query execution on one or more shards.
virtual BlockInputStreamPtr createRemote(ConnectionPoolsPtr & pools, const std::string & query, virtual BlockInputStreamPtr createRemote(ConnectionPoolsPtr & pools, const std::string & query,

View File

@ -2,6 +2,7 @@
#include <DB/Parsers/IAST.h> #include <DB/Parsers/IAST.h>
#include <DB/Storages/IStorage.h> #include <DB/Storages/IStorage.h>
#include <DB/Interpreters/Cluster.h>
namespace DB namespace DB
{ {
@ -24,7 +25,7 @@ class IQueryConstructor;
class Query class Query
{ {
public: public:
Query(IQueryConstructor & query_constructor_, const Cluster & cluster_, Query(IQueryConstructor & query_constructor_, const ClusterPtr & cluster_,
ASTPtr query_ast_, const Context & context_, const Settings & settings_, bool enable_shard_multiplexing_); ASTPtr query_ast_, const Context & context_, const Settings & settings_, bool enable_shard_multiplexing_);
/// For each location at which we perform the query, create an input stream /// For each location at which we perform the query, create an input stream
@ -33,7 +34,7 @@ public:
private: private:
IQueryConstructor & query_constructor; IQueryConstructor & query_constructor;
const Cluster & cluster; ClusterPtr cluster;
ASTPtr query_ast; ASTPtr query_ast;
const Context & context; const Context & context;
const Settings & settings; const Settings & settings;

View File

@ -16,7 +16,7 @@ public:
SelectQueryConstructor(const QueryProcessingStage::Enum & processed_stage, const Tables & external_tables); SelectQueryConstructor(const QueryProcessingStage::Enum & processed_stage, const Tables & external_tables);
BlockInputStreamPtr createLocal(ASTPtr query_ast, const Context & context, const Cluster::Address & address) override; BlockInputStreamPtr createLocal(ASTPtr query_ast, const Context & context, const Cluster::Address & address) override;
BlockInputStreamPtr createRemote(IConnectionPool * pool, const std::string & query, BlockInputStreamPtr createRemote(ConnectionPoolPtr & pool, const std::string & query,
const Settings & settings, ThrottlerPtr throttler, const Context & context) override; const Settings & settings, ThrottlerPtr throttler, const Context & context) override;
BlockInputStreamPtr createRemote(ConnectionPoolsPtr & pools, const std::string & query, BlockInputStreamPtr createRemote(ConnectionPoolsPtr & pools, const std::string & query,
const Settings & settings, ThrottlerPtr throttler, const Context & context) override; const Settings & settings, ThrottlerPtr throttler, const Context & context) override;

View File

@ -118,7 +118,7 @@ public:
* Список пользователей полностью заменяется. * Список пользователей полностью заменяется.
* Накопленные значения у квоты не сбрасываются, если квота не удалена. * Накопленные значения у квоты не сбрасываются, если квота не удалена.
*/ */
void setUsersConfig(ConfigurationPtr config); void setUsersConfig(const ConfigurationPtr & config);
ConfigurationPtr getUsersConfig(); ConfigurationPtr getUsersConfig();
@ -278,8 +278,10 @@ public:
*/ */
void resetCaches() const; void resetCaches() const;
const Cluster & getCluster(const std::string & cluster_name) const; Clusters & getClusters() const;
std::shared_ptr<Clusters> getClusters() const; std::shared_ptr<Cluster> getCluster(const std::string & cluster_name) const;
std::shared_ptr<Cluster> tryGetCluster(const std::string & cluster_name) const;
void setClustersConfig(const ConfigurationPtr & config);
Compiler & getCompiler(); Compiler & getCompiler();
QueryLog & getQueryLog(); QueryLog & getQueryLog();

View File

@ -58,16 +58,16 @@ private:
std::uint64_t error_count; std::uint64_t error_count;
}; };
/** Имя словаря -> словарь. /** name -> dictionary.
*/ */
std::unordered_map<std::string, DictionaryInfo> dictionaries; std::unordered_map<std::string, DictionaryInfo> dictionaries;
/** Здесь находятся словари, которых ещё ни разу не удалось загрузить. /** Here are dictionaries, that has been never loaded sussessfully.
* В dictionaries они тоже присутствуют, но с нулевым указателем dict. * They are also in 'dictionaries', but with nullptr as 'dict'.
*/ */
std::unordered_map<std::string, FailedDictionaryInfo> failed_dictionaries; std::unordered_map<std::string, FailedDictionaryInfo> failed_dictionaries;
/** И для обычных и для failed_dictionaries. /** Both for dictionaries and failed_dictionaries.
*/ */
std::unordered_map<std::string, std::chrono::system_clock::time_point> update_times; std::unordered_map<std::string, std::chrono::system_clock::time_point> update_times;
@ -99,13 +99,13 @@ private:
} }
public: public:
/// Справочники будут обновляться в отдельном потоке, каждые reload_period секунд. /// Dictionaries will be loaded immediately and then will be updated in separate thread, each 'reload_period' seconds.
ExternalDictionaries(Context & context, const bool throw_on_error) ExternalDictionaries(Context & context, const bool throw_on_error)
: context(context), log(&Logger::get("ExternalDictionaries")) : context(context), log(&Logger::get("ExternalDictionaries"))
{ {
{ {
/** При синхронной загрузки внешних словарей в момент выполнения запроса, /** During synchronous loading of external dictionaries at moment of query execution,
* не нужно использовать ограничение на расход памяти запросом. * we should not use per query memory limit.
*/ */
struct TemporarilyDisableMemoryTracker struct TemporarilyDisableMemoryTracker
{ {

View File

@ -34,10 +34,10 @@ protected:
{ {
if (!database.empty()) if (!database.empty())
{ {
settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << database << (settings.hilite ? hilite_none : ""); settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << backQuoteIfNeed(database) << (settings.hilite ? hilite_none : "");
settings.ostr << "."; settings.ostr << ".";
} }
settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << table << (settings.hilite ? hilite_none : ""); settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << backQuoteIfNeed(table) << (settings.hilite ? hilite_none : "");
} }
settings.ostr << nl_or_ws; settings.ostr << nl_or_ws;
} }

View File

@ -3,6 +3,7 @@
#include <DB/Parsers/formatAST.h> #include <DB/Parsers/formatAST.h>
#include <DB/DataStreams/IBlockOutputStream.h> #include <DB/DataStreams/IBlockOutputStream.h>
#include <DB/Core/Block.h> #include <DB/Core/Block.h>
#include <DB/Interpreters/Cluster.h>
namespace DB namespace DB
{ {
@ -20,7 +21,7 @@ class StorageDistributed;
class DistributedBlockOutputStream : public IBlockOutputStream class DistributedBlockOutputStream : public IBlockOutputStream
{ {
public: public:
DistributedBlockOutputStream(StorageDistributed & storage, const ASTPtr & query_ast); DistributedBlockOutputStream(StorageDistributed & storage, const ASTPtr & query_ast, const ClusterPtr & cluster_);
void write(const Block & block) override; void write(const Block & block) override;
@ -38,6 +39,7 @@ private:
private: private:
StorageDistributed & storage; StorageDistributed & storage;
ASTPtr query_ast; ASTPtr query_ast;
ClusterPtr cluster;
}; };
} }

View File

@ -8,6 +8,7 @@
#include <mutex> #include <mutex>
#include <Poco/RWLock.h> #include <Poco/RWLock.h>
#include <Poco/Event.h> #include <Poco/Event.h>
#include <Poco/Timestamp.h>
#include <DB/Core/Types.h> #include <DB/Core/Types.h>
namespace DB namespace DB
@ -51,30 +52,29 @@ public:
Counters & local_counters; Counters & local_counters;
}; };
/// Возвращает true, если что-то получилось сделать. В таком случае поток не будет спать перед следующим вызовом. /// Returns true, if some useful work was done. In that case, thread will not sleep before next run of this task.
using Task = std::function<bool (Context & context)>; using Task = std::function<bool (Context & context)>;
class TaskInfo class TaskInfo
{ {
public: public:
/// Разбудить какой-нибудь поток. /// Wake up any thread.
void wake(); void wake();
TaskInfo(BackgroundProcessingPool & pool_, const Task & function_) : pool(pool_), function(function_) {}
private: private:
friend class BackgroundProcessingPool; friend class BackgroundProcessingPool;
BackgroundProcessingPool & pool; BackgroundProcessingPool & pool;
Task function; Task function;
/// При выполнении задачи, держится read lock. /// Read lock is hold when task is executed.
Poco::RWLock rwlock; Poco::RWLock rwlock;
std::atomic<bool> removed {false}; std::atomic<bool> removed {false};
std::atomic<time_t> next_time_to_execute {0}; /// Приоритет задачи. Для совпадающего времени в секундах берётся первая по списку задача.
std::list<std::shared_ptr<TaskInfo>>::iterator iterator; std::multimap<Poco::Timestamp, std::shared_ptr<TaskInfo>>::iterator iterator;
TaskInfo(BackgroundProcessingPool & pool_, const Task & function_) : pool(pool_), function(function_) {}
}; };
using TaskHandle = std::shared_ptr<TaskInfo>; using TaskHandle = std::shared_ptr<TaskInfo>;
@ -95,14 +95,14 @@ public:
~BackgroundProcessingPool(); ~BackgroundProcessingPool();
private: private:
using Tasks = std::list<TaskHandle>; using Tasks = std::multimap<Poco::Timestamp, TaskHandle>; /// key is desired next time to execute (priority).
using Threads = std::vector<std::thread>; using Threads = std::vector<std::thread>;
const size_t size; const size_t size;
static constexpr double sleep_seconds = 10; static constexpr double sleep_seconds = 10;
static constexpr double sleep_seconds_random_part = 1.0; static constexpr double sleep_seconds_random_part = 1.0;
Tasks tasks; /// Задачи в порядке, в котором мы планируем их выполнять. Tasks tasks; /// Ordered in priority.
std::mutex tasks_mutex; std::mutex tasks_mutex;
Counters counters; Counters counters;

View File

@ -55,9 +55,11 @@ struct MergeTreeSettings
/// Если в таблице хотя бы столько активных кусков, искусственно замедлять вставки в таблицу. /// Если в таблице хотя бы столько активных кусков, искусственно замедлять вставки в таблицу.
size_t parts_to_delay_insert = 150; size_t parts_to_delay_insert = 150;
/// Если в таблице parts_to_delay_insert + k кусков, спать insert_delay_step^k миллисекунд перед вставкой каждого блока. /// Если в таблице хотя бы столько активных кусков, выдавать ошибку 'Too much parts ...'
/// Таким образом, скорость вставок автоматически замедлится примерно до скорости слияний. size_t parts_to_throw_insert = 300;
double insert_delay_step = 1.1;
/// Насколько секунд можно максимально задерживать вставку в таблицу типа MergeTree, если в ней много недомердженных кусков.
size_t max_delay_to_insert = 200;
/** Настройки репликации. */ /** Настройки репликации. */
@ -127,7 +129,8 @@ struct MergeTreeSettings
SET_SIZE_T(old_parts_lifetime); SET_SIZE_T(old_parts_lifetime);
SET_SIZE_T(temporary_directories_lifetime); SET_SIZE_T(temporary_directories_lifetime);
SET_SIZE_T(parts_to_delay_insert); SET_SIZE_T(parts_to_delay_insert);
SET_DOUBLE(insert_delay_step); SET_SIZE_T(parts_to_throw_insert);
SET_SIZE_T(max_delay_to_insert);
SET_SIZE_T(replicated_deduplication_window); SET_SIZE_T(replicated_deduplication_window);
SET_SIZE_T(replicated_logs_to_keep); SET_SIZE_T(replicated_logs_to_keep);
SET_SIZE_T(prefer_fetch_merged_part_time_threshold); SET_SIZE_T(prefer_fetch_merged_part_time_threshold);

View File

@ -71,9 +71,6 @@ private:
void updateQuorumIfWeHavePart(); void updateQuorumIfWeHavePart();
void partialShutdown(); void partialShutdown();
/// Запретить запись в таблицу и завершить все фоновые потоки.
void goReadOnlyPermanently();
}; };

View File

@ -7,6 +7,7 @@
#include <DB/Client/ConnectionPoolWithFailover.h> #include <DB/Client/ConnectionPoolWithFailover.h>
#include <DB/Interpreters/Settings.h> #include <DB/Interpreters/Settings.h>
#include <DB/Interpreters/Context.h> #include <DB/Interpreters/Context.h>
#include <DB/Interpreters/Cluster.h>
#include <DB/Interpreters/ExpressionActions.h> #include <DB/Interpreters/ExpressionActions.h>
#include <common/logger_useful.h> #include <common/logger_useful.h>
@ -45,7 +46,7 @@ public:
NamesAndTypesListPtr columns_, /// Список столбцов. NamesAndTypesListPtr columns_, /// Список столбцов.
const String & remote_database_, /// БД на удалённых серверах. const String & remote_database_, /// БД на удалённых серверах.
const String & remote_table_, /// Имя таблицы на удалённых серверах. const String & remote_table_, /// Имя таблицы на удалённых серверах.
std::shared_ptr<Cluster> & owned_cluster_, ClusterPtr & owned_cluster_,
Context & context_); Context & context_);
std::string getName() const override { return "Distributed"; } std::string getName() const override { return "Distributed"; }
@ -95,7 +96,7 @@ public:
const String & getPath() const { return path; } const String & getPath() const { return path; }
std::string getRemoteDatabaseName() const { return remote_database; } std::string getRemoteDatabaseName() const { return remote_database; }
std::string getRemoteTableName() const { return remote_table; } std::string getRemoteTableName() const { return remote_table; }
std::string getClusterName() const { return cluster_name; } /// Returns empty string if tables is used by TableFunctionRemote
private: private:
StorageDistributed( StorageDistributed(
@ -103,7 +104,7 @@ private:
NamesAndTypesListPtr columns_, NamesAndTypesListPtr columns_,
const String & remote_database_, const String & remote_database_,
const String & remote_table_, const String & remote_table_,
const Cluster & cluster_, const String & cluster_name_,
Context & context_, Context & context_,
const ASTPtr & sharding_key_ = nullptr, const ASTPtr & sharding_key_ = nullptr,
const String & data_path_ = String{}); const String & data_path_ = String{});
@ -116,7 +117,7 @@ private:
const ColumnDefaults & column_defaults_, const ColumnDefaults & column_defaults_,
const String & remote_database_, const String & remote_database_,
const String & remote_table_, const String & remote_table_,
const Cluster & cluster_, const String & cluster_name_,
Context & context_, Context & context_,
const ASTPtr & sharding_key_ = nullptr, const ASTPtr & sharding_key_ = nullptr,
const String & data_path_ = String{}); const String & data_path_ = String{});
@ -129,6 +130,9 @@ private:
/// ensure directory monitor creation /// ensure directory monitor creation
void requireDirectoryMonitor(const std::string & name); void requireDirectoryMonitor(const std::string & name);
ClusterPtr getCluster() const;
private:
String name; String name;
NamesAndTypesListPtr columns; NamesAndTypesListPtr columns;
String remote_database; String remote_database;
@ -137,16 +141,15 @@ private:
Context & context; Context & context;
Logger * log = &Logger::get("StorageDistributed"); Logger * log = &Logger::get("StorageDistributed");
/// Используется только, если таблица должна владеть объектом Cluster, /// Used to implement TableFunctionRemote.
/// которым больше никто не владеет - для реализации TableFunctionRemote.
std::shared_ptr<Cluster> owned_cluster; std::shared_ptr<Cluster> owned_cluster;
/// Соединения с удалёнными серверами. /// Is empty if this storage implements TableFunctionRemote.
const Cluster & cluster; const String cluster_name;
bool has_sharding_key;
ExpressionActionsPtr sharding_key_expr; ExpressionActionsPtr sharding_key_expr;
String sharding_key_column_name; String sharding_key_column_name;
bool write_enabled;
String path; /// Может быть пустым, если data_path_ пустой. В этом случае, директория для данных для отправки не создаётся. String path; /// Может быть пустым, если data_path_ пустой. В этом случае, директория для данных для отправки не создаётся.
class DirectoryMonitor; class DirectoryMonitor;

View File

@ -200,6 +200,7 @@ private:
friend class ReplicatedMergeTreePartCheckThread; friend class ReplicatedMergeTreePartCheckThread;
friend class ReplicatedMergeTreeCleanupThread; friend class ReplicatedMergeTreeCleanupThread;
friend class ReplicatedMergeTreeAlterThread; friend class ReplicatedMergeTreeAlterThread;
friend class ReplicatedMergeTreeRestartingThread;
friend struct ReplicatedMergeTreeLogEntry; friend struct ReplicatedMergeTreeLogEntry;
friend class ScopedPartitionMergeLock; friend class ScopedPartitionMergeLock;
@ -247,6 +248,7 @@ private:
/** Является ли эта реплика "ведущей". Ведущая реплика выбирает куски для слияния. /** Является ли эта реплика "ведущей". Ведущая реплика выбирает куски для слияния.
*/ */
bool is_leader_node = false; bool is_leader_node = false;
std::mutex leader_node_mutex;
InterserverIOEndpointHolderPtr endpoint_holder; InterserverIOEndpointHolderPtr endpoint_holder;
InterserverIOEndpointHolderPtr disk_space_monitor_endpoint_holder; InterserverIOEndpointHolderPtr disk_space_monitor_endpoint_holder;

View File

@ -569,8 +569,16 @@ private:
while (isWhitespace(*begin) || *begin == ';') while (isWhitespace(*begin) || *begin == ';')
++begin; ++begin;
if (!processSingleQuery(query, ast) || got_exception) if (!processSingleQuery(query, ast))
return false; return false;
if (got_exception)
{
if (is_interactive)
break;
else
return false;
}
} }
return true; return true;

View File

@ -282,6 +282,38 @@ void ConfigProcessor::doIncludes(DocumentPtr config, DocumentPtr include_from)
doIncludesRecursive(config, include_from, getRootNode(&*config)); doIncludesRecursive(config, include_from, getRootNode(&*config));
} }
ConfigProcessor::Files ConfigProcessor::getConfigMergeFiles(const std::string & config_path)
{
Files res;
Poco::Path merge_dir_path(config_path);
merge_dir_path.setExtension("d");
std::vector<std::string> merge_dirs;
merge_dirs.push_back(merge_dir_path.toString());
if (merge_dir_path.getBaseName() != "conf") {
merge_dir_path.setBaseName("conf");
merge_dirs.push_back(merge_dir_path.toString());
}
for (const std::string & merge_dir_name : merge_dirs)
{
Poco::File merge_dir(merge_dir_name);
if (!merge_dir.exists() || !merge_dir.isDirectory())
continue;
for (Poco::DirectoryIterator it(merge_dir_name); it != Poco::DirectoryIterator(); ++it)
{
Poco::File & file = *it;
if (file.isFile() && (endsWith(file.path(), ".xml") || endsWith(file.path(), ".conf")))
{
res.push_back(file.path());
}
}
}
return res;
}
XMLDocumentPtr ConfigProcessor::processConfig(const std::string & path_str) XMLDocumentPtr ConfigProcessor::processConfig(const std::string & path_str)
{ {
/// We need larger name pool to allow to support vast amount of users in users.xml files for ClickHouse. /// We need larger name pool to allow to support vast amount of users in users.xml files for ClickHouse.
@ -294,36 +326,18 @@ XMLDocumentPtr ConfigProcessor::processConfig(const std::string & path_str)
std::vector<std::string> contributing_files; std::vector<std::string> contributing_files;
contributing_files.push_back(path_str); contributing_files.push_back(path_str);
Poco::Path merge_dir_path(path_str);
merge_dir_path.setExtension("d"); for (auto & merge_file : getConfigMergeFiles(path_str))
std::vector<std::string> merge_dirs;
merge_dirs.push_back(merge_dir_path.toString());
if (merge_dir_path.getBaseName() != "conf")
{ {
merge_dir_path.setBaseName("conf"); try
merge_dirs.push_back(merge_dir_path.toString());
}
for (const std::string & merge_dir_name : merge_dirs)
{
Poco::File merge_dir(merge_dir_name);
if (!merge_dir.exists() || !merge_dir.isDirectory())
continue;
for (Poco::DirectoryIterator it(merge_dir_name); it != Poco::DirectoryIterator(); ++it)
{ {
Poco::File & file = *it; DocumentPtr with = dom_parser.parse(merge_file);
try merge(config, with);
{ contributing_files.push_back(merge_file);
if (file.isFile() && (endsWith(file.path(), ".xml") || endsWith(file.path(), ".conf"))) }
{ catch (Poco::Exception & e)
contributing_files.push_back(file.path()); {
DocumentPtr with = dom_parser.parse(file.path()); throw Poco::Exception("Failed to merge config with " + merge_file + ": " + e.displayText());
merge(config, with);
}
}
catch (Poco::Exception & e)
{
throw Poco::Exception("Failed to merge config with " + file.path() + ": " + e.displayText());
}
} }
} }

View File

@ -31,7 +31,7 @@ RemoteBlockInputStream::RemoteBlockInputStream(ConnectionPool::Entry & pool_entr
init(settings_); init(settings_);
} }
RemoteBlockInputStream::RemoteBlockInputStream(IConnectionPool * pool_, const String & query_, RemoteBlockInputStream::RemoteBlockInputStream(ConnectionPoolPtr & pool_, const String & query_,
const Settings * settings_, ThrottlerPtr throttler_, const Tables & external_tables_, const Settings * settings_, ThrottlerPtr throttler_, const Tables & external_tables_,
QueryProcessingStage::Enum stage_, const Context & context_) QueryProcessingStage::Enum stage_, const Context & context_)
: pool(pool_), query(query_), throttler(throttler_), external_tables(external_tables_), : pool(pool_), query(query_), throttler(throttler_), external_tables(external_tables_),
@ -238,7 +238,7 @@ void RemoteBlockInputStream::createMultiplexedConnections()
if (connection != nullptr) if (connection != nullptr)
multiplexed_connections = std::make_unique<MultiplexedConnections>(connection, multiplexed_connections_settings, throttler); multiplexed_connections = std::make_unique<MultiplexedConnections>(connection, multiplexed_connections_settings, throttler);
else if (pool != nullptr) else if (pool != nullptr)
multiplexed_connections = std::make_unique<MultiplexedConnections>(pool, multiplexed_connections_settings, throttler, multiplexed_connections = std::make_unique<MultiplexedConnections>(pool.get(), multiplexed_connections_settings, throttler,
append_extra_info, pool_mode); append_extra_info, pool_mode);
else if (pools != nullptr) else if (pools != nullptr)
multiplexed_connections = std::make_unique<MultiplexedConnections>(*pools, multiplexed_connections_settings, throttler, multiplexed_connections = std::make_unique<MultiplexedConnections>(*pools, multiplexed_connections_settings, throttler,

View File

@ -76,10 +76,8 @@ Poco::Net::SocketAddress resolveSocketAddress(const String & host_and_port)
/// Implementation of Cluster::Address class /// Implementation of Cluster::Address class
Cluster::Address::Address(const String & config_prefix) Cluster::Address::Address(Poco::Util::AbstractConfiguration & config, const String & config_prefix)
{ {
const auto & config = Poco::Util::Application::instance().config();
host_name = config.getString(config_prefix + ".host"); host_name = config.getString(config_prefix + ".host");
port = config.getInt(config_prefix + ".port"); port = config.getInt(config_prefix + ".port");
resolved_address = resolveSocketAddress(host_name, port); resolved_address = resolveSocketAddress(host_name, port);
@ -111,23 +109,58 @@ Cluster::Address::Address(const String & host_port_, const String & user_, const
/// Implementation of Clusters class /// Implementation of Clusters class
Clusters::Clusters(const Settings & settings, const String & config_name) Clusters::Clusters(Poco::Util::AbstractConfiguration & config, const Settings & settings, const String & config_name)
{ {
Poco::Util::AbstractConfiguration & config = Poco::Util::Application::instance().config();
Poco::Util::AbstractConfiguration::Keys config_keys; Poco::Util::AbstractConfiguration::Keys config_keys;
config.keys(config_name, config_keys); config.keys(config_name, config_keys);
for (const auto & key : config_keys) for (const auto & key : config_keys)
impl.emplace(std::piecewise_construct, impl.emplace(key, std::make_shared<Cluster>(config, settings, config_name + "." + key));
std::forward_as_tuple(key), }
std::forward_as_tuple(settings, config_name + "." + key));
ClusterPtr Clusters::getCluster(const std::string & cluster_name) const
{
std::lock_guard<std::mutex> lock(mutex);
auto it = impl.find(cluster_name);
return (it != impl.end()) ? it->second : nullptr;
}
void Clusters::updateClusters(Poco::Util::AbstractConfiguration & config, const Settings & settings, const String & config_name)
{
Poco::Util::AbstractConfiguration::Keys config_keys;
config.keys(config_name, config_keys);
std::lock_guard<std::mutex> lock(mutex);
for (const auto & key : config_keys)
{
auto it = impl.find(key);
auto new_cluster = std::make_shared<Cluster>(config, settings, config_name + "." + key);
if (it == impl.end())
impl.emplace(key, std::move(new_cluster));
else
{
//TODO: Check that cluster update is necessarily
it->second = std::move(new_cluster);
}
}
}
Clusters::Impl Clusters::getContainer() const
{
std::lock_guard<std::mutex> lock(mutex);
/// The following line copies container of shared_ptrs to return value under lock
return impl;
} }
/// Реализация класса Cluster /// Реализация класса Cluster
Cluster::Cluster(const Settings & settings, const String & cluster_name) Cluster::Cluster(Poco::Util::AbstractConfiguration & config, const Settings & settings, const String & cluster_name)
{ {
Poco::Util::AbstractConfiguration & config = Poco::Util::Application::instance().config();
Poco::Util::AbstractConfiguration::Keys config_keys; Poco::Util::AbstractConfiguration::Keys config_keys;
config.keys(cluster_name, config_keys); config.keys(cluster_name, config_keys);
@ -149,7 +182,7 @@ Cluster::Cluster(const Settings & settings, const String & cluster_name)
if (weight == 0) if (weight == 0)
continue; continue;
addresses.emplace_back(prefix); addresses.emplace_back(config, prefix);
addresses.back().replica_num = 1; addresses.back().replica_num = 1;
const auto & address = addresses.back(); const auto & address = addresses.back();
@ -206,7 +239,7 @@ Cluster::Cluster(const Settings & settings, const String & cluster_name)
if (startsWith(replica_key, "replica")) if (startsWith(replica_key, "replica"))
{ {
replica_addresses.emplace_back(partial_prefix + replica_key); replica_addresses.emplace_back(config, partial_prefix + replica_key);
replica_addresses.back().replica_num = current_replica_num; replica_addresses.back().replica_num = current_replica_num;
++current_replica_num; ++current_replica_num;

View File

@ -31,7 +31,7 @@ BlockInputStreamPtr AlterQueryConstructor::createLocal(ASTPtr query_ast, const C
return stream; return stream;
} }
BlockInputStreamPtr AlterQueryConstructor::createRemote(IConnectionPool * pool, const std::string & query, BlockInputStreamPtr AlterQueryConstructor::createRemote(ConnectionPoolPtr & pool, const std::string & query,
const Settings & settings, ThrottlerPtr throttler, const Context & context) const Settings & settings, ThrottlerPtr throttler, const Context & context)
{ {
auto stream = std::make_shared<RemoteBlockInputStream>(pool, query, &settings, throttler); auto stream = std::make_shared<RemoteBlockInputStream>(pool, query, &settings, throttler);

View File

@ -42,7 +42,7 @@ BlockInputStreamPtr DescribeQueryConstructor::createLocal(ASTPtr query_ast, cons
return std::make_shared<BlockExtraInfoInputStream>(materialized_stream, toBlockExtraInfo(address)); return std::make_shared<BlockExtraInfoInputStream>(materialized_stream, toBlockExtraInfo(address));
} }
BlockInputStreamPtr DescribeQueryConstructor::createRemote(IConnectionPool * pool, const std::string & query, BlockInputStreamPtr DescribeQueryConstructor::createRemote(ConnectionPoolPtr & pool, const std::string & query,
const Settings & settings, ThrottlerPtr throttler, const Context & context) const Settings & settings, ThrottlerPtr throttler, const Context & context)
{ {
auto stream = std::make_shared<RemoteBlockInputStream>(pool, query, &settings, throttler); auto stream = std::make_shared<RemoteBlockInputStream>(pool, query, &settings, throttler);

View File

@ -13,7 +13,7 @@ namespace DB
namespace ClusterProxy namespace ClusterProxy
{ {
Query::Query(IQueryConstructor & query_constructor_, const Cluster & cluster_, Query::Query(IQueryConstructor & query_constructor_, const ClusterPtr & cluster_,
ASTPtr query_ast_, const Context & context_, const Settings & settings_, bool enable_shard_multiplexing_) ASTPtr query_ast_, const Context & context_, const Settings & settings_, bool enable_shard_multiplexing_)
: query_constructor{query_constructor_}, cluster{cluster_}, query_ast{query_ast_}, : query_constructor{query_constructor_}, cluster{cluster_}, query_ast{query_ast_},
context{context_}, settings{settings_}, enable_shard_multiplexing{enable_shard_multiplexing_} context{context_}, settings{settings_}, enable_shard_multiplexing{enable_shard_multiplexing_}
@ -45,14 +45,14 @@ BlockInputStreams Query::execute()
if (query_constructor.getPoolMode() == PoolMode::GET_ALL) if (query_constructor.getPoolMode() == PoolMode::GET_ALL)
{ {
for (const auto & shard_info : cluster.getShardsInfo()) for (const auto & shard_info : cluster->getShardsInfo())
{ {
if (shard_info.hasRemoteConnections()) if (shard_info.hasRemoteConnections())
++remote_count; ++remote_count;
} }
} }
else else
remote_count = cluster.getRemoteShardCount(); remote_count = cluster->getRemoteShardCount();
size_t thread_count; size_t thread_count;
@ -73,7 +73,7 @@ BlockInputStreams Query::execute()
/// Цикл по шардам. /// Цикл по шардам.
size_t current_thread = 0; size_t current_thread = 0;
for (const auto & shard_info : cluster.getShardsInfo()) for (const auto & shard_info : cluster->getShardsInfo())
{ {
bool create_local_queries = shard_info.isLocal(); bool create_local_queries = shard_info.isLocal();
@ -105,7 +105,7 @@ BlockInputStreams Query::execute()
if (actual_pools_per_thread == 1) if (actual_pools_per_thread == 1)
{ {
res.emplace_back(query_constructor.createRemote(shard_info.pool.get(), query, new_settings, throttler, context)); res.emplace_back(query_constructor.createRemote(shard_info.pool, query, new_settings, throttler, context));
++current_thread; ++current_thread;
} }
else else

View File

@ -34,7 +34,7 @@ BlockInputStreamPtr SelectQueryConstructor::createLocal(ASTPtr query_ast, const
return std::make_shared<MaterializingBlockInputStream>(stream); return std::make_shared<MaterializingBlockInputStream>(stream);
} }
BlockInputStreamPtr SelectQueryConstructor::createRemote(IConnectionPool * pool, const std::string & query, BlockInputStreamPtr SelectQueryConstructor::createRemote(ConnectionPoolPtr & pool, const std::string & query,
const Settings & settings, ThrottlerPtr throttler, const Context & context) const Settings & settings, ThrottlerPtr throttler, const Context & context)
{ {
auto stream = std::make_shared<RemoteBlockInputStream>(pool, query, &settings, throttler, external_tables, processed_stage, context); auto stream = std::make_shared<RemoteBlockInputStream>(pool, query, &settings, throttler, external_tables, processed_stage, context);

View File

@ -107,9 +107,11 @@ struct ContextShared
mutable std::unique_ptr<CompressionMethodSelector> compression_method_selector; mutable std::unique_ptr<CompressionMethodSelector> compression_method_selector;
std::unique_ptr<MergeTreeSettings> merge_tree_settings; /// Настройки для движка MergeTree. std::unique_ptr<MergeTreeSettings> merge_tree_settings; /// Настройки для движка MergeTree.
/// Кластеры для distributed таблиц /// Clusters for distributed tables
/// Создаются при создании Distributed таблиц, так как нужно дождаться пока будут выставлены Settings /// Initialized on demand (on distributed storages initialization) since Settings should be initialized
mutable std::shared_ptr<Clusters> clusters; std::unique_ptr<Clusters> clusters;
ConfigurationPtr clusters_config; /// Soteres updated configs
mutable std::mutex clusters_mutex; /// Guards clusters and clusters_config
Poco::UUIDGenerator uuid_generator; Poco::UUIDGenerator uuid_generator;
@ -268,7 +270,7 @@ void Context::setTemporaryPath(const String & path)
} }
void Context::setUsersConfig(ConfigurationPtr config) void Context::setUsersConfig(const ConfigurationPtr & config)
{ {
auto lock = getLock(); auto lock = getLock();
shared->users_config = config; shared->users_config = config;
@ -909,32 +911,49 @@ UInt16 Context::getTCPPort() const
} }
const Cluster & Context::getCluster(const std::string & cluster_name) const std::shared_ptr<Cluster> Context::getCluster(const std::string & cluster_name) const
{ {
{ auto res = getClusters().getCluster(cluster_name);
auto lock = getLock();
if (!shared->clusters)
shared->clusters = std::make_shared<Clusters>(settings);
}
Clusters::Impl::iterator it = shared->clusters->impl.find(cluster_name); if (!res)
if (it != shared->clusters->impl.end()) throw Exception("Requested cluster '" + cluster_name + "' not found", ErrorCodes::BAD_GET);
return it->second;
else return res;
throw Poco::Exception("Failed to find cluster with name = " + cluster_name);
} }
std::shared_ptr<Clusters> Context::getClusters() const
std::shared_ptr<Cluster> Context::tryGetCluster(const std::string & cluster_name) const
{
return getClusters().getCluster(cluster_name);
}
Clusters & Context::getClusters() const
{ {
{ {
auto lock = getLock(); std::lock_guard<std::mutex> lock(shared->clusters_mutex);
if (!shared->clusters) if (!shared->clusters)
shared->clusters = std::make_shared<Clusters>(settings); {
auto & config = shared->clusters_config ? *shared->clusters_config : Poco::Util::Application::instance().config();
shared->clusters = std::make_unique<Clusters>(config, settings);
}
} }
return shared->clusters; return *shared->clusters;
} }
/// On repeating calls updates existing clusters and adds new clusters, doesn't delete old clusters
void Context::setClustersConfig(const ConfigurationPtr & config)
{
std::lock_guard<std::mutex> lock(shared->clusters_mutex);
shared->clusters_config = config;
if (shared->clusters)
shared->clusters->updateClusters(*shared->clusters_config, settings);
}
Compiler & Context::getCompiler() Compiler & Context::getCompiler()
{ {
auto lock = getLock(); auto lock = getLock();

View File

@ -74,10 +74,10 @@ void ASTAlterQuery::formatImpl(const FormatSettings & settings, FormatState & st
{ {
if (!database.empty()) if (!database.empty())
{ {
settings.ostr << indent_str << database; settings.ostr << indent_str << backQuoteIfNeed(database);
settings.ostr << "."; settings.ostr << ".";
} }
settings.ostr << indent_str << table; settings.ostr << indent_str << backQuoteIfNeed(table);
} }
settings.ostr << settings.nl_or_ws; settings.ostr << settings.nl_or_ws;

View File

@ -4,7 +4,7 @@ add_executable(clickhouse-server
TCPHandler.cpp TCPHandler.cpp
InterserverIOHTTPHandler.cpp InterserverIOHTTPHandler.cpp
MetricsTransmitter.cpp MetricsTransmitter.cpp
UsersConfigReloader.cpp ConfigReloader.cpp
StatusFile.cpp StatusFile.cpp
ReplicasStatusHandler.cpp) ReplicasStatusHandler.cpp)

View File

@ -0,0 +1,152 @@
#include "ConfigReloader.h"
#include <Poco/Util/Application.h>
#include <Poco/File.h>
#include <common/logger_useful.h>
#include <DB/Interpreters/Context.h>
#include <DB/Common/setThreadName.h>
#include <DB/Common/ConfigProcessor.h>
namespace DB
{
namespace ErrorCodes { extern const int FILE_DOESNT_EXIST; }
constexpr decltype(ConfigReloader::reload_interval) ConfigReloader::reload_interval;
ConfigReloader::ConfigReloader(const std::string & main_config_path_, const std::string & users_config_path_,
const std::string & include_from_path_, Context * context_)
: main_config_path(main_config_path_), users_config_path(users_config_path_),
include_from_path(include_from_path_), context(context_)
{
/// If path to users' config isn't absolute, try guess its root (current) dir.
/// At first, try to find it in dir of main config, after will use current dir.
if (users_config_path.empty() || users_config_path[0] != '/')
{
std::string config_dir = Poco::Path(main_config_path).parent().toString();
if (Poco::File(config_dir + users_config_path).exists())
users_config_path = config_dir + users_config_path;
}
/// Setup users on server init
reloadIfNewer(false, true);
thread = std::thread(&ConfigReloader::run, this);
}
ConfigReloader::~ConfigReloader()
{
try
{
LOG_DEBUG(log, "ConfigReloader::~ConfigReloader()");
quit = true;
thread.join();
}
catch (...)
{
tryLogCurrentException("~ConfigReloader");
}
}
void ConfigReloader::run()
{
setThreadName("ConfigReloader");
while (!quit)
{
std::this_thread::sleep_for(reload_interval);
reloadIfNewer(false, false);
}
}
ConfigReloader::FilesChangesTracker ConfigReloader::getFileListFor(const std::string & root_config_path)
{
FilesChangesTracker file_list;
file_list.addIfExists(root_config_path);
file_list.addIfExists(include_from_path);
for (const auto & path : ConfigProcessor::getConfigMergeFiles(root_config_path))
file_list.addIfExists(path);
return file_list;
}
ConfigurationPtr ConfigReloader::loadConfigFor(const std::string & root_config_path, bool throw_on_error)
{
ConfigurationPtr config;
LOG_DEBUG(log, "Loading config '" << root_config_path << "'");
try
{
config = ConfigProcessor().loadConfig(root_config_path);
}
catch (...)
{
if (throw_on_error)
throw;
tryLogCurrentException(log, "Error loading config from '" + root_config_path + "' ");
return nullptr;
}
return config;
}
void ConfigReloader::reloadIfNewer(bool force_main, bool force_users)
{
FilesChangesTracker main_config_files = getFileListFor(main_config_path);
if (force_main || main_config_files.isDifferOrNewerThan(last_main_config_files))
{
last_main_config_files = std::move(main_config_files);
ConfigurationPtr config = loadConfigFor(main_config_path, force_main);
if (config)
{
try
{
context->setClustersConfig(config);
}
catch (...)
{
if (force_main)
throw;
tryLogCurrentException(log, "Error updating remote_servers config from '" + main_config_path + "' ");
}
}
}
FilesChangesTracker users_config_files = getFileListFor(users_config_path);
if (force_users || users_config_files.isDifferOrNewerThan(last_users_config_files))
{
last_users_config_files = std::move(users_config_files);
ConfigurationPtr config = loadConfigFor(users_config_path, force_users);
if (config)
{
try
{
context->setUsersConfig(config);
}
catch (...)
{
if (force_users)
throw;
tryLogCurrentException(log, "Error updating users config from '" + users_config_path + "' ");
}
}
}
}
}

View File

@ -0,0 +1,104 @@
#pragma once
#include <DB/Common/ConfigProcessor.h>
#include <time.h>
#include <string>
#include <thread>
#include <atomic>
#include <list>
namespace Poco { class Logger; }
namespace DB
{
class Context;
/** Every two seconds checks configuration files for update.
* If configuration is changed, then config will be reloaded by ConfigProcessor
* and the reloaded config will be applied via setUsersConfig() and setClusters() methods of Context.
* So, ConfigReloader actually reloads only <users> and <remote_servers> "tags".
* Also, it doesn't take into account changes of --config-file, <users_config> and <include_from> parameters.
*/
class ConfigReloader
{
public:
/** main_config_path is usually /path/to/.../clickhouse-server/config.xml (i.e. --config-file value)
* users_config_path is usually /path/to/.../clickhouse-server/users.xml (i.e. value of <users_config> tag)
* include_from_path is usually /path/to/.../etc/metrika.xml (i.e. value of <include_from> tag)
*/
ConfigReloader(const std::string & main_config_path_, const std::string & users_config_path_, const std::string & include_from_path_, Context * context_);
~ConfigReloader();
private:
struct FileWithTimestamp
{
std::string path;
time_t modification_time;
FileWithTimestamp(const std::string & path_, time_t modification_time_)
: path(path_), modification_time(modification_time_) {}
bool operator < (const FileWithTimestamp & rhs) const
{
return path < rhs.path;
}
static bool isTheSame(const FileWithTimestamp & lhs, const FileWithTimestamp & rhs)
{
return (lhs.modification_time == rhs.modification_time) && (lhs.path == rhs.path);
}
};
struct FilesChangesTracker
{
std::set<FileWithTimestamp> files;
void addIfExists(const std::string & path)
{
if (!path.empty() && Poco::File(path).exists())
{
files.emplace(path, Poco::File(path).getLastModified().epochTime());
}
}
bool isDifferOrNewerThan(const FilesChangesTracker & rhs)
{
return (files.size() != rhs.files.size()) ||
!std::equal(files.begin(), files.end(), rhs.files.begin(), FileWithTimestamp::isTheSame);
}
};
private:
/// Make sense to separate this function on two threads
void reloadIfNewer(bool force_main, bool force_users);
void run();
FilesChangesTracker getFileListFor(const std::string & root_config_path);
ConfigurationPtr loadConfigFor(const std::string & root_config_path, bool throw_error);
private:
static constexpr auto reload_interval = std::chrono::seconds(2);
std::string main_config_path;
std::string users_config_path;
std::string include_from_path;
Context * context;
FilesChangesTracker last_main_config_files;
FilesChangesTracker last_users_config_files;
std::atomic<bool> quit{false};
std::thread thread;
Poco::Logger * log = &Logger::get("ConfigReloader");
};
}

View File

@ -25,7 +25,7 @@ public:
std::shared_ptr<WriteBuffer> out_maybe_compressed; std::shared_ptr<WriteBuffer> out_maybe_compressed;
}; };
void handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response); void handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response) override;
void trySendExceptionToClient(const std::string & s, void trySendExceptionToClient(const std::string & s,
Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response, Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response,

View File

@ -16,7 +16,7 @@ public:
{ {
} }
void handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response); void handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response) override;
private: private:
Server & server; Server & server;

View File

@ -17,7 +17,7 @@ private:
public: public:
ReplicasStatusHandler(Context & context_); ReplicasStatusHandler(Context & context_);
void handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response); void handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response) override;
}; };

View File

@ -50,7 +50,7 @@
#include "InterserverIOHTTPHandler.h" #include "InterserverIOHTTPHandler.h"
#include "TCPHandler.h" #include "TCPHandler.h"
#include "MetricsTransmitter.h" #include "MetricsTransmitter.h"
#include "UsersConfigReloader.h" #include "ConfigReloader.h"
#include "StatusFile.h" #include "StatusFile.h"
@ -63,11 +63,11 @@ namespace ErrorCodes
} }
/// Отвечает "Ok.\n". Используется для проверки живости. /// Response with "Ok.\n". Used for availability checks.
class PingRequestHandler : public Poco::Net::HTTPRequestHandler class PingRequestHandler : public Poco::Net::HTTPRequestHandler
{ {
public: public:
void handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response) void handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response) override
{ {
try try
{ {
@ -82,11 +82,11 @@ public:
}; };
/// Отвечает 404 с подробным объяснением. /// Response with 404 and verbose description.
class NotFoundHandler : public Poco::Net::HTTPRequestHandler class NotFoundHandler : public Poco::Net::HTTPRequestHandler
{ {
public: public:
void handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response) void handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response) override
{ {
try try
{ {
@ -122,7 +122,7 @@ public:
HTTPRequestHandlerFactory(Server & server_, const std::string & name_) HTTPRequestHandlerFactory(Server & server_, const std::string & name_)
: server(server_), log(&Logger::get(name_)), name(name_) {} : server(server_), log(&Logger::get(name_)), name(name_) {}
Poco::Net::HTTPRequestHandler * createRequestHandler(const Poco::Net::HTTPServerRequest & request) Poco::Net::HTTPRequestHandler * createRequestHandler(const Poco::Net::HTTPServerRequest & request) override
{ {
LOG_TRACE(log, "HTTP Request for " << name << ". " LOG_TRACE(log, "HTTP Request for " << name << ". "
<< "Method: " << request.getMethod() << "Method: " << request.getMethod()
@ -167,7 +167,7 @@ private:
public: public:
TCPConnectionFactory(Server & server_) : server(server_), log(&Logger::get("TCPConnectionFactory")) {} TCPConnectionFactory(Server & server_) : server(server_), log(&Logger::get("TCPConnectionFactory")) {}
Poco::Net::TCPServerConnection * createConnection(const Poco::Net::StreamSocket & socket) Poco::Net::TCPServerConnection * createConnection(const Poco::Net::StreamSocket & socket) override
{ {
LOG_TRACE(log, "TCP Request. " << "Address: " << socket.peerAddress().toString()); LOG_TRACE(log, "TCP Request. " << "Address: " << socket.peerAddress().toString());
@ -187,9 +187,23 @@ int Server::main(const std::vector<std::string> & args)
if (path.back() != '/') if (path.back() != '/')
path += '/'; path += '/';
/** Context contains all that query execution is dependent:
* settings, available functions, data types, aggregate functions, databases...
*/
global_context = std::make_unique<Context>();
global_context->setGlobalContext(*global_context);
global_context->setPath(path);
std::string default_database = config().getString("default_database", "default");
/// Create directories for 'path' and for default database, if not exist.
Poco::File(path + "data/" + default_database).createDirectories();
Poco::File(path + "metadata/" + default_database).createDirectories();
StatusFile status{path + "status"}; StatusFile status{path + "status"};
/// Попробуем повысить ограничение на число открытых файлов. /// Try to increase limit on number of opened files.
{ {
rlimit rlim; rlimit rlim;
if (getrlimit(RLIMIT_NOFILE, &rlim)) if (getrlimit(RLIMIT_NOFILE, &rlim))
@ -213,19 +227,11 @@ int Server::main(const std::vector<std::string> & args)
static ServerErrorHandler error_handler; static ServerErrorHandler error_handler;
Poco::ErrorHandler::set(&error_handler); Poco::ErrorHandler::set(&error_handler);
/// Заранее инициализируем DateLUT, чтобы первая инициализация потом не влияла на измеряемую скорость выполнения. /// Initialize DateLUT early, to not interfere with running time of first query.
LOG_DEBUG(log, "Initializing DateLUT."); LOG_DEBUG(log, "Initializing DateLUT.");
DateLUT::instance(); DateLUT::instance();
LOG_TRACE(log, "Initialized DateLUT."); LOG_TRACE(log, "Initialized DateLUT.");
global_context = std::make_unique<Context>();
/** Контекст содержит всё, что влияет на обработку запроса:
* настройки, набор функций, типов данных, агрегатных функций, баз данных...
*/
global_context->setGlobalContext(*global_context);
global_context->setPath(path);
/// Directory with temporary data for processing of hard queries. /// Directory with temporary data for processing of hard queries.
{ {
std::string tmp_path = config().getString("tmp_path", path + "tmp/"); std::string tmp_path = config().getString("tmp_path", path + "tmp/");
@ -279,23 +285,26 @@ int Server::main(const std::vector<std::string> & args)
if (config().has("macros")) if (config().has("macros"))
global_context->setMacros(Macros(config(), "macros")); global_context->setMacros(Macros(config(), "macros"));
std::string users_config_path = config().getString("users_config", config().getString("config-file", "config.xml")); /// Initialize automatic config updater
auto users_config_reloader = std::make_unique<UsersConfigReloader>(users_config_path, global_context.get()); std::string main_config_path = config().getString("config-file", "config.xml");
std::string users_config_path = config().getString("users_config", main_config_path);
std::string include_from_path = config().getString("include_from", "/etc/metrika.xml");
auto config_reloader = std::make_unique<ConfigReloader>(main_config_path, users_config_path, include_from_path, global_context.get());
/// Максимальное количество одновременно выполняющихся запросов. /// Limit on total number of coucurrently executed queries.
global_context->getProcessList().setMaxSize(config().getInt("max_concurrent_queries", 0)); global_context->getProcessList().setMaxSize(config().getInt("max_concurrent_queries", 0));
/// Размер кэша разжатых блоков. Если нулевой - кэш отключён. /// Size of cache for uncompressed blocks. Zero means disabled.
size_t uncompressed_cache_size = parse<size_t>(config().getString("uncompressed_cache_size", "0")); size_t uncompressed_cache_size = parse<size_t>(config().getString("uncompressed_cache_size", "0"));
if (uncompressed_cache_size) if (uncompressed_cache_size)
global_context->setUncompressedCache(uncompressed_cache_size); global_context->setUncompressedCache(uncompressed_cache_size);
/// Размер кэша засечек. Обязательный параметр. /// Size of cache for marks (index of MergeTree family of tables). It is necessary.
size_t mark_cache_size = parse<size_t>(config().getString("mark_cache_size")); size_t mark_cache_size = parse<size_t>(config().getString("mark_cache_size"));
if (mark_cache_size) if (mark_cache_size)
global_context->setMarkCache(mark_cache_size); global_context->setMarkCache(mark_cache_size);
/// Загружаем настройки. /// Load global settings from default profile.
Settings & settings = global_context->getSettingsRef(); Settings & settings = global_context->getSettingsRef();
global_context->setSetting("profile", config().getString("default_profile", "default")); global_context->setSetting("profile", config().getString("default_profile", "default"));
@ -303,6 +312,8 @@ int Server::main(const std::vector<std::string> & args)
loadMetadata(*global_context); loadMetadata(*global_context);
LOG_DEBUG(log, "Loaded metadata."); LOG_DEBUG(log, "Loaded metadata.");
global_context->setCurrentDatabase(default_database);
/// Create system tables. /// Create system tables.
if (!global_context->isDatabaseExist("system")) if (!global_context->isDatabaseExist("system"))
{ {
@ -339,8 +350,6 @@ int Server::main(const std::vector<std::string> & args)
if (has_zookeeper) if (has_zookeeper)
system_database->attachTable("zookeeper", StorageSystemZooKeeper::create("zookeeper")); system_database->attachTable("zookeeper", StorageSystemZooKeeper::create("zookeeper"));
global_context->setCurrentDatabase(config().getString("default_database", "default"));
bool has_resharding_worker = false; bool has_resharding_worker = false;
if (has_zookeeper && config().has("resharding")) if (has_zookeeper && config().has("resharding"))
{ {
@ -353,17 +362,17 @@ int Server::main(const std::vector<std::string> & args)
SCOPE_EXIT( SCOPE_EXIT(
LOG_DEBUG(log, "Closed all connections."); LOG_DEBUG(log, "Closed all connections.");
/** Попросим завершить фоновую работу у всех движков таблиц, /** Ask to cancel background jobs all table engines,
* а также у query_log-а. * and also query_log.
* Это важно делать заранее, не в деструкторе Context-а, так как * It is important to do early, not in destructor of Context, because
* движки таблиц могут при уничтожении всё ещё пользоваться Context-ом. * table engines could use Context on destroy.
*/ */
LOG_INFO(log, "Shutting down storages."); LOG_INFO(log, "Shutting down storages.");
global_context->shutdown(); global_context->shutdown();
LOG_DEBUG(log, "Shutted down storages."); LOG_DEBUG(log, "Shutted down storages.");
/** Явно уничтожаем контекст - это удобнее, чем в деструкторе Server-а, так как ещё доступен логгер. /** Explicitly destroy Context. It is more convenient than in destructor of Server, becuase logger is still available.
* В этот момент никто больше не должен владеть shared-частью контекста. * At this moment, no one could own shared part of Context.
*/ */
global_context.reset(); global_context.reset();
@ -471,7 +480,7 @@ int Server::main(const std::vector<std::string> & args)
LOG_DEBUG(log, "Waiting for current connections to close."); LOG_DEBUG(log, "Waiting for current connections to close.");
users_config_reloader.reset(); config_reloader.reset();
is_cancelled = true; is_cancelled = true;

View File

@ -1,135 +0,0 @@
#include "UsersConfigReloader.h"
#include <Poco/Util/Application.h>
#include <Poco/File.h>
#include <common/logger_useful.h>
#include <DB/Interpreters/Context.h>
#include <DB/Common/setThreadName.h>
#include <DB/Common/ConfigProcessor.h>
namespace DB
{
namespace ErrorCodes { extern const int FILE_DOESNT_EXIST; }
UsersConfigReloader::UsersConfigReloader(const std::string & path_, Context * context_)
: path(path_), context(context_), file_modification_time(0), quit(false), log(&Logger::get("UsersConfigReloader"))
{
/// Если путь к конфигу не абсолютный, угадаем, относительно чего он задан.
/// Сначала поищем его рядом с основным конфигом, потом - в текущей директории.
if (path.empty() || path[0] != '/')
{
std::string main_config_path = Poco::Util::Application::instance().config().getString("config-file", "config.xml");
std::string config_dir = Poco::Path(main_config_path).parent().toString();
if (Poco::File(config_dir + path).exists())
path = config_dir + path;
}
reloadIfNewer(true);
thread = std::thread(&UsersConfigReloader::run, this);
}
UsersConfigReloader::~UsersConfigReloader()
{
try
{
quit = true;
thread.join();
}
catch (...)
{
tryLogCurrentException("~UsersConfigReloader");
}
}
void UsersConfigReloader::run()
{
setThreadName("UserConfReload");
while (!quit)
{
std::this_thread::sleep_for(std::chrono::seconds(2));
reloadIfNewer(false);
}
}
void UsersConfigReloader::reloadIfNewer(bool force)
{
Poco::File f(path);
if (!f.exists())
{
if (force)
throw Exception("Users config not found at: " + path, ErrorCodes::FILE_DOESNT_EXIST);
if (file_modification_time)
{
LOG_ERROR(log, "Users config not found at: " << path);
file_modification_time = 0;
}
return;
}
time_t new_modification_time = f.getLastModified().epochTime();
if (!force && new_modification_time == file_modification_time)
return;
file_modification_time = new_modification_time;
LOG_DEBUG(log, "Loading users config");
ConfigurationPtr config;
try
{
config = ConfigProcessor().loadConfig(path);
}
catch (Poco::Exception & e)
{
if (force)
throw;
LOG_ERROR(log, "Error loading users config: " << e.what() << ": " << e.displayText());
return;
}
catch (...)
{
if (force)
throw;
LOG_ERROR(log, "Error loading users config.");
return;
}
try
{
context->setUsersConfig(config);
}
catch (Exception & e)
{
if (force)
throw;
LOG_ERROR(log, "Error updating users config: " << e.what() << ": " << e.displayText() << "\n" << e.getStackTrace().toString());
}
catch (Poco::Exception & e)
{
if (force)
throw;
LOG_ERROR(log, "Error updating users config: " << e.what() << ": " << e.displayText());
}
catch (...)
{
if (force)
throw;
LOG_ERROR(log, "Error updating users config.");
}
}
}

View File

@ -1,41 +0,0 @@
#pragma once
#include <time.h>
#include <string>
#include <thread>
#include <atomic>
namespace Poco { class Logger; }
namespace DB
{
class Context;
/** Каждые две секунды проверяет, не изменился ли конфиг.
* Когда изменился, запускает на нем ConfigProcessor и вызывает setUsersConfig у контекста.
* NOTE: Не перезагружает конфиг, если изменились другие файлы, влияющие на обработку конфига: metrika.xml
* и содержимое conf.d и users.d. Это можно исправить, переместив проверку времени изменения файлов в ConfigProcessor.
*/
class UsersConfigReloader
{
public:
UsersConfigReloader(const std::string & path, Context * context);
~UsersConfigReloader();
private:
std::string path;
Context * context;
time_t file_modification_time;
std::atomic<bool> quit;
std::thread thread;
Poco::Logger * log;
void reloadIfNewer(bool force);
void run();
};
}

View File

@ -30,14 +30,14 @@ std::vector<IColumn::Filter> createFiltersImpl(const size_t num_rows, const ICol
} }
DistributedBlockOutputStream::DistributedBlockOutputStream(StorageDistributed & storage, const ASTPtr & query_ast) DistributedBlockOutputStream::DistributedBlockOutputStream(StorageDistributed & storage, const ASTPtr & query_ast, const ClusterPtr & cluster_)
: storage(storage), query_ast(query_ast) : storage(storage), query_ast(query_ast), cluster(cluster_)
{ {
} }
void DistributedBlockOutputStream::write(const Block & block) void DistributedBlockOutputStream::write(const Block & block)
{ {
if (storage.getShardingKeyExpr() && (storage.cluster.getShardsInfo().size() > 1)) if (storage.getShardingKeyExpr() && (cluster->getShardsInfo().size() > 1))
return writeSplit(block); return writeSplit(block);
writeImpl(block); writeImpl(block);
@ -66,7 +66,7 @@ std::vector<IColumn::Filter> DistributedBlockOutputStream::createFilters(Block b
const auto it = creators.find(key_column.type->getName()); const auto it = creators.find(key_column.type->getName());
return it != std::end(creators) return it != std::end(creators)
? (*it->second)(block.rowsInFirstColumn(), key_column.column.get(), storage.cluster) ? (*it->second)(block.rowsInFirstColumn(), key_column.column.get(), *cluster)
: throw Exception{ : throw Exception{
"Sharding key expression does not evaluate to an integer type", "Sharding key expression does not evaluate to an integer type",
ErrorCodes::TYPE_MISMATCH ErrorCodes::TYPE_MISMATCH
@ -83,7 +83,7 @@ void DistributedBlockOutputStream::writeSplit(const Block & block)
auto filters = createFilters(block); auto filters = createFilters(block);
const auto num_shards = storage.cluster.getShardsInfo().size(); const auto num_shards = cluster->getShardsInfo().size();
ssize_t size_hint = ((block.rowsInFirstColumn() + num_shards - 1) / num_shards) * 1.1; /// Число 1.1 выбрано наугад. ssize_t size_hint = ((block.rowsInFirstColumn() + num_shards - 1) / num_shards) * 1.1; /// Число 1.1 выбрано наугад.
@ -101,7 +101,7 @@ void DistributedBlockOutputStream::writeSplit(const Block & block)
void DistributedBlockOutputStream::writeImpl(const Block & block, const size_t shard_id) void DistributedBlockOutputStream::writeImpl(const Block & block, const size_t shard_id)
{ {
const auto & shard_info = storage.cluster.getShardsInfo()[shard_id]; const auto & shard_info = cluster->getShardsInfo()[shard_id];
if (shard_info.getLocalNodeCount() > 0) if (shard_info.getLocalNodeCount() > 0)
writeToLocal(block, shard_info.getLocalNodeCount()); writeToLocal(block, shard_info.getLocalNodeCount());

View File

@ -20,18 +20,23 @@ void BackgroundProcessingPool::TaskInfo::wake()
if (removed) if (removed)
return; return;
time_t current_time = time(0); Poco::Timestamp current_time;
{ {
std::unique_lock<std::mutex> lock(pool.tasks_mutex); std::unique_lock<std::mutex> lock(pool.tasks_mutex);
pool.tasks.splice(pool.tasks.begin(), pool.tasks, iterator);
/// Если эта задача в прошлый раз ничего не сделала, и ей было назначено спать, то отменим время сна. auto next_time_to_execute = iterator->first;
TaskHandle this_task_handle = iterator->second;
/// If this task was done nothing at previous time and it has to sleep, then cancel sleep time.
if (next_time_to_execute > current_time) if (next_time_to_execute > current_time)
next_time_to_execute = current_time; next_time_to_execute = current_time;
pool.tasks.erase(iterator);
iterator = pool.tasks.emplace(next_time_to_execute, this_task_handle);
} }
/// Если все потоки сейчас выполняют работу, этот вызов никого не разбудит. /// Note that if all threads are currently do some work, this call will not wakeup any thread.
pool.wake_event.notify_one(); pool.wake_event.notify_one();
} }
@ -54,11 +59,13 @@ int BackgroundProcessingPool::getCounter(const String & name)
BackgroundProcessingPool::TaskHandle BackgroundProcessingPool::addTask(const Task & task) BackgroundProcessingPool::TaskHandle BackgroundProcessingPool::addTask(const Task & task)
{ {
TaskHandle res(new TaskInfo(*this, task)); TaskHandle res = std::make_shared<TaskInfo>(*this, task);
Poco::Timestamp current_time;
{ {
std::unique_lock<std::mutex> lock(tasks_mutex); std::unique_lock<std::mutex> lock(tasks_mutex);
res->iterator = tasks.insert(tasks.begin(), res); res->iterator = tasks.emplace(current_time, res);
} }
wake_event.notify_all(); wake_event.notify_all();
@ -71,7 +78,7 @@ void BackgroundProcessingPool::removeTask(const TaskHandle & task)
if (task->removed.exchange(true)) if (task->removed.exchange(true))
return; return;
/// Дождёмся завершения всех выполнений этой задачи. /// Wait for all execution of this task.
{ {
Poco::ScopedWriteRWLock wlock(task->rwlock); Poco::ScopedWriteRWLock wlock(task->rwlock);
} }
@ -108,49 +115,27 @@ void BackgroundProcessingPool::threadFunction()
while (!shutdown) while (!shutdown)
{ {
Counters counters_diff; Counters counters_diff;
bool has_exception = false; bool done_work = false;
TaskHandle task;
try try
{ {
TaskHandle task; Poco::Timestamp min_time;
time_t min_time = std::numeric_limits<time_t>::max();
{ {
std::unique_lock<std::mutex> lock(tasks_mutex); std::unique_lock<std::mutex> lock(tasks_mutex);
if (!tasks.empty()) if (!tasks.empty())
{ {
/** Number of tasks is about number of tables of MergeTree family. for (const auto & time_handle : tasks)
* Select task with minimal 'next_time_to_execute', and place to end of queue.
* Remind that one task could be selected and executed simultaneously from many threads.
*
* Tasks is like priority queue,
* but we must have ability to change priority of any task in queue.
*
* If there is too much tasks, select from first 100.
* TODO Change list to multimap.
*/
size_t i = 0;
for (const auto & handle : tasks)
{ {
if (handle->removed) if (!time_handle.second->removed)
continue;
time_t next_time_to_execute = handle->next_time_to_execute;
if (next_time_to_execute < min_time)
{ {
min_time = next_time_to_execute; min_time = time_handle.first;
task = handle; task = time_handle.second;
}
++i;
if (i > 100)
break; break;
}
} }
if (task) /// Переложим в конец очереди (уменьшим приоритет среди задач с одинаковым next_time_to_execute).
tasks.splice(tasks.end(), tasks, task->iterator);
} }
} }
@ -166,13 +151,13 @@ void BackgroundProcessingPool::threadFunction()
continue; continue;
} }
/// Лучшей задачи не нашлось, а эта задача в прошлый раз ничего не сделала, и поэтому ей назначено некоторое время спать. /// No tasks ready for execution.
time_t current_time = time(0); Poco::Timestamp current_time;
if (min_time > current_time) if (min_time > current_time)
{ {
std::unique_lock<std::mutex> lock(tasks_mutex); std::unique_lock<std::mutex> lock(tasks_mutex);
wake_event.wait_for(lock, std::chrono::duration<double>( wake_event.wait_for(lock, std::chrono::microseconds(
min_time - current_time + std::uniform_real_distribution<double>(0, sleep_seconds_random_part)(rng))); min_time - current_time + std::uniform_int_distribution<uint64_t>(0, sleep_seconds_random_part * 1000000)(rng)));
} }
Poco::ScopedReadRWLock rlock(task->rwlock); Poco::ScopedReadRWLock rlock(task->rwlock);
@ -184,20 +169,15 @@ void BackgroundProcessingPool::threadFunction()
CurrentMetrics::Increment metric_increment{CurrentMetrics::BackgroundPoolTask}; CurrentMetrics::Increment metric_increment{CurrentMetrics::BackgroundPoolTask};
Context context(*this, counters_diff); Context context(*this, counters_diff);
bool done_work = task->function(context); done_work = task->function(context);
/// Если задача сделала полезную работу, то она сможет выполняться в следующий раз хоть сразу.
/// Если нет - добавляем задержку перед повторным исполнением.
task->next_time_to_execute = time(0) + (done_work ? 0 : sleep_seconds);
} }
} }
catch (...) catch (...)
{ {
has_exception = true;
tryLogCurrentException(__PRETTY_FUNCTION__); tryLogCurrentException(__PRETTY_FUNCTION__);
} }
/// Вычтем все счётчики обратно. /// Subtract counters backwards.
if (!counters_diff.empty()) if (!counters_diff.empty())
{ {
std::unique_lock<std::mutex> lock(counters_mutex); std::unique_lock<std::mutex> lock(counters_mutex);
@ -208,10 +188,18 @@ void BackgroundProcessingPool::threadFunction()
if (shutdown) if (shutdown)
break; break;
if (has_exception) /// If task has done work, it could be executed again immediately.
/// If not, add delay before next run.
Poco::Timestamp next_time_to_execute = Poco::Timestamp() + (done_work ? 0 : sleep_seconds * 1000000);
{ {
std::unique_lock<std::mutex> lock(tasks_mutex); std::unique_lock<std::mutex> lock(tasks_mutex);
wake_event.wait_for(lock, std::chrono::duration<double>(sleep_seconds));
if (task->removed)
return;
tasks.erase(task->iterator);
task->iterator = tasks.emplace(next_time_to_execute, task);
} }
} }
} }

View File

@ -1224,32 +1224,31 @@ bool MergeTreeData::hasBlockNumberInMonth(Int64 block_number, DayNum_t month) co
return false; return false;
} }
void MergeTreeData::delayInsertIfNeeded(Poco::Event * until) void MergeTreeData::delayInsertIfNeeded(Poco::Event * until)
{ {
size_t parts_count = getMaxPartsCountForMonth(); const size_t parts_count = getMaxPartsCountForMonth();
if (parts_count > settings.parts_to_delay_insert) if (parts_count < settings.parts_to_delay_insert)
return;
if (parts_count >= settings.parts_to_throw_insert)
{ {
double delay = std::pow(settings.insert_delay_step, parts_count - settings.parts_to_delay_insert); ProfileEvents::increment(ProfileEvents::RejectedInserts);
delay /= 1000; throw Exception("Too much parts. Merges are processing significantly slower than inserts.", ErrorCodes::TOO_MUCH_PARTS);
if (delay > DBMS_MAX_DELAY_OF_INSERT)
{
ProfileEvents::increment(ProfileEvents::RejectedInserts);
throw Exception("Too much parts. Merges are processing significantly slower than inserts.", ErrorCodes::TOO_MUCH_PARTS);
}
ProfileEvents::increment(ProfileEvents::DelayedInserts);
ProfileEvents::increment(ProfileEvents::DelayedInsertsMilliseconds, delay * 1000);
LOG_INFO(log, "Delaying inserting block by "
<< std::fixed << std::setprecision(4) << delay << " sec. because there are " << parts_count << " parts");
if (until)
until->tryWait(delay * 1000);
else
std::this_thread::sleep_for(std::chrono::duration<double>(delay));
} }
const size_t max_k = settings.parts_to_throw_insert - settings.parts_to_delay_insert; /// always > 0
const size_t k = 1 + parts_count - settings.parts_to_delay_insert; /// from 1 to max_k
const double delay_sec = ::pow(settings.max_delay_to_insert, static_cast<double>(k)/static_cast<double>(max_k));
ProfileEvents::increment(ProfileEvents::DelayedInserts);
ProfileEvents::increment(ProfileEvents::DelayedInsertsMilliseconds, delay_sec * 1000);
LOG_INFO(log, "Delaying inserting block by "
<< std::fixed << std::setprecision(4) << delay_sec << " sec. because there are " << parts_count << " parts");
if (until)
until->tryWait(delay_sec * 1000);
else
std::this_thread::sleep_for(std::chrono::duration<double>(delay_sec));
} }
MergeTreeData::DataPartPtr MergeTreeData::getActiveContainingPart(const String & part_name) MergeTreeData::DataPartPtr MergeTreeData::getActiveContainingPart(const String & part_name)

View File

@ -45,16 +45,15 @@ void ReplicatedMergeTreeRestartingThread::run()
setThreadName("ReplMTRestart"); setThreadName("ReplMTRestart");
try bool first_time = true; /// Активация реплики в первый раз.
{ time_t prev_time_of_check_delay = 0;
bool first_time = true; /// Активация реплики в первый раз.
bool need_restart = false; /// Перезапуск по собственной инициативе, чтобы отдать лидерство.
time_t prev_time_of_check_delay = 0;
/// Запуск реплики при старте сервера/создании таблицы. Перезапуск реплики при истечении сессии с ZK. /// Запуск реплики при старте сервера/создании таблицы. Перезапуск реплики при истечении сессии с ZK.
while (!need_stop) while (!need_stop)
{
try
{ {
if (first_time || need_restart || storage.getZooKeeper()->expired()) if (first_time || storage.getZooKeeper()->expired())
{ {
if (first_time) if (first_time)
{ {
@ -62,10 +61,7 @@ void ReplicatedMergeTreeRestartingThread::run()
} }
else else
{ {
if (need_restart) LOG_WARNING(log, "ZooKeeper session has expired. Switching to a new session.");
LOG_WARNING(log, "Will reactivate replica.");
else
LOG_WARNING(log, "ZooKeeper session has expired. Switching to a new session.");
if (!storage.is_readonly) if (!storage.is_readonly)
CurrentMetrics::add(CurrentMetrics::ReadonlyReplica); CurrentMetrics::add(CurrentMetrics::ReadonlyReplica);
@ -101,7 +97,6 @@ void ReplicatedMergeTreeRestartingThread::run()
CurrentMetrics::sub(CurrentMetrics::ReadonlyReplica); CurrentMetrics::sub(CurrentMetrics::ReadonlyReplica);
storage.is_readonly = false; storage.is_readonly = false;
first_time = false; first_time = false;
need_restart = false;
} }
time_t current_time = time(0); time_t current_time = time(0);
@ -111,47 +106,37 @@ void ReplicatedMergeTreeRestartingThread::run()
time_t absolute_delay = 0; time_t absolute_delay = 0;
time_t relative_delay = 0; time_t relative_delay = 0;
bool error = false; storage.getReplicaDelays(absolute_delay, relative_delay);
try LOG_TRACE(log, "Absolute delay: " << absolute_delay << ". Relative delay: " << relative_delay << ".");
{
storage.getReplicaDelays(absolute_delay, relative_delay);
LOG_TRACE(log, "Absolute delay: " << absolute_delay << ". Relative delay: " << relative_delay << ".");
}
catch (...)
{
tryLogCurrentException(__PRETTY_FUNCTION__, "Cannot get replica delays");
error = true;
}
prev_time_of_check_delay = current_time; prev_time_of_check_delay = current_time;
/// Уступаем лидерство, если относительное отставание больше порога. /// Уступаем лидерство, если относительное отставание больше порога.
if (storage.is_leader_node if (storage.is_leader_node
&& (error || relative_delay > static_cast<time_t>(storage.data.settings.min_relative_delay_to_yield_leadership))) && relative_delay > static_cast<time_t>(storage.data.settings.min_relative_delay_to_yield_leadership))
{ {
if (error) LOG_INFO(log, "Relative replica delay (" << relative_delay << " seconds) is bigger than threshold ("
LOG_INFO(log, "Will yield leadership."); << storage.data.settings.min_relative_delay_to_yield_leadership << "). Will yield leadership.");
else
LOG_INFO(log, "Relative replica delay (" << relative_delay << " seconds) is bigger than threshold ("
<< storage.data.settings.min_relative_delay_to_yield_leadership << "). Will yield leadership.");
ProfileEvents::increment(ProfileEvents::ReplicaYieldLeadership); ProfileEvents::increment(ProfileEvents::ReplicaYieldLeadership);
need_restart = true; if (storage.is_leader_node)
continue; {
storage.is_leader_node = false;
if (storage.merge_selecting_thread.joinable())
storage.merge_selecting_thread.join();
storage.leader_election->yield();
}
} }
} }
wakeup_event.tryWait(check_period_ms);
} }
} catch (...)
catch (...) {
{ tryLogCurrentException(__PRETTY_FUNCTION__);
tryLogCurrentException("StorageReplicatedMergeTree::restartingThread"); }
LOG_ERROR(log, "Unexpected exception in restartingThread. The storage will be readonly until server restart.");
goReadOnlyPermanently(); wakeup_event.tryWait(check_period_ms);
LOG_DEBUG(log, "Restarting thread finished");
return;
} }
try try
@ -176,6 +161,10 @@ void ReplicatedMergeTreeRestartingThread::run()
storage.unreplicated_merger->cancelForever(); storage.unreplicated_merger->cancelForever();
partialShutdown(); partialShutdown();
if (storage.queue_task_handle)
storage.context.getBackgroundPool().removeTask(storage.queue_task_handle);
storage.queue_task_handle.reset();
} }
catch (...) catch (...)
{ {
@ -211,9 +200,10 @@ bool ReplicatedMergeTreeRestartingThread::tryStartup()
storage.alter_thread = std::make_unique<ReplicatedMergeTreeAlterThread>(storage); storage.alter_thread = std::make_unique<ReplicatedMergeTreeAlterThread>(storage);
storage.cleanup_thread = std::make_unique<ReplicatedMergeTreeCleanupThread>(storage); storage.cleanup_thread = std::make_unique<ReplicatedMergeTreeCleanupThread>(storage);
storage.part_check_thread.start(); storage.part_check_thread.start();
storage.queue_task_handle = storage.context.getBackgroundPool().addTask(
std::bind(&StorageReplicatedMergeTree::queueTask, &storage, std::placeholders::_1)); if (!storage.queue_task_handle)
storage.queue_task_handle->wake(); storage.queue_task_handle = storage.context.getBackgroundPool().addTask(
std::bind(&StorageReplicatedMergeTree::queueTask, &storage, std::placeholders::_1));
return true; return true;
} }
@ -362,11 +352,15 @@ void ReplicatedMergeTreeRestartingThread::partialShutdown()
storage.replica_is_active_node = nullptr; storage.replica_is_active_node = nullptr;
LOG_TRACE(log, "Waiting for threads to finish"); LOG_TRACE(log, "Waiting for threads to finish");
if (storage.is_leader_node)
{ {
storage.is_leader_node = false; std::lock_guard<std::mutex> lock(storage.leader_node_mutex);
if (storage.merge_selecting_thread.joinable())
storage.merge_selecting_thread.join(); if (storage.is_leader_node)
{
storage.is_leader_node = false;
if (storage.merge_selecting_thread.joinable())
storage.merge_selecting_thread.join();
}
} }
if (storage.queue_updating_thread.joinable()) if (storage.queue_updating_thread.joinable())
storage.queue_updating_thread.join(); storage.queue_updating_thread.join();
@ -375,30 +369,21 @@ void ReplicatedMergeTreeRestartingThread::partialShutdown()
storage.alter_thread.reset(); storage.alter_thread.reset();
storage.part_check_thread.stop(); storage.part_check_thread.stop();
if (storage.queue_task_handle)
storage.context.getBackgroundPool().removeTask(storage.queue_task_handle);
storage.queue_task_handle.reset();
/// Yielding leadership only after finish of merge_selecting_thread. /// Yielding leadership only after finish of merge_selecting_thread.
/// Otherwise race condition with parallel run of merge selecting thread on different servers is possible. /// Otherwise race condition with parallel run of merge selecting thread on different servers is possible.
///
/// On the other hand, leader_election could call becomeLeader() from own thread after
/// merge_selecting_thread is finished and restarting_thread is destroyed.
/// becomeLeader() recreates merge_selecting_thread and it becomes joinable again, even restarting_thread is destroyed.
/// But restarting_thread is responsible to stop merge_selecting_thread.
/// It will lead to std::terminate in ~StorageReplicatedMergeTree().
/// Such behaviour was rarely observed on DROP queries.
/// Therefore we need either avoid becoming leader after first shutdown call (more deliberate choice),
/// either manually wait merge_selecting_thread.join() inside ~StorageReplicatedMergeTree(), either or something third.
/// So, we added shutdown check in becomeLeader() and made its creation and deletion atomic.
storage.leader_election = nullptr; storage.leader_election = nullptr;
LOG_TRACE(log, "Threads finished"); LOG_TRACE(log, "Threads finished");
} }
void ReplicatedMergeTreeRestartingThread::goReadOnlyPermanently()
{
LOG_INFO(log, "Going to readonly mode");
ProfileEvents::increment(ProfileEvents::ReplicaPermanentlyReadonly);
if (!storage.is_readonly)
CurrentMetrics::add(CurrentMetrics::ReadonlyReplica);
storage.is_readonly = true;
stop();
partialShutdown();
}
} }

View File

@ -79,16 +79,15 @@ StorageDistributed::StorageDistributed(
NamesAndTypesListPtr columns_, NamesAndTypesListPtr columns_,
const String & remote_database_, const String & remote_database_,
const String & remote_table_, const String & remote_table_,
const Cluster & cluster_, const String & cluster_name_,
Context & context_, Context & context_,
const ASTPtr & sharding_key_, const ASTPtr & sharding_key_,
const String & data_path_) const String & data_path_)
: name(name_), columns(columns_), : name(name_), columns(columns_),
remote_database(remote_database_), remote_table(remote_table_), remote_database(remote_database_), remote_table(remote_table_),
context(context_), cluster(cluster_), context(context_), cluster_name(cluster_name_), has_sharding_key(sharding_key_),
sharding_key_expr(sharding_key_ ? ExpressionAnalyzer(sharding_key_, context, nullptr, *columns).getActions(false) : nullptr), sharding_key_expr(sharding_key_ ? ExpressionAnalyzer(sharding_key_, context, nullptr, *columns).getActions(false) : nullptr),
sharding_key_column_name(sharding_key_ ? sharding_key_->getColumnName() : String{}), sharding_key_column_name(sharding_key_ ? sharding_key_->getColumnName() : String{}),
write_enabled(!data_path_.empty() && (((cluster.getLocalShardCount() + cluster.getRemoteShardCount()) < 2) || sharding_key_)),
path(data_path_.empty() ? "" : (data_path_ + escapeForFileName(name) + '/')) path(data_path_.empty() ? "" : (data_path_ + escapeForFileName(name) + '/'))
{ {
createDirectoryMonitors(); createDirectoryMonitors();
@ -102,17 +101,16 @@ StorageDistributed::StorageDistributed(
const ColumnDefaults & column_defaults_, const ColumnDefaults & column_defaults_,
const String & remote_database_, const String & remote_database_,
const String & remote_table_, const String & remote_table_,
const Cluster & cluster_, const String & cluster_name_,
Context & context_, Context & context_,
const ASTPtr & sharding_key_, const ASTPtr & sharding_key_,
const String & data_path_) const String & data_path_)
: IStorage{materialized_columns_, alias_columns_, column_defaults_}, : IStorage{materialized_columns_, alias_columns_, column_defaults_},
name(name_), columns(columns_), name(name_), columns(columns_),
remote_database(remote_database_), remote_table(remote_table_), remote_database(remote_database_), remote_table(remote_table_),
context(context_), cluster(cluster_), context(context_), cluster_name(cluster_name_), has_sharding_key(sharding_key_),
sharding_key_expr(sharding_key_ ? ExpressionAnalyzer(sharding_key_, context, nullptr, *columns).getActions(false) : nullptr), sharding_key_expr(sharding_key_ ? ExpressionAnalyzer(sharding_key_, context, nullptr, *columns).getActions(false) : nullptr),
sharding_key_column_name(sharding_key_ ? sharding_key_->getColumnName() : String{}), sharding_key_column_name(sharding_key_ ? sharding_key_->getColumnName() : String{}),
write_enabled(!data_path_.empty() && (((cluster.getLocalShardCount() + cluster.getRemoteShardCount()) < 2) || sharding_key_)),
path(data_path_.empty() ? "" : (data_path_ + escapeForFileName(name) + '/')) path(data_path_.empty() ? "" : (data_path_ + escapeForFileName(name) + '/'))
{ {
createDirectoryMonitors(); createDirectoryMonitors();
@ -126,7 +124,7 @@ StoragePtr StorageDistributed::create(
const ColumnDefaults & column_defaults_, const ColumnDefaults & column_defaults_,
const String & remote_database_, const String & remote_database_,
const String & remote_table_, const String & remote_table_,
const String & cluster_name, const String & cluster_name_,
Context & context_, Context & context_,
const ASTPtr & sharding_key_, const ASTPtr & sharding_key_,
const String & data_path_) const String & data_path_)
@ -135,7 +133,7 @@ StoragePtr StorageDistributed::create(
name_, columns_, name_, columns_,
materialized_columns_, alias_columns_, column_defaults_, materialized_columns_, alias_columns_, column_defaults_,
remote_database_, remote_table_, remote_database_, remote_table_,
context_.getCluster(cluster_name), context_, cluster_name_, context_,
sharding_key_, data_path_ sharding_key_, data_path_
); );
} }
@ -146,15 +144,14 @@ StoragePtr StorageDistributed::create(
NamesAndTypesListPtr columns_, NamesAndTypesListPtr columns_,
const String & remote_database_, const String & remote_database_,
const String & remote_table_, const String & remote_table_,
std::shared_ptr<Cluster> & owned_cluster_, ClusterPtr & owned_cluster_,
Context & context_) Context & context_)
{ {
auto res = make_shared( auto res = make_shared(
name_, columns_, remote_database_, name_, columns_, remote_database_,
remote_table_, *owned_cluster_, context_ remote_table_, String{}, context_
); );
/// Захватываем владение объектом-кластером.
res->owned_cluster = owned_cluster_; res->owned_cluster = owned_cluster_;
return res; return res;
@ -169,7 +166,9 @@ BlockInputStreams StorageDistributed::read(
const size_t max_block_size, const size_t max_block_size,
const unsigned threads) const unsigned threads)
{ {
size_t result_size = (cluster.getRemoteShardCount() * settings.max_parallel_replicas) + cluster.getLocalShardCount(); auto cluster = getCluster();
size_t result_size = (cluster->getRemoteShardCount() * settings.max_parallel_replicas) + cluster->getLocalShardCount();
processed_stage = result_size == 1 || settings.distributed_group_by_no_merge processed_stage = result_size == 1 || settings.distributed_group_by_no_merge
? QueryProcessingStage::Complete ? QueryProcessingStage::Complete
@ -201,6 +200,11 @@ BlockInputStreams StorageDistributed::read(
BlockOutputStreamPtr StorageDistributed::write(ASTPtr query, const Settings & settings) BlockOutputStreamPtr StorageDistributed::write(ASTPtr query, const Settings & settings)
{ {
auto cluster = context.getCluster(cluster_name);
/// TODO: !path.empty() can be replaced by !owned_cluster or !cluster_name.empty() ?
bool write_enabled = !path.empty() && (((cluster->getLocalShardCount() + cluster->getRemoteShardCount()) < 2) || has_sharding_key);
if (!write_enabled) if (!write_enabled)
throw Exception{ throw Exception{
"Method write is not supported by storage " + getName() + "Method write is not supported by storage " + getName() +
@ -208,9 +212,9 @@ BlockOutputStreamPtr StorageDistributed::write(ASTPtr query, const Settings & se
ErrorCodes::STORAGE_REQUIRES_PARAMETER ErrorCodes::STORAGE_REQUIRES_PARAMETER
}; };
/// DistributedBlockOutputStream will not own cluster, but will own ConnectionPools of the cluster
return std::make_shared<DistributedBlockOutputStream>( return std::make_shared<DistributedBlockOutputStream>(
*this, *this, rewriteInsertQuery(query, remote_database, remote_table), cluster);
rewriteInsertQuery(query, remote_database, remote_table));
} }
void StorageDistributed::alter(const AlterCommands & params, const String & database_name, const String & table_name, const Context & context) void StorageDistributed::alter(const AlterCommands & params, const String & database_name, const String & table_name, const Context & context)
@ -247,7 +251,10 @@ void StorageDistributed::reshardPartitions(ASTPtr query, const String & database
" queries for distributed tables", " queries for distributed tables",
ErrorCodes::RESHARDING_INVALID_PARAMETERS}; ErrorCodes::RESHARDING_INVALID_PARAMETERS};
std::string coordinator_id = resharding_worker.createCoordinator(cluster); auto cluster = getCluster();
/// resharding_worker doesn't need to own cluster, here only meta-information of cluster is used
std::string coordinator_id = resharding_worker.createCoordinator(*cluster);
std::atomic<bool> has_notified_error{false}; std::atomic<bool> has_notified_error{false};
@ -374,6 +381,7 @@ void StorageDistributed::reshardPartitions(ASTPtr query, const String & database
BlockInputStreams StorageDistributed::describe(const Context & context, const Settings & settings) BlockInputStreams StorageDistributed::describe(const Context & context, const Settings & settings)
{ {
/// Создать запрос DESCRIBE TABLE. /// Создать запрос DESCRIBE TABLE.
auto cluster = getCluster();
ASTPtr describe_query_ptr = std::make_shared<ASTDescribeQuery>(); ASTPtr describe_query_ptr = std::make_shared<ASTDescribeQuery>();
auto & describe_query = static_cast<ASTDescribeQuery &>(*describe_query_ptr); auto & describe_query = static_cast<ASTDescribeQuery &>(*describe_query_ptr);
@ -432,7 +440,12 @@ void StorageDistributed::requireDirectoryMonitor(const std::string & name)
size_t StorageDistributed::getShardCount() const size_t StorageDistributed::getShardCount() const
{ {
return cluster.getRemoteShardCount(); return getCluster()->getRemoteShardCount();
}
ClusterPtr StorageDistributed::getCluster() const
{
return (owned_cluster) ? owned_cluster : context.getCluster(cluster_name);
} }
} }

View File

@ -1872,6 +1872,11 @@ void StorageReplicatedMergeTree::removePartAndEnqueueFetch(const String & part_n
void StorageReplicatedMergeTree::becomeLeader() void StorageReplicatedMergeTree::becomeLeader()
{ {
std::lock_guard<std::mutex> lock(leader_node_mutex);
if (shutdown_called)
return;
LOG_INFO(log, "Became leader"); LOG_INFO(log, "Became leader");
is_leader_node = true; is_leader_node = true;
merge_selecting_thread = std::thread(&StorageReplicatedMergeTree::mergeSelectingThread, this); merge_selecting_thread = std::thread(&StorageReplicatedMergeTree::mergeSelectingThread, this);

View File

@ -73,14 +73,14 @@ BlockInputStreams StorageSystemClusters::read(
default_database_column->insert(address.default_database); default_database_column->insert(address.default_database);
}; };
const auto & clusters = context.getClusters(); auto clusters = context.getClusters().getContainer();
for (const auto & entry : clusters->impl) for (const auto & entry : clusters)
{ {
const std::string cluster_name = entry.first; const std::string cluster_name = entry.first;
const Cluster & cluster = entry.second; const ClusterPtr cluster = entry.second;
const auto & addresses = cluster.getShardsAddresses(); const auto & addresses = cluster->getShardsAddresses();
const auto & addresses_with_failover = cluster.getShardsWithFailoverAddresses(); const auto & addresses_with_failover = cluster->getShardsWithFailoverAddresses();
const auto & shards_info = cluster.getShardsInfo(); const auto & shards_info = cluster->getShardsInfo();
if (!addresses.empty()) if (!addresses.empty())
{ {

View File

@ -64,10 +64,10 @@ StoragePtr TableFunctionShardByHash::execute(ASTPtr ast_function, Context & cont
if (ASTIdentifier * id = typeid_cast<ASTIdentifier *>(arg.get())) if (ASTIdentifier * id = typeid_cast<ASTIdentifier *>(arg.get()))
id->kind = ASTIdentifier::Table; id->kind = ASTIdentifier::Table;
const Cluster & cluster = context.getCluster(cluster_name); auto cluster = context.getCluster(cluster_name);
size_t shard_index = sipHash64(key) % cluster.getShardCount(); size_t shard_index = sipHash64(key) % cluster->getShardCount();
std::shared_ptr<Cluster> shard(cluster.getClusterWithSingleShard(shard_index).release()); std::shared_ptr<Cluster> shard(cluster->getClusterWithSingleShard(shard_index).release());
return StorageDistributed::create( return StorageDistributed::create(
getName(), getName(),

View File

@ -36,7 +36,7 @@ NamesAndTypesList getStructureOfRemoteTable(
BlockInputStreamPtr input = BlockInputStreamPtr input =
std::make_shared<RemoteBlockInputStream>( std::make_shared<RemoteBlockInputStream>(
pool.get(), query, &settings, nullptr, pool, query, &settings, nullptr,
Tables(), QueryProcessingStage::Complete, context); Tables(), QueryProcessingStage::Complete, context);
input->readPrefix(); input->readPrefix();

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/bin/bash
set -e set -e
curl --local-port 1390 'http://localhost:8123?query=SELECT%20port%20FROM%20system.processes%20ORDER%20BY%20elapsed%20LIMIT%201' curl -sS --local-port 1390 'http://localhost:8123?query=SELECT%20port%20FROM%20system.processes%20ORDER%20BY%20elapsed%20LIMIT%201'

View File

@ -0,0 +1,3 @@
#!/bin/bash
clickhouse-client --multiquery --query="SELECT 1; SELECT xyz; SELECT 2;" 2> /dev/null || true;

View File

@ -0,0 +1,3 @@
canada congo net-domena
yandex yandex yandex yandex яндекс яндекс
canada hello hello hello hello hello canada canada

View File

@ -0,0 +1,31 @@
SELECT
firstSignificantSubdomain('http://hello.canada.ca') AS canada,
firstSignificantSubdomain('http://hello.congo.com') AS congo,
firstSignificantSubdomain('http://pochemu.net-domena.ru') AS why;
SELECT
firstSignificantSubdomain('ftp://www.yandex.com.tr/news.html'),
firstSignificantSubdomain('https://www.yandex.ua/news.html'),
firstSignificantSubdomain('magnet:yandex.abc'),
firstSignificantSubdomain('ftp://www.yandex.co.uk/news.html'),
firstSignificantSubdomain('ftp://yandex.co.yandex'),
firstSignificantSubdomain('http://ввв.яндекс.org.рф'),
firstSignificantSubdomain('https://api.www3.static.dev.ввв.яндекс.рф');
SELECT
firstSignificantSubdomain('http://hello.canada.c'),
firstSignificantSubdomain('http://hello.canada.'),
firstSignificantSubdomain('http://hello.canada'),
firstSignificantSubdomain('http://hello.c'),
firstSignificantSubdomain('http://hello.'),
firstSignificantSubdomain('http://hello'),
firstSignificantSubdomain('http://'),
firstSignificantSubdomain('http:/'),
firstSignificantSubdomain('http:'),
firstSignificantSubdomain('http'),
firstSignificantSubdomain('h'),
firstSignificantSubdomain('.'),
firstSignificantSubdomain(''),
firstSignificantSubdomain('http://hello.canada..com'),
firstSignificantSubdomain('http://hello..canada.com'),
firstSignificantSubdomain('http://hello.canada.com.');

View File

@ -264,7 +264,7 @@ In ClickHouse, data can reside on different shards. Each shard can be a group of
If you are familiar with standard SQL, we can&#39;t really talk about SQL support. If you are familiar with standard SQL, we can&#39;t really talk about SQL support.
NULLs are not supported. All the functions have different names. However, this is a declarative query language based on SQL that can&#39;t be differentiated from SQL in many instances. NULLs are not supported. All the functions have different names. However, this is a declarative query language based on SQL that can&#39;t be differentiated from SQL in many instances.
JOINs are supported. Subqueries are supported in FROM, IN, JOIN clauses; and scalar subqueries. JOINs are supported. Subqueries are supported in FROM, IN, JOIN clauses; and scalar subqueries.
Correllated subqueries are not supported. Correlated subqueries are not supported.
<h3 class="not-for-contents">7. Vector engine.</h3> <h3 class="not-for-contents">7. Vector engine.</h3>
@ -352,17 +352,17 @@ At this time (May 2016), there aren&#39;t any available open-source and free sys
<h3 class="not-for-contents">1. Why not to use systems like map-reduce?</h3> <h3 class="not-for-contents">1. Why not to use systems like map-reduce?</h3>
Systems like map-reduce are distributed computing systems, where the reduce phase is performed using distributed sorting. Systems like map-reduce are distributed computing systems, where the reduce phase is performed using distributed sorting.
Regading this aspect, map-reduce is similar to other systems like YAMR, <a href="http://hadoop.apache.org/">Hadoop</a>, <a href="https://yandexdataschool.ru/about/conference/program/babenko">YT</a>. Regarding this aspect, map-reduce is similar to other systems like YAMR, <a href="http://hadoop.apache.org/">Hadoop</a>, <a href="https://yandexdataschool.ru/about/conference/program/babenko">YT</a>.
These systems are not suitable for online queries because of latency, So they can't be used in backend-level for web interface. These systems are not suitable for online queries because of latency, So they can't be used in backend-level for web interface.
Systems like this also are not suitable for realtime updates. Systems like this also are not suitable for real-time updates.
Distributed sorting is not optimal solution for reduce operations, if the result of the operation and all intermediate results, shall they exist, fit in operational memory of a single server, as usually happens in case of online analytical queries. Distributed sorting is not optimal solution for reduce operations, if the result of the operation and all intermediate results, shall they exist, fit in operational memory of a single server, as usually happens in case of online analytical queries.
In this case the optimal way to perform reduce operations is by using a hash-table. A common optimization method for map-reduce tasks is combine operation (partial reduce) which uses hash-tables in memory. This optimization is done by the user manually. In this case the optimal way to perform reduce operations is by using a hash-table. A common optimization method for map-reduce tasks is combine operation (partial reduce) which uses hash-tables in memory. This optimization is done by the user manually.
Distributed sorting is the main reason for long latencies of simple map-reduce jobs. Distributed sorting is the main reason for long latencies of simple map-reduce jobs.
Systems similar to map-reduce enable running any code on the cluster. But for OLAP use-cases declerative query languages are better suited as they allow to carry out investigations faster. For example, for Hadoop there are <a href="https://hive.apache.org/">Hive</a> and <a href="https://pig.apache.org/">Pig</a>. There are others: <a href="http://impala.io/">Cloudera Impala</a>, <a href="http://shark.cs.berkeley.edu/">Shark (depricated)</a> and <a href="http://spark.apache.org/sql/">Spark SQL</a> for <a href="http://spark.apache.org/">Spark</a>, <a href="https://prestodb.io/">Presto</a>, <a href="https://drill.apache.org/">Apache Drill</a>. Systems similar to map-reduce enable running any code on the cluster. But for OLAP use-cases declarative query languages are better suited as they allow to carry out investigations faster. For example, for Hadoop there are <a href="https://hive.apache.org/">Hive</a> and <a href="https://pig.apache.org/">Pig</a>. There are others: <a href="http://impala.io/">Cloudera Impala</a>, <a href="http://shark.cs.berkeley.edu/">Shark (deprecated)</a> and <a href="http://spark.apache.org/sql/">Spark SQL</a> for <a href="http://spark.apache.org/">Spark</a>, <a href="https://prestodb.io/">Presto</a>, <a href="https://drill.apache.org/">Apache Drill</a>.
However, performance of such tasks is highly sub-optimal compared to the performance of specialized systems and relatively high latency does not allow the use of these systems as a backend for the web interface. However, performance of such tasks is highly sub-optimal compared to the performance of specialized systems and relatively high latency does not allow the use of these systems as a backend for the web interface.
YT allows you to store separate groups of columns. But YT is not a truly columnar storage system, as the system has no fixed length data types (so you can efficiently store a number without "garbage"), and there is no vector engine. Tasks in YT are performed by arbitrary code in streaming mode, so can not be suficiently optimized (up to hundreds of millions of lines per second per server). In 2014-2016 YT is to develop "dynamic table sorting" functionality using Merge Tree, strongly typed values and SQL-like language support. Dynamicly sorted tables are not suited for OLAP tasks, since the data is stored in rows. Query language development in YT is still in incubating phase, which does not allow it to focus on this functionality. YT developers are considering dynamicly sorted tables for use in OLTP and Key-Value scenarios. YT allows you to store separate groups of columns. But YT is not a truly columnar storage system, as the system has no fixed length data types (so you can efficiently store a number without "garbage"), and there is no vector engine. Tasks in YT are performed by arbitrary code in streaming mode, so can not be sufficiently optimized (up to hundreds of millions of lines per second per server). In 2014-2016 YT is to develop "dynamic table sorting" functionality using Merge Tree, strongly typed values and SQL-like language support. Dynamically sorted tables are not suited for OLAP tasks, since the data is stored in rows. Query language development in YT is still in incubating phase, which does not allow it to focus on this functionality. YT developers are considering dynamically sorted tables for use in OLTP and Key-Value scenarios.
==Performance== ==Performance==
@ -769,7 +769,7 @@ There is official JDBC driver for ClickHouse. See <a href="https://github.com/ya
==Third-party client libraries== ==Third-party client libraries==
There exist third-party client libraries for <a href="https://github.com/Infinidat/infi.clickhouse_orm">Python</a>, PHP (<a href="https://github.com/8bitov/clickhouse-php-client">1</a>, <a href="https://github.com/SevaCode/PhpClickHouseClient">2</a>, <a href="https://github.com/smi2/phpClickHouse">3</a>), <a href="https://github.com/roistat/go-clickhouse">Go</a>, Node.js (<a href="https://github.com/TimonKK/clickhouse">1</a>, <a href="https://github.com/apla/node-clickhouse">2</a>), <a href="https://github.com/elcamlost/perl-DBD-ClickHouse">Perl</a>. There exist third-party client libraries for Python (<a href="https://github.com/Infinidat/infi.clickhouse_orm">1</a>), PHP (<a href="https://github.com/8bitov/clickhouse-php-client">1</a>, <a href="https://github.com/SevaCode/PhpClickHouseClient">2</a>, <a href="https://github.com/smi2/phpClickHouse">3</a>), Go (<a href="https://github.com/roistat/go-clickhouse">1</a>), Node.js (<a href="https://github.com/TimonKK/clickhouse">1</a>, <a href="https://github.com/apla/node-clickhouse">2</a>), Perl (<a href="https://github.com/elcamlost/perl-DBD-ClickHouse">1</a>, <a href="https://metacpan.org/release/HTTP-ClickHouse">2</a>, <a href="https://metacpan.org/release/AnyEvent-ClickHouse">3</a>), Ruby (<a href="https://github.com/archan937/clickhouse">1</a>), R (<a href="https://github.com/hannesmuehleisen/clickhouse-r">1</a>).
Libraries was not tested by us. Ordering is arbitrary. Libraries was not tested by us. Ordering is arbitrary.
@ -2932,7 +2932,9 @@ When the server starts (or establishes a new session with ZooKeeper), it only ch
If the local set of data differs too much from the expected one, a safety mechanism is triggered. The server enters this in the log and refuses to launch. The reason for this is that this case may indicate a configuration error, such as if a replica on a shard was accidentally configured like a replica on a different shard. However, the thresholds for this mechanism are set fairly low, and this situation might occur during normal failure recovery. In this case, data is restored semi-automatically - by &quot;pushing a button&quot;. If the local set of data differs too much from the expected one, a safety mechanism is triggered. The server enters this in the log and refuses to launch. The reason for this is that this case may indicate a configuration error, such as if a replica on a shard was accidentally configured like a replica on a different shard. However, the thresholds for this mechanism are set fairly low, and this situation might occur during normal failure recovery. In this case, data is restored semi-automatically - by &quot;pushing a button&quot;.
To start recovery, create the node <span class="inline-example">/<i>path_to_table</i>/<i>replica_name</i>/flags/force_restore_data</span> in ZooKeeper with any content, and launch the server. On start, the server deletes this flag and starts recovery. To start recovery, create the node <span class="inline-example">/<i>path_to_table</i>/<i>replica_name</i>/flags/force_restore_data</span> in ZooKeeper with any content or run command to recover all replicated tables:
%%sudo -u metrika touch /opt/clickhouse/flags/force_restore_data%%
Then launch the server. On start, the server deletes these flags and starts recovery.
===Recovery after complete data loss=== ===Recovery after complete data loss===
@ -2945,7 +2947,9 @@ If all data and metadata disappeared from one of the servers, follow these steps
3. Copy table definitions located in %%/opt/clickhouse/metadata/%% from a replica. If a shard or replica identifier is defined explicitly in the table definitions, correct it so that it corresponds to this replica. (Alternatively, launch the server and make all the ATTACH TABLE queries that should have been in the .sql files in %%/opt/clickhouse/metadata/%%.) 3. Copy table definitions located in %%/opt/clickhouse/metadata/%% from a replica. If a shard or replica identifier is defined explicitly in the table definitions, correct it so that it corresponds to this replica. (Alternatively, launch the server and make all the ATTACH TABLE queries that should have been in the .sql files in %%/opt/clickhouse/metadata/%%.)
4. Create the <span class="inline-example">/<i>path_to_table</i>/<i>replica_name</i>/flags/force_restore_data</span> node in ZooKeeper with any content, and launch the server (restart it if it is already running). Data will be downloaded from replicas. 4. Create the <span class="inline-example">/<i>path_to_table</i>/<i>replica_name</i>/flags/force_restore_data</span> node in ZooKeeper with any content or run command to recover all replicated tables:
%%sudo -u metrika touch /opt/clickhouse/flags/force_restore_data%%
Then launch the server (restart it if it is already running). Data will be downloaded from replicas.
An alternative recovery option is to delete information about the lost replica from ZooKeeper ( <span class="inline-example">/<i>path_to_table</i>/<i>replica_name</i></span>), then create the replica again as described in &quot;Creating replicated tables&quot;. An alternative recovery option is to delete information about the lost replica from ZooKeeper ( <span class="inline-example">/<i>path_to_table</i>/<i>replica_name</i></span>), then create the replica again as described in &quot;Creating replicated tables&quot;.
@ -4329,7 +4333,7 @@ In this section we discuss regular functions. For aggregate functions, see the s
In contrast to standard SQL, ClickHouse has strong typing. In other words, it doesn&#39;t make implicit conversions between types. Each function works for a specific set of types. This means that sometimes you need to use type conversion functions. In contrast to standard SQL, ClickHouse has strong typing. In other words, it doesn&#39;t make implicit conversions between types. Each function works for a specific set of types. This means that sometimes you need to use type conversion functions.
===Сommon subexpression elimination=== ===Common subexpression elimination===
All expressions in a query that have the same AST (the same record or same result of syntactic parsing) are considered to have identical values. Such expressions are concatenated and executed once. Identical subqueries are also eliminated this way. All expressions in a query that have the same AST (the same record or same result of syntactic parsing) are considered to have identical values. Such expressions are concatenated and executed once. Identical subqueries are also eliminated this way.
@ -6943,7 +6947,7 @@ We could have defined this list of networks directly in &#39;users.xml&#39;, or
The config includes comments explaining how to open access from everywhere. The config includes comments explaining how to open access from everywhere.
For use in production, only specify IP elements (IP addresses and their masks), since using &#39;host&#39; and &#39;hoost_regexp&#39; might cause extra latency. For use in production, only specify IP elements (IP addresses and their masks), since using &#39;host&#39; and &#39;host_regexp&#39; might cause extra latency.
Next the user settings profile is specified (see the section &quot;Settings profiles&quot;). You can specify the default profile, &#39;default&#39;. The profile can have any name. You can specify the same profile for different users. The most important thing you can write in the settings profile is &#39;readonly&#39; set to 1, which provides read-only access. Next the user settings profile is specified (see the section &quot;Settings profiles&quot;). You can specify the default profile, &#39;default&#39;. The profile can have any name. You can specify the same profile for different users. The most important thing you can write in the settings profile is &#39;readonly&#39; set to 1, which provides read-only access.

View File

@ -137,7 +137,7 @@ ClickHouse - столбцовая СУБД для OLAP (Columnar DBMS).
- при чтении, вынимается достаточно большое количество строк из БД, но только небольшое подмножество столбцов; - при чтении, вынимается достаточно большое количество строк из БД, но только небольшое подмножество столбцов;
- таблицы являются "широкими", то есть, содержат большое количество столбцов; - таблицы являются "широкими", то есть, содержат большое количество столбцов;
- запросы идут сравнительно редко (обычно не более сотни в секунду на сервер); - запросы идут сравнительно редко (обычно не более сотни в секунду на сервер);
- при выполнении простых запросов, допустимы задержки в районе 50мс; - при выполнении простых запросов, допустимы задержки в районе 50 мс;
- значения в столбцах достаточно мелкие - числа и небольшие строки (пример - 60 байт на URL); - значения в столбцах достаточно мелкие - числа и небольшие строки (пример - 60 байт на URL);
- требуется высокая пропускная способность при обработке одного запроса (до миллиардов строк в секунду на один сервер); - требуется высокая пропускная способность при обработке одного запроса (до миллиардов строк в секунду на один сервер);
- транзакции отсутствуют; - транзакции отсутствуют;
@ -238,7 +238,7 @@ LIMIT 20
В по-настоящему столбцовой СУБД рядом со значениями не хранится никакого "мусора". Например, должны поддерживаться значения постоянной длины, чтобы не хранить рядом со значениями типа "число" их длины. Для примера, миллиард значений типа UInt8 должен действительно занимать в несжатом виде около 1GB, иначе это сильно ударит по эффективности использования CPU. Очень важно хранить данные компактно (без "мусора") в том числе в несжатом виде, так как скорость разжатия (использование CPU) зависит, в основном, от объёма несжатых данных. В по-настоящему столбцовой СУБД рядом со значениями не хранится никакого "мусора". Например, должны поддерживаться значения постоянной длины, чтобы не хранить рядом со значениями типа "число" их длины. Для примера, миллиард значений типа UInt8 должен действительно занимать в несжатом виде около 1GB, иначе это сильно ударит по эффективности использования CPU. Очень важно хранить данные компактно (без "мусора") в том числе в несжатом виде, так как скорость разжатия (использование CPU) зависит, в основном, от объёма несжатых данных.
Этот пункт пришлось выделить, так как существуют системы, которые могут хранить значения отдельных столбцов по отдельности, но не могут эффективно выполять аналитические запросы в силу оптимизации под другой сценарий работы. Примеры: HBase, BigTable, Cassandra, HyperTable. В этих системах вы получите throughput в районе сотен тысяч строк в секунду, но не сотен миллионов строк в секунду. Этот пункт пришлось выделить, так как существуют системы, которые могут хранить значения отдельных столбцов по отдельности, но не могут эффективно выполнять аналитические запросы в силу оптимизации под другой сценарий работы. Примеры: HBase, BigTable, Cassandra, HyperTable. В этих системах вы получите throughput в районе сотен тысяч строк в секунду, но не сотен миллионов строк в секунду.
Также стоит заметить, что ClickHouse является СУБД, а не одной базой данных. То есть, ClickHouse позволяет создавать таблицы и базы данных в runtime, загружать данные и выполнять запросы без переконфигурирования и перезапуска сервера. Также стоит заметить, что ClickHouse является СУБД, а не одной базой данных. То есть, ClickHouse позволяет создавать таблицы и базы данных в runtime, загружать данные и выполнять запросы без переконфигурирования и перезапуска сервера.
@ -281,7 +281,7 @@ ClickHouse поддерживает таблицы с первичным клю
<h3 class="not-for-contents">10. Подходит для онлайн запросов.</h3> <h3 class="not-for-contents">10. Подходит для онлайн запросов.</h3>
Это позволяет использовать систему в качестве бэкенда для веб интерфейса. Низкие задержки позволяют не откладывать выполнение запроса, а выполнять его в момент загрузки страницы интерфейса Яндекс.Метрики. То есть, в режиме онлайн. Это позволяет использовать систему в качестве бэкенда для веб-интерфейса. Низкие задержки позволяют не откладывать выполнение запроса, а выполнять его в момент загрузки страницы интерфейса Яндекс.Метрики. То есть, в режиме онлайн.
<h3 class="not-for-contents">11. Поддержка приближённых вычислений.</h3> <h3 class="not-for-contents">11. Поддержка приближённых вычислений.</h3>
@ -315,7 +315,7 @@ ClickHouse поддерживает таблицы с первичным клю
Но агрегированные данные являются очень ограниченным решением, по следующим причинам: Но агрегированные данные являются очень ограниченным решением, по следующим причинам:
- вы должны заранее знать перечень отчётов, необходимых пользователю; - вы должны заранее знать перечень отчётов, необходимых пользователю;
- то есть, пользователь не может построить произвольный отчёт; - то есть, пользователь не может построить произвольный отчёт;
- при агрегации по большому количествую ключей, объём данных не уменьшается и агрегация бесполезна; - при агрегации по большому количеству ключей, объём данных не уменьшается и агрегация бесполезна;
- при большом количестве отчётов, получается слишком много вариантов агрегации (комбинаторный взрыв); - при большом количестве отчётов, получается слишком много вариантов агрегации (комбинаторный взрыв);
- при агрегации по ключам высокой кардинальности (например, URL) объём данных уменьшается не сильно (менее чем в 2 раза); - при агрегации по ключам высокой кардинальности (например, URL) объём данных уменьшается не сильно (менее чем в 2 раза);
- из-за этого, объём данных при агрегации может не уменьшиться, а вырасти; - из-за этого, объём данных при агрегации может не уменьшиться, а вырасти;
@ -361,7 +361,7 @@ ClickHouse имеет более десятка инсталляций в дру
Системами типа map-reduce будем называть системы распределённых вычислений, в которых операция reduce сделана на основе распределённой сортировки. Таким образом, к ним относятся YAMR, <a href="http://hadoop.apache.org/">Hadoop</a>, <a href="https://yandexdataschool.ru/about/conference/program/babenko">YT</a>. Системами типа map-reduce будем называть системы распределённых вычислений, в которых операция reduce сделана на основе распределённой сортировки. Таким образом, к ним относятся YAMR, <a href="http://hadoop.apache.org/">Hadoop</a>, <a href="https://yandexdataschool.ru/about/conference/program/babenko">YT</a>.
Такие системы не подходят для онлайн запросов в силу слишком большой latency. То есть, не могут быть использованы в качестве бэкенда для веб интерфейса. Такие системы не подходят для онлайн запросов в силу слишком большой latency. То есть, не могут быть использованы в качестве бэкенда для веб-интерфейса.
Такие системы не подходят для обновления данных в реальном времени. Такие системы не подходят для обновления данных в реальном времени.
Распределённая сортировка не является оптимальным способом выполнения операции reduce, если результат выполнения операции и все промежуточные результаты, при их наличии, помещаются в оперативку на одном сервере, как обычно бывает в запросах, выполняющихся в режиме онлайн. В таком случае, оптимальным способом выполнения операции reduce является хэш-таблица. Частым способом оптимизации map-reduce задач является предагрегация (частичный reduce) с использованием хэш-таблицы в оперативке. Эта оптимизация делается пользователем в ручном режиме. Распределённая сортировка не является оптимальным способом выполнения операции reduce, если результат выполнения операции и все промежуточные результаты, при их наличии, помещаются в оперативку на одном сервере, как обычно бывает в запросах, выполняющихся в режиме онлайн. В таком случае, оптимальным способом выполнения операции reduce является хэш-таблица. Частым способом оптимизации map-reduce задач является предагрегация (частичный reduce) с использованием хэш-таблицы в оперативке. Эта оптимизация делается пользователем в ручном режиме.
Распределённая сортировка является основной причиной тормозов при выполнении несложных map-reduce задач. Распределённая сортировка является основной причиной тормозов при выполнении несложных map-reduce задач.
@ -558,7 +558,7 @@ Connected to ClickHouse server version 0.0.18749.
Если вы являетесь сотрудником Яндекса, обращайтесь на внутреннюю рассылку по ClickHouse. Если вы являетесь сотрудником Яндекса, обращайтесь на внутреннюю рассылку по ClickHouse.
Вы можете подписаться на эту рассылку, чтобы получать анонсы, быть в курсе нововведений, а также видеть вопросы, которые возникают у других пользователей. Вы можете подписаться на эту рассылку, чтобы получать анонсы, быть в курсе нововведений, а также видеть вопросы, которые возникают у других пользователей.
Иначе вы можете задавать вопросы на <a href='https://stackoverflow.com/'>Stackoverflow</a> или участвовать в обсуждениях на <a href='https://groups.google.com/group/clickhouse'>Google Groups</a>. Также вы можете отправить приватное сообщение для разрабочиков по адресу <a href='mailto:clickhouse-feedback@yandex-team.com'>clickhouse-feedback@yandex-team.com</a>. Иначе вы можете задавать вопросы на <a href='https://stackoverflow.com/'>Stackoverflow</a> или участвовать в обсуждениях на <a href='https://groups.google.com/group/clickhouse'>Google Groups</a>. Также вы можете отправить приватное сообщение для разработчиков по адресу <a href='mailto:clickhouse-feedback@yandex-team.com'>clickhouse-feedback@yandex-team.com</a>.
@ -744,7 +744,7 @@ echo 'SELECT 1' | curl 'http://localhost:8123/?user=user&amp;password=password'
Если имя пользователя не указано, то используется имя пользователя default. Если пароль не указан, то используется пустой пароль. Если имя пользователя не указано, то используется имя пользователя default. Если пароль не указан, то используется пустой пароль.
Также в параметрах URL вы можете указать любые настроки, которые будут использованы для обработки одного запроса, или целые профили настроек. Пример: Также в параметрах URL вы можете указать любые настройки, которые будут использованы для обработки одного запроса, или целые профили настроек. Пример:
%%http://localhost:8123/?profile=web&amp;max_rows_to_read=1000000000&amp;query=SELECT+1%% %%http://localhost:8123/?profile=web&amp;max_rows_to_read=1000000000&amp;query=SELECT+1%%
@ -782,7 +782,7 @@ HTTP интерфейс позволяет передать внешние да
==Библиотеки от сторонних разработчиков== ==Библиотеки от сторонних разработчиков==
Существуют библиотеки для работы с ClickHouse для <a href="https://github.com/Infinidat/infi.clickhouse_orm">Python</a>, PHP (<a href="https://github.com/8bitov/clickhouse-php-client">1</a>, <a href="https://github.com/SevaCode/PhpClickHouseClient">2</a>, <a href="https://github.com/smi2/phpClickHouse">3</a>), <a href="https://github.com/roistat/go-clickhouse">Go</a>, Node.js (<a href="https://github.com/TimonKK/clickhouse">1</a>, <a href="https://github.com/apla/node-clickhouse">2</a>), <a href="https://github.com/elcamlost/perl-DBD-ClickHouse">Perl</a>. Существуют библиотеки для работы с ClickHouse для Python (<a href="https://github.com/Infinidat/infi.clickhouse_orm">1</a>), PHP (<a href="https://github.com/8bitov/clickhouse-php-client">1</a>, <a href="https://github.com/SevaCode/PhpClickHouseClient">2</a>, <a href="https://github.com/smi2/phpClickHouse">3</a>), Go (<a href="https://github.com/roistat/go-clickhouse">1</a>), Node.js (<a href="https://github.com/TimonKK/clickhouse">1</a>, <a href="https://github.com/apla/node-clickhouse">2</a>), Perl (<a href="https://github.com/elcamlost/perl-DBD-ClickHouse">1</a>, <a href="https://metacpan.org/release/HTTP-ClickHouse">2</a>, <a href="https://metacpan.org/release/AnyEvent-ClickHouse">3</a>), Ruby (<a href="https://github.com/archan937/clickhouse">1</a>), R (<a href="https://github.com/hannesmuehleisen/clickhouse-r">1</a>).
Библиотеки не тестировались нами. Порядок перечисления произвольный. Библиотеки не тестировались нами. Порядок перечисления произвольный.
@ -844,7 +844,7 @@ Connected to ClickHouse server version 0.0.26176.
/etc/clickhouse-client/config.xml /etc/clickhouse-client/config.xml
Настройки берутся только из первого найденного файла. Настройки берутся только из первого найденного файла.
Также вы можете указать любые настроки, которые будут использованы для обработки запросов. Например, %%clickhouse-client --max_threads=1%%. Подробнее см. раздел "Настройки". Также вы можете указать любые настройки, которые будут использованы для обработки запросов. Например, %%clickhouse-client --max_threads=1%%. Подробнее см. раздел "Настройки".
Клиент может быть использован в интерактивном и неинтерактивном (batch) режиме. Клиент может быть использован в интерактивном и неинтерактивном (batch) режиме.
Чтобы использовать batch режим, укажите параметр query, или отправьте данные в stdin (проверяется, что stdin - не терминал), или и то, и другое. Чтобы использовать batch режим, укажите параметр query, или отправьте данные в stdin (проверяется, что stdin - не терминал), или и то, и другое.
@ -950,7 +950,7 @@ cat file.csv | clickhouse-client --database=test --query="INSERT INTO test FORMA
<h4>Числовые литералы</h4> <h4>Числовые литералы</h4>
Числовой литерал пытается распарситься: Числовой литерал пытается распарситься:
- сначала как 64-битное число без знака, с помощью функци strtoull; - сначала как 64-битное число без знака, с помощью функции strtoull;
- если не получилось - то как 64-битное число со знаком, с помощью функци strtoll; - если не получилось - то как 64-битное число со знаком, с помощью функци strtoll;
- если не получилось - то как число с плавающей запятой, с помощью функци strtod; - если не получилось - то как число с плавающей запятой, с помощью функци strtod;
- иначе - ошибка. - иначе - ошибка.
@ -1463,7 +1463,7 @@ ALTER TABLE ... FREEZE PARTITION копирует только данные, н
Позволяет установить настройку param в значение value. Также можно одним запросом установить все настройки из заданного профиля настроек - для этого, укажите в качестве имени настройки profile. Подробнее смотри раздел "Настройки". Позволяет установить настройку param в значение value. Также можно одним запросом установить все настройки из заданного профиля настроек - для этого, укажите в качестве имени настройки profile. Подробнее смотри раздел "Настройки".
Настройка устанавливается на сессию, или на сервер (глобально), если указано GLOBAL. Настройка устанавливается на сессию, или на сервер (глобально), если указано GLOBAL.
При установке глобальной настроки, настройка на все уже запущенные сессии, включая текущую сессию, не устанавливается, а будет использована только для новых сессий. При установке глобальной настройки, настройка на все уже запущенные сессии, включая текущую сессию, не устанавливается, а будет использована только для новых сессий.
Настройки, заданные с помощью SET GLOBAL имеют меньший приоритет по сравнению с настройками, указанными в профиле пользователя, в конфигурационном файле. То есть, переопределить такие настройки с помощью SET GLOBAL невозможно. Настройки, заданные с помощью SET GLOBAL имеют меньший приоритет по сравнению с настройками, указанными в профиле пользователя, в конфигурационном файле. То есть, переопределить такие настройки с помощью SET GLOBAL невозможно.
@ -1930,7 +1930,7 @@ LIMIT 10
%% %%
У подзапросов нет возможности задать имена и нет возможности их использовать для того, чтобы сослаться на столбец из конкретного подзапроса. У подзапросов нет возможности задать имена и нет возможности их использовать для того, чтобы сослаться на столбец из конкретного подзапроса.
Требуется, чтобы стобцы, указанные в USING, назывались одинаково в обоих подзапросах, а остальные столбцы - по-разному. Изменить имена столбцов в подзапросах можно с помощью алиасов (в примере используются алиасы hits и visits). Требуется, чтобы столбцы, указанные в USING, назывались одинаково в обоих подзапросах, а остальные столбцы - по-разному. Изменить имена столбцов в подзапросах можно с помощью алиасов (в примере используются алиасы hits и visits).
В секции USING указывается один или несколько столбцов для соединения, что обозначает условие на равенство этих столбцов. Список столбцов задаётся без скобок. Более сложные условия соединения не поддерживаются. В секции USING указывается один или несколько столбцов для соединения, что обозначает условие на равенство этих столбцов. Список столбцов задаётся без скобок. Более сложные условия соединения не поддерживаются.
@ -2025,7 +2025,7 @@ WITH TOTALS может выполняться по-разному при нал
<b>after_having_inclusive</b> - учитывать в totals все строчки, не прошедшие max_rows_to_group_by. То есть в totals попадёт больше или столько же строчек, чем если бы max_rows_to_group_by не было. <b>after_having_inclusive</b> - учитывать в totals все строчки, не прошедшие max_rows_to_group_by. То есть в totals попадёт больше или столько же строчек, чем если бы max_rows_to_group_by не было.
<b>after_having_auto</b> - считать долю строчек, прошедших через HAVING. Если она больше некоторого значения (по умолчанию - 50%), то включить все строчки, не прошедшние max_rows_to_group_by в totals, иначе - не включить. <b>after_having_auto</b> - считать долю строчек, прошедших через HAVING. Если она больше некоторого значения (по умолчанию - 50%), то включить все строчки, не прошедшие max_rows_to_group_by в totals, иначе - не включить.
<b>totals_auto_threshold</b> - по умолчанию 0.5. Коэффициент для работы <b>after_having_auto</b>. <b>totals_auto_threshold</b> - по умолчанию 0.5. Коэффициент для работы <b>after_having_auto</b>.
@ -2041,7 +2041,7 @@ WITH TOTALS может выполняться по-разному при нал
При использовании %%max_bytes_before_external_group_by%% рекомендуется выставить %%max_memory_usage%% примерно в два раза больше. Это следует сделать, потому что агрегация выполняется в две стадии: чтение и формирование промежуточных данных (1) и слияние промежуточных данных (2). Сброс данных на файловую систему может производиться только на стадии 1. Если сброса временных данных не было, то на стадии 2 может потребляться до такого же объёма памяти, как на стадии 1. При использовании %%max_bytes_before_external_group_by%% рекомендуется выставить %%max_memory_usage%% примерно в два раза больше. Это следует сделать, потому что агрегация выполняется в две стадии: чтение и формирование промежуточных данных (1) и слияние промежуточных данных (2). Сброс данных на файловую систему может производиться только на стадии 1. Если сброса временных данных не было, то на стадии 2 может потребляться до такого же объёма памяти, как на стадии 1.
Например, если у вас %%max_memory_usage%% было высталвлено в 10000000000, и вы хотите использовать внешнюю агрегацию, то имеет смысл выставить %%max_bytes_before_external_group_by%% в 10000000000, а %%max_memory_usage%% в 20000000000. При срабатывании внешней агрегации (если был хотя бы один сброс временных данных в файловую систему) максимальное потребление оперативки будет лишь чуть-чуть больше %%max_bytes_before_external_group_by%%. Например, если у вас %%max_memory_usage%% было выставлено в 10000000000, и вы хотите использовать внешнюю агрегацию, то имеет смысл выставить %%max_bytes_before_external_group_by%% в 10000000000, а %%max_memory_usage%% в 20000000000. При срабатывании внешней агрегации (если был хотя бы один сброс временных данных в файловую систему) максимальное потребление оперативки будет лишь чуть-чуть больше %%max_bytes_before_external_group_by%%.
При распределённой обработке запроса внешняя агрегация производится на удалённых серверах. Для того чтобы на сервере-инициаторе запроса использовалось немного оперативки, нужно выставить настройку %%distributed_aggregation_memory_efficient%% в 1. При распределённой обработке запроса внешняя агрегация производится на удалённых серверах. Для того чтобы на сервере-инициаторе запроса использовалось немного оперативки, нужно выставить настройку %%distributed_aggregation_memory_efficient%% в 1.
@ -2064,14 +2064,14 @@ WHERE и HAVING отличаются тем, что WHERE выполняется
Секция ORDER BY содержит список выражений, к каждому из которых также может быть приписано DESC или ASC (направление сортировки). Если ничего не приписано - это аналогично приписыванию ASC. ASC - сортировка по возрастанию, DESC - сортировка по убыванию. Обозначение направления сортировки действует на одно выражение, а не на весь список. Пример: %%ORDER BY Visits DESC, SearchPhrase%% Секция ORDER BY содержит список выражений, к каждому из которых также может быть приписано DESC или ASC (направление сортировки). Если ничего не приписано - это аналогично приписыванию ASC. ASC - сортировка по возрастанию, DESC - сортировка по убыванию. Обозначение направления сортировки действует на одно выражение, а не на весь список. Пример: %%ORDER BY Visits DESC, SearchPhrase%%
Для сортировки по значениям типа String есть возможность указать collation (сравнение). Пример: %%ORDER BY SearchPhrase COLLATE 'tr'%% - для сортировки по поисковой фразе, по возрастанию, с учётом турецкого алфавита, регистронезависимо, при допущении, что строки в кодировке UTF-8. COLLATE может быть указан или не указан для каждого выражения в ORDER BY независимо. Если есть ASC или DESC, то COLLATE указывается после них. При использовании COLLATE сортировка всегда регистронезависима. Для сортировки по значениям типа String есть возможность указать collation (сравнение). Пример: %%ORDER BY SearchPhrase COLLATE 'tr'%% - для сортировки по поисковой фразе, по возрастанию, с учётом турецкого алфавита, регистронезависимо, при допущении, что строки в кодировке UTF-8. COLLATE может быть указан или не указан для каждого выражения в ORDER BY независимо. Если есть ASC или DESC, то COLLATE указывается после них. При использовании COLLATE сортировка всегда регистронезависимо.
Рекомендуется использовать COLLATE только для окончательной сортировки небольшого количества строк, так как производительность сортировки с указанием COLLATE меньше, чем обычной сортировки по байтам. Рекомендуется использовать COLLATE только для окончательной сортировки небольшого количества строк, так как производительность сортировки с указанием COLLATE меньше, чем обычной сортировки по байтам.
Строки, для которых список выражений, по которым производится сортировка, принимает одинаковые значения, выводятся в произвольном порядке, который может быть также недетерминированным (каждый раз разным). Строки, для которых список выражений, по которым производится сортировка, принимает одинаковые значения, выводятся в произвольном порядке, который может быть также недетерминированным (каждый раз разным).
Если секция ORDER BY отсутствует, то, аналогично, порядок, в котором идут строки, не определён, и может быть недетерминированным. Если секция ORDER BY отсутствует, то, аналогично, порядок, в котором идут строки, не определён, и может быть недетерминированным.
При сортировке чисел с плавающей запятой, NaN-ы идут отдельно от остальных значений. Вне зависимости от порядка сортировки, NaN-ы помещаются в конец. То есть, при сортировке по возрастанию, они как будто больше всех чисел, а при сортировке по-убыванию - как будто меньше всех. При сортировке чисел с плавающей запятой, NaN-ы идут отдельно от остальных значений. Вне зависимости от порядка сортировки, NaN-ы помещаются в конец. То есть, при сортировке по возрастанию, они как будто больше всех чисел, а при сортировке по убыванию - как будто меньше всех.
Если кроме ORDER BY указан также не слишком большой LIMIT, то расходуется меньше оперативки. Иначе расходуется количество памяти, пропорциональное количеству данных для сортировки. При распределённой обработке запроса, если отсутствует GROUP BY, сортировка частично делается на удалённых серверах, а на сервере-инициаторе запроса производится слияние результатов. Таким образом, при распределённой сортировке, может сортироваться объём данных, превышающий размер памяти на одном сервере. Если кроме ORDER BY указан также не слишком большой LIMIT, то расходуется меньше оперативки. Иначе расходуется количество памяти, пропорциональное количеству данных для сортировки. При распределённой обработке запроса, если отсутствует GROUP BY, сортировка частично делается на удалённых серверах, а на сервере-инициаторе запроса производится слияние результатов. Таким образом, при распределённой сортировке, может сортироваться объём данных, превышающий размер памяти на одном сервере.
@ -2139,7 +2139,7 @@ SELECT CounterID, 2 AS table, sum(Sign) AS c
При указании FORMAT format вы можете получить данные в любом указанном формате. При указании FORMAT format вы можете получить данные в любом указанном формате.
Это может использоваться для удобства или для создания дампов. Это может использоваться для удобства или для создания дампов.
Подробнее смотрите раздел "Форматы". Подробнее смотрите раздел "Форматы".
Если секция FORMAT отсутствует, то используется формат по умолчанию, который зависит от используемого интерфейся для доступа к БД и от настроек. Для HTTP интерфейса, а также для клиента командной строки, используемого в batch режиме, по умолчанию используется формат TabSeparated. Для клиента командной строки, используемого в интерактивном режиме, по умолчанию используется формат PrettyCompact (прикольные таблички, компактные). Если секция FORMAT отсутствует, то используется формат по умолчанию, который зависит от используемого интерфейса для доступа к БД и от настроек. Для HTTP интерфейса, а также для клиента командной строки, используемого в batch-режиме, по умолчанию используется формат TabSeparated. Для клиента командной строки, используемого в интерактивном режиме, по умолчанию используется формат PrettyCompact (прикольные таблички, компактные).
При использовании клиента командной строки данные на клиент передаются во внутреннем эффективном формате. При этом клиент самостоятельно интерпретирует секцию FORMAT запроса и форматирует данные на своей стороне (снимая нагрузку на сеть и сервер). При использовании клиента командной строки данные на клиент передаются во внутреннем эффективном формате. При этом клиент самостоятельно интерпретирует секцию FORMAT запроса и форматирует данные на своей стороне (снимая нагрузку на сеть и сервер).
@ -2240,14 +2240,14 @@ ORDER BY EventDate ASC
Это будет работать правильно и оптимально, если вы предусмотрели такой случай, и раскладываете данные по серверам кластера таким образом, чтобы данные одного UserID-а лежали только на одном сервере. В таком случае все необходимые данные будут присутствовать на каждом сервере локально. В противном случае результат будет посчитан неточно. Назовём этот вариант запроса "локальный IN". Это будет работать правильно и оптимально, если вы предусмотрели такой случай, и раскладываете данные по серверам кластера таким образом, чтобы данные одного UserID-а лежали только на одном сервере. В таком случае все необходимые данные будут присутствовать на каждом сервере локально. В противном случае результат будет посчитан неточно. Назовём этот вариант запроса "локальный IN".
Чтобы исправить работу запроса, когда данные размазаны по серверам кластера произвольным образом, можно было бы указать <b>distributed_table</b> внутри подзапроса. Запрос будет выглядить так: Чтобы исправить работу запроса, когда данные размазаны по серверам кластера произвольным образом, можно было бы указать <b>distributed_table</b> внутри подзапроса. Запрос будет выглядеть так:
%%SELECT uniq(UserID) FROM distributed_table WHERE CounterID = 101500 AND UserID IN (SELECT UserID FROM distributed_table WHERE CounterID = 34)%% %%SELECT uniq(UserID) FROM distributed_table WHERE CounterID = 101500 AND UserID IN (SELECT UserID FROM distributed_table WHERE CounterID = 34)%%
Этот запрос будет отправлен на все удалённые серверы в виде Этот запрос будет отправлен на все удалённые серверы в виде
%%SELECT uniq(UserID) FROM local_table WHERE CounterID = 101500 AND UserID IN (SELECT UserID FROM distributed_table WHERE CounterID = 34)%% %%SELECT uniq(UserID) FROM local_table WHERE CounterID = 101500 AND UserID IN (SELECT UserID FROM distributed_table WHERE CounterID = 34)%%
На каждом удалённом сервере начнёт выполняться подзапрос. Так как в подзапросе используется распределённая таблица, то подзапрос будет, на каждом удалённом сервере, снова отправлен на каждый удалённый сервер, в виде На каждом удалённом сервере начнёт выполняться подзапрос. Так как в подзапросе используется распределённая таблица, то подзапрос будет, на каждом удалённом сервере, снова отправлен на каждый удалённый сервер, в виде
%%SELECT UserID FROM local_table WHERE CounterID = 34%% %%SELECT UserID FROM local_table WHERE CounterID = 34%%
Например, если у вас кластер из 100 серверов, то выполнение всего запроса потребует 10 000 элементарных запросов, что, как правило, является неприемлимым. Например, если у вас кластер из 100 серверов, то выполнение всего запроса потребует 10 000 элементарных запросов, что, как правило, является неприемлемым.
В таких случаях всегда следует использовать %%GLOBAL IN%% вместо %%IN%%. Рассмотрим его работу для запроса В таких случаях всегда следует использовать %%GLOBAL IN%% вместо %%IN%%. Рассмотрим его работу для запроса
%%SELECT uniq(UserID) FROM distributed_table WHERE CounterID = 101500 AND UserID GLOBAL IN (SELECT UserID FROM distributed_table WHERE CounterID = 34)%% %%SELECT uniq(UserID) FROM distributed_table WHERE CounterID = 101500 AND UserID GLOBAL IN (SELECT UserID FROM distributed_table WHERE CounterID = 34)%%
@ -2287,7 +2287,7 @@ ORDER BY EventDate ASC
Вы можете использовать синонимы (алиасы AS) в любом месте запроса. Вы можете использовать синонимы (алиасы AS) в любом месте запроса.
В любом месте запроса, вместо выражения, может стоять звёздочка. При анализе запроса звёздочка раскрывается в список всех столбцов таблицы (за исключением MATERIALIZED и ALIAS столбцов). Есть лишь немного случаев, когда оправданно использовать звёздочку: В любом месте запроса, вместо выражения, может стоять звёздочка. При анализе запроса звёздочка раскрывается в список всех столбцов таблицы (за исключением MATERIALIZED и ALIAS столбцов). Есть лишь немного случаев, когда оправдано использовать звёздочку:
- при создании дампа таблицы; - при создании дампа таблицы;
- для таблиц, содержащих всего несколько столбцов - например, системных таблиц; - для таблиц, содержащих всего несколько столбцов - например, системных таблиц;
- для получения информации о том, какие столбцы есть в таблице; в этом случае, укажите LIMIT 1. Но лучше используйте запрос <b>DESC TABLE</b>; - для получения информации о том, какие столбцы есть в таблице; в этом случае, укажите LIMIT 1. Но лучше используйте запрос <b>DESC TABLE</b>;
@ -2510,10 +2510,10 @@ calcs - имя кластера в конфигурационном файле
В качестве параметров для каждого сервера указываются host, port и, не обязательно, user, password. В качестве параметров для каждого сервера указываются host, port и, не обязательно, user, password.
<b>host</b> - адрес удалённого сервера. Может быть указан домен, или IPv4 или IPv6 адрес. В случае указания домена, при старте сервера делается DNS запрос, и результат запоминается на всё время работы сервера. Если DNS запрос неуспешен, то сервер не запускается. Если вы изменяете DNS-запись, перезапустите сервер. <b>host</b> - адрес удалённого сервера. Может быть указан домен, или IPv4 или IPv6 адрес. В случае указания домена, при старте сервера делается DNS запрос, и результат запоминается на всё время работы сервера. Если DNS запрос неуспешен, то сервер не запускается. Если вы изменяете DNS-запись, перезапустите сервер.
<b>port</b> - TCP-порт для межсерверного взаимодействия (в конфиге - tcp_port, обычно 9000). Не перепутайте с http_port. <b>port</b> - TCP-порт для межсерверного взаимодействия (в конфиге - tcp_port, обычно 9000). Не перепутайте с http_port.
<b>user</b> - имя пользователя для соединения с удалённым сервером. По-умолчанию - default. Этот пользователь должен иметь доступ для соединения с указанным сервером. Доступы настраиваются в файле users.xml, подробнее смотрите в разделе "Права доступа". <b>user</b> - имя пользователя для соединения с удалённым сервером. по умолчанию - default. Этот пользователь должен иметь доступ для соединения с указанным сервером. Доступы настраиваются в файле users.xml, подробнее смотрите в разделе "Права доступа".
<b>password</b> - пароль для соединения с удалённым сервером, в открытом виде. По-умолчанию - пустая строка. <b>password</b> - пароль для соединения с удалённым сервером, в открытом виде. по умолчанию - пустая строка.
При указании реплик, для каждого из шардов, при чтении, будет вырбана одна из доступных реплик. Можно настроить алгоритм балансировки нагрузки (то есть, предпочтения, на какую из реплик идти) - см. настройку load_balancing. При указании реплик, для каждого из шардов, при чтении, будет выбрана одна из доступных реплик. Можно настроить алгоритм балансировки нагрузки (то есть, предпочтения, на какую из реплик идти) - см. настройку load_balancing.
Если соединение с сервером не установлено, то будет произведена попытка соединения с небольшим таймаутом. Если соединиться не удалось, то будет выбрана следующая реплика, и так для всех реплик. Если попытка соединения для всех реплик не удалась, то будут снова произведены попытки соединения по кругу, и так несколько раз. Если соединение с сервером не установлено, то будет произведена попытка соединения с небольшим таймаутом. Если соединиться не удалось, то будет выбрана следующая реплика, и так для всех реплик. Если попытка соединения для всех реплик не удалась, то будут снова произведены попытки соединения по кругу, и так несколько раз.
Это работает в пользу отказоустойчивости, хотя и не обеспечивает полную отказоустойчивость: удалённый сервер может принять соединение, но не работать, или плохо работать. Это работает в пользу отказоустойчивости, хотя и не обеспечивает полную отказоустойчивость: удалённый сервер может принять соединение, но не работать, или плохо работать.
@ -2673,7 +2673,7 @@ CollapsingMergeTree принимает дополнительный параме
При чтении, суммирование не делается само по себе. Если оно необходимо - напишите соответствующий GROUP BY. При чтении, суммирование не делается само по себе. Если оно необходимо - напишите соответствующий GROUP BY.
Дополнительно, таблица может иметь вложенные структуры данных, которые обрабатываются особым образом. Дополнительно, таблица может иметь вложенные структуры данных, которые обрабатываются особым образом.
Если название вложенной таблицы заканчинвается на Map и она содержит не менее двух столбцов, удовлетворяющих следующим критериям: Если название вложенной таблицы заканчивается на Map и она содержит не менее двух столбцов, удовлетворяющих следующим критериям:
- первый столбец - числовой ((U)IntN, Date, DateTime), назовем его условно key, - первый столбец - числовой ((U)IntN, Date, DateTime), назовем его условно key,
- остальные столбцы - арифметические ((U)IntN, Float32/64), условно (values...), - остальные столбцы - арифметические ((U)IntN, Float32/64), условно (values...),
то такая вложенная таблица воспринимается как отображение key => (values...) и при слиянии ее строк выполняется слияние элементов двух множеств по key со сложением соответствующих (values...). то такая вложенная таблица воспринимается как отображение key => (values...) и при слиянии ее строк выполняется слияние элементов двух множеств по key со сложением соответствующих (values...).
@ -2697,7 +2697,7 @@ CollapsingMergeTree принимает дополнительный параме
Чтобы это работало, используются: тип данных AggregateFunction, а также модификаторы -State и -Merge для агрегатных функций. Рассмотрим подробнее. Чтобы это работало, используются: тип данных AggregateFunction, а также модификаторы -State и -Merge для агрегатных функций. Рассмотрим подробнее.
Существует тип данных AggregateFunction. Это параметрический тип данных. В качестве параметров передаются: имя аргетатной функции, затем типы её аргументов. Существует тип данных AggregateFunction. Это параметрический тип данных. В качестве параметров передаются: имя агрегатной функции, затем типы её аргументов.
Примеры: Примеры:
%%CREATE TABLE t %%CREATE TABLE t
@ -2823,7 +2823,7 @@ ORDER BY StartDate;
Параметры движка: Параметры движка:
database, table - таблица, в которую сбрасывать данные. Вместо имени базы данных может использоваться константное выражение, возвращающее строку. database, table - таблица, в которую сбрасывать данные. Вместо имени базы данных может использоваться константное выражение, возвращающее строку.
num_layers - уровень паралеллизма. Физически таблица будет представлена в виде num_layers независимых буферов. Рекомендуемое значение - 16. num_layers - уровень параллелизма. Физически таблица будет представлена в виде num_layers независимых буферов. Рекомендуемое значение - 16.
min_time, max_time, min_rows, max_rows, min_bytes, max_bytes - условия для сброса данных из буфера. min_time, max_time, min_rows, max_rows, min_bytes, max_bytes - условия для сброса данных из буфера.
Данные сбрасываются из буфера и записываются в таблицу назначения, если выполнены все min-условия или хотя бы одно max-условие. Данные сбрасываются из буфера и записываются в таблицу назначения, если выполнены все min-условия или хотя бы одно max-условие.
@ -2987,7 +2987,9 @@ min_bytes, max_bytes - условие на количество байт в бу
Если обнаруживается, что локальный набор данных слишком сильно отличается от ожидаемого, то срабатывает защитный механизм - сервер сообщает об этом в лог и отказывается запускаться. Это сделано, так как такой случай может свидетельствовать об ошибке конфигурации - например, если реплика одного шарда была случайно сконфигурирована, как реплика другого шарда. Тем не менее, пороги защитного механизма поставлены довольно низкими, и такая ситуация может возникнуть и при обычном восстановлении после сбоя. В этом случае, восстановление делается полуавтоматически - "по кнопке". Если обнаруживается, что локальный набор данных слишком сильно отличается от ожидаемого, то срабатывает защитный механизм - сервер сообщает об этом в лог и отказывается запускаться. Это сделано, так как такой случай может свидетельствовать об ошибке конфигурации - например, если реплика одного шарда была случайно сконфигурирована, как реплика другого шарда. Тем не менее, пороги защитного механизма поставлены довольно низкими, и такая ситуация может возникнуть и при обычном восстановлении после сбоя. В этом случае, восстановление делается полуавтоматически - "по кнопке".
Для запуска восстановления, создайте в ZooKeeper узел <span class="inline-example">/<i>path_to_table</i>/<i>replica_name</i>/flags/force_restore_data</span> с любым содержимым и запустите сервер. При старте, сервер удалит этот флаг и запустит восстановление. Для запуска восстановления, создайте в ZooKeeper узел <span class="inline-example">/<i>path_to_table</i>/<i>replica_name</i>/flags/force_restore_data</span> с любым содержимым или выполните команду для восстановления всех реплицируемых таблиц:
%%sudo -u metrika touch /opt/clickhouse/flags/force_restore_data%%
Затем запустите сервер. При старте, сервер удалит эти флаги и запустит восстановление.
===Восстановление в случае потери всех данных=== ===Восстановление в случае потери всех данных===
@ -3001,7 +3003,9 @@ min_bytes, max_bytes - условие на количество байт в бу
3. Скопируйте с реплики определения таблиц, находящиеся в %%/opt/clickhouse/metadata/%%. Если в определениях таблиц, идентификатор шарда или реплики, прописаны в явном виде - исправьте их, чтобы они соответствовали данной реплике. 3. Скопируйте с реплики определения таблиц, находящиеся в %%/opt/clickhouse/metadata/%%. Если в определениях таблиц, идентификатор шарда или реплики, прописаны в явном виде - исправьте их, чтобы они соответствовали данной реплике.
(Альтернативный вариант - запустить сервер и сделать самостоятельно все запросы ATTACH TABLE, которые должны были бы быть в соответствующих .sql файлах в %%/opt/clickhouse/metadata/%%.) (Альтернативный вариант - запустить сервер и сделать самостоятельно все запросы ATTACH TABLE, которые должны были бы быть в соответствующих .sql файлах в %%/opt/clickhouse/metadata/%%.)
4. Создайте в ZooKeeper узел <span class="inline-example">/<i>path_to_table</i>/<i>replica_name</i>/flags/force_restore_data</span> с любым содержимым и запустите сервер (перезапустите, если уже запущен). Данные будут скачаны с реплик. 4. Создайте в ZooKeeper узел <span class="inline-example">/<i>path_to_table</i>/<i>replica_name</i>/flags/force_restore_data</span> с любым содержимым или выполните команду для восстановления всех реплицируемых таблиц:
%%sudo -u metrika touch /opt/clickhouse/flags/force_restore_data%%
Затем запустите сервер (перезапустите, если уже запущен). Данные будут скачаны с реплик.
В качестве альтернативного варианта восстановления, вы можете удалить из ZooKeeper информацию о потерянной реплике - <span class="inline-example">/<i>path_to_table</i>/<i>replica_name</i></span>, и затем создать реплику заново, как написано в разделе "Создание реплицируемых таблиц". В качестве альтернативного варианта восстановления, вы можете удалить из ZooKeeper информацию о потерянной реплике - <span class="inline-example">/<i>path_to_table</i>/<i>replica_name</i></span>, и затем создать реплику заново, как написано в разделе "Создание реплицируемых таблиц".
@ -3069,7 +3073,7 @@ min_bytes, max_bytes - условие на количество байт в бу
Описание кластера - список шардов с весами, на которые нужно перераспределить указанные данные. Описание кластера - список шардов с весами, на которые нужно перераспределить указанные данные.
Шард указывается в виде адреса таблицы в ZooKeeper. Например, %%/clickhouse/tables/01-03/hits%% Шард указывается в виде адреса таблицы в ZooKeeper. Например, %%/clickhouse/tables/01-03/hits%%
Относительный вес шарда (не обязательно, по-умолчнию, 1) может быть указан после ключевого слова %%WEIGHT%%. Относительный вес шарда (не обязательно, по умолчанию, 1) может быть указан после ключевого слова %%WEIGHT%%.
Пример: Пример:
%% %%
@ -3700,9 +3704,9 @@ world%%
Формат comma separated values (<a href="https://tools.ietf.org/html/rfc4180">RFC</a>). Формат comma separated values (<a href="https://tools.ietf.org/html/rfc4180">RFC</a>).
При форматировании, строки выводятся в двойных кавычках. Двойная кавычка внутри строки выводится как две двойные кавычки подряд. Других правил экранирования нет. Даты и даты-с-временем выводятся в двойных кавычках. Числа выводятся без кавычек. Значения разделяются запятыми. Строки разделяются unix переводом строки (LF). Массивы сериализуются в CSV следующим образом: сначла массив сериализуется в строку, как в формате TabSeparated, а затем полученная строка выводится в CSV в двойных кавычках. Кортежи в формате CSV сериализуются, как отдельные столбцы (то есть, теряется их вложенность в кортеж). При форматировании, строки выводятся в двойных кавычках. Двойная кавычка внутри строки выводится как две двойные кавычки подряд. Других правил экранирования нет. Даты и даты-с-временем выводятся в двойных кавычках. Числа выводятся без кавычек. Значения разделяются запятыми. Строки разделяются unix переводом строки (LF). Массивы сериализуются в CSV следующим образом: сначала массив сериализуется в строку, как в формате TabSeparated, а затем полученная строка выводится в CSV в двойных кавычках. Кортежи в формате CSV сериализуются, как отдельные столбцы (то есть, теряется их вложенность в кортеж).
При парсинге, все значения могут парситься как в кавычках, так и без кавычек. Поддерживаются как двойные так и одинарные кавычки. В том числе, строки могут быть расположены без кавычек - тогда они парсятся до запятой или перевода строки (CR или LF). В нарушение RFC, в случае парсинга строк не в кавычках, начальные и конечные пробелы и табы игнорируются. В качестве перевода строки, поддерживаются как Unix (LF), так и Windows (CR LF) и Mac OS Classic (LF CR) варианты. При парсинге, все значения могут парситься как в кавычках, так и без кавычек. Поддерживаются как двойные, так и одинарные кавычки. В том числе, строки могут быть расположены без кавычек - тогда они парсятся до запятой или перевода строки (CR или LF). В нарушение RFC, в случае парсинга строк не в кавычках, начальные и конечные пробелы и табы игнорируются. В качестве перевода строки, поддерживаются как Unix (LF), так и Windows (CR LF) и Mac OS Classic (LF CR) варианты.
Формат CSV поддерживает вывод totals и extremes аналогично TabSeparated. Формат CSV поддерживает вывод totals и extremes аналогично TabSeparated.
@ -3881,7 +3885,7 @@ Extremes:
} }
%% %%
JSON совместим с JavaScript. Для этого, дополнительно эскейпятся некоторые символы: символ прямого слеша %%/%% экранируется в виде %%\/%%; альтернативные переводы строк %%U+2028%%, %%U+2029%%, на которых ломаются некоторые браузеры, экранируются в виде <span class="inline-example">\u<i>XXXX</i></span>-последовательностей. Эскейпятся ASCII control characters: backspace, form feed, line feed, carriage return, horizontal tab в виде %%\b%%, %%\f%%, %%\n%%, %%\r%%, %%\t%% соответственно, а также остальные байты из диапазона 00-1F с помощью <span class="inline-example">\u<i>XXXX</i></span>-последовательностей. Невалидные UTF-8 последовательности заменяются на replacement character %%<25>%% и, таким образом, выводимый текст будет состоять из валидных UTF-8 последовательностей. Числа типа UInt64 и Int64, для совместимости с JavaScript, по-умолчанию выводятся в двойных кавычках, чтобы они выводились без кавычек можно установить конфигурационный параметр output_format_json_quote_64bit_integers равным 0. JSON совместим с JavaScript. Для этого, дополнительно эскейпятся некоторые символы: символ прямого слеша %%/%% экранируется в виде %%\/%%; альтернативные переводы строк %%U+2028%%, %%U+2029%%, на которых ломаются некоторые браузеры, экранируются в виде <span class="inline-example">\u<i>XXXX</i></span>-последовательностей. Эскейпятся ASCII control characters: backspace, form feed, line feed, carriage return, horizontal tab в виде %%\b%%, %%\f%%, %%\n%%, %%\r%%, %%\t%% соответственно, а также остальные байты из диапазона 00-1F с помощью <span class="inline-example">\u<i>XXXX</i></span>-последовательностей. Невалидные UTF-8 последовательности заменяются на replacement character %%<25>%% и, таким образом, выводимый текст будет состоять из валидных UTF-8 последовательностей. Числа типа UInt64 и Int64, для совместимости с JavaScript, по умолчанию выводятся в двойных кавычках, чтобы они выводились без кавычек можно установить конфигурационный параметр output_format_json_quote_64bit_integers равным 0.
%%rows%% - общее количество выведенных строчек. %%rows%% - общее количество выведенных строчек.
%%rows_before_limit_at_least%% - не менее скольких строчек получилось бы, если бы не было LIMIT-а. Выводится только если запрос содержит LIMIT. %%rows_before_limit_at_least%% - не менее скольких строчек получилось бы, если бы не было LIMIT-а. Выводится только если запрос содержит LIMIT.
@ -3958,7 +3962,7 @@ JSON совместим с JavaScript. Для этого, дополнитель
В отличие от формата JSON, нет замены невалидных UTF-8 последовательностей. В строках может выводиться произвольный набор байт. Это сделано для того, чтобы данные форматировались без потери информации. Экранирование значений осуществляется аналогично формату JSON. В отличие от формата JSON, нет замены невалидных UTF-8 последовательностей. В строках может выводиться произвольный набор байт. Это сделано для того, чтобы данные форматировались без потери информации. Экранирование значений осуществляется аналогично формату JSON.
При парсинге, поддерживается расположение значений разных столбцов в произвольном порядке. Допустимо отсутствие некоторых значений - тогда они воспринимаются как равные значениям по-умолчанию. При этом, в качестве значений по-умолчанию используются нули, пустые строки и не поддерживаются сложные значения по-умолчанию, которые могут быть заданы в таблице. Пропускаются пробельные символы между элементами. После объектов может быть расположена запятая, которая игнорируется. Объекты не обязательно должны быть разделены переводами строк. При парсинге, поддерживается расположение значений разных столбцов в произвольном порядке. Допустимо отсутствие некоторых значений - тогда они воспринимаются как равные значениям по умолчанию. При этом, в качестве значений по умолчанию используются нули, пустые строки и не поддерживаются сложные значения по умолчанию, которые могут быть заданы в таблице. Пропускаются пробельные символы между элементами. После объектов может быть расположена запятая, которая игнорируется. Объекты не обязательно должны быть разделены переводами строк.
==TSKV== ==TSKV==
@ -3980,7 +3984,7 @@ SearchPhrase=баку count()=1000
При большом количестве маленьких столбцов, этот формат существенно неэффективен, и обычно нет причин его использовать. Он реализован, так как используется в некоторых отделах Яндекса. При большом количестве маленьких столбцов, этот формат существенно неэффективен, и обычно нет причин его использовать. Он реализован, так как используется в некоторых отделах Яндекса.
Поддерживается как вывод так и парсинг данных в этом формате. При парсинге, поддерживается расположение значений разных столбцов в произвольном порядке. Допустимо отсутствие некоторых значений - тогда они воспринимаются как равные значениям по-умолчанию. При этом, в качестве значений по-умолчанию используются нули, пустые строки и не поддерживаются сложные значения по-умолчанию, которые могут быть заданы в таблице. Поддерживается как вывод, так и парсинг данных в этом формате. При парсинге, поддерживается расположение значений разных столбцов в произвольном порядке. Допустимо отсутствие некоторых значений - тогда они воспринимаются как равные значениям по умолчанию. При этом, в качестве значений по умолчанию используются нули, пустые строки и не поддерживаются сложные значения по умолчанию, которые могут быть заданы в таблице.
При парсинге, в качестве дополнительного поля, может присутствовать %%tskv%% без знака равенства и без значения. Это поле игнорируется. При парсинге, в качестве дополнительного поля, может присутствовать %%tskv%% без знака равенства и без значения. Это поле игнорируется.
@ -4141,7 +4145,7 @@ Enum8 или Enum16. Представляет собой конечное мно
В оперативке столбец такого типа представлен так же, как %%Int8%% или %%Int16%% соответствующими числовыми значениями. В оперативке столбец такого типа представлен так же, как %%Int8%% или %%Int16%% соответствующими числовыми значениями.
При чтении в текстовом виде, парсит значение как строку и ищет соответствующую строку из множества значений Enum-а. Если не находит - кидается исключение. При чтении в текстовом виде, парсит значение как строку и ищет соответствующую строку из множества значений Enum-а. Если не находит - кидается исключение.
При записи в текстовом виде, записывает значение как соответствующую строку. Если в данных столбца есть мусор - числа не из допустимого множества, то кидается исключение. При чтении и записи в бинарном виде, оно осуществляется так же, как для типов данных %%Int8%%, %%Int16%%. При записи в текстовом виде, записывает значение как соответствующую строку. Если в данных столбца есть мусор - числа не из допустимого множества, то кидается исключение. При чтении и записи в бинарном виде, оно осуществляется так же, как для типов данных %%Int8%%, %%Int16%%.
Неявное значение по-умолчанию - это значение с минимальным номером. Неявное значение по умолчанию - это значение с минимальным номером.
При %%ORDER BY%%, %%GROUP BY%%, %%IN%%, %%DISTINCT%% и т. п., Enum-ы ведут себя так же, как соответствующие числа. Например, при %%ORDER BY%% они сортируются по числовым значениям. Функции сравнения на равенство и сравнения на отношение порядка двух Enum-ов работают с Enum-ами так же, как с числами. При %%ORDER BY%%, %%GROUP BY%%, %%IN%%, %%DISTINCT%% и т. п., Enum-ы ведут себя так же, как соответствующие числа. Например, при %%ORDER BY%% они сортируются по числовым значениям. Функции сравнения на равенство и сравнения на отношение порядка двух Enum-ов работают с Enum-ами так же, как с числами.
@ -4637,7 +4641,7 @@ SELECT
==Функции для работы с датами и временем== ==Функции для работы с датами и временем==
===Поддержка часовых поясов=== ===Поддержка часовых поясов===
Все функции по работе с датой и временем, для которых это имеет смысл, могут принимать второй, необязательный аргумент - имя часового пояса. Пример: %%Asia/Yekaterinburg%%. В этом случае, они используют не локальный часовой пояс (по-умолчанию), а указанный. Все функции по работе с датой и временем, для которых это имеет смысл, могут принимать второй, необязательный аргумент - имя часового пояса. Пример: %%Asia/Yekaterinburg%%. В этом случае, они используют не локальный часовой пояс (по умолчанию), а указанный.
%% %%
SELECT SELECT
@ -4700,7 +4704,7 @@ SELECT
===toStartOfFiveMinute=== ===toStartOfFiveMinute===
- округляет дату-с-временем вниз до начала пятиминутного интервала. - округляет дату-с-временем вниз до начала пятиминутного интервала.
Замечение: если вам нужно округлить дату-с-временем до какого-либо другого количества секунд, минут или часов, вы можете перевести её в число с помощью функции %%toUInt32%%, затем округлить число с помощью функции %%intDiv%% и умножения, а затем перевести обратно, с помощью функции %%toDateTime%%. Замечание: если вам нужно округлить дату-с-временем до какого-либо другого количества секунд, минут или часов, вы можете перевести её в число с помощью функции %%toUInt32%%, затем округлить число с помощью функции %%intDiv%% и умножения, а затем перевести обратно, с помощью функции %%toDateTime%%.
===toStartOfHour=== ===toStartOfHour===
- округляет дату-с-временем вниз до начала часа. - округляет дату-с-временем вниз до начала часа.
@ -5702,7 +5706,7 @@ id должен иметь тип UInt64.
===dictGet<i>T</i>OrDefault=== ===dictGet<i>T</i>OrDefault===
%%dictGet<i>T</i>('dict_name', 'attr_name', id, default)%% %%dictGet<i>T</i>('dict_name', 'attr_name', id, default)%%
Аналогично функциям dictGet<i>T</i>, но значение по-умолчанию берётся из последнего аргумента функции. Аналогично функциям dictGet<i>T</i>, но значение по умолчанию берётся из последнего аргумента функции.
===dictIsIn=== ===dictIsIn===
%%dictIsIn('dict_name', child_id, ancestor_id)%% %%dictIsIn('dict_name', child_id, ancestor_id)%%
@ -6062,7 +6066,7 @@ SELECT
Результат детерминирован (не зависит от порядка выполнения запроса). Результат детерминирован (не зависит от порядка выполнения запроса).
Функция uniqCombined является хорошим выбором по-умолчанию для подсчёта количества различных значений. Функция uniqCombined является хорошим выбором по умолчанию для подсчёта количества различных значений.
==uniqHLL12(x)== ==uniqHLL12(x)==
@ -6258,7 +6262,7 @@ cond1, cond2 ... - от одного до 32 аргументов типа UInt8
вместо >= могут использоваться операторы &lt;, &gt;, &lt;=; вместо >= могут использоваться операторы &lt;, &gt;, &lt;=;
вместо 1800 может быть любое число; вместо 1800 может быть любое число;
События, произошедшие в одну секунду, могут оказаться в цепочке в произвольном порядке. От этого может зависить результат работы функции. События, произошедшие в одну секунду, могут оказаться в цепочке в произвольном порядке. От этого может зависеть результат работы функции.
==sequenceCount(pattern)(time, cond1, cond2, ...)== ==sequenceCount(pattern)(time, cond1, cond2, ...)==
@ -6279,7 +6283,7 @@ cond1, cond2 ... - от одного до 32 аргументов типа UInt8
Функция также работает для нескольких аргументов. Функция также работает для нескольких аргументов.
Работает максимально быстро за исключением паталогических случаев, когда используется большое значение N и количество уникальных значений чуть меньше N. Работает максимально быстро за исключением патологических случаев, когда используется большое значение N и количество уникальных значений чуть меньше N.
Пример применения: Пример применения:
Задача: показывать в отчёте только поисковые фразы, по которым было хотя бы 5 уникальных посетителей. Задача: показывать в отчёте только поисковые фразы, по которым было хотя бы 5 уникальных посетителей.
@ -6482,7 +6486,7 @@ regions_names_*.txt: TabSeparated (без заголовка), столбцы:
&lt;!-- Структура. --&gt; &lt;!-- Структура. --&gt;
&lt;structure&gt; &lt;structure&gt;
&lt;!-- Описание столбца, являющегося идентификатором (ключём) словаря. --&gt; &lt;!-- Описание столбца, являющегося идентификатором (ключом) словаря. --&gt;
&lt;id&gt; &lt;id&gt;
&lt;!-- Имя столбца с идентификатором. --&gt; &lt;!-- Имя столбца с идентификатором. --&gt;
&lt;name&gt;Id&lt;/name&gt; &lt;name&gt;Id&lt;/name&gt;
@ -6582,7 +6586,7 @@ regions_names_*.txt: TabSeparated (без заголовка), столбцы:
Эта настройка действует только в тех случаях, когда сервер сам формирует такие блоки. Эта настройка действует только в тех случаях, когда сервер сам формирует такие блоки.
Например, при INSERT-е через HTTP интерфейс, сервер парсит формат данных, и формирует блоки указанного размера. Например, при INSERT-е через HTTP интерфейс, сервер парсит формат данных, и формирует блоки указанного размера.
А при использовании clickhouse-client, клиент сам парсит данные, и настройка max_insert_block_size на сервере не влияет на размер вставляемых блоков. А при использовании clickhouse-client, клиент сам парсит данные, и настройка max_insert_block_size на сервере не влияет на размер вставляемых блоков.
При использованиии INSERT SELECT, настройка так же не имеет смысла, так как данные будут вставляться теми блоками, которые вышли после SELECT-а. При использовании INSERT SELECT, настройка так же не имеет смысла, так как данные будут вставляться теми блоками, которые вышли после SELECT-а.
По умолчанию - 1 048 576. По умолчанию - 1 048 576.
@ -6763,7 +6767,7 @@ regions_names_*.txt: TabSeparated (без заголовка), столбцы:
==min_count_to_compile== ==min_count_to_compile==
После скольки раз, когда скомпилированный кусок кода мог пригодиться, выполнить его компиляцию. По-умолчанию, 3. После скольки раз, когда скомпилированный кусок кода мог пригодиться, выполнить его компиляцию. по умолчанию, 3.
В случае, если значение равно нулю, то компиляция выполняется синхронно, и запрос будет ждать окончания процесса компиляции перед продолжением выполнения. Это можно использовать для тестирования, иначе используйте значения, начиная с 1. Как правило, компиляция занимает по времени около 5-10 секунд. В случае, если значение равно нулю, то компиляция выполняется синхронно, и запрос будет ждать окончания процесса компиляции перед продолжением выполнения. Это можно использовать для тестирования, иначе используйте значения, начиная с 1. Как правило, компиляция занимает по времени около 5-10 секунд.
В случае, если значение равно 1 или больше, компиляция выполняется асинхронно, в отдельном потоке. При готовности результата, он сразу же будет использован, в том числе, уже выполняющимися в данный момент запросами. В случае, если значение равно 1 или больше, компиляция выполняется асинхронно, в отдельном потоке. При готовности результата, он сразу же будет использован, в том числе, уже выполняющимися в данный момент запросами.
@ -6772,12 +6776,12 @@ regions_names_*.txt: TabSeparated (без заголовка), столбцы:
==input_format_skip_unknown_fields== ==input_format_skip_unknown_fields==
Если значение истино, то при выполнении INSERT из входных данных пропускаются (не рассматриваются) колонки с неизвестными именами, иначе в данной ситуации будет сгенерировано исключение. Если значение истинно, то при выполнении INSERT из входных данных пропускаются (не рассматриваются) колонки с неизвестными именами, иначе в данной ситуации будет сгенерировано исключение.
Работает для форматов JSONEachRow и TSKV. Работает для форматов JSONEachRow и TSKV.
==output_format_json_quote_64bit_integers== ==output_format_json_quote_64bit_integers==
Если значение истино, то при использовании JSON* форматов UInt64 и Int64 числа выводятся в кавычках (из соображений совместимости с большинством реализаций JavaScript), иначе - без кавычек. Если значение истинно, то при использовании JSON* форматов UInt64 и Int64 числа выводятся в кавычках (из соображений совместимости с большинством реализаций JavaScript), иначе - без кавычек.
==Ограничения на сложность запроса== ==Ограничения на сложность запроса==
@ -6814,7 +6818,7 @@ any (только для group_by_overflow_mode) - продолжить агре
Некоторые случаи потребления оперативки не отслеживаются: Некоторые случаи потребления оперативки не отслеживаются:
- большие константы (например, очень длинная константная строка); - большие константы (например, очень длинная константная строка);
- состояния некотрых агрегатных функций; - состояния некоторых агрегатных функций;
Потребление оперативки не полностью учитывается для состояний агрегатных функций min, max, any, anyLast, argMin, argMax от аргументов String и Array. Потребление оперативки не полностью учитывается для состояний агрегатных функций min, max, any, anyLast, argMin, argMax от аргументов String и Array.
@ -7008,7 +7012,7 @@ SET profile = 'web'
&lt;/profiles&gt; &lt;/profiles&gt;
%% %%
В примере задано два профиля: default и web. Профиль default имеет специальное значение - он всегда обязан присуствовать и применяется при запуске сервера. То есть, профиль default содержит настройки по умолчанию. Профиль web - обычный профиль, который может быть установлен с помощью запроса SET или с помощью параметра URL при запросе по HTTP. В примере задано два профиля: default и web. Профиль default имеет специальное значение - он всегда обязан присутствовать и применяется при запуске сервера. То есть, профиль default содержит настройки по умолчанию. Профиль web - обычный профиль, который может быть установлен с помощью запроса SET или с помощью параметра URL при запросе по HTTP.
Профили настроек могут наследоваться от друг-друга - это реализуется указанием настройки profile перед остальными настройками, перечисленными в профиле. Профили настроек могут наследоваться от друг-друга - это реализуется указанием настройки profile перед остальными настройками, перечисленными в профиле.

View File

@ -9,64 +9,30 @@
namespace zkutil namespace zkutil
{ {
/** Реализует метод выбора лидера, описанный здесь: http://zookeeper.apache.org/doc/r3.4.5/recipes.html#sc_leaderElection /** Implements leader election algorithm described here: http://zookeeper.apache.org/doc/r3.4.5/recipes.html#sc_leaderElection
*/ */
class LeaderElection class LeaderElection
{ {
public: public:
using LeadershipHandler = std::function<void()>; using LeadershipHandler = std::function<void()>;
/** handler вызывается, когда этот экземпляр становится лидером. /** handler is called when this instance become leader.
*/ */
LeaderElection(const std::string & path_, ZooKeeper & zookeeper_, LeadershipHandler handler_, const std::string & identifier_ = "") LeaderElection(const std::string & path_, ZooKeeper & zookeeper_, LeadershipHandler handler_, const std::string & identifier_ = "")
: path(path_), zookeeper(zookeeper_), handler(handler_), identifier(identifier_), : path(path_), zookeeper(zookeeper_), handler(handler_), identifier(identifier_)
log(&Logger::get("LeaderElection"))
{ {
node = EphemeralNodeHolder::createSequential(path + "/leader_election-", zookeeper, identifier); createNode();
std::string node_path = node->getPath();
node_name = node_path.substr(node_path.find_last_of('/') + 1);
thread = std::thread(&LeaderElection::threadFunction, this);
} }
enum State void yield()
{ {
WAITING_LEADERSHIP, releaseNode();
LEADER, createNode();
LEADERSHIP_LOST
};
/// если возвращает LEADER, то еще sessionTimeoutMs мы будем лидером, даже если порвется соединение с zookeeper
State getState()
{
if (state == LEADER)
{
try
{
/// возможно, если сессия разорвалась и заново был вызван init
if (!zookeeper.exists(node->getPath()))
{
LOG_WARNING(log, "Leadership lost. Node " << node->getPath() << " doesn't exist.");
state = LEADERSHIP_LOST;
}
}
catch (const KeeperException & e)
{
LOG_WARNING(log, "Leadership lost. e.message() = " << e.message());
state = LEADERSHIP_LOST;
}
}
return state;
} }
~LeaderElection() ~LeaderElection()
{ {
shutdown = true; releaseNode();
event->set();
thread.join();
} }
private: private:
@ -79,12 +45,28 @@ private:
std::string node_name; std::string node_name;
std::thread thread; std::thread thread;
volatile bool shutdown = false; std::atomic<bool> shutdown {false};
zkutil::EventPtr event = std::make_shared<Poco::Event>(); zkutil::EventPtr event = std::make_shared<Poco::Event>();
State state = WAITING_LEADERSHIP; void createNode()
{
shutdown = false;
node = EphemeralNodeHolder::createSequential(path + "/leader_election-", zookeeper, identifier);
Logger * log; std::string node_path = node->getPath();
node_name = node_path.substr(node_path.find_last_of('/') + 1);
thread = std::thread(&LeaderElection::threadFunction, this);
}
void releaseNode()
{
shutdown = true;
event->set();
if (thread.joinable())
thread.join();
node = nullptr;
}
void threadFunction() void threadFunction()
{ {
@ -102,7 +84,6 @@ private:
if (it == children.begin()) if (it == children.begin())
{ {
state = LEADER;
handler(); handler();
return; return;
} }