Skip to content

Commit ee9f1c8

Browse files
authored
Merge pull request #729 from Altinity/backports/24.3/77766_stop_merges_without_written_blocks
24.3 Backport of ClickHouse#77766 and ClickHouse#59138 - Stop merges without written blocks / Fix REPLACE PARTITION waiting for mutations/merges on unrelated partitions
2 parents b03ca1b + cdf9c31 commit ee9f1c8

16 files changed

+796
-66
lines changed

src/Common/ActionBlocker.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class ActionBlocker
1717

1818
bool isCancelled() const { return *counter > 0; }
1919

20-
/// Temporarily blocks corresponding actions (while the returned object is alive)
20+
/// Temporary blocks corresponding actions (while the returned object is alive)
2121
friend class ActionLock;
2222
ActionLock cancel() { return ActionLock(*this); }
2323

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#include <gtest/gtest.h>
2+
3+
#include <chrono>
4+
#include <mutex>
5+
#include <stdexcept>
6+
#include <string_view>
7+
#include <vector>
8+
#include <thread>
9+
10+
#include <Common/ActionBlocker.h>
11+
#include <Common/ActionLock.h>
12+
13+
using namespace DB;
14+
15+
TEST(ActionBlocker, TestDefaultConstructor)
16+
{
17+
ActionBlocker blocker;
18+
19+
EXPECT_FALSE(blocker.isCancelled());
20+
EXPECT_EQ(0, blocker.getCounter().load());
21+
}
22+
23+
TEST(ActionBlocker, TestCancelForever)
24+
{
25+
ActionBlocker blocker;
26+
27+
blocker.cancelForever();
28+
EXPECT_TRUE(blocker.isCancelled());
29+
EXPECT_EQ(1, blocker.getCounter().load());
30+
}
31+
32+
TEST(ActionBlocker, TestCancel)
33+
{
34+
ActionBlocker blocker;
35+
36+
{
37+
auto lock = blocker.cancel();
38+
EXPECT_TRUE(blocker.isCancelled());
39+
EXPECT_EQ(1, blocker.getCounter().load());
40+
}
41+
// automatically un-cancelled on `lock` destruction
42+
EXPECT_FALSE(blocker.isCancelled());
43+
}
44+
45+
46+
47+
TEST(ActionLock, TestDefaultConstructor)
48+
{
49+
ActionLock locker;
50+
EXPECT_TRUE(locker.expired());
51+
}
52+
53+
TEST(ActionLock, TestConstructorWithActionBlocker)
54+
{
55+
ActionBlocker blocker;
56+
ActionLock lock(blocker);
57+
58+
EXPECT_FALSE(lock.expired());
59+
EXPECT_TRUE(blocker.isCancelled());
60+
EXPECT_EQ(1, blocker.getCounter().load());
61+
}
62+
63+
TEST(ActionLock, TestMoveAssignmentToEmpty)
64+
{
65+
ActionBlocker blocker;
66+
67+
{
68+
ActionLock lock;
69+
lock = blocker.cancel();
70+
EXPECT_TRUE(blocker.isCancelled());
71+
}
72+
// automatically un-cancelled on `lock` destruction
73+
EXPECT_FALSE(blocker.isCancelled());
74+
EXPECT_EQ(0, blocker.getCounter().load());
75+
}
76+
77+
TEST(ActionLock, TestMoveAssignmentToNonEmpty)
78+
{
79+
ActionBlocker blocker;
80+
{
81+
auto lock = blocker.cancel();
82+
EXPECT_TRUE(blocker.isCancelled());
83+
84+
// cause a move
85+
lock = blocker.cancel();
86+
87+
// blocker should remain locked
88+
EXPECT_TRUE(blocker.isCancelled());
89+
}
90+
// automatically un-cancelled on `lock` destruction
91+
EXPECT_FALSE(blocker.isCancelled());
92+
EXPECT_EQ(0, blocker.getCounter().load());
93+
}
94+
95+
TEST(ActionLock, TestMoveAssignmentToNonEmpty2)
96+
{
97+
ActionBlocker blocker1;
98+
ActionBlocker blocker2;
99+
{
100+
auto lock = blocker1.cancel();
101+
EXPECT_TRUE(blocker1.isCancelled());
102+
103+
// cause a move
104+
lock = blocker2.cancel();
105+
106+
// blocker2 be remain locked, blocker1 - unlocked
107+
EXPECT_TRUE(blocker2.isCancelled());
108+
109+
EXPECT_FALSE(blocker1.isCancelled());
110+
}
111+
// automatically un-cancelled on `lock` destruction
112+
EXPECT_FALSE(blocker1.isCancelled());
113+
EXPECT_FALSE(blocker2.isCancelled());
114+
EXPECT_EQ(0, blocker1.getCounter().load());
115+
EXPECT_EQ(0, blocker2.getCounter().load());
116+
}
117+
118+
TEST(ActionLock, TestExpiration)
119+
{
120+
ActionLock lock;
121+
{
122+
ActionBlocker blocker;
123+
lock = blocker.cancel();
124+
EXPECT_FALSE(lock.expired());
125+
}
126+
127+
EXPECT_TRUE(lock.expired());
128+
}

src/Storages/MergeTree/DataPartsExchange.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <IO/copyData.h>
99
#include <IO/ConnectionTimeouts.h>
1010
#include <Common/Throttler.h>
11+
#include <Common/ActionBlocker.h>
1112

1213

1314
namespace zkutil

src/Storages/MergeTree/MergeProgress.h

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
#pragma once
22

3-
#include <base/types.h>
4-
#include <Common/ProfileEvents.h>
3+
#include <functional>
54
#include <IO/Progress.h>
65
#include <Storages/MergeTree/MergeList.h>
6+
#include <base/types.h>
77

88

99
namespace ProfileEvents
@@ -47,27 +47,23 @@ struct MergeStageProgress
4747
class MergeProgressCallback
4848
{
4949
public:
50+
// It should throw an exception in case the operation should be cancelled
51+
using CancellationChecker = std::function<void()>;
52+
5053
MergeProgressCallback(
51-
MergeListElement * merge_list_element_ptr_, UInt64 & watch_prev_elapsed_, MergeStageProgress & stage_)
54+
MergeListElement * merge_list_element_ptr_,
55+
UInt64 & watch_prev_elapsed_,
56+
MergeStageProgress & stage_,
57+
CancellationChecker && cancellation_checker_)
5258
: merge_list_element_ptr(merge_list_element_ptr_)
5359
, watch_prev_elapsed(watch_prev_elapsed_)
5460
, stage(stage_)
61+
, cancellation_checker(std::move(cancellation_checker_))
5562
{
5663
updateWatch();
5764
}
5865

59-
MergeListElement * merge_list_element_ptr;
60-
UInt64 & watch_prev_elapsed;
61-
MergeStageProgress & stage;
62-
63-
void updateWatch()
64-
{
65-
UInt64 watch_curr_elapsed = merge_list_element_ptr->watch.elapsed();
66-
ProfileEvents::increment(ProfileEvents::MergesTimeMilliseconds, (watch_curr_elapsed - watch_prev_elapsed) / 1000000);
67-
watch_prev_elapsed = watch_curr_elapsed;
68-
}
69-
70-
void operator() (const Progress & value)
66+
void operator()(const Progress & value)
7167
{
7268
ProfileEvents::increment(ProfileEvents::MergedUncompressedBytes, value.read_bytes);
7369
if (stage.is_first)
@@ -77,6 +73,8 @@ class MergeProgressCallback
7773
}
7874
updateWatch();
7975

76+
cancellation_checker();
77+
8078
merge_list_element_ptr->bytes_read_uncompressed += value.read_bytes;
8179
if (stage.is_first)
8280
merge_list_element_ptr->rows_read += value.read_rows;
@@ -90,6 +88,19 @@ class MergeProgressCallback
9088
std::memory_order_relaxed);
9189
}
9290
}
91+
92+
private:
93+
MergeListElement * merge_list_element_ptr;
94+
UInt64 & watch_prev_elapsed;
95+
MergeStageProgress & stage;
96+
CancellationChecker cancellation_checker;
97+
98+
void updateWatch()
99+
{
100+
UInt64 watch_curr_elapsed = merge_list_element_ptr->watch.elapsed();
101+
watch_prev_elapsed = watch_curr_elapsed;
102+
}
103+
93104
};
94105

95106
}

src/Storages/MergeTree/MergeTask.cpp

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,19 @@ static void addMissedColumnsToSerializationInfos(
128128
}
129129
}
130130

131+
bool MergeTask::GlobalRuntimeContext::isCancelled() const
132+
{
133+
return (future_part ? merges_blocker->isCancelledForPartition(future_part->part_info.partition_id) : merges_blocker->isCancelled())
134+
|| merge_list_element_ptr->is_cancelled.load(std::memory_order_relaxed);
135+
}
136+
137+
void MergeTask::GlobalRuntimeContext::checkOperationIsNotCanceled() const
138+
{
139+
if (isCancelled())
140+
{
141+
throw Exception(ErrorCodes::ABORTED, "Cancelled merging parts");
142+
}
143+
}
131144

132145
bool MergeTask::ExecuteAndFinalizeHorizontalPart::prepare()
133146
{
@@ -140,8 +153,7 @@ bool MergeTask::ExecuteAndFinalizeHorizontalPart::prepare()
140153
}
141154
const String local_tmp_suffix = global_ctx->parent_part ? ctx->suffix : "";
142155

143-
if (global_ctx->merges_blocker->isCancelled() || global_ctx->merge_list_element_ptr->is_cancelled.load(std::memory_order_relaxed))
144-
throw Exception(ErrorCodes::ABORTED, "Cancelled merging parts");
156+
global_ctx->checkOperationIsNotCanceled();
145157

146158
/// We don't want to perform merge assigned with TTL as normal merge, so
147159
/// throw exception
@@ -391,9 +403,10 @@ bool MergeTask::ExecuteAndFinalizeHorizontalPart::prepare()
391403
ctx->is_cancelled = [merges_blocker = global_ctx->merges_blocker,
392404
ttl_merges_blocker = global_ctx->ttl_merges_blocker,
393405
need_remove = ctx->need_remove_expired_values,
394-
merge_list_element = global_ctx->merge_list_element_ptr]() -> bool
406+
merge_list_element = global_ctx->merge_list_element_ptr,
407+
partition_id = global_ctx->future_part->part_info.partition_id]() -> bool
395408
{
396-
return merges_blocker->isCancelled()
409+
return merges_blocker->isCancelledForPartition(partition_id)
397410
|| (need_remove && ttl_merges_blocker->isCancelled())
398411
|| merge_list_element->is_cancelled.load(std::memory_order_relaxed);
399412
};
@@ -478,8 +491,7 @@ bool MergeTask::ExecuteAndFinalizeHorizontalPart::executeImpl()
478491
global_ctx->merging_executor.reset();
479492
global_ctx->merged_pipeline.reset();
480493

481-
if (global_ctx->merges_blocker->isCancelled() || global_ctx->merge_list_element_ptr->is_cancelled.load(std::memory_order_relaxed))
482-
throw Exception(ErrorCodes::ABORTED, "Cancelled merging parts");
494+
global_ctx->checkOperationIsNotCanceled();
483495

484496
if (ctx->need_remove_expired_values && global_ctx->ttl_merges_blocker->isCancelled())
485497
throw Exception(ErrorCodes::ABORTED, "Cancelled merging parts with expired TTL");
@@ -607,7 +619,8 @@ void MergeTask::VerticalMergeStage::prepareVerticalMergeForOneColumn() const
607619
ctx->column_parts_pipeline.setProgressCallback(MergeProgressCallback(
608620
global_ctx->merge_list_element_ptr,
609621
global_ctx->watch_prev_elapsed,
610-
*global_ctx->column_progress));
622+
*global_ctx->column_progress,
623+
[&my_ctx = *global_ctx]() { my_ctx.checkOperationIsNotCanceled(); }));
611624

612625
/// Is calculated inside MergeProgressCallback.
613626
ctx->column_parts_pipeline.disableProfileEventUpdate();
@@ -634,8 +647,7 @@ void MergeTask::VerticalMergeStage::prepareVerticalMergeForOneColumn() const
634647
bool MergeTask::VerticalMergeStage::executeVerticalMergeForOneColumn() const
635648
{
636649
Block block;
637-
if (!global_ctx->merges_blocker->isCancelled() && !global_ctx->merge_list_element_ptr->is_cancelled.load(std::memory_order_relaxed)
638-
&& ctx->executor->pull(block))
650+
if (!global_ctx->isCancelled() && ctx->executor->pull(block))
639651
{
640652
ctx->column_elems_written += block.rows();
641653
ctx->column_to->write(block);
@@ -650,8 +662,7 @@ bool MergeTask::VerticalMergeStage::executeVerticalMergeForOneColumn() const
650662
void MergeTask::VerticalMergeStage::finalizeVerticalMergeForOneColumn() const
651663
{
652664
const String & column_name = ctx->it_name_and_type->name;
653-
if (global_ctx->merges_blocker->isCancelled() || global_ctx->merge_list_element_ptr->is_cancelled.load(std::memory_order_relaxed))
654-
throw Exception(ErrorCodes::ABORTED, "Cancelled merging parts");
665+
global_ctx->checkOperationIsNotCanceled();
655666

656667
ctx->executor.reset();
657668
auto changed_checksums = ctx->column_to->fillChecksums(global_ctx->new_data_part, global_ctx->checksums_gathered_columns);
@@ -1120,7 +1131,11 @@ void MergeTask::ExecuteAndFinalizeHorizontalPart::createMergedStream()
11201131

11211132
global_ctx->merged_pipeline = QueryPipelineBuilder::getPipeline(std::move(*builder));
11221133
/// Dereference unique_ptr and pass horizontal_stage_progress by reference
1123-
global_ctx->merged_pipeline.setProgressCallback(MergeProgressCallback(global_ctx->merge_list_element_ptr, global_ctx->watch_prev_elapsed, *global_ctx->horizontal_stage_progress));
1134+
global_ctx->merged_pipeline.setProgressCallback(MergeProgressCallback(
1135+
global_ctx->merge_list_element_ptr,
1136+
global_ctx->watch_prev_elapsed,
1137+
*global_ctx->horizontal_stage_progress,
1138+
[&my_ctx = *global_ctx]() { my_ctx.checkOperationIsNotCanceled(); }));
11241139
/// Is calculated inside MergeProgressCallback.
11251140
global_ctx->merged_pipeline.disableProfileEventUpdate();
11261141

src/Storages/MergeTree/MergeTask.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
#include <Storages/MergeTree/MergedColumnOnlyOutputStream.h>
2525
#include <Storages/MergeTree/MergeProgress.h>
2626
#include <Storages/MergeTree/MergeTreeData.h>
27+
#include <Storages/MergeTree/MergeTreeIndices.h>
28+
#include <Storages/MergeTree/PartitionActionBlocker.h>
2729

2830

2931
namespace DB
@@ -75,7 +77,7 @@ class MergeTask
7577
MergeTreeTransactionPtr txn,
7678
MergeTreeData * data_,
7779
MergeTreeDataMergerMutator * mutator_,
78-
ActionBlocker * merges_blocker_,
80+
PartitionActionBlocker * merges_blocker_,
7981
ActionBlocker * ttl_merges_blocker_)
8082
{
8183
global_ctx = std::make_shared<GlobalRuntimeContext>();
@@ -148,7 +150,7 @@ class MergeTask
148150
MergeListElement * merge_list_element_ptr{nullptr};
149151
MergeTreeData * data{nullptr};
150152
MergeTreeDataMergerMutator * mutator{nullptr};
151-
ActionBlocker * merges_blocker{nullptr};
153+
PartitionActionBlocker * merges_blocker{nullptr};
152154
ActionBlocker * ttl_merges_blocker{nullptr};
153155
StorageSnapshotPtr storage_snapshot{nullptr};
154156
StorageMetadataPtr metadata_snapshot{nullptr};
@@ -195,6 +197,10 @@ class MergeTask
195197
bool need_prefix;
196198

197199
scope_guard temporary_directory_lock;
200+
201+
// will throw an exception if merge was cancelled in any way.
202+
void checkOperationIsNotCanceled() const;
203+
bool isCancelled() const;
198204
};
199205

200206
using GlobalRuntimeContextPtr = std::shared_ptr<GlobalRuntimeContext>;

src/Storages/MergeTree/MergeTreeDataMergerMutator.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ public :
209209
/** Is used to cancel all merges and mutations. On cancel() call all currently running actions will throw exception soon.
210210
* All new attempts to start a merge or mutation will throw an exception until all 'LockHolder' objects will be destroyed.
211211
*/
212-
ActionBlocker merges_blocker;
212+
PartitionActionBlocker merges_blocker;
213213
ActionBlocker ttl_merges_blocker;
214214

215215
private:

0 commit comments

Comments
 (0)