-
Notifications
You must be signed in to change notification settings - Fork 6.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix SYSTEM UNFREEZE for ordinary database #38262
Changes from 2 commits
3296ba2
85bf022
8f14c60
dd59612
7627f83
5aca68a
b548979
46de699
4c60921
f140ced
9c371d5
4d08154
c2ba9ea
15b0b8f
39e1fc7
5e7cf1f
e91d22a
98509e6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -7,10 +7,13 @@ | |||
|
||||
namespace DB | ||||
{ | ||||
namespace ErrorCodes | ||||
{ | ||||
extern const int INVALID_PARTITION_VALUE; | ||||
} | ||||
|
||||
void FreezeMetaData::fill(const StorageReplicatedMergeTree & storage) | ||||
{ | ||||
is_replicated = storage.supportsReplication(); | ||||
is_remote = storage.isRemote(); | ||||
replica_name = storage.getReplicaName(); | ||||
zookeeper_name = storage.getZooKeeperName(); | ||||
table_shared_id = storage.getTableSharedID(); | ||||
|
@@ -26,11 +29,16 @@ void FreezeMetaData::save(DiskPtr data_disk, const String & path) const | |||
|
||||
writeIntText(version, buffer); | ||||
buffer.write("\n", 1); | ||||
writeBoolText(is_replicated, buffer); | ||||
buffer.write("\n", 1); | ||||
writeBoolText(is_remote, buffer); | ||||
buffer.write("\n", 1); | ||||
writeString(replica_name, buffer); | ||||
if (version == 1) { | ||||
/// is_replicated and is_remote are not used | ||||
bool is_replicated = true; | ||||
writeBoolText(is_replicated, buffer); | ||||
buffer.write("\n", 1); | ||||
bool is_remote = true; | ||||
writeBoolText(is_remote, buffer); | ||||
buffer.write("\n", 1); | ||||
} | ||||
writeString(escapeForFileName(replica_name), buffer); | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please also check that other strings cannot contain unexpected chars |
||||
buffer.write("\n", 1); | ||||
writeString(zookeeper_name, buffer); | ||||
buffer.write("\n", 1); | ||||
|
@@ -51,17 +59,23 @@ bool FreezeMetaData::load(DiskPtr data_disk, const String & path) | |||
auto metadata_str = metadata_storage->readFileToString(file_path); | ||||
ReadBufferFromString buffer(metadata_str); | ||||
readIntText(version, buffer); | ||||
if (version != 1) | ||||
if (version < 1 or version > 2) | ||||
tavplubix marked this conversation as resolved.
Show resolved
Hide resolved
|
||||
{ | ||||
LOG_ERROR(&Poco::Logger::get("FreezeMetaData"), "Unknown freezed metadata version: {}", version); | ||||
return false; | ||||
} | ||||
DB::assertChar('\n', buffer); | ||||
readBoolText(is_replicated, buffer); | ||||
DB::assertChar('\n', buffer); | ||||
readBoolText(is_remote, buffer); | ||||
DB::assertChar('\n', buffer); | ||||
readString(replica_name, buffer); | ||||
if (version == 1) { | ||||
/// is_replicated and is_remote are not used | ||||
bool is_replicated; | ||||
readBoolText(is_replicated, buffer); | ||||
DB::assertChar('\n', buffer); | ||||
bool is_remote; | ||||
readBoolText(is_remote, buffer); | ||||
DB::assertChar('\n', buffer); | ||||
} | ||||
auto unescaped_replica_name = unescapeForFileName(replica_name); | ||||
readString(unescaped_replica_name, buffer); | ||||
DB::assertChar('\n', buffer); | ||||
readString(zookeeper_name, buffer); | ||||
DB::assertChar('\n', buffer); | ||||
|
@@ -87,43 +101,48 @@ String FreezeMetaData::getFileName(const String & path) | |||
return fs::path(path) / "frozen_metadata.txt"; | ||||
} | ||||
|
||||
BlockIO Unfreezer::unfreeze(const String & backup_name, ContextPtr local_context) | ||||
BlockIO Unfreezer::unfreeze(const String & backup_name) | ||||
{ | ||||
LOG_DEBUG(log, "Unfreezing backup {}", backup_name); | ||||
auto disks_map = local_context->getDisksMap(); | ||||
LOG_DEBUG(log, "Unfreezing backup {}", escapeForFileName(backup_name)); | ||||
auto disks_map = local_context_->getDisksMap(); | ||||
Disks disks; | ||||
for (auto & [name, disk]: disks_map) | ||||
{ | ||||
disks.push_back(disk); | ||||
} | ||||
auto backup_path = fs::path(backup_directory_prefix) / escapeForFileName(backup_name); | ||||
auto store_path = backup_path / "store"; | ||||
auto store_paths = {backup_path / "store", backup_path / "data"}; | ||||
|
||||
PartitionCommandsResultInfo result_info; | ||||
|
||||
for (const auto & disk: disks) | ||||
{ | ||||
if (!disk->exists(store_path)) | ||||
continue; | ||||
for (auto prefix_it = disk->iterateDirectory(store_path); prefix_it->isValid(); prefix_it->next()) | ||||
for (auto store_path: store_paths) | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just for loop added |
||||
{ | ||||
auto prefix_directory = store_path / prefix_it->name(); | ||||
for (auto table_it = disk->iterateDirectory(prefix_directory); table_it->isValid(); table_it->next()) | ||||
if (!disk->exists(store_path)) | ||||
continue; | ||||
for (auto prefix_it = disk->iterateDirectory(store_path); prefix_it->isValid(); prefix_it->next()) | ||||
{ | ||||
auto table_directory = prefix_directory / table_it->name(); | ||||
auto current_result_info = unfreezePartitionsFromTableDirectory([] (const String &) { return true; }, backup_name, {disk}, table_directory, local_context); | ||||
for (auto & command_result : current_result_info) | ||||
auto prefix_directory = store_path / prefix_it->name(); | ||||
for (auto table_it = disk->iterateDirectory(prefix_directory); table_it->isValid(); table_it->next()) | ||||
{ | ||||
command_result.command_type = "SYSTEM UNFREEZE"; | ||||
auto table_directory = prefix_directory / table_it->name(); | ||||
auto current_result_info = unfreezePartitionsFromTableDirectory( | ||||
[](const String &) { return true; }, backup_name, {disk}, table_directory, std::nullopt); | ||||
for (auto & command_result : current_result_info) | ||||
{ | ||||
command_result.command_type = "SYSTEM UNFREEZE"; | ||||
} | ||||
result_info.insert( | ||||
result_info.end(), | ||||
std::make_move_iterator(current_result_info.begin()), | ||||
std::make_move_iterator(current_result_info.end())); | ||||
} | ||||
result_info.insert( | ||||
result_info.end(), | ||||
std::make_move_iterator(current_result_info.begin()), | ||||
std::make_move_iterator(current_result_info.end())); | ||||
} | ||||
} | ||||
if (disk->exists(backup_path)) | ||||
{ | ||||
/// After unfreezing we need to clear revision.txt file and empty directories | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Still not clear why we cannot just remove everything recursively. Why do we need complex "unfreeze" logic? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. /// After unfreezing we need to clear revision.txt file and empty directories. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is that clear? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No. I mean that FREEZE PARTITION only creates hardlinks. So UNFREEZE should only remove hardlinks. It's not clear why do we need some complex logic for "unfreezing", it was supposed to do just |
||||
disk->removeRecursive(backup_path); | ||||
} | ||||
} | ||||
|
@@ -136,18 +155,15 @@ BlockIO Unfreezer::unfreeze(const String & backup_name, ContextPtr local_context | |||
return result; | ||||
} | ||||
|
||||
bool Unfreezer::removeFreezedPart(DiskPtr disk, const String & path, const String & part_name, ContextPtr local_context) | ||||
bool Unfreezer::removeFreezedPart(DiskPtr disk, const String & path, const String & part_name, ContextPtr local_context, zkutil::ZooKeeperPtr zookeeper) | ||||
{ | ||||
if (disk->supportZeroCopyReplication()) | ||||
{ | ||||
FreezeMetaData meta; | ||||
if (meta.load(disk, path)) | ||||
{ | ||||
if (meta.is_replicated) | ||||
{ | ||||
FreezeMetaData::clean(disk, path); | ||||
return StorageReplicatedMergeTree::removeSharedDetachedPart(disk, path, part_name, meta.table_shared_id, meta.zookeeper_name, meta.replica_name, "", local_context); | ||||
} | ||||
FreezeMetaData::clean(disk, path); | ||||
return StorageReplicatedMergeTree::removeSharedDetachedPart(disk, path, part_name, meta.table_shared_id, meta.zookeeper_name, meta.replica_name, "", local_context, zookeeper); | ||||
} | ||||
} | ||||
|
||||
|
@@ -156,7 +172,7 @@ bool Unfreezer::removeFreezedPart(DiskPtr disk, const String & path, const Strin | |||
return false; | ||||
} | ||||
|
||||
PartitionCommandsResultInfo Unfreezer::unfreezePartitionsFromTableDirectory(MergeTreeData::MatcherFn matcher, const String & backup_name, const Disks & disks, const fs::path & table_directory, ContextPtr local_context) | ||||
PartitionCommandsResultInfo Unfreezer::unfreezePartitionsFromTableDirectory(MergeTreeData::MatcherFn matcher, const String & backup_name, const Disks & disks, const fs::path & table_directory, std::optional<MergeTreeDataFormatVersion> format_version) | ||||
{ | ||||
PartitionCommandsResultInfo result; | ||||
|
||||
|
@@ -168,8 +184,13 @@ PartitionCommandsResultInfo Unfreezer::unfreezePartitionsFromTableDirectory(Merg | |||
for (auto it = disk->iterateDirectory(table_directory); it->isValid(); it->next()) | ||||
{ | ||||
const auto & partition_directory = it->name(); | ||||
|
||||
int count_underscores = std::count_if(partition_directory.begin(), partition_directory.end(), []( char c ){return c =='_';}); | ||||
if ((format_version.has_value() && format_version == 0) || count_underscores == 4) { | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Number of underscores can be 4 with new syntax as well (if mutation version is not 0) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also we don't need to parse partition_id at all if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand. Parsing partition_id is needed in order to output it. If I remove check with underscores,
So not checking this leads to different behaviour of these queries. Am I right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I missed that SYSTEM UNFREEZE prints partition ids in query result, I though ids are needed only for filtering. So currently it prints invalid ids if table was created with old syntax. It's not clear how to fix it, I see the following options:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, I think not returning list is easier and for sure possible. But still what about deleting data? Different data will be deleted for alter and system queries if not doing autodetect There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because if format_version is 0, there will be an exception and since then the partition will not be removed with ALTER query. But as soon as there is no exception when called from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's ok, SYSTEM UNFREEZE will simply remove everything, because it's impossible to distinguish partitions in this case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, it seems like throwing an exception may break some user queries since this logic was introduced for alter unfreeze. May it? Is it critical?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would rather mark it as TODO and / or create a new issue about this. If I touch this code, the only solution that doesn't break any query I see is to autodetect format version. Otherwise, it may break query and behave differently for alter and system queries. I would create a separate issue for autodetection as soon as it is not about just SYSTEM UNFREEZE feature. It is about fixing small bug for both ALTER UNFREEZE and SYSTEM UNFREEZE which may introduce bigger bug There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||
throw Exception(ErrorCodes::INVALID_PARTITION_VALUE, "Can not complete unfreeze query because part directory name is obsolete: " + partition_directory); | ||||
tavplubix marked this conversation as resolved.
Show resolved
Hide resolved
|
||||
} | ||||
|
||||
/// Partition ID is prefix of part directory name: <partition id>_<rest of part directory name> | ||||
/// For format_version == 1, partition ID is prefix of part directory name: <partition id>_<rest of part directory name> | ||||
auto found = partition_directory.find('_'); | ||||
if (found == std::string::npos) | ||||
continue; | ||||
|
@@ -180,7 +201,7 @@ PartitionCommandsResultInfo Unfreezer::unfreezePartitionsFromTableDirectory(Merg | |||
|
||||
const auto & path = it->path(); | ||||
|
||||
bool keep_shared = removeFreezedPart(disk, path, partition_directory, local_context); | ||||
bool keep_shared = removeFreezedPart(disk, path, partition_directory, local_context_, zookeeper_); | ||||
|
||||
result.push_back(PartitionCommandResultInfo{ | ||||
.partition_id = partition_id, | ||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer to completely remove these flags without changing version. It will break compatibility with
22.6.1
, but I think it's not a problem, because22.6
was just released recently and I doubt that someone has already started to use22.6.1
with this freeze-metadata-stuff in production. And previous serialization format was invalid anyway (I mean issues with escaping).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The thing is, flags were introduced before this PR. See history of removed code for details