| 1 | #include "Server.h" | 
|---|
| 2 |  | 
|---|
| 3 | #include <memory> | 
|---|
| 4 | #include <sys/resource.h> | 
|---|
| 5 | #include <sys/stat.h> | 
|---|
| 6 | #include <sys/types.h> | 
|---|
| 7 | #include <errno.h> | 
|---|
| 8 | #include <pwd.h> | 
|---|
| 9 | #include <unistd.h> | 
|---|
| 10 | #include <Poco/Version.h> | 
|---|
| 11 | #include <Poco/DirectoryIterator.h> | 
|---|
| 12 | #include <Poco/Net/HTTPServer.h> | 
|---|
| 13 | #include <Poco/Net/NetException.h> | 
|---|
| 14 | #include <Poco/Util/HelpFormatter.h> | 
|---|
| 15 | #include <ext/scope_guard.h> | 
|---|
| 16 | #include <common/logger_useful.h> | 
|---|
| 17 | #include <common/phdr_cache.h> | 
|---|
| 18 | #include <common/config_common.h> | 
|---|
| 19 | #include <common/ErrorHandlers.h> | 
|---|
| 20 | #include <common/getMemoryAmount.h> | 
|---|
| 21 | #include <common/coverage.h> | 
|---|
| 22 | #include <Common/ClickHouseRevision.h> | 
|---|
| 23 | #include <Common/DNSResolver.h> | 
|---|
| 24 | #include <Common/CurrentMetrics.h> | 
|---|
| 25 | #include <Common/Macros.h> | 
|---|
| 26 | #include <Common/StringUtils/StringUtils.h> | 
|---|
| 27 | #include <Common/ZooKeeper/ZooKeeper.h> | 
|---|
| 28 | #include <Common/ZooKeeper/ZooKeeperNodeCache.h> | 
|---|
| 29 | #include "config_core.h" | 
|---|
| 30 | #include <Common/getFQDNOrHostName.h> | 
|---|
| 31 | #include <Common/getMultipleKeysFromConfig.h> | 
|---|
| 32 | #include <Common/getNumberOfPhysicalCPUCores.h> | 
|---|
| 33 | #include <Common/getExecutablePath.h> | 
|---|
| 34 | #include <Common/TaskStatsInfoGetter.h> | 
|---|
| 35 | #include <Common/ThreadStatus.h> | 
|---|
| 36 | #include <IO/HTTPCommon.h> | 
|---|
| 37 | #include <IO/UseSSL.h> | 
|---|
| 38 | #include <Interpreters/AsynchronousMetrics.h> | 
|---|
| 39 | #include <Interpreters/DDLWorker.h> | 
|---|
| 40 | #include <Interpreters/ExternalDictionariesLoader.h> | 
|---|
| 41 | #include <Interpreters/ExternalModelsLoader.h> | 
|---|
| 42 | #include <Interpreters/ProcessList.h> | 
|---|
| 43 | #include <Interpreters/loadMetadata.h> | 
|---|
| 44 | #include <Interpreters/DNSCacheUpdater.h> | 
|---|
| 45 | #include <Interpreters/SystemLog.cpp> | 
|---|
| 46 | #include <Interpreters/ExternalLoaderXMLConfigRepository.h> | 
|---|
| 47 | #include <Storages/StorageReplicatedMergeTree.h> | 
|---|
| 48 | #include <Storages/System/attachSystemTables.h> | 
|---|
| 49 | #include <AggregateFunctions/registerAggregateFunctions.h> | 
|---|
| 50 | #include <Functions/registerFunctions.h> | 
|---|
| 51 | #include <TableFunctions/registerTableFunctions.h> | 
|---|
| 52 | #include <Storages/registerStorages.h> | 
|---|
| 53 | #include <Dictionaries/registerDictionaries.h> | 
|---|
| 54 | #include <Disks/registerDisks.h> | 
|---|
| 55 | #include <Common/Config/ConfigReloader.h> | 
|---|
| 56 | #include "HTTPHandlerFactory.h" | 
|---|
| 57 | #include "MetricsTransmitter.h" | 
|---|
| 58 | #include <Common/StatusFile.h> | 
|---|
| 59 | #include "TCPHandlerFactory.h" | 
|---|
| 60 | #include "Common/config_version.h" | 
|---|
| 61 | #include <Common/SensitiveDataMasker.h> | 
|---|
| 62 | #include "MySQLHandlerFactory.h" | 
|---|
| 63 |  | 
|---|
| 64 | #if defined(OS_LINUX) | 
|---|
| 65 | #include <Common/hasLinuxCapability.h> | 
|---|
| 66 | #include <sys/mman.h> | 
|---|
| 67 | #endif | 
|---|
| 68 |  | 
|---|
| 69 | #if USE_POCO_NETSSL | 
|---|
| 70 | #include <Poco/Net/Context.h> | 
|---|
| 71 | #include <Poco/Net/SecureServerSocket.h> | 
|---|
| 72 | #endif | 
|---|
| 73 |  | 
|---|
| 74 | namespace CurrentMetrics | 
|---|
| 75 | { | 
|---|
| 76 | extern const Metric Revision; | 
|---|
| 77 | extern const Metric VersionInteger; | 
|---|
| 78 | } | 
|---|
| 79 |  | 
|---|
| 80 | namespace DB | 
|---|
| 81 | { | 
|---|
| 82 |  | 
|---|
| 83 | namespace ErrorCodes | 
|---|
| 84 | { | 
|---|
| 85 | extern const int NO_ELEMENTS_IN_CONFIG; | 
|---|
| 86 | extern const int SUPPORT_IS_DISABLED; | 
|---|
| 87 | extern const int ARGUMENT_OUT_OF_BOUND; | 
|---|
| 88 | extern const int EXCESSIVE_ELEMENT_IN_CONFIG; | 
|---|
| 89 | extern const int INVALID_CONFIG_PARAMETER; | 
|---|
| 90 | extern const int SYSTEM_ERROR; | 
|---|
| 91 | extern const int FAILED_TO_GETPWUID; | 
|---|
| 92 | extern const int MISMATCHING_USERS_FOR_PROCESS_AND_DATA; | 
|---|
| 93 | extern const int NETWORK_ERROR; | 
|---|
| 94 | extern const int PATH_ACCESS_DENIED; | 
|---|
| 95 | } | 
|---|
| 96 |  | 
|---|
| 97 |  | 
|---|
| 98 | static std::string getCanonicalPath(std::string && path) | 
|---|
| 99 | { | 
|---|
| 100 | Poco::trimInPlace(path); | 
|---|
| 101 | if (path.empty()) | 
|---|
| 102 | throw Exception( "path configuration parameter is empty", ErrorCodes::INVALID_CONFIG_PARAMETER); | 
|---|
| 103 | if (path.back() != '/') | 
|---|
| 104 | path += '/'; | 
|---|
| 105 | return std::move(path); | 
|---|
| 106 | } | 
|---|
| 107 |  | 
|---|
| 108 | static std::string getUserName(uid_t user_id) | 
|---|
| 109 | { | 
|---|
| 110 | /// Try to convert user id into user name. | 
|---|
| 111 | auto buffer_size = sysconf(_SC_GETPW_R_SIZE_MAX); | 
|---|
| 112 | if (buffer_size <= 0) | 
|---|
| 113 | buffer_size = 1024; | 
|---|
| 114 | std::string buffer; | 
|---|
| 115 | buffer.reserve(buffer_size); | 
|---|
| 116 |  | 
|---|
| 117 | struct passwd passwd_entry; | 
|---|
| 118 | struct passwd * result = nullptr; | 
|---|
| 119 | const auto error = getpwuid_r(user_id, &passwd_entry, buffer.data(), buffer_size, &result); | 
|---|
| 120 |  | 
|---|
| 121 | if (error) | 
|---|
| 122 | throwFromErrno( "Failed to find user name for "+ toString(user_id), ErrorCodes::FAILED_TO_GETPWUID, error); | 
|---|
| 123 | else if (result) | 
|---|
| 124 | return result->pw_name; | 
|---|
| 125 | return toString(user_id); | 
|---|
| 126 | } | 
|---|
| 127 |  | 
|---|
| 128 | void Server::uninitialize() | 
|---|
| 129 | { | 
|---|
| 130 | logger().information( "shutting down"); | 
|---|
| 131 | BaseDaemon::uninitialize(); | 
|---|
| 132 | } | 
|---|
| 133 |  | 
|---|
| 134 | int Server::run() | 
|---|
| 135 | { | 
|---|
| 136 | if (config().hasOption( "help")) | 
|---|
| 137 | { | 
|---|
| 138 | Poco::Util::HelpFormatter helpFormatter(Server::options()); | 
|---|
| 139 | std::stringstream ; | 
|---|
| 140 | header << commandName() << " [OPTION] [-- [ARG]...]\n"; | 
|---|
| 141 | header << "positional arguments can be used to rewrite config.xml properties, for example, --http_port=8010"; | 
|---|
| 142 | helpFormatter.setHeader(header.str()); | 
|---|
| 143 | helpFormatter.format(std::cout); | 
|---|
| 144 | return 0; | 
|---|
| 145 | } | 
|---|
| 146 | if (config().hasOption( "version")) | 
|---|
| 147 | { | 
|---|
| 148 | std::cout << DBMS_NAME << " server version "<< VERSION_STRING << VERSION_OFFICIAL << "."<< std::endl; | 
|---|
| 149 | return 0; | 
|---|
| 150 | } | 
|---|
| 151 | return Application::run(); | 
|---|
| 152 | } | 
|---|
| 153 |  | 
|---|
| 154 | void Server::initialize(Poco::Util::Application & self) | 
|---|
| 155 | { | 
|---|
| 156 | BaseDaemon::initialize(self); | 
|---|
| 157 | logger().information( "starting up"); | 
|---|
| 158 | } | 
|---|
| 159 |  | 
|---|
| 160 | std::string Server::getDefaultCorePath() const | 
|---|
| 161 | { | 
|---|
| 162 | return getCanonicalPath(config().getString( "path", DBMS_DEFAULT_PATH)) + "cores"; | 
|---|
| 163 | } | 
|---|
| 164 |  | 
|---|
| 165 | void Server::defineOptions(Poco::Util::OptionSet & options) | 
|---|
| 166 | { | 
|---|
| 167 | options.addOption( | 
|---|
| 168 | Poco::Util::Option( "help", "h", "show help and exit") | 
|---|
| 169 | .required(false) | 
|---|
| 170 | .repeatable(false) | 
|---|
| 171 | .binding( "help")); | 
|---|
| 172 | options.addOption( | 
|---|
| 173 | Poco::Util::Option( "version", "V", "show version and exit") | 
|---|
| 174 | .required(false) | 
|---|
| 175 | .repeatable(false) | 
|---|
| 176 | .binding( "version")); | 
|---|
| 177 | BaseDaemon::defineOptions(options); | 
|---|
| 178 | } | 
|---|
| 179 |  | 
|---|
| 180 | int Server::main(const std::vector<std::string> & /*args*/) | 
|---|
| 181 | { | 
|---|
| 182 | Logger * log = &logger(); | 
|---|
| 183 | UseSSL use_ssl; | 
|---|
| 184 |  | 
|---|
| 185 | ThreadStatus thread_status; | 
|---|
| 186 |  | 
|---|
| 187 | registerFunctions(); | 
|---|
| 188 | registerAggregateFunctions(); | 
|---|
| 189 | registerTableFunctions(); | 
|---|
| 190 | registerStorages(); | 
|---|
| 191 | registerDictionaries(); | 
|---|
| 192 | registerDisks(); | 
|---|
| 193 |  | 
|---|
| 194 | CurrentMetrics::set(CurrentMetrics::Revision, ClickHouseRevision::get()); | 
|---|
| 195 | CurrentMetrics::set(CurrentMetrics::VersionInteger, ClickHouseRevision::getVersionInteger()); | 
|---|
| 196 |  | 
|---|
| 197 | /** Context contains all that query execution is dependent: | 
|---|
| 198 | *  settings, available functions, data types, aggregate functions, databases... | 
|---|
| 199 | */ | 
|---|
| 200 | global_context = std::make_unique<Context>(Context::createGlobal()); | 
|---|
| 201 | global_context->makeGlobalContext(); | 
|---|
| 202 | global_context->setApplicationType(Context::ApplicationType::SERVER); | 
|---|
| 203 |  | 
|---|
| 204 | bool has_zookeeper = config().has( "zookeeper"); | 
|---|
| 205 |  | 
|---|
| 206 | zkutil::ZooKeeperNodeCache main_config_zk_node_cache([&] { return global_context->getZooKeeper(); }); | 
|---|
| 207 | zkutil::EventPtr main_config_zk_changed_event = std::make_shared<Poco::Event>(); | 
|---|
| 208 | if (loaded_config.has_zk_includes) | 
|---|
| 209 | { | 
|---|
| 210 | auto old_configuration = loaded_config.configuration; | 
|---|
| 211 | ConfigProcessor config_processor(config_path); | 
|---|
| 212 | loaded_config = config_processor.loadConfigWithZooKeeperIncludes( | 
|---|
| 213 | main_config_zk_node_cache, main_config_zk_changed_event, /* fallback_to_preprocessed = */ true); | 
|---|
| 214 | config_processor.savePreprocessedConfig(loaded_config, config().getString( "path", DBMS_DEFAULT_PATH)); | 
|---|
| 215 | config().removeConfiguration(old_configuration.get()); | 
|---|
| 216 | config().add(loaded_config.configuration.duplicate(), PRIO_DEFAULT, false); | 
|---|
| 217 | } | 
|---|
| 218 |  | 
|---|
| 219 | const auto memory_amount = getMemoryAmount(); | 
|---|
| 220 |  | 
|---|
| 221 | #if defined(__linux__) | 
|---|
| 222 | std::string executable_path = getExecutablePath(); | 
|---|
| 223 | if (executable_path.empty()) | 
|---|
| 224 | executable_path = "/usr/bin/clickhouse";    /// It is used for information messages. | 
|---|
| 225 |  | 
|---|
| 226 | /// After full config loaded | 
|---|
| 227 | { | 
|---|
| 228 | if (config().getBool( "mlock_executable", false)) | 
|---|
| 229 | { | 
|---|
| 230 | if (hasLinuxCapability(CAP_IPC_LOCK)) | 
|---|
| 231 | { | 
|---|
| 232 | LOG_TRACE(log, "Will mlockall to prevent executable memory from being paged out. It may take a few seconds."); | 
|---|
| 233 | if (0 != mlockall(MCL_CURRENT)) | 
|---|
| 234 | LOG_WARNING(log, "Failed mlockall: "+ errnoToString(ErrorCodes::SYSTEM_ERROR)); | 
|---|
| 235 | else | 
|---|
| 236 | LOG_TRACE(log, "The memory map of clickhouse executable has been mlock'ed"); | 
|---|
| 237 | } | 
|---|
| 238 | else | 
|---|
| 239 | { | 
|---|
| 240 | LOG_INFO(log, "It looks like the process has no CAP_IPC_LOCK capability, binary mlock will be disabled." | 
|---|
| 241 | " It could happen due to incorrect ClickHouse package installation." | 
|---|
| 242 | " You could resolve the problem manually with 'sudo setcap cap_ipc_lock=+ep "<< executable_path << "'." | 
|---|
| 243 | " Note that it will not work on 'nosuid' mounted filesystems."); | 
|---|
| 244 | } | 
|---|
| 245 | } | 
|---|
| 246 | } | 
|---|
| 247 | #endif | 
|---|
| 248 |  | 
|---|
| 249 | global_context->setRemoteHostFilter(config()); | 
|---|
| 250 |  | 
|---|
| 251 | std::string path = getCanonicalPath(config().getString( "path", DBMS_DEFAULT_PATH)); | 
|---|
| 252 | std::string default_database = config().getString( "default_database", "default"); | 
|---|
| 253 |  | 
|---|
| 254 | /// Check that the process' user id matches the owner of the data. | 
|---|
| 255 | const auto effective_user_id = geteuid(); | 
|---|
| 256 | struct stat statbuf; | 
|---|
| 257 | if (stat(path.c_str(), &statbuf) == 0 && effective_user_id != statbuf.st_uid) | 
|---|
| 258 | { | 
|---|
| 259 | const auto effective_user = getUserName(effective_user_id); | 
|---|
| 260 | const auto data_owner = getUserName(statbuf.st_uid); | 
|---|
| 261 | std::string message = "Effective user of the process ("+ effective_user + | 
|---|
| 262 | ") does not match the owner of the data ("+ data_owner + ")."; | 
|---|
| 263 | if (effective_user_id == 0) | 
|---|
| 264 | { | 
|---|
| 265 | message += " Run under 'sudo -u "+ data_owner + "'."; | 
|---|
| 266 | throw Exception(message, ErrorCodes::MISMATCHING_USERS_FOR_PROCESS_AND_DATA); | 
|---|
| 267 | } | 
|---|
| 268 | else | 
|---|
| 269 | { | 
|---|
| 270 | LOG_WARNING(log, message); | 
|---|
| 271 | } | 
|---|
| 272 | } | 
|---|
| 273 |  | 
|---|
| 274 | global_context->setPath(path); | 
|---|
| 275 |  | 
|---|
| 276 | /// Create directories for 'path' and for default database, if not exist. | 
|---|
| 277 | Poco::File(path + "data/"+ default_database).createDirectories(); | 
|---|
| 278 | Poco::File(path + "metadata/"+ default_database).createDirectories(); | 
|---|
| 279 |  | 
|---|
| 280 | /// Check that we have read and write access to all data paths | 
|---|
| 281 | auto disk_selector = global_context->getDiskSelector(); | 
|---|
| 282 | for (const auto & [name, disk] : disk_selector.getDisksMap()) | 
|---|
| 283 | { | 
|---|
| 284 | Poco::File disk_path(disk->getPath()); | 
|---|
| 285 | if (!disk_path.canRead() || !disk_path.canWrite()) | 
|---|
| 286 | throw Exception( "There is no RW access to disk "+ name + " ("+ disk->getPath() + ")", ErrorCodes::PATH_ACCESS_DENIED); | 
|---|
| 287 | } | 
|---|
| 288 |  | 
|---|
| 289 | StatusFile status{path + "status"}; | 
|---|
| 290 |  | 
|---|
| 291 | SCOPE_EXIT({ | 
|---|
| 292 | /** Ask to cancel background jobs all table engines, | 
|---|
| 293 | *  and also query_log. | 
|---|
| 294 | * It is important to do early, not in destructor of Context, because | 
|---|
| 295 | *  table engines could use Context on destroy. | 
|---|
| 296 | */ | 
|---|
| 297 | LOG_INFO(log, "Shutting down storages."); | 
|---|
| 298 |  | 
|---|
| 299 | global_context->shutdown(); | 
|---|
| 300 |  | 
|---|
| 301 | LOG_DEBUG(log, "Shut down storages."); | 
|---|
| 302 |  | 
|---|
| 303 | /** Explicitly destroy Context. It is more convenient than in destructor of Server, because logger is still available. | 
|---|
| 304 | * At this moment, no one could own shared part of Context. | 
|---|
| 305 | */ | 
|---|
| 306 | global_context.reset(); | 
|---|
| 307 | LOG_DEBUG(log, "Destroyed global context."); | 
|---|
| 308 | }); | 
|---|
| 309 |  | 
|---|
| 310 | /// Try to increase limit on number of open files. | 
|---|
| 311 | { | 
|---|
| 312 | rlimit rlim; | 
|---|
| 313 | if (getrlimit(RLIMIT_NOFILE, &rlim)) | 
|---|
| 314 | throw Poco::Exception( "Cannot getrlimit"); | 
|---|
| 315 |  | 
|---|
| 316 | if (rlim.rlim_cur == rlim.rlim_max) | 
|---|
| 317 | { | 
|---|
| 318 | LOG_DEBUG(log, "rlimit on number of file descriptors is "<< rlim.rlim_cur); | 
|---|
| 319 | } | 
|---|
| 320 | else | 
|---|
| 321 | { | 
|---|
| 322 | rlim_t old = rlim.rlim_cur; | 
|---|
| 323 | rlim.rlim_cur = config().getUInt( "max_open_files", rlim.rlim_max); | 
|---|
| 324 | int rc = setrlimit(RLIMIT_NOFILE, &rlim); | 
|---|
| 325 | if (rc != 0) | 
|---|
| 326 | LOG_WARNING(log, | 
|---|
| 327 | "Cannot set max number of file descriptors to "<< rlim.rlim_cur | 
|---|
| 328 | << ". Try to specify max_open_files according to your system limits. error: " | 
|---|
| 329 | << strerror(errno)); | 
|---|
| 330 | else | 
|---|
| 331 | LOG_DEBUG(log, "Set max number of file descriptors to "<< rlim.rlim_cur << " (was "<< old << ")."); | 
|---|
| 332 | } | 
|---|
| 333 | } | 
|---|
| 334 |  | 
|---|
| 335 | static ServerErrorHandler error_handler; | 
|---|
| 336 | Poco::ErrorHandler::set(&error_handler); | 
|---|
| 337 |  | 
|---|
| 338 | /// Initialize DateLUT early, to not interfere with running time of first query. | 
|---|
| 339 | LOG_DEBUG(log, "Initializing DateLUT."); | 
|---|
| 340 | DateLUT::instance(); | 
|---|
| 341 | LOG_TRACE(log, "Initialized DateLUT with time zone '"<< DateLUT::instance().getTimeZone() << "'."); | 
|---|
| 342 |  | 
|---|
| 343 | /// Directory with temporary data for processing of heavy queries. | 
|---|
| 344 | { | 
|---|
| 345 | std::string tmp_path = config().getString( "tmp_path", path + "tmp/"); | 
|---|
| 346 | global_context->setTemporaryPath(tmp_path); | 
|---|
| 347 | Poco::File(tmp_path).createDirectories(); | 
|---|
| 348 |  | 
|---|
| 349 | /// Clearing old temporary files. | 
|---|
| 350 | Poco::DirectoryIterator dir_end; | 
|---|
| 351 | for (Poco::DirectoryIterator it(tmp_path); it != dir_end; ++it) | 
|---|
| 352 | { | 
|---|
| 353 | if (it->isFile() && startsWith(it.name(), "tmp")) | 
|---|
| 354 | { | 
|---|
| 355 | LOG_DEBUG(log, "Removing old temporary file "<< it->path()); | 
|---|
| 356 | it->remove(); | 
|---|
| 357 | } | 
|---|
| 358 | } | 
|---|
| 359 | } | 
|---|
| 360 |  | 
|---|
| 361 | /** Directory with 'flags': files indicating temporary settings for the server set by system administrator. | 
|---|
| 362 | * Flags may be cleared automatically after being applied by the server. | 
|---|
| 363 | * Examples: do repair of local data; clone all replicated tables from replica. | 
|---|
| 364 | */ | 
|---|
| 365 | { | 
|---|
| 366 | Poco::File(path + "flags/").createDirectories(); | 
|---|
| 367 | global_context->setFlagsPath(path + "flags/"); | 
|---|
| 368 | } | 
|---|
| 369 |  | 
|---|
| 370 | /** Directory with user provided files that are usable by 'file' table function. | 
|---|
| 371 | */ | 
|---|
| 372 | { | 
|---|
| 373 |  | 
|---|
| 374 | std::string user_files_path = config().getString( "user_files_path", path + "user_files/"); | 
|---|
| 375 | global_context->setUserFilesPath(user_files_path); | 
|---|
| 376 | Poco::File(user_files_path).createDirectories(); | 
|---|
| 377 | } | 
|---|
| 378 |  | 
|---|
| 379 | { | 
|---|
| 380 | std::string dictionaries_lib_path = config().getString( "dictionaries_lib_path", path + "dictionaries_lib/"); | 
|---|
| 381 | global_context->setDictionariesLibPath(dictionaries_lib_path); | 
|---|
| 382 | Poco::File(dictionaries_lib_path).createDirectories(); | 
|---|
| 383 | } | 
|---|
| 384 |  | 
|---|
| 385 | if (config().has( "interserver_http_port") && config().has( "interserver_https_port")) | 
|---|
| 386 | throw Exception( "Both http and https interserver ports are specified", ErrorCodes::EXCESSIVE_ELEMENT_IN_CONFIG); | 
|---|
| 387 |  | 
|---|
| 388 | static const auto interserver_tags = | 
|---|
| 389 | { | 
|---|
| 390 | std::make_tuple( "interserver_http_host", "interserver_http_port", "http"), | 
|---|
| 391 | std::make_tuple( "interserver_https_host", "interserver_https_port", "https") | 
|---|
| 392 | }; | 
|---|
| 393 |  | 
|---|
| 394 | for (auto [host_tag, port_tag, scheme] : interserver_tags) | 
|---|
| 395 | { | 
|---|
| 396 | if (config().has(port_tag)) | 
|---|
| 397 | { | 
|---|
| 398 | String this_host = config().getString(host_tag, ""); | 
|---|
| 399 |  | 
|---|
| 400 | if (this_host.empty()) | 
|---|
| 401 | { | 
|---|
| 402 | this_host = getFQDNOrHostName(); | 
|---|
| 403 | LOG_DEBUG(log, | 
|---|
| 404 | "Configuration parameter '"+ String(host_tag) + "' doesn't exist or exists and empty. Will use '"+ this_host | 
|---|
| 405 | + "' as replica host."); | 
|---|
| 406 | } | 
|---|
| 407 |  | 
|---|
| 408 | String port_str = config().getString(port_tag); | 
|---|
| 409 | int port = parse<int>(port_str); | 
|---|
| 410 |  | 
|---|
| 411 | if (port < 0 || port > 0xFFFF) | 
|---|
| 412 | throw Exception( "Out of range '"+ String(port_tag) + "': "+ toString(port), ErrorCodes::ARGUMENT_OUT_OF_BOUND); | 
|---|
| 413 |  | 
|---|
| 414 | global_context->setInterserverIOAddress(this_host, port); | 
|---|
| 415 | global_context->setInterserverScheme(scheme); | 
|---|
| 416 | } | 
|---|
| 417 | } | 
|---|
| 418 |  | 
|---|
| 419 | if (config().has( "interserver_http_credentials")) | 
|---|
| 420 | { | 
|---|
| 421 | String user = config().getString( "interserver_http_credentials.user", ""); | 
|---|
| 422 | String password = config().getString( "interserver_http_credentials.password", ""); | 
|---|
| 423 |  | 
|---|
| 424 | if (user.empty()) | 
|---|
| 425 | throw Exception( "Configuration parameter interserver_http_credentials user can't be empty", ErrorCodes::NO_ELEMENTS_IN_CONFIG); | 
|---|
| 426 |  | 
|---|
| 427 | global_context->setInterserverCredentials(user, password); | 
|---|
| 428 | } | 
|---|
| 429 |  | 
|---|
| 430 | if (config().has( "macros")) | 
|---|
| 431 | global_context->setMacros(std::make_unique<Macros>(config(), "macros")); | 
|---|
| 432 |  | 
|---|
| 433 | /// Initialize main config reloader. | 
|---|
| 434 | std::string include_from_path = config().getString( "include_from", "/etc/metrika.xml"); | 
|---|
| 435 |  | 
|---|
| 436 | if (config().has( "query_masking_rules")) | 
|---|
| 437 | { | 
|---|
| 438 | SensitiveDataMasker::setInstance(std::make_unique<SensitiveDataMasker>(config(), "query_masking_rules")); | 
|---|
| 439 | } | 
|---|
| 440 |  | 
|---|
| 441 | auto main_config_reloader = std::make_unique<ConfigReloader>(config_path, | 
|---|
| 442 | include_from_path, | 
|---|
| 443 | config().getString( "path", ""), | 
|---|
| 444 | std::move(main_config_zk_node_cache), | 
|---|
| 445 | main_config_zk_changed_event, | 
|---|
| 446 | [&](ConfigurationPtr config) | 
|---|
| 447 | { | 
|---|
| 448 | setTextLog(global_context->getTextLog()); | 
|---|
| 449 | buildLoggers(*config, logger()); | 
|---|
| 450 | global_context->setClustersConfig(config); | 
|---|
| 451 | global_context->setMacros(std::make_unique<Macros>(*config, "macros")); | 
|---|
| 452 |  | 
|---|
| 453 | /// Setup protection to avoid accidental DROP for big tables (that are greater than 50 GB by default) | 
|---|
| 454 | if (config->has( "max_table_size_to_drop")) | 
|---|
| 455 | global_context->setMaxTableSizeToDrop(config->getUInt64( "max_table_size_to_drop")); | 
|---|
| 456 |  | 
|---|
| 457 | if (config->has( "max_partition_size_to_drop")) | 
|---|
| 458 | global_context->setMaxPartitionSizeToDrop(config->getUInt64( "max_partition_size_to_drop")); | 
|---|
| 459 | }, | 
|---|
| 460 | /* already_loaded = */ true); | 
|---|
| 461 |  | 
|---|
| 462 | /// Initialize users config reloader. | 
|---|
| 463 | std::string users_config_path = config().getString( "users_config", config_path); | 
|---|
| 464 | /// If path to users' config isn't absolute, try guess its root (current) dir. | 
|---|
| 465 | /// At first, try to find it in dir of main config, after will use current dir. | 
|---|
| 466 | if (users_config_path.empty() || users_config_path[0] != '/') | 
|---|
| 467 | { | 
|---|
| 468 | std::string config_dir = Poco::Path(config_path).parent().toString(); | 
|---|
| 469 | if (Poco::File(config_dir + users_config_path).exists()) | 
|---|
| 470 | users_config_path = config_dir + users_config_path; | 
|---|
| 471 | } | 
|---|
| 472 | auto users_config_reloader = std::make_unique<ConfigReloader>(users_config_path, | 
|---|
| 473 | include_from_path, | 
|---|
| 474 | config().getString( "path", ""), | 
|---|
| 475 | zkutil::ZooKeeperNodeCache([&] { return global_context->getZooKeeper(); }), | 
|---|
| 476 | std::make_shared<Poco::Event>(), | 
|---|
| 477 | [&](ConfigurationPtr config) { global_context->setUsersConfig(config); }, | 
|---|
| 478 | /* already_loaded = */ false); | 
|---|
| 479 |  | 
|---|
| 480 | /// Reload config in SYSTEM RELOAD CONFIG query. | 
|---|
| 481 | global_context->setConfigReloadCallback([&]() | 
|---|
| 482 | { | 
|---|
| 483 | main_config_reloader->reload(); | 
|---|
| 484 | users_config_reloader->reload(); | 
|---|
| 485 | }); | 
|---|
| 486 |  | 
|---|
| 487 | /// Limit on total number of concurrently executed queries. | 
|---|
| 488 | global_context->getProcessList().setMaxSize(config().getInt( "max_concurrent_queries", 0)); | 
|---|
| 489 |  | 
|---|
| 490 | /// Set up caches. | 
|---|
| 491 |  | 
|---|
| 492 | /// Lower cache size on low-memory systems. | 
|---|
| 493 | double cache_size_to_ram_max_ratio = config().getDouble( "cache_size_to_ram_max_ratio", 0.5); | 
|---|
| 494 | size_t max_cache_size = memory_amount * cache_size_to_ram_max_ratio; | 
|---|
| 495 |  | 
|---|
| 496 | /// Size of cache for uncompressed blocks. Zero means disabled. | 
|---|
| 497 | size_t uncompressed_cache_size = config().getUInt64( "uncompressed_cache_size", 0); | 
|---|
| 498 | if (uncompressed_cache_size > max_cache_size) | 
|---|
| 499 | { | 
|---|
| 500 | uncompressed_cache_size = max_cache_size; | 
|---|
| 501 | LOG_INFO(log, "Uncompressed cache size was lowered to "<< formatReadableSizeWithBinarySuffix(uncompressed_cache_size) | 
|---|
| 502 | << " because the system has low amount of memory"); | 
|---|
| 503 | } | 
|---|
| 504 | global_context->setUncompressedCache(uncompressed_cache_size); | 
|---|
| 505 |  | 
|---|
| 506 | /// Load global settings from default_profile and system_profile. | 
|---|
| 507 | global_context->setDefaultProfiles(config()); | 
|---|
| 508 | Settings & settings = global_context->getSettingsRef(); | 
|---|
| 509 |  | 
|---|
| 510 | /// Size of cache for marks (index of MergeTree family of tables). It is mandatory. | 
|---|
| 511 | size_t mark_cache_size = config().getUInt64( "mark_cache_size"); | 
|---|
| 512 | if (!mark_cache_size) | 
|---|
| 513 | LOG_ERROR(log, "Too low mark cache size will lead to severe performance degradation."); | 
|---|
| 514 | if (mark_cache_size > max_cache_size) | 
|---|
| 515 | { | 
|---|
| 516 | mark_cache_size = max_cache_size; | 
|---|
| 517 | LOG_INFO(log, "Mark cache size was lowered to "<< formatReadableSizeWithBinarySuffix(uncompressed_cache_size) | 
|---|
| 518 | << " because the system has low amount of memory"); | 
|---|
| 519 | } | 
|---|
| 520 | global_context->setMarkCache(mark_cache_size); | 
|---|
| 521 |  | 
|---|
| 522 | #if USE_EMBEDDED_COMPILER | 
|---|
| 523 | size_t compiled_expression_cache_size = config().getUInt64( "compiled_expression_cache_size", 500); | 
|---|
| 524 | if (compiled_expression_cache_size) | 
|---|
| 525 | global_context->setCompiledExpressionCache(compiled_expression_cache_size); | 
|---|
| 526 | #endif | 
|---|
| 527 |  | 
|---|
| 528 | /// Set path for format schema files | 
|---|
| 529 | auto format_schema_path = Poco::File(config().getString( "format_schema_path", path + "format_schemas/")); | 
|---|
| 530 | global_context->setFormatSchemaPath(format_schema_path.path()); | 
|---|
| 531 | format_schema_path.createDirectories(); | 
|---|
| 532 |  | 
|---|
| 533 | LOG_INFO(log, "Loading metadata from "+ path); | 
|---|
| 534 |  | 
|---|
| 535 | try | 
|---|
| 536 | { | 
|---|
| 537 | loadMetadataSystem(*global_context); | 
|---|
| 538 | /// After attaching system databases we can initialize system log. | 
|---|
| 539 | global_context->initializeSystemLogs(); | 
|---|
| 540 | /// After the system database is created, attach virtual system tables (in addition to query_log and part_log) | 
|---|
| 541 | attachSystemTablesServer(*global_context->getDatabase( "system"), has_zookeeper); | 
|---|
| 542 | /// Then, load remaining databases | 
|---|
| 543 | loadMetadata(*global_context); | 
|---|
| 544 | } | 
|---|
| 545 | catch (...) | 
|---|
| 546 | { | 
|---|
| 547 | tryLogCurrentException(log, "Caught exception while loading metadata"); | 
|---|
| 548 | throw; | 
|---|
| 549 | } | 
|---|
| 550 | LOG_DEBUG(log, "Loaded metadata."); | 
|---|
| 551 |  | 
|---|
| 552 | /// Init trace collector only after trace_log system table was created | 
|---|
| 553 | /// Disable it if we collect test coverage information, because it will work extremely slow. | 
|---|
| 554 | /// | 
|---|
| 555 | /// It also cannot work with sanitizers. | 
|---|
| 556 | /// Sanitizers are using quick "frame walking" stack unwinding (this implies -fno-omit-frame-pointer) | 
|---|
| 557 | /// And they do unwiding frequently (on every malloc/free, thread/mutex operations, etc). | 
|---|
| 558 | /// They change %rbp during unwinding and it confuses libunwind if signal comes during sanitizer unwiding | 
|---|
| 559 | ///  and query profiler decide to unwind stack with libunwind at this moment. | 
|---|
| 560 | /// | 
|---|
| 561 | /// Symptoms: you'll get silent Segmentation Fault - without sanitizer message and without usual ClickHouse diagnostics. | 
|---|
| 562 | /// | 
|---|
| 563 | /// Look at compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.h | 
|---|
| 564 | /// | 
|---|
| 565 | #if USE_UNWIND && !WITH_COVERAGE && !defined(SANITIZER) | 
|---|
| 566 | /// QueryProfiler cannot work reliably with any other libunwind or without PHDR cache. | 
|---|
| 567 | if (hasPHDRCache()) | 
|---|
| 568 | global_context->initializeTraceCollector(); | 
|---|
| 569 | #endif | 
|---|
| 570 |  | 
|---|
| 571 | global_context->setCurrentDatabase(default_database); | 
|---|
| 572 |  | 
|---|
| 573 | if (has_zookeeper && config().has( "distributed_ddl")) | 
|---|
| 574 | { | 
|---|
| 575 | /// DDL worker should be started after all tables were loaded | 
|---|
| 576 | String ddl_zookeeper_path = config().getString( "distributed_ddl.path", "/clickhouse/task_queue/ddl/"); | 
|---|
| 577 | global_context->setDDLWorker(std::make_unique<DDLWorker>(ddl_zookeeper_path, *global_context, &config(), "distributed_ddl")); | 
|---|
| 578 | } | 
|---|
| 579 |  | 
|---|
| 580 | std::unique_ptr<DNSCacheUpdater> dns_cache_updater; | 
|---|
| 581 | if (config().has( "disable_internal_dns_cache") && config().getInt( "disable_internal_dns_cache")) | 
|---|
| 582 | { | 
|---|
| 583 | /// Disable DNS caching at all | 
|---|
| 584 | DNSResolver::instance().setDisableCacheFlag(); | 
|---|
| 585 | } | 
|---|
| 586 | else | 
|---|
| 587 | { | 
|---|
| 588 | /// Initialize a watcher periodically updating DNS cache | 
|---|
| 589 | dns_cache_updater = std::make_unique<DNSCacheUpdater>(*global_context, config().getInt( "dns_cache_update_period", 15)); | 
|---|
| 590 | } | 
|---|
| 591 |  | 
|---|
| 592 | #if defined(__linux__) | 
|---|
| 593 | if (!TaskStatsInfoGetter::checkPermissions()) | 
|---|
| 594 | { | 
|---|
| 595 | LOG_INFO(log, "It looks like the process has no CAP_NET_ADMIN capability, 'taskstats' performance statistics will be disabled." | 
|---|
| 596 | " It could happen due to incorrect ClickHouse package installation." | 
|---|
| 597 | " You could resolve the problem manually with 'sudo setcap cap_net_admin=+ep "<< executable_path << "'." | 
|---|
| 598 | " Note that it will not work on 'nosuid' mounted filesystems." | 
|---|
| 599 | " It also doesn't work if you run clickhouse-server inside network namespace as it happens in some containers."); | 
|---|
| 600 | } | 
|---|
| 601 |  | 
|---|
| 602 | if (!hasLinuxCapability(CAP_SYS_NICE)) | 
|---|
| 603 | { | 
|---|
| 604 | LOG_INFO(log, "It looks like the process has no CAP_SYS_NICE capability, the setting 'os_thread_nice' will have no effect." | 
|---|
| 605 | " It could happen due to incorrect ClickHouse package installation." | 
|---|
| 606 | " You could resolve the problem manually with 'sudo setcap cap_sys_nice=+ep "<< executable_path << "'." | 
|---|
| 607 | " Note that it will not work on 'nosuid' mounted filesystems."); | 
|---|
| 608 | } | 
|---|
| 609 | #else | 
|---|
| 610 | LOG_INFO(log, "TaskStats is not implemented for this OS. IO accounting will be disabled."); | 
|---|
| 611 | #endif | 
|---|
| 612 |  | 
|---|
| 613 | { | 
|---|
| 614 | Poco::Timespan keep_alive_timeout(config().getUInt( "keep_alive_timeout", 10), 0); | 
|---|
| 615 |  | 
|---|
| 616 | Poco::ThreadPool server_pool(3, config().getUInt( "max_connections", 1024)); | 
|---|
| 617 | Poco::Net::HTTPServerParams::Ptr http_params = new Poco::Net::HTTPServerParams; | 
|---|
| 618 | http_params->setTimeout(settings.http_receive_timeout); | 
|---|
| 619 | http_params->setKeepAliveTimeout(keep_alive_timeout); | 
|---|
| 620 |  | 
|---|
| 621 | std::vector<std::unique_ptr<Poco::Net::TCPServer>> servers; | 
|---|
| 622 |  | 
|---|
| 623 | std::vector<std::string> listen_hosts = DB::getMultipleValuesFromConfig(config(), "", "listen_host"); | 
|---|
| 624 |  | 
|---|
| 625 | bool listen_try = config().getBool( "listen_try", false); | 
|---|
| 626 | if (listen_hosts.empty()) | 
|---|
| 627 | { | 
|---|
| 628 | listen_hosts.emplace_back( "::1"); | 
|---|
| 629 | listen_hosts.emplace_back( "127.0.0.1"); | 
|---|
| 630 | listen_try = true; | 
|---|
| 631 | } | 
|---|
| 632 |  | 
|---|
| 633 | auto make_socket_address = [&](const std::string & host, UInt16 port) | 
|---|
| 634 | { | 
|---|
| 635 | Poco::Net::SocketAddress socket_address; | 
|---|
| 636 | try | 
|---|
| 637 | { | 
|---|
| 638 | socket_address = Poco::Net::SocketAddress(host, port); | 
|---|
| 639 | } | 
|---|
| 640 | catch (const Poco::Net::DNSException & e) | 
|---|
| 641 | { | 
|---|
| 642 | const auto code = e.code(); | 
|---|
| 643 | if (code == EAI_FAMILY | 
|---|
| 644 | #if defined(EAI_ADDRFAMILY) | 
|---|
| 645 | || code == EAI_ADDRFAMILY | 
|---|
| 646 | #endif | 
|---|
| 647 | ) | 
|---|
| 648 | { | 
|---|
| 649 | LOG_ERROR(log, | 
|---|
| 650 | "Cannot resolve listen_host ("<< host << "), error "<< e.code() << ": "<< e.message() << ". " | 
|---|
| 651 | "If it is an IPv6 address and your host has disabled IPv6, then consider to " | 
|---|
| 652 | "specify IPv4 address to listen in <listen_host> element of configuration " | 
|---|
| 653 | "file. Example: <listen_host>0.0.0.0</listen_host>"); | 
|---|
| 654 | } | 
|---|
| 655 |  | 
|---|
| 656 | throw; | 
|---|
| 657 | } | 
|---|
| 658 | return socket_address; | 
|---|
| 659 | }; | 
|---|
| 660 |  | 
|---|
| 661 | auto socket_bind_listen = [&](auto & socket, const std::string & host, UInt16 port, [[maybe_unused]] bool secure = 0) | 
|---|
| 662 | { | 
|---|
| 663 | auto address = make_socket_address(host, port); | 
|---|
| 664 | #if !defined(POCO_CLICKHOUSE_PATCH) || POCO_VERSION < 0x01090100 | 
|---|
| 665 | if (secure) | 
|---|
| 666 | /// Bug in old (<1.9.1) poco, listen() after bind() with reusePort param will fail because have no implementation in SecureServerSocketImpl | 
|---|
| 667 | /// https://github.com/pocoproject/poco/pull/2257 | 
|---|
| 668 | socket.bind(address, /* reuseAddress = */ true); | 
|---|
| 669 | else | 
|---|
| 670 | #endif | 
|---|
| 671 | #if POCO_VERSION < 0x01080000 | 
|---|
| 672 | socket.bind(address, /* reuseAddress = */ true); | 
|---|
| 673 | #else | 
|---|
| 674 | socket.bind(address, /* reuseAddress = */ true, /* reusePort = */ config().getBool( "listen_reuse_port", false)); | 
|---|
| 675 | #endif | 
|---|
| 676 |  | 
|---|
| 677 | socket.listen(/* backlog = */ config().getUInt( "listen_backlog", 64)); | 
|---|
| 678 |  | 
|---|
| 679 | return address; | 
|---|
| 680 | }; | 
|---|
| 681 |  | 
|---|
| 682 | /// This object will periodically calculate some metrics. | 
|---|
| 683 | AsynchronousMetrics async_metrics(*global_context); | 
|---|
| 684 | attachSystemTablesAsync(*global_context->getDatabase( "system"), async_metrics); | 
|---|
| 685 |  | 
|---|
| 686 | for (const auto & listen_host : listen_hosts) | 
|---|
| 687 | { | 
|---|
| 688 | auto create_server = [&](const char * port_name, auto && func) | 
|---|
| 689 | { | 
|---|
| 690 | /// For testing purposes, user may omit tcp_port or http_port or https_port in configuration file. | 
|---|
| 691 | if (!config().has(port_name)) | 
|---|
| 692 | return; | 
|---|
| 693 |  | 
|---|
| 694 | auto port = config().getInt(port_name); | 
|---|
| 695 | try | 
|---|
| 696 | { | 
|---|
| 697 | func(port); | 
|---|
| 698 | } | 
|---|
| 699 | catch (const Poco::Exception &) | 
|---|
| 700 | { | 
|---|
| 701 | std::string message = "Listen ["+ listen_host + "]:"+ std::to_string(port) + " failed: "+ getCurrentExceptionMessage(false); | 
|---|
| 702 |  | 
|---|
| 703 | if (listen_try) | 
|---|
| 704 | { | 
|---|
| 705 | LOG_ERROR(log, message | 
|---|
| 706 | << ". If it is an IPv6 or IPv4 address and your host has disabled IPv6 or IPv4, then consider to " | 
|---|
| 707 | "specify not disabled IPv4 or IPv6 address to listen in <listen_host> element of configuration " | 
|---|
| 708 | "file. Example for disabled IPv6: <listen_host>0.0.0.0</listen_host> ." | 
|---|
| 709 | " Example for disabled IPv4: <listen_host>::</listen_host>"); | 
|---|
| 710 | } | 
|---|
| 711 | else | 
|---|
| 712 | { | 
|---|
| 713 | throw Exception{message, ErrorCodes::NETWORK_ERROR}; | 
|---|
| 714 | } | 
|---|
| 715 | } | 
|---|
| 716 | }; | 
|---|
| 717 |  | 
|---|
| 718 | /// HTTP | 
|---|
| 719 | create_server( "http_port", [&](UInt16 port) | 
|---|
| 720 | { | 
|---|
| 721 | Poco::Net::ServerSocket socket; | 
|---|
| 722 | auto address = socket_bind_listen(socket, listen_host, port); | 
|---|
| 723 | socket.setReceiveTimeout(settings.http_receive_timeout); | 
|---|
| 724 | socket.setSendTimeout(settings.http_send_timeout); | 
|---|
| 725 | auto handler_factory = createDefaultHandlerFatory<HTTPHandler>(*this, "HTTPHandler-factory"); | 
|---|
| 726 | if (config().has( "prometheus") && config().getInt( "prometheus.port", 0) == 0) | 
|---|
| 727 | handler_factory->addHandler<PrometeusHandlerFactory>(async_metrics); | 
|---|
| 728 |  | 
|---|
| 729 | servers.emplace_back(std::make_unique<Poco::Net::HTTPServer>( | 
|---|
| 730 | handler_factory, | 
|---|
| 731 | server_pool, | 
|---|
| 732 | socket, | 
|---|
| 733 | http_params)); | 
|---|
| 734 |  | 
|---|
| 735 | LOG_INFO(log, "Listening for http://"+ address.toString()); | 
|---|
| 736 | }); | 
|---|
| 737 |  | 
|---|
| 738 | /// HTTPS | 
|---|
| 739 | create_server( "https_port", [&](UInt16 port) | 
|---|
| 740 | { | 
|---|
| 741 | #if USE_POCO_NETSSL | 
|---|
| 742 | Poco::Net::SecureServerSocket socket; | 
|---|
| 743 | auto address = socket_bind_listen(socket, listen_host, port, /* secure = */ true); | 
|---|
| 744 | socket.setReceiveTimeout(settings.http_receive_timeout); | 
|---|
| 745 | socket.setSendTimeout(settings.http_send_timeout); | 
|---|
| 746 | servers.emplace_back(std::make_unique<Poco::Net::HTTPServer>( | 
|---|
| 747 | createDefaultHandlerFatory<HTTPHandler>(*this, "HTTPSHandler-factory"), | 
|---|
| 748 | server_pool, | 
|---|
| 749 | socket, | 
|---|
| 750 | http_params)); | 
|---|
| 751 |  | 
|---|
| 752 | LOG_INFO(log, "Listening for https://"+ address.toString()); | 
|---|
| 753 | #else | 
|---|
| 754 | UNUSED(port); | 
|---|
| 755 | throw Exception{ "HTTPS protocol is disabled because Poco library was built without NetSSL support.", | 
|---|
| 756 | ErrorCodes::SUPPORT_IS_DISABLED}; | 
|---|
| 757 | #endif | 
|---|
| 758 | }); | 
|---|
| 759 |  | 
|---|
| 760 | /// TCP | 
|---|
| 761 | create_server( "tcp_port", [&](UInt16 port) | 
|---|
| 762 | { | 
|---|
| 763 | Poco::Net::ServerSocket socket; | 
|---|
| 764 | auto address = socket_bind_listen(socket, listen_host, port); | 
|---|
| 765 | socket.setReceiveTimeout(settings.receive_timeout); | 
|---|
| 766 | socket.setSendTimeout(settings.send_timeout); | 
|---|
| 767 | servers.emplace_back(std::make_unique<Poco::Net::TCPServer>( | 
|---|
| 768 | new TCPHandlerFactory(*this), | 
|---|
| 769 | server_pool, | 
|---|
| 770 | socket, | 
|---|
| 771 | new Poco::Net::TCPServerParams)); | 
|---|
| 772 |  | 
|---|
| 773 | LOG_INFO(log, "Listening for connections with native protocol (tcp): "+ address.toString()); | 
|---|
| 774 | }); | 
|---|
| 775 |  | 
|---|
| 776 | /// TCP with SSL | 
|---|
| 777 | create_server( "tcp_port_secure", [&](UInt16 port) | 
|---|
| 778 | { | 
|---|
| 779 | #if USE_POCO_NETSSL | 
|---|
| 780 | Poco::Net::SecureServerSocket socket; | 
|---|
| 781 | auto address = socket_bind_listen(socket, listen_host, port, /* secure = */ true); | 
|---|
| 782 | socket.setReceiveTimeout(settings.receive_timeout); | 
|---|
| 783 | socket.setSendTimeout(settings.send_timeout); | 
|---|
| 784 | servers.emplace_back(std::make_unique<Poco::Net::TCPServer>( | 
|---|
| 785 | new TCPHandlerFactory(*this, /* secure= */ true), | 
|---|
| 786 | server_pool, | 
|---|
| 787 | socket, | 
|---|
| 788 | new Poco::Net::TCPServerParams)); | 
|---|
| 789 | LOG_INFO(log, "Listening for connections with secure native protocol (tcp_secure): "+ address.toString()); | 
|---|
| 790 | #else | 
|---|
| 791 | UNUSED(port); | 
|---|
| 792 | throw Exception{ "SSL support for TCP protocol is disabled because Poco library was built without NetSSL support.", | 
|---|
| 793 | ErrorCodes::SUPPORT_IS_DISABLED}; | 
|---|
| 794 | #endif | 
|---|
| 795 | }); | 
|---|
| 796 |  | 
|---|
| 797 | /// Interserver IO HTTP | 
|---|
| 798 | create_server( "interserver_http_port", [&](UInt16 port) | 
|---|
| 799 | { | 
|---|
| 800 | Poco::Net::ServerSocket socket; | 
|---|
| 801 | auto address = socket_bind_listen(socket, listen_host, port); | 
|---|
| 802 | socket.setReceiveTimeout(settings.http_receive_timeout); | 
|---|
| 803 | socket.setSendTimeout(settings.http_send_timeout); | 
|---|
| 804 | servers.emplace_back(std::make_unique<Poco::Net::HTTPServer>( | 
|---|
| 805 | createDefaultHandlerFatory<InterserverIOHTTPHandler>(*this, "InterserverIOHTTPHandler-factory"), | 
|---|
| 806 | server_pool, | 
|---|
| 807 | socket, | 
|---|
| 808 | http_params)); | 
|---|
| 809 |  | 
|---|
| 810 | LOG_INFO(log, "Listening for replica communication (interserver): http://"+ address.toString()); | 
|---|
| 811 | }); | 
|---|
| 812 |  | 
|---|
| 813 | create_server( "interserver_https_port", [&](UInt16 port) | 
|---|
| 814 | { | 
|---|
| 815 | #if USE_POCO_NETSSL | 
|---|
| 816 | Poco::Net::SecureServerSocket socket; | 
|---|
| 817 | auto address = socket_bind_listen(socket, listen_host, port, /* secure = */ true); | 
|---|
| 818 | socket.setReceiveTimeout(settings.http_receive_timeout); | 
|---|
| 819 | socket.setSendTimeout(settings.http_send_timeout); | 
|---|
| 820 | servers.emplace_back(std::make_unique<Poco::Net::HTTPServer>( | 
|---|
| 821 | createDefaultHandlerFatory<InterserverIOHTTPHandler>(*this, "InterserverIOHTTPHandler-factory"), | 
|---|
| 822 | server_pool, | 
|---|
| 823 | socket, | 
|---|
| 824 | http_params)); | 
|---|
| 825 |  | 
|---|
| 826 | LOG_INFO(log, "Listening for secure replica communication (interserver): https://"+ address.toString()); | 
|---|
| 827 | #else | 
|---|
| 828 | UNUSED(port); | 
|---|
| 829 | throw Exception{ "SSL support for TCP protocol is disabled because Poco library was built without NetSSL support.", | 
|---|
| 830 | ErrorCodes::SUPPORT_IS_DISABLED}; | 
|---|
| 831 | #endif | 
|---|
| 832 | }); | 
|---|
| 833 |  | 
|---|
| 834 | create_server( "mysql_port", [&](UInt16 port) | 
|---|
| 835 | { | 
|---|
| 836 | Poco::Net::ServerSocket socket; | 
|---|
| 837 | auto address = socket_bind_listen(socket, listen_host, port, /* secure = */ true); | 
|---|
| 838 | socket.setReceiveTimeout(Poco::Timespan()); | 
|---|
| 839 | socket.setSendTimeout(settings.send_timeout); | 
|---|
| 840 | servers.emplace_back(std::make_unique<Poco::Net::TCPServer>( | 
|---|
| 841 | new MySQLHandlerFactory(*this), | 
|---|
| 842 | server_pool, | 
|---|
| 843 | socket, | 
|---|
| 844 | new Poco::Net::TCPServerParams)); | 
|---|
| 845 |  | 
|---|
| 846 | LOG_INFO(log, "Listening for MySQL compatibility protocol: "+ address.toString()); | 
|---|
| 847 | }); | 
|---|
| 848 |  | 
|---|
| 849 | /// Prometheus (if defined and not setup yet with http_port) | 
|---|
| 850 | create_server( "prometheus.port", [&](UInt16 port) | 
|---|
| 851 | { | 
|---|
| 852 | Poco::Net::ServerSocket socket; | 
|---|
| 853 | auto address = socket_bind_listen(socket, listen_host, port); | 
|---|
| 854 | socket.setReceiveTimeout(settings.http_receive_timeout); | 
|---|
| 855 | socket.setSendTimeout(settings.http_send_timeout); | 
|---|
| 856 | auto handler_factory = new HTTPRequestHandlerFactoryMain(*this, "PrometheusHandler-factory"); | 
|---|
| 857 | handler_factory->addHandler<PrometeusHandlerFactory>(async_metrics); | 
|---|
| 858 | servers.emplace_back(std::make_unique<Poco::Net::HTTPServer>( | 
|---|
| 859 | handler_factory, | 
|---|
| 860 | server_pool, | 
|---|
| 861 | socket, | 
|---|
| 862 | http_params)); | 
|---|
| 863 |  | 
|---|
| 864 | LOG_INFO(log, "Listening for Prometheus: http://"+ address.toString()); | 
|---|
| 865 | }); | 
|---|
| 866 | } | 
|---|
| 867 |  | 
|---|
| 868 | if (servers.empty()) | 
|---|
| 869 | throw Exception( "No servers started (add valid listen_host and 'tcp_port' or 'http_port' to configuration file.)", ErrorCodes::NO_ELEMENTS_IN_CONFIG); | 
|---|
| 870 |  | 
|---|
| 871 | for (auto & server : servers) | 
|---|
| 872 | server->start(); | 
|---|
| 873 |  | 
|---|
| 874 | main_config_reloader->start(); | 
|---|
| 875 | users_config_reloader->start(); | 
|---|
| 876 | if (dns_cache_updater) | 
|---|
| 877 | dns_cache_updater->start(); | 
|---|
| 878 |  | 
|---|
| 879 | { | 
|---|
| 880 | std::stringstream message; | 
|---|
| 881 | message << "Available RAM: "<< formatReadableSizeWithBinarySuffix(memory_amount) << ";" | 
|---|
| 882 | << " physical cores: "<< getNumberOfPhysicalCPUCores() << ";" | 
|---|
| 883 | // on ARM processors it can show only enabled at current moment cores | 
|---|
| 884 | << " logical cores: "<< std::thread::hardware_concurrency() << "."; | 
|---|
| 885 | LOG_INFO(log, message.str()); | 
|---|
| 886 | } | 
|---|
| 887 |  | 
|---|
| 888 | LOG_INFO(log, "Ready for connections."); | 
|---|
| 889 |  | 
|---|
| 890 | SCOPE_EXIT({ | 
|---|
| 891 | LOG_DEBUG(log, "Received termination signal."); | 
|---|
| 892 | LOG_DEBUG(log, "Waiting for current connections to close."); | 
|---|
| 893 |  | 
|---|
| 894 | is_cancelled = true; | 
|---|
| 895 |  | 
|---|
| 896 | int current_connections = 0; | 
|---|
| 897 | for (auto & server : servers) | 
|---|
| 898 | { | 
|---|
| 899 | server->stop(); | 
|---|
| 900 | current_connections += server->currentConnections(); | 
|---|
| 901 | } | 
|---|
| 902 |  | 
|---|
| 903 | LOG_INFO(log, | 
|---|
| 904 | "Closed all listening sockets." | 
|---|
| 905 | << (current_connections ? " Waiting for "+ toString(current_connections) + " outstanding connections.": "")); | 
|---|
| 906 |  | 
|---|
| 907 | /// Killing remaining queries. | 
|---|
| 908 | global_context->getProcessList().killAllQueries(); | 
|---|
| 909 |  | 
|---|
| 910 | if (current_connections) | 
|---|
| 911 | { | 
|---|
| 912 | const int sleep_max_ms = 1000 * config().getInt( "shutdown_wait_unfinished", 5); | 
|---|
| 913 | const int sleep_one_ms = 100; | 
|---|
| 914 | int sleep_current_ms = 0; | 
|---|
| 915 | while (sleep_current_ms < sleep_max_ms) | 
|---|
| 916 | { | 
|---|
| 917 | current_connections = 0; | 
|---|
| 918 | for (auto & server : servers) | 
|---|
| 919 | current_connections += server->currentConnections(); | 
|---|
| 920 | if (!current_connections) | 
|---|
| 921 | break; | 
|---|
| 922 | sleep_current_ms += sleep_one_ms; | 
|---|
| 923 | std::this_thread::sleep_for(std::chrono::milliseconds(sleep_one_ms)); | 
|---|
| 924 | } | 
|---|
| 925 | } | 
|---|
| 926 |  | 
|---|
| 927 | LOG_INFO( | 
|---|
| 928 | log, "Closed connections."<< (current_connections ? " But "+ toString(current_connections) + " remains." | 
|---|
| 929 | " Tip: To increase wait time add to config: <shutdown_wait_unfinished>60</shutdown_wait_unfinished>": "")); | 
|---|
| 930 |  | 
|---|
| 931 | dns_cache_updater.reset(); | 
|---|
| 932 | main_config_reloader.reset(); | 
|---|
| 933 | users_config_reloader.reset(); | 
|---|
| 934 |  | 
|---|
| 935 | if (current_connections) | 
|---|
| 936 | { | 
|---|
| 937 | /// There is no better way to force connections to close in Poco. | 
|---|
| 938 | /// Otherwise connection handlers will continue to live | 
|---|
| 939 | /// (they are effectively dangling objects, but they use global thread pool | 
|---|
| 940 | ///  and global thread pool destructor will wait for threads, preventing server shutdown). | 
|---|
| 941 |  | 
|---|
| 942 | /// Dump coverage here, because std::atexit callback would not be called. | 
|---|
| 943 | dumpCoverageReportIfPossible(); | 
|---|
| 944 | LOG_INFO(log, "Will shutdown forcefully."); | 
|---|
| 945 | _exit(Application::EXIT_OK); | 
|---|
| 946 | } | 
|---|
| 947 | }); | 
|---|
| 948 |  | 
|---|
| 949 | /// try to load dictionaries immediately, throw on error and die | 
|---|
| 950 | ext::scope_guard dictionaries_xmls, models_xmls; | 
|---|
| 951 | try | 
|---|
| 952 | { | 
|---|
| 953 | if (!config().getBool( "dictionaries_lazy_load", true)) | 
|---|
| 954 | { | 
|---|
| 955 | global_context->tryCreateEmbeddedDictionaries(); | 
|---|
| 956 | global_context->getExternalDictionariesLoader().enableAlwaysLoadEverything(true); | 
|---|
| 957 | } | 
|---|
| 958 | dictionaries_xmls = global_context->getExternalDictionariesLoader().addConfigRepository( | 
|---|
| 959 | std::make_unique<ExternalLoaderXMLConfigRepository>(config(), "dictionaries_config")); | 
|---|
| 960 | models_xmls = global_context->getExternalModelsLoader().addConfigRepository( | 
|---|
| 961 | std::make_unique<ExternalLoaderXMLConfigRepository>(config(), "models_config")); | 
|---|
| 962 | } | 
|---|
| 963 | catch (...) | 
|---|
| 964 | { | 
|---|
| 965 | LOG_ERROR(log, "Caught exception while loading dictionaries."); | 
|---|
| 966 | throw; | 
|---|
| 967 | } | 
|---|
| 968 |  | 
|---|
| 969 | std::vector<std::unique_ptr<MetricsTransmitter>> metrics_transmitters; | 
|---|
| 970 | for (const auto & graphite_key : DB::getMultipleKeysFromConfig(config(), "", "graphite")) | 
|---|
| 971 | { | 
|---|
| 972 | metrics_transmitters.emplace_back(std::make_unique<MetricsTransmitter>( | 
|---|
| 973 | global_context->getConfigRef(), graphite_key, async_metrics)); | 
|---|
| 974 | } | 
|---|
| 975 |  | 
|---|
| 976 | SessionCleaner session_cleaner(*global_context); | 
|---|
| 977 |  | 
|---|
| 978 | waitForTerminationRequest(); | 
|---|
| 979 | } | 
|---|
| 980 |  | 
|---|
| 981 | return Application::EXIT_OK; | 
|---|
| 982 | } | 
|---|
| 983 | } | 
|---|
| 984 |  | 
|---|
| 985 | #pragma GCC diagnostic ignored "-Wunused-function" | 
|---|
| 986 | #pragma GCC diagnostic ignored "-Wmissing-declarations" | 
|---|
| 987 |  | 
|---|
| 988 | int mainEntryClickHouseServer(int argc, char ** argv) | 
|---|
| 989 | { | 
|---|
| 990 | DB::Server app; | 
|---|
| 991 | try | 
|---|
| 992 | { | 
|---|
| 993 | return app.run(argc, argv); | 
|---|
| 994 | } | 
|---|
| 995 | catch (...) | 
|---|
| 996 | { | 
|---|
| 997 | std::cerr << DB::getCurrentExceptionMessage(true) << "\n"; | 
|---|
| 998 | auto code = DB::getCurrentExceptionCode(); | 
|---|
| 999 | return code ? code : 1; | 
|---|
| 1000 | } | 
|---|
| 1001 | } | 
|---|
| 1002 |  | 
|---|