Skip to content
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

Improve concurrent parts removal with zero copy replication #49630

Merged
merged 5 commits into from May 17, 2023

Conversation

tavplubix
Copy link
Member

Changelog category (leave one):

  • Improvement

Changelog entry (a user-readable short description of the changes that goes to CHANGELOG.md):

More parallelism on Outdated parts removal with "zero-copy replication"

It should avoid cases like this (https://pastila.nl/?7fff376d/54299199b5aba5ed8c7a293d8ceb4b87), when some threads remove just a few parts and one thread removes 5000 parts

@robot-clickhouse robot-clickhouse added the pr-improvement Pull request with some product improvements label May 7, 2023
@robot-clickhouse
Copy link
Member

robot-clickhouse commented May 7, 2023

This is an automated comment for commit 9a824a0 with description of existing statuses. It's updated for the latest CI running
The full report is available here
The overall status of the commit is 🟡 pending

Check nameDescriptionStatus
CI runningA meta-check that indicates the running CI. Normally, it's in success or pending state. The failed status indicates some problems with the PR🟡 pending
Mergeable CheckChecks if all other necessary checks are successful🟢 success
Push to DockerhubThe check for building and pushing the CI related docker images to docker hub🟢 success

@tavplubix tavplubix force-pushed the improve_concurrent_parts_removal branch from 5a480a8 to 1b8b509 Compare May 7, 2023 18:00
@alesapin alesapin self-assigned this May 7, 2023
@tavplubix tavplubix force-pushed the improve_concurrent_parts_removal branch from 1b8b509 to 2a68bef Compare May 7, 2023 22:30
Copy link
Member

@alesapin alesapin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH I don't think it's simpler than layered scheme that we discussed yesterday....

size_t num_threads = std::min<size_t>(settings->max_part_removal_threads, parts_to_remove.size());
size_t num_threads = settings->max_part_removal_threads;
if (!num_threads)
num_threads = getNumberOfPhysicalCPUCores() * 2;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH I don't understand why size of this pure IO-thread pool depend on number of cores.... But maybe it make sense.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's for max_part_removal_threads = 0 which means "auto", and it's default value. Previously it was just getNumberOfPhysicalCPUCores() and also min(threads, parts) did not work correctly when thread = 0. We can change the default value to, for example, 32 or 128, and throw an exception if it's set to 0.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, agree

std::vector<UInt64> split_level;
};

auto split_into_independent_ranges = [this](const DataPartsVector & parts_to_remove_, size_t split_level = 0) -> RemovalRanges
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please, let's make split_level without default value.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually it's not level (collision with term merge level). It's just how many times we tried to split source range? (split_times?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a kind of "reversed level", each time we increase it, we get parts with lower merge levels. Okay, let's rename

std::move(subranges.split_level.begin(), subranges.split_level.end(), std::back_inserter(independent_ranges.split_level));
num_ranges += subranges.infos.size();
total_excluded += top_level_count;
continue;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What exactly is redundant and why?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just last line of loop, looks like we will continue without this continue :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not the last line

};

size_t top_level_count = std::count_if(parts_in_range.begin(), parts_in_range.end(), top_level_parts_pred);
if (settings->zero_copy_concurrent_part_removal_max_postpone_ratio < static_cast<Float32>(top_level_count) / parts_in_range.size())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition is not obvious. So if we have range with 100 parts where 1 is covering and 99 is covered (merge from [0, ..., 99] to [0_99]) it will be true (while it should be false, isn't it?). But I'm not sure about right condition here...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why should it be false? The purpose of this condition is to limit excluded parts percentage. For example, if we have 100 parts and 50 of them are top-level (it's possible when we have long mutations chain or repeated merges of the same blocks range), then excluding 50 parts probably will make it worse

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

@tavplubix
Copy link
Member Author

TBH I don't think it's simpler than layered scheme that we discussed yesterday....

I doubt layered scheme will work well with mutations, and after #49619 we need it for mutations mostly. And it's still simpler than combined scheme

size_t num_threads = std::min<size_t>(settings->max_part_removal_threads, parts_to_remove.size());
size_t num_threads = settings->max_part_removal_threads;
if (!num_threads)
num_threads = getNumberOfPhysicalCPUCores() * 2;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, agree

@tavplubix tavplubix added the do not test disable testing on pull request label May 11, 2023
@tavplubix tavplubix force-pushed the improve_concurrent_parts_removal branch from 3921f79 to 7298652 Compare May 15, 2023 23:05
@tavplubix tavplubix removed the do not test disable testing on pull request label May 15, 2023
Copy link
Member

@alesapin alesapin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, but failures related to changes.

@tavplubix
Copy link
Member Author

Integration tests (asan) [3/6] - #48726
Unit tests (asan) - IOResourceDynamicResourceManager was broken in master
Upgrade check (tsan) - we don't have diagnostics for OOMs in prev release

@tavplubix tavplubix merged commit 36c31e1 into master May 17, 2023
9 of 11 checks passed
@tavplubix tavplubix deleted the improve_concurrent_parts_removal branch May 17, 2023 11:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants