Skip to content

Commit 3fb7c4d

Browse files
Merge pull request ClickHouse#71722 from ClickHouse/clickhouse-local-persistency
Persistent databases in clickhouse-local
2 parents e236a61 + 6c532f8 commit 3fb7c4d

32 files changed

+505
-117
lines changed

programs/local/LocalServer.cpp

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
#include <boost/program_options/options_description.hpp>
5353
#include <base/argsToConfig.h>
5454
#include <filesystem>
55+
#include <Common/filesystemHelpers.h>
5556

5657
#include "config.h"
5758

@@ -280,7 +281,19 @@ static DatabasePtr createMemoryDatabaseIfNotExists(ContextPtr context, const Str
280281
static DatabasePtr createClickHouseLocalDatabaseOverlay(const String & name_, ContextPtr context)
281282
{
282283
auto overlay = std::make_shared<DatabasesOverlay>(name_, context);
283-
overlay->registerNextDatabase(std::make_shared<DatabaseAtomic>(name_, fs::weakly_canonical(context->getPath()), UUIDHelpers::generateV4(), context));
284+
285+
UUID default_database_uuid;
286+
287+
fs::path existing_path_symlink = fs::weakly_canonical(context->getPath()) / "metadata" / "default";
288+
if (FS::isSymlinkNoThrow(existing_path_symlink))
289+
default_database_uuid = parse<UUID>(FS::readSymlink(existing_path_symlink).filename());
290+
else
291+
default_database_uuid = UUIDHelpers::generateV4();
292+
293+
fs::path default_database_metadata_path = fs::weakly_canonical(context->getPath()) / "store"
294+
/ DatabaseCatalog::getPathForUUID(default_database_uuid);
295+
296+
overlay->registerNextDatabase(std::make_shared<DatabaseAtomic>(name_, default_database_metadata_path, default_database_uuid, context));
284297
overlay->registerNextDatabase(std::make_shared<DatabaseFilesystem>(name_, "", context));
285298
return overlay;
286299
}
@@ -292,7 +305,7 @@ void LocalServer::tryInitPath()
292305

293306
if (getClientConfiguration().has("path"))
294307
{
295-
// User-supplied path.
308+
/// User-supplied path.
296309
path = getClientConfiguration().getString("path");
297310
Poco::trimInPlace(path);
298311

@@ -306,15 +319,15 @@ void LocalServer::tryInitPath()
306319
}
307320
else
308321
{
309-
// The path is not provided explicitly - use a unique path in the system temporary directory
310-
// (or in the current dir if a temporary doesn't exist)
322+
/// The user requested to use a temporary path - use a unique path in the system temporary directory
323+
/// (or in the current dir if a temporary doesn't exist)
311324
LoggerRawPtr log = &logger();
312325
std::filesystem::path parent_folder;
313326
std::filesystem::path default_path;
314327

315328
try
316329
{
317-
// try to guess a tmp folder name, and check if it's a directory (throw exception otherwise)
330+
/// Try to guess a tmp folder name, and check if it's a directory (throw an exception otherwise).
318331
parent_folder = std::filesystem::temp_directory_path();
319332

320333
}
@@ -342,7 +355,7 @@ void LocalServer::tryInitPath()
342355
temporary_directory_to_delete = default_path;
343356

344357
path = default_path.string();
345-
LOG_DEBUG(log, "Working directory created: {}", path);
358+
LOG_DEBUG(log, "Working directory will be created as needed: {}", path);
346359
}
347360

348361
global_context->setPath(fs::path(path) / "");
@@ -883,30 +896,38 @@ void LocalServer::processConfig()
883896

884897
if (getClientConfiguration().has("path"))
885898
{
899+
attachSystemTablesServer(global_context, *createMemoryDatabaseIfNotExists(global_context, DatabaseCatalog::SYSTEM_DATABASE), false);
900+
attachInformationSchema(global_context, *createMemoryDatabaseIfNotExists(global_context, DatabaseCatalog::INFORMATION_SCHEMA));
901+
attachInformationSchema(global_context, *createMemoryDatabaseIfNotExists(global_context, DatabaseCatalog::INFORMATION_SCHEMA_UPPERCASE));
902+
886903
String path = global_context->getPath();
887-
fs::create_directories(fs::path(path));
888904

889905
/// Lock path directory before read
906+
fs::create_directories(fs::path(path));
890907
status.emplace(fs::path(path) / "status", StatusFile::write_full_info);
891908

892-
LOG_DEBUG(log, "Loading metadata from {}", path);
893-
auto load_system_metadata_tasks = loadMetadataSystem(global_context);
894-
attachSystemTablesServer(global_context, *createMemoryDatabaseIfNotExists(global_context, DatabaseCatalog::SYSTEM_DATABASE), false);
895-
attachInformationSchema(global_context, *createMemoryDatabaseIfNotExists(global_context, DatabaseCatalog::INFORMATION_SCHEMA));
896-
attachInformationSchema(global_context, *createMemoryDatabaseIfNotExists(global_context, DatabaseCatalog::INFORMATION_SCHEMA_UPPERCASE));
897-
waitLoad(TablesLoaderForegroundPoolId, load_system_metadata_tasks);
898-
899-
if (!getClientConfiguration().has("only-system-tables"))
909+
if (fs::exists(fs::path(path) / "metadata"))
900910
{
901-
DatabaseCatalog::instance().createBackgroundTasks();
902-
waitLoad(loadMetadata(global_context));
903-
DatabaseCatalog::instance().startupBackgroundTasks();
904-
}
911+
LOG_DEBUG(log, "Loading metadata from {}", path);
905912

906-
/// For ClickHouse local if path is not set the loader will be disabled.
907-
global_context->getUserDefinedSQLObjectsStorage().loadObjects();
913+
if (fs::exists(std::filesystem::path(path) / "metadata" / "system.sql"))
914+
{
915+
LoadTaskPtrs load_system_metadata_tasks = loadMetadataSystem(global_context);
916+
waitLoad(TablesLoaderForegroundPoolId, load_system_metadata_tasks);
917+
}
908918

909-
LOG_DEBUG(log, "Loaded metadata.");
919+
if (!getClientConfiguration().has("only-system-tables"))
920+
{
921+
DatabaseCatalog::instance().createBackgroundTasks();
922+
waitLoad(loadMetadata(global_context));
923+
DatabaseCatalog::instance().startupBackgroundTasks();
924+
}
925+
926+
/// For ClickHouse local if path is not set the loader will be disabled.
927+
global_context->getUserDefinedSQLObjectsStorage().loadObjects();
928+
929+
LOG_DEBUG(log, "Loaded metadata.");
930+
}
910931
}
911932
else if (!getClientConfiguration().has("no-system-tables"))
912933
{
@@ -981,7 +1002,7 @@ void LocalServer::addExtraOptions(OptionsDescription & options_description)
9811002
("logger.level", po::value<std::string>(), "Log level")
9821003

9831004
("no-system-tables", "do not attach system tables (better startup time)")
984-
("path", po::value<std::string>(), "Storage path")
1005+
("path", po::value<std::string>(), "Storage path. If it was not specified, we will use a temporary directory, that is cleaned up on exit.")
9851006
("only-system-tables", "attach only system tables from specified path")
9861007
("top_level_domains_path", po::value<std::string>(), "Path to lists with custom TLDs")
9871008
;
@@ -1023,8 +1044,6 @@ void LocalServer::processOptions(const OptionsDescription &, const CommandLineOp
10231044
getClientConfiguration().setBool("no-system-tables", true);
10241045
if (options.count("only-system-tables"))
10251046
getClientConfiguration().setBool("only-system-tables", true);
1026-
if (options.count("database"))
1027-
getClientConfiguration().setString("default_database", options["database"].as<std::string>());
10281047

10291048
if (options.count("input-format"))
10301049
getClientConfiguration().setString("table-data-format", options["input-format"].as<std::string>());

programs/server/Server.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2400,7 +2400,7 @@ try
24002400

24012401
/// Set current database name before loading tables and databases because
24022402
/// system logs may copy global context.
2403-
std::string default_database = server_settings[ServerSetting::default_database].toString();
2403+
std::string default_database = server_settings[ServerSetting::default_database];
24042404
if (default_database.empty())
24052405
throw Exception(ErrorCodes::BAD_ARGUMENTS, "default_database cannot be empty");
24062406
global_context->setCurrentDatabaseNameInGlobalContext(default_database);

src/Access/IAccessStorage.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ bool IAccessStorage::insertImpl(const UUID &, const AccessEntityPtr & entity, bo
308308
{
309309
if (isReadOnly())
310310
throwReadonlyCannotInsert(entity->getType(), entity->getName());
311-
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "insertImpl() is not implemented in {}", getStorageType());
311+
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "insertImpl is not implemented in {}", getStorageType());
312312
}
313313

314314

@@ -404,7 +404,7 @@ bool IAccessStorage::removeImpl(const UUID & id, bool throw_if_not_exists)
404404
return false;
405405
throwReadonlyCannotRemove(entity->getType(), entity->getName());
406406
}
407-
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "removeImpl() is not implemented in {}", getStorageType());
407+
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "removeImpl is not implemented in {}", getStorageType());
408408
}
409409

410410

@@ -500,7 +500,7 @@ bool IAccessStorage::updateImpl(const UUID & id, const UpdateFunc &, bool throw_
500500
return false;
501501
throwReadonlyCannotUpdate(entity->getType(), entity->getName());
502502
}
503-
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "updateImpl() is not implemented in {}", getStorageType());
503+
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "updateImpl is not implemented in {}", getStorageType());
504504
}
505505

506506

src/Access/RowPolicy.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ void RowPolicy::setFullName(const RowPolicyName & full_name_)
4545

4646
void RowPolicy::setName(const String &)
4747
{
48-
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "RowPolicy::setName() is not implemented");
48+
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "RowPolicy::setName is not implemented");
4949
}
5050

5151

src/Client/LocalConnection.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,7 @@ bool LocalConnection::pollImpl()
616616

617617
UInt64 LocalConnection::receivePacketType()
618618
{
619-
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "receivePacketType() is not implemented for LocalConnection");
619+
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "receivePacketType is not implemented for LocalConnection");
620620
}
621621

622622
Packet LocalConnection::receivePacket()

src/DataTypes/DataTypeObjectDeprecated.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class DataTypeObjectDeprecated : public IDataType
3030

3131
Field getDefault() const override
3232
{
33-
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Method getDefault() is not implemented for data type {}", getName());
33+
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Method getDefault is not implemented for data type {}", getName());
3434
}
3535

3636
bool haveSubtypes() const override { return false; }

src/DataTypes/IDataType.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ class IDataType : private boost::noncopyable, public std::enable_shared_from_thi
362362
bool throw_if_null) const
363363
{
364364
if (throw_if_null)
365-
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Method getDynamicSubcolumnData() is not implemented for type {}", getName());
365+
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Method getDynamicSubcolumnData is not implemented for type {}", getName());
366366
return nullptr;
367367
}
368368
};

src/DataTypes/IDataTypeDummy.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,17 @@ class IDataTypeDummy : public IDataType
2828
public:
2929
MutableColumnPtr createColumn() const override
3030
{
31-
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Method createColumn() is not implemented for data type {}", getName());
31+
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Method createColumn is not implemented for data type {}", getName());
3232
}
3333

3434
Field getDefault() const override
3535
{
36-
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Method getDefault() is not implemented for data type {}", getName());
36+
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Method getDefault is not implemented for data type {}", getName());
3737
}
3838

3939
void insertDefaultInto(IColumn &) const override
4040
{
41-
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Method insertDefaultInto() is not implemented for data type {}", getName());
41+
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Method insertDefaultInto is not implemented for data type {}", getName());
4242
}
4343

4444
bool haveSubtypes() const override { return false; }

src/Databases/DatabaseAtomic.cpp

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,13 @@
44
#include <Databases/DatabaseFactory.h>
55
#include <Databases/DatabaseOnDisk.h>
66
#include <Databases/DatabaseReplicated.h>
7-
#include <IO/ReadBufferFromFile.h>
8-
#include <IO/ReadHelpers.h>
9-
#include <Interpreters/Context.h>
107
#include <Interpreters/DDLTask.h>
118
#include <Interpreters/DatabaseCatalog.h>
129
#include <Interpreters/ExternalDictionariesLoader.h>
1310
#include <Storages/StorageMaterializedView.h>
1411
#include <Common/logger_useful.h>
1512
#include <Common/PoolId.h>
1613
#include <Common/atomicRename.h>
17-
#include <Common/filesystemHelpers.h>
1814
#include <Core/Settings.h>
1915

2016

@@ -54,8 +50,9 @@ class AtomicDatabaseTablesSnapshotIterator final : public DatabaseTablesSnapshot
5450

5551
DatabaseAtomic::DatabaseAtomic(String name_, String metadata_path_, UUID uuid, const String & logger_name, ContextPtr context_)
5652
: DatabaseOrdinary(name_, metadata_path_, "store/", logger_name, context_)
57-
, path_to_table_symlinks(fs::path("data") / escapeForFileName(name_) / "")
58-
, path_to_metadata_symlink(fs::path("metadata") / escapeForFileName(name_))
53+
, root_path(fs::weakly_canonical(context_->getPath()))
54+
, path_to_table_symlinks(root_path / "data" / escapeForFileName(name_) / "")
55+
, path_to_metadata_symlink(root_path / "metadata" / escapeForFileName(name_))
5956
, db_uuid(uuid)
6057
{
6158
assert(db_uuid != UUIDHelpers::Nil);
@@ -436,6 +433,8 @@ void DatabaseAtomic::setDetachedTableNotInUseForce(const UUID & uuid)
436433
DatabaseAtomic::DetachedTables DatabaseAtomic::cleanupDetachedTables()
437434
{
438435
DetachedTables not_in_use;
436+
if (detached_tables.empty())
437+
return not_in_use;
439438
auto it = detached_tables.begin();
440439
LOG_DEBUG(log, "There are {} detached tables. Start searching non used tables.", detached_tables.size());
441440
while (it != detached_tables.end())
@@ -573,15 +572,16 @@ void DatabaseAtomic::tryCreateSymlink(const StoragePtr & table, bool if_data_pat
573572
{
574573
if (!db_disk->isSymlinkSupported())
575574
return;
575+
576576
try
577577
{
578578
String table_name = table->getStorageID().getTableName();
579579

580580
if (!table->storesDataOnDisk())
581581
throw Exception(ErrorCodes::LOGICAL_ERROR, "Table {} doesn't have data path to create symlink", table_name);
582582

583-
String link = path_to_table_symlinks + escapeForFileName(table_name);
584-
fs::path data = fs::weakly_canonical(table->getDataPaths()[0]);
583+
String link = path_to_table_symlinks / escapeForFileName(table_name);
584+
fs::path data = fs::proximate(table->getDataPaths()[0], path_to_table_symlinks);
585585

586586
/// If it already points where needed.
587587
if (db_disk->equivalentNoThrow(data, link))
@@ -590,7 +590,7 @@ void DatabaseAtomic::tryCreateSymlink(const StoragePtr & table, bool if_data_pat
590590
if (if_data_path_exist && !db_disk->existsFileOrDirectory(data))
591591
return;
592592

593-
db_disk->createDirectoriesSymlink(data, link);
593+
db_disk->createDirectorySymlink(data, link);
594594
}
595595
catch (...)
596596
{
@@ -605,7 +605,7 @@ void DatabaseAtomic::tryRemoveSymlink(const String & table_name)
605605

606606
try
607607
{
608-
String path = path_to_table_symlinks + escapeForFileName(table_name);
608+
String path = path_to_table_symlinks / escapeForFileName(table_name);
609609
db_disk->removeFileIfExists(path);
610610
}
611611
catch (...)
@@ -621,21 +621,24 @@ void DatabaseAtomic::tryCreateMetadataSymlink()
621621

622622
/// Symlinks in data/db_name/ directory and metadata/db_name/ are not used by ClickHouse,
623623
/// it's needed only for convenient introspection.
624-
assert(path_to_metadata_symlink != metadata_path);
625-
fs::path metadata_symlink(path_to_metadata_symlink);
626-
if (db_disk->existsFileOrDirectory(metadata_symlink))
624+
chassert(path_to_metadata_symlink != metadata_path);
625+
if (db_disk->existsFileOrDirectory(path_to_metadata_symlink))
627626
{
628-
if (!db_disk->isSymlink(metadata_symlink))
629-
throw Exception(ErrorCodes::FILE_ALREADY_EXISTS, "Directory {} exists", path_to_metadata_symlink);
627+
if (!db_disk->isSymlink(path_to_metadata_symlink))
628+
throw Exception(ErrorCodes::FILE_ALREADY_EXISTS, "Directory {} already exists", path_to_metadata_symlink);
630629
}
631630
else
632631
{
633632
try
634633
{
635634
/// fs::exists could return false for broken symlink
636-
if (db_disk->isSymlinkNoThrow(metadata_symlink))
637-
db_disk->removeFileIfExists(metadata_symlink);
638-
db_disk->createDirectoriesSymlink(metadata_path, path_to_metadata_symlink);
635+
if (db_disk->isSymlinkNoThrow(path_to_metadata_symlink))
636+
db_disk->removeFileIfExists(path_to_metadata_symlink);
637+
638+
String symlink = fs::proximate(root_path / metadata_path, path_to_metadata_symlink.parent_path());
639+
640+
LOG_TEST(log, "Creating directory symlink, path_to_metadata_symlink: {}, metadata_path: {}, symlink content: {}", path_to_metadata_symlink, metadata_path, symlink);
641+
db_disk->createDirectorySymlink(symlink, path_to_metadata_symlink);
639642
}
640643
catch (...)
641644
{
@@ -670,8 +673,8 @@ void DatabaseAtomic::renameDatabase(ContextPtr query_context, const String & new
670673
}
671674

672675
auto new_name_escaped = escapeForFileName(new_name);
673-
auto old_database_metadata_path = fs::path("metadata") / (escapeForFileName(getDatabaseName()) + ".sql");
674-
auto new_database_metadata_path = fs::path("metadata") / (new_name_escaped + ".sql");
676+
auto old_database_metadata_path = root_path / "metadata" / (escapeForFileName(getDatabaseName()) + ".sql");
677+
auto new_database_metadata_path = root_path / "metadata" / (new_name_escaped + ".sql");
675678
db_disk->moveFile(old_database_metadata_path, new_database_metadata_path);
676679

677680
String old_path_to_table_symlinks;
@@ -699,9 +702,9 @@ void DatabaseAtomic::renameDatabase(ContextPtr query_context, const String & new
699702
snapshot.database = database_name;
700703
}
701704

702-
path_to_metadata_symlink = fs::path("metadata") / new_name_escaped;
705+
path_to_metadata_symlink = root_path / "metadata" / new_name_escaped;
703706
old_path_to_table_symlinks = path_to_table_symlinks;
704-
path_to_table_symlinks = fs::path("data") / new_name_escaped / "";
707+
path_to_table_symlinks = root_path / "data" / new_name_escaped / "";
705708
}
706709

707710
if (db_disk->isSymlinkSupported())

src/Databases/DatabaseAtomic.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,9 @@ class DatabaseAtomic : public DatabaseOrdinary
8888
NameToPathMap table_name_to_path TSA_GUARDED_BY(mutex);
8989

9090
DetachedTables detached_tables TSA_GUARDED_BY(mutex);
91-
String path_to_table_symlinks;
92-
String path_to_metadata_symlink;
91+
std::filesystem::path root_path;
92+
std::filesystem::path path_to_table_symlinks;
93+
std::filesystem::path path_to_metadata_symlink;
9394
const UUID db_uuid;
9495

9596
LoadTaskPtr startup_atomic_database_task TSA_GUARDED_BY(mutex);

0 commit comments

Comments
 (0)