-
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
Improve performance of SELECTs with active mutations #59531
Conversation
This is an automated comment for commit d78f760 with description of existing statuses. It's updated for the latest CI running ❌ Click here to open a full report in a separate page Successful checks
|
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.
Looks reasonable. Please fix the perf test: https://s3.amazonaws.com/clickhouse-test-reports/59531/3d73043c12623fd746f2888e21d50b6144af00a2/performance_comparison_[3_4]/report.html
Other test failures look unrelated:
@@ -1378,7 +1378,7 @@ class MergeTreeData : public IStorage, public WithMutableContext | |||
/// Used to receive AlterConversions for part and apply them on fly. This | |||
/// method has different implementations for replicated and non replicated | |||
/// MergeTree because they store mutations in different way. | |||
virtual std::map<int64_t, MutationCommands> getAlterMutationCommandsForPart(const DataPartPtr & part) const = 0; | |||
virtual std::vector<MutationCommands> getAlterMutationCommandsForPart(const DataPartPtr & part) const = 0; |
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.
Consider adding a comment about whether the returned vector is sorted. Looks like it isn't, and the caller doesn't care about the order? Also mention that only RENAME_COLUMN are included.
(I'm confused by this. I would've expected the order to matter: if a column was renamed twice A -> B -> C then it wouldn't work if we tried to do B -> C before A -> B. But I see that AlterConversions doesn't handle that. I guess such chain-rename is just not handled correctly here? It's also weird that getAlterMutationCommandsForPart()
doesn't take a metadata snapshot, and instead returns commands to promote the part all the way to the latest schema; I guess that's also incorrect? If it used a metadata snapshot, maybe the whole thing would be correct because at most one ALTER can be in progress at a time, and dependent renames are not allowed within one ALTER. This is all just speculation, I'm not familiar with this code.)
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.
Also mention that only RENAME_COLUMN are included.
Ok, I've added the comment.
I would've expected the order to matter: if a column was renamed twice A -> B -> C then it wouldn't work if we tried to do B -> C before A -> B. But I see that AlterConversions doesn't handle that.
It don't need to, since only one ALTER_METADATA can be executed concurrently, so if you have two ALTER that modifies metadata the first one should be finished completely, only after that the second can proceed -
if (!alter_sequence.canExecuteMetaAlter(entry.alter_version, state_lock)) |
It's also weird that getAlterMutationCommandsForPart() doesn't take a metadata snapshot, and instead returns commands to promote the part all the way to the latest schema; I guess that's also incorrect? If it used a metadata snapshot, maybe the whole thing would be correct because at most one ALTER can be in progress at a time, and dependent renames are not allowed within one ALTER
This code is required to handle RENAME COLUMN that had not been finished yet, while the metadata had been already updated, so when query contains new names it can transform new names to old ones, to find this data on the disk. Metadata snapshot does not have information about mutations so it cannot be used here.
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.
Does anything prevent the following scenario?:
- A SELECT calls
StorageMergeTree::read()
. StorageMergeTree::read()
grabs the current list of active parts. Say, it has one part: all_1_1_1, with one column A.ALTER RENAME COLUMN A TO B
starts and finishes.ALTER RENAME COLUMN B TO C
starts and finishes.- The SELECT from step 1 calls getAlterMutationCommandsForPart() for part all_1_1_1.
- getAlterMutationCommandsForPart() returns both renames: A->B, B->C.
- AlterConversions gets confused (with or without this PR), and the SELECT fails.
- Even if AlterConversions could handle A->B->C correctly, this SELECT would still fail because it expects data to match the schema from the metadata snapshot obtained in step 1, so column C would be unexpected.
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.
Non-mutated part (all_1_1_1
) should not have any mutations (i.e. getAlterConversionsForPart
should return nothing), so it should be OK.
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.
getAlterConversionsForPart()
-> getAlterMutationCommandsForPart()
take all mutations that happened in the table (current_mutations_by_version
) after this part was created (part_data_version
). For all_1_1_1 that would be both of the renames. On SELECT path, getAlterConversionsForPart()
is called from MergeTreeData::getStorageSnapshot()
.
Reproduced this scenario by adding a sleep in the middle of MergeTreeData::getStorageSnapshot()
and doing ALTER while SELECT is stuck. The SELECT fails even with one ALTER, presumably because the metadata snapshot expects the old column name, while AlterConversions produce the new name:
:) desc (select * from a); select * from a;
DESCRIBE TABLE
(
SELECT *
FROM a
)
Query id: 0af8ba13-838c-4a21-9337-c0a1c2800636
┌─name─┬─type──┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ k │ Int64 │ │ │ │ │ │
│ x │ Int64 │ │ │ │ │ │
└──────┴───────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
2 rows in set. Elapsed: 0.001 sec.
SELECT *
FROM a
Query id: b42a9aca-0d37-43bc-92f8-2c001efd203f
Elapsed: 10.014 sec.
Received exception from server (version 24.2.1):
Code: 47. DB::Exception: Received from localhost:9000. DB::Exception: Missing columns: 'z' while processing query: 'SELECT k, z FROM a', required columns: 'k' 'z', maybe you meant: 'k' or 'z'. (UNKNOWN_IDENTIFIER)
(Also directly confirmed that the getAlterMutationCommandsForPart()
returns 2 commands in this case, if there were 2 ALTERs.)
So I guess my understanding was correct.
This is all mostly unrelated to this PR, I just wanted to figure out if this code is as broken as it looks.
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.
Oh, yes, you are right, actually my comment was wrong it should be the opposite all_1_1_1 should have mutations, so yes, the SELECT will fail.
I guess this can be fixed by looking at which metadata version had bee used for the SELECT, but this is completely different story...
9531690
to
1253a73
Compare
Timeouts:
|
Oops, turns out the cloud version of CH actually implements other mutation types in AlterConversions, not just RENAME_COLUMN (https://clickhouse.com/docs/en/guides/developer/lightweight-update). I pushed a commit to this PR that adds |
Thanks, LGTM! Though clang-tidy fails:
|
Huh, that warning makes absolutely no sense to me. Shuffled the code a little to work around it, guess it's a clang issue. |
Signed-off-by: Azat Khuzhin <a.khuzhin@semrush.com>
getAlterMutationCommandsForPart() can be a hot path for query execution when there are pending mutations. - LOG_TEST - it is not only check one bool, but actually a bunch of atomics as well. - Return std::vector over std::map (map is not required there) - no changes in performance. - Copy only RENAME_COLUMN (since only this mutation is required by AlterConversions). And here are results: run|result -|- SELECT w/o ALTER|queries: 1565, QPS: 355.259, RPS: 355.259 SELECT w/ ALTER unpatched|queries: 2099, QPS: 220.623, RPS: 220.623 SELECT w/ ALTER and w/o LOG_TEST|queries: 2730, QPS: 235.859, RPS: 235.859 SELECT w/ ALTER and w/o LOG_TEST and w/ RENAME_COLUMN only|queries: 2995, QPS: 290.982, RPS: 290.982 But there are still room for improvements, at least MergeTree engines could implement getStorageSnapshotForQuery(). Signed-off-by: Azat Khuzhin <a.khuzhin@semrush.com>
…ector<MutationCommand>>
bfa84f0
to
d78f760
Compare
(cherry picked from commit 666208f)
* [GLUTEN-1632][CH]Daily Update Clickhouse Version (20240225) * Add custom target libch to avoid name conflict(due to ClickHouse/ClickHouse#58609) (cherry picked from commit 0f6281d) (cherry picked from commit 46c4f8f) * Revert workaround for fixing bug introduced by ClickHouse/ClickHouse#58886 since ClickHouse/ClickHouse#59911 reverts #58886 (cherry picked from commit 7708bb3) (cherry picked from commit 4da4f72) * Fix array_max and array_min due to ClickHouse/ClickHouse#60188 (cherry picked from commit e576f70) (cherry picked from commit d032fde) * Fix build CustomStorageMergeTree.h due to ClickHouse/ClickHouse#59531 (cherry picked from commit 666208f) (cherry picked from commit 60f3415) (cherry picked from commit f073c5b) * Fix build CustomStorageMergeTree.cpp due to ClickHouse/ClickHouse#60159 (cherry picked from commit 7cf074a) (cherry picked from commit a797ba7) * Fix ActionDAG bug introduced by ClickHouse/ClickHouse#58554 (cherry picked from commit 3432fda) (cherry picked from commit 066271703498828fa98b31d75bc2dbc27967f78b) (cherry picked from commit 8d78221) (cherry picked from commit 3d34c62) --------- Co-authored-by: kyligence-git <gluten@kyligence.io> Co-authored-by: Chang Chen <baibaichen@gmail.com>
Changelog category (leave one):
Changelog entry (a user-readable short description of the changes that goes to CHANGELOG.md):
Improve performance of SELECTs with active mutations
getAlterMutationCommandsForPart() can be a hot path for query execution when there are pending mutations.
LOG_TEST - it is not only check one bool, but actually a bunch of atomics as well.
Return std::vector over std::map (map is not required there) - no changes in performance.
Copy only RENAME_COLUMN (since only this mutation is required by AlterConversions).
And here are results:
But there are still room for improvements, at least MergeTree engines could implement getStorageSnapshotForQuery().