Skip to content

Commit c0bd212

Browse files
authored
Merge pull request ClickHouse#66519 from XuJia0210/feature_disable_insertion_and_mutation
Feature disable insertion and mutation
2 parents 765d028 + 48e7708 commit c0bd212

10 files changed

Lines changed: 122 additions & 0 deletions

File tree

docs/en/operations/settings/settings.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5608,3 +5608,9 @@ Default value: `10000000`.
56085608
Minimal size of block to compress in CROSS JOIN. Zero value means - disable this threshold. This block is compressed when any of the two thresholds (by rows or by bytes) are reached.
56095609

56105610
Default value: `1GiB`.
5611+
5612+
## disable_insertion_and_mutation
5613+
5614+
Disable all insert and mutations (alter table update / alter table delete / alter table drop partition). Set to true, can make this node focus on reading queries.
5615+
5616+
Default value: `false`.

src/Core/ServerSettings.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ namespace DB
166166
M(Bool, prepare_system_log_tables_on_startup, false, "If true, ClickHouse creates all configured `system.*_log` tables before the startup. It can be helpful if some startup scripts depend on these tables.", 0) \
167167
M(Double, gwp_asan_force_sample_probability, 0.0003, "Probability that an allocation from specific places will be sampled by GWP Asan (i.e. PODArray allocations)", 0) \
168168
M(UInt64, config_reload_interval_ms, 2000, "How often clickhouse will reload config and check for new changes", 0) \
169+
M(Bool, disable_insertion_and_mutation, false, "Disable all insert/alter/delete queries. This setting will be enabled if someone needs read-only nodes to prevent insertion and mutation affect reading performance.", 0)
169170

170171
/// If you add a setting which can be updated at runtime, please update 'changeable_settings' map in StorageSystemServerSettings.cpp
171172

src/Interpreters/InterpreterAlterQuery.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <Access/Common/AccessRightsElement.h>
66
#include <Common/typeid_cast.h>
77
#include <Core/Settings.h>
8+
#include <Core/ServerSettings.h>
89
#include <Databases/DatabaseFactory.h>
910
#include <Databases/DatabaseReplicated.h>
1011
#include <Databases/IDatabase.h>
@@ -47,6 +48,7 @@ namespace ErrorCodes
4748
extern const int BAD_ARGUMENTS;
4849
extern const int UNKNOWN_TABLE;
4950
extern const int UNKNOWN_DATABASE;
51+
extern const int QUERY_IS_PROHIBITED;
5052
}
5153

5254

@@ -191,6 +193,12 @@ BlockIO InterpreterAlterQuery::executeToTable(const ASTAlterQuery & alter)
191193
"to execute ALTERs of different types (replicated and non replicated) in single query");
192194
}
193195

196+
if (mutation_commands.hasNonEmptyMutationCommands() || !partition_commands.empty())
197+
{
198+
if (getContext()->getServerSettings().disable_insertion_and_mutation)
199+
throw Exception(ErrorCodes::QUERY_IS_PROHIBITED, "Mutations are prohibited");
200+
}
201+
194202
if (!alter_commands.empty())
195203
{
196204
auto alter_lock = table->lockForAlter(getContext()->getSettingsRef().lock_acquire_timeout);

src/Interpreters/InterpreterDeleteQuery.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include <Access/ContextAccess.h>
55
#include <Core/Settings.h>
6+
#include <Core/ServerSettings.h>
67
#include <Databases/DatabaseReplicated.h>
78
#include <Databases/IDatabase.h>
89
#include <Interpreters/Context.h>
@@ -27,6 +28,7 @@ namespace ErrorCodes
2728
extern const int SUPPORT_IS_DISABLED;
2829
extern const int BAD_ARGUMENTS;
2930
extern const int NOT_IMPLEMENTED;
31+
extern const int QUERY_IS_PROHIBITED;
3032
}
3133

3234

@@ -51,6 +53,9 @@ BlockIO InterpreterDeleteQuery::execute()
5153
if (table->isStaticStorage())
5254
throw Exception(ErrorCodes::TABLE_IS_READ_ONLY, "Table is read-only");
5355

56+
if (getContext()->getGlobalContext()->getServerSettings().disable_insertion_and_mutation)
57+
throw Exception(ErrorCodes::QUERY_IS_PROHIBITED, "Delete queries are prohibited");
58+
5459
DatabasePtr database = DatabaseCatalog::instance().getDatabase(table_id.database_name);
5560
if (database->shouldReplicateQuery(getContext(), query_ptr))
5661
{

src/Interpreters/InterpreterInsertQuery.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <AggregateFunctions/AggregateFunctionFactory.h>
77
#include <Columns/ColumnNullable.h>
88
#include <Core/Settings.h>
9+
#include <Core/ServerSettings.h>
910
#include <Processors/Transforms/buildPushingToViewsChain.h>
1011
#include <DataTypes/DataTypeNullable.h>
1112
#include <Interpreters/DatabaseCatalog.h>
@@ -60,6 +61,7 @@ namespace ErrorCodes
6061
extern const int NO_SUCH_COLUMN_IN_TABLE;
6162
extern const int ILLEGAL_COLUMN;
6263
extern const int DUPLICATE_COLUMN;
64+
extern const int QUERY_IS_PROHIBITED;
6365
}
6466

6567
InterpreterInsertQuery::InterpreterInsertQuery(
@@ -732,6 +734,9 @@ BlockIO InterpreterInsertQuery::execute()
732734
const Settings & settings = getContext()->getSettingsRef();
733735
auto & query = query_ptr->as<ASTInsertQuery &>();
734736

737+
if (getContext()->getServerSettings().disable_insertion_and_mutation
738+
&& query.table_id.database_name != DatabaseCatalog::SYSTEM_DATABASE)
739+
throw Exception(ErrorCodes::QUERY_IS_PROHIBITED, "Insert queries are prohibited");
735740

736741
StoragePtr table = getTable(query);
737742
checkStorageSupportsTransactionsIfNeeded(table, getContext());

tests/integration/test_disable_insertion_and_mutation/__init__.py

Whitespace-only changes.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<clickhouse>
2+
<remote_servers>
3+
<default>
4+
<shard>
5+
<replica>
6+
<host>writing_node</host>
7+
<port>9000</port>
8+
</replica>
9+
<replica>
10+
<host>reading_node</host>
11+
<port>9000</port>
12+
</replica>
13+
</shard>
14+
</default>
15+
</remote_servers>
16+
</clickhouse>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<clickhouse>
2+
<disable_insertion_and_mutation>true</disable_insertion_and_mutation>
3+
</clickhouse>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<clickhouse>
2+
<disable_insertion_and_mutation>false</disable_insertion_and_mutation>
3+
</clickhouse>
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import pytest
2+
from helpers.client import QueryRuntimeException
3+
from helpers.cluster import ClickHouseCluster
4+
import time
5+
6+
cluster = ClickHouseCluster(__file__)
7+
8+
writing_node = cluster.add_instance(
9+
"writing_node",
10+
main_configs=["config/writing_node.xml", "config/cluster.xml"],
11+
with_zookeeper=True,
12+
with_minio=True,
13+
stay_alive=True,
14+
macros={"shard": 1, "replica": 1},
15+
)
16+
reading_node = cluster.add_instance(
17+
"reading_node",
18+
main_configs=["config/reading_node.xml", "config/cluster.xml"],
19+
with_zookeeper=True,
20+
with_minio=True,
21+
stay_alive=True,
22+
macros={"shard": 1, "replica": 2},
23+
)
24+
25+
26+
@pytest.fixture(scope="module")
27+
def started_cluster():
28+
try:
29+
cluster.start()
30+
31+
yield cluster
32+
33+
finally:
34+
cluster.shutdown()
35+
36+
37+
def test_disable_insertion_and_mutation(started_cluster):
38+
writing_node.query(
39+
"""CREATE TABLE my_table on cluster default (key UInt64, value String) ENGINE=ReplicatedMergeTree('/clickhouse/tables/{shard}/default.my_table', '{replica}') ORDER BY key partition by (key % 5) """
40+
)
41+
42+
assert "QUERY_IS_PROHIBITED" in reading_node.query_and_get_error(
43+
"INSERT INTO my_table VALUES (1, 'hello')"
44+
)
45+
46+
assert "QUERY_IS_PROHIBITED" in reading_node.query_and_get_error(
47+
"INSERT INTO my_table SETTINGS async_insert = 1 VALUES (1, 'hello')"
48+
)
49+
50+
assert "QUERY_IS_PROHIBITED" in reading_node.query_and_get_error(
51+
"ALTER TABLE my_table delete where 1"
52+
)
53+
54+
assert "QUERY_IS_PROHIBITED" in reading_node.query_and_get_error(
55+
"ALTER table my_table update key = 1 where 1"
56+
)
57+
58+
assert "QUERY_IS_PROHIBITED" in reading_node.query_and_get_error(
59+
"ALTER TABLE my_table drop partition 0"
60+
)
61+
62+
reading_node.query("SELECT * from my_table")
63+
writing_node.query("INSERT INTO my_table VALUES (1, 'hello')")
64+
writing_node.query("ALTER TABLE my_table delete where 1")
65+
writing_node.query("ALTER table my_table update value = 'no hello' where 1")
66+
67+
reading_node.query("ALTER TABLE my_table ADD COLUMN new_column UInt64")
68+
writing_node.query("SELECT new_column from my_table")
69+
reading_node.query("SELECT new_column from my_table")
70+
71+
reading_node.query("ALter Table my_table MODIFY COLUMN new_column String")
72+
73+
assert "new_column\tString" in reading_node.query("DESC my_table")
74+
75+
assert "new_column\tString" in writing_node.query("DESC my_table")

0 commit comments

Comments
 (0)