# pylint: disable=unused-argument # pylint: disable=redefined-outer-name # pylint: disable=line-too-long import pytest from helpers.client import QueryRuntimeException from helpers.cluster import ClickHouseCluster cluster = ClickHouseCluster(__file__) node = cluster.add_instance("node", stay_alive=True) @pytest.fixture(scope="module", autouse=True) def start_cluster(): try: cluster.start() yield cluster finally: cluster.shutdown() def break_part(table, part_name): node.exec_in_container( [ "bash", "-c", f"rm /var/lib/clickhouse/data/default/{table}/{part_name}/columns.txt", ] ) def remove_part(table, part_name): node.exec_in_container( ["bash", "-c", f"rm -r /var/lib/clickhouse/data/default/{table}/{part_name}"] ) def get_count(table): return int(node.query(f"SELECT count() FROM {table}").strip()) def detach_table(table): node.query(f"DETACH TABLE {table}") def attach_table(table): node.query(f"ATTACH TABLE {table}") def check_table(table): rows = 900 per_part_rows = 90 node.query(f"INSERT INTO {table} SELECT * FROM numbers(900)") assert get_count(table) == rows # break one part, and check that clickhouse will be alive break_part(table, "0_1_1_0") rows -= per_part_rows detach_table(table) attach_table(table) assert get_count(table) == rows # break two parts, and check that clickhouse will not start break_part(table, "1_2_2_0") break_part(table, "2_3_3_0") rows -= per_part_rows * 2 detach_table(table) with pytest.raises(QueryRuntimeException): attach_table(table) # now remove one part, and check remove_part(table, "1_2_2_0") attach_table(table) assert get_count(table) == rows node.query(f"DROP TABLE {table}") def test_max_suspicious_broken_parts(): node.query( """ CREATE TABLE test_max_suspicious_broken_parts ( key Int ) ENGINE=MergeTree ORDER BY key PARTITION BY key%10 SETTINGS max_suspicious_broken_parts = 1; """ ) check_table("test_max_suspicious_broken_parts") def test_max_suspicious_broken_parts_bytes(): node.query( """ CREATE TABLE test_max_suspicious_broken_parts_bytes ( key Int ) ENGINE=MergeTree ORDER BY key PARTITION BY key%10 SETTINGS max_suspicious_broken_parts = 10, /* one part takes ~751 byte, so we allow failure of one part with these limit */ max_suspicious_broken_parts_bytes = 1000; """ ) check_table("test_max_suspicious_broken_parts_bytes") def test_max_suspicious_broken_parts__wide(): node.query( """ CREATE TABLE test_max_suspicious_broken_parts__wide ( key Int ) ENGINE=MergeTree ORDER BY key PARTITION BY key%10 SETTINGS min_bytes_for_wide_part = 0, max_suspicious_broken_parts = 1; """ ) check_table("test_max_suspicious_broken_parts__wide") def test_max_suspicious_broken_parts_bytes__wide(): node.query( """ CREATE TABLE test_max_suspicious_broken_parts_bytes__wide ( key Int ) ENGINE=MergeTree ORDER BY key PARTITION BY key%10 SETTINGS min_bytes_for_wide_part = 0, max_suspicious_broken_parts = 10, /* one part takes ~750 byte, so we allow failure of one part with these limit */ max_suspicious_broken_parts_bytes = 1000; """ ) check_table("test_max_suspicious_broken_parts_bytes__wide")