Files
Alexey MilovidovandClaude Opus 4.8 295ced78d1 Enable more Ruff checks (F401/F403/F405/F541/F841) and clean up tests
Follow-up to #105171, which added `ruff` with a deliberately narrow,
bug-class-only rule set and left the unused-name and star-import rules
off pending a dedicated cleanup pass. This enables that second batch in
`pyproject.toml` and fixes every violation across the tree (the bulk in
integration tests):

- `F401` unused-import and `F841` unused-variable: removed, preserving
  any side-effecting right-hand side (e.g. `result = node.query(...)`
  becomes `node.query(...)`). Two `try: import ...` feature probes are
  kept with `# noqa: F401`.
- `F541` f-string-missing-placeholders: dropped the redundant `f` prefix.
- `F403`/`F405` star imports: every `from module import *` is expanded
  into explicit imports of the names actually used, taken from each
  name's real origin, so stdlib/third-party names import from their real
  source rather than through a test "convenience bundle". The now-empty
  `tests/integration/helpers/kafka/common_direct.py` bundle is removed and
  its three remaining consumers import `_VarintBytes`/`KafkaAdminClient`
  directly.

Eliminating the star imports surfaced a real bug the wildcard had masked
from `F821`: `generate_presets.py` in `test_prometheus_protocols` called
an undefined `execute_instant_query_with_http_api`; fixed to the existing
`execute_query_via_http_api`.

No `black`/`isort` reformatting was applied (neither is enforced in the
style check), so the diff is limited to the lint fixes themselves.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-18 23:22:25 +00:00

223 lines
8.4 KiB
Python

import random
import string
import uuid
from typing import Any, Dict
import pytest
from helpers.cluster import ClickHouseCluster
cluster = ClickHouseCluster(__file__)
node1 = cluster.add_instance(
"node1",
main_configs=["configs/config.xml", "configs/database_replicated_settings.xml"],
user_configs=["configs/users.xml"],
keeper_required_feature_flags=["multi_read", "create_if_not_exists"],
macros={"shard": "shard1", "replica": "1"},
stay_alive=True,
with_zookeeper=True,
)
node2 = cluster.add_instance(
"node2",
main_configs=["configs/config.xml"],
user_configs=["configs/users.xml"],
keeper_required_feature_flags=["multi_read", "create_if_not_exists"],
macros={"shard": "shard1", "replica": "2"},
with_zookeeper=True,
)
node3 = cluster.add_instance(
"node3",
main_configs=["configs/config.xml"],
user_configs=["configs/users.xml"],
keeper_required_feature_flags=["multi_read", "create_if_not_exists"],
macros={"shard": "shard2", "replica": "1"},
with_zookeeper=True,
)
@pytest.fixture(scope="module")
def started_cluster():
try:
cluster.start()
yield cluster
finally:
cluster.shutdown()
def get_random_string(string_length=8):
alphabet = string.ascii_letters + string.digits
return "".join((random.choice(alphabet) for _ in range(string_length)))
def convert_setting(key : str, value: str):
match key:
case "node_with_database_replicated_settings" | "check_consistency":
if value.lower() == "false":
value = False
elif value.lower() == "true":
value = True
else:
raise Exception(f"Invalid settings {key}: {value}")
return key, value
case "max_broken_tables_ratio":
value = float(value)
return key, value
case (
"max_replication_lag_to_enqueue"
| "wait_entry_commited_timeout_sec"
| "max_retries_before_automatic_recovery"
| "logs_to_keep"
):
value = int(value)
return key, value
case "collection_name":
return key, value
raise Exception(f"Unknown settings {key}: {value}")
def get_settings_from_logs(node, db_name) ->Dict[str, Any]:
node.query("SYSTEM FLUSH LOGS")
result = str(node.query(
f"SELECT message FROM system.text_log WHERE logger_name='DatabaseReplicated ({db_name})' AND position(message, 'DatabaseReplicatedSettings') = 1"
).strip())
assert result.startswith("DatabaseReplicatedSettings")
settings = result.removeprefix("DatabaseReplicatedSettings").strip()
if settings == "":
return {}
config_dict = {}
# Split into key-value pairs
pairs = settings.split(",")
for pair in pairs:
key, value = pair.strip().split("=", 1)
key = key.strip()
value = value.strip()
if(value.startswith(r"\'")):
value = value.strip(r"\'")
key, value = convert_setting(key, value)
config_dict[key] = value
return config_dict
@pytest.mark.parametrize("node_with_database_replicated_settings", [True, False])
@pytest.mark.parametrize("max_broken_tables_ratio", [None, 0.5])
@pytest.mark.parametrize("max_replication_lag_to_enqueue", [None, 10])
@pytest.mark.parametrize("wait_entry_commited_timeout_sec", [None, 900])
@pytest.mark.parametrize("collection_name", [None, "postgres2"])
@pytest.mark.parametrize("check_consistency", [None, True, False])
@pytest.mark.parametrize("max_retries_before_automatic_recovery", [None, 1])
@pytest.mark.parametrize("logs_to_keep", [None, 100])
def test_database_replicated_settings(
started_cluster,
node_with_database_replicated_settings: bool, # default config from database_replicated_settings.xml
max_broken_tables_ratio,
max_replication_lag_to_enqueue,
wait_entry_commited_timeout_sec,
collection_name,
check_consistency,
max_retries_before_automatic_recovery,
logs_to_keep,
):
db_name = "test_" + get_random_string()
node = node1 if node_with_database_replicated_settings else node2
node.query(f"DROP DATABASE IF EXISTS {db_name}")
settings_in_query = ""
setting_dict : Dict[str, Any]= {}
def add_setting(key, val, default):
nonlocal settings_in_query
nonlocal setting_dict
nonlocal node_with_database_replicated_settings
if val == None:
if node_with_database_replicated_settings:
setting_dict[key] = default
return
setting_dict[key] = val
if isinstance(val, str):
val = f"'{val}'"
if settings_in_query != "":
settings_in_query += f", {key}={val}"
else :
settings_in_query += f"{key}={val}"
add_setting("max_broken_tables_ratio", max_broken_tables_ratio, float(0.75) if node_with_database_replicated_settings else float(1.0))
add_setting("max_replication_lag_to_enqueue", max_replication_lag_to_enqueue, int(100) if node_with_database_replicated_settings else int(50))
add_setting("wait_entry_commited_timeout_sec", wait_entry_commited_timeout_sec, int(1800) if node_with_database_replicated_settings else int(3600))
add_setting("collection_name", collection_name, "postgres1" if node_with_database_replicated_settings else "")
add_setting("check_consistency", check_consistency, False if node_with_database_replicated_settings else True)
add_setting(
"max_retries_before_automatic_recovery", max_retries_before_automatic_recovery, int(5) if node_with_database_replicated_settings else int(10)
)
add_setting(
"logs_to_keep",
logs_to_keep,
int(200) if node_with_database_replicated_settings else int(1000),
)
if settings_in_query != "":
settings_in_query = f"SETTINGS {settings_in_query}"
node.query(
f"CREATE DATABASE {db_name} ENGINE=Replicated('/test/{db_name}', "
+ r"'{shard}', '{replica}') "
+ settings_in_query
)
settings_dict_from_logs = get_settings_from_logs(node, db_name)
assert setting_dict == settings_dict_from_logs
node.query(f"DROP DATABASE IF EXISTS {db_name}")
def test_database_replicated_settings_zero_logs_to_keep(started_cluster):
db_name = "test_" + get_random_string()
assert "A setting's value has to be greater than 0" in node1.query_and_get_error(
f"CREATE DATABASE {db_name} ENGINE=Replicated('/test/{db_name}', "
+ r"'{shard}', '{replica}') "
+ "SETTINGS logs_to_keep=0"
)
def test_create_database_replicated_with_default_args(started_cluster):
db_name = "test_" + get_random_string()
db_uuid = uuid.uuid4()
assert "within an ON CLUSTER query" in node1.query_and_get_error(f"CREATE DATABASE {db_name} Engine=Replicated")
node1.query(f"CREATE DATABASE {db_name}_1 ON CLUSTER default Engine=Replicated")
node2.query(f"CREATE DATABASE {db_name}_2 ON CLUSTER default Engine=Replicated")
node1.query(f"CREATE DATABASE {db_name}_explicit UUID '{db_uuid}' Engine=Replicated")
node2.query(f"CREATE DATABASE {db_name}_explicit UUID '{db_uuid}' Engine=Replicated")
for db in [f"{db_name}_1", f"{db_name}_2", f"{db_name}_explicit"]:
resp1 = node1.query(f"SELECT database, zookeeper_path FROM system.database_replicas WHERE database='{db}' ORDER BY ALL")
resp2 = node2.query(f"SELECT database, zookeeper_path FROM system.database_replicas WHERE database='{db}' ORDER BY ALL")
assert resp1 == resp2
@pytest.mark.parametrize("async_insert", [0, 1])
@pytest.mark.parametrize("prefer_localhost_replica", [0, 1])
def test_database_replicated_with_internal_replication(started_cluster, async_insert, prefer_localhost_replica):
db_name = "test_internal_replication_" + get_random_string()
node1.query(f"CREATE DATABASE {db_name} ON CLUSTER default Engine=Replicated SETTINGS internal_replication=true")
node1.query(f"CREATE TABLE {db_name}.data_table (a Int) Engine=ReplicatedMergeTree ORDER BY a")
node1.query(f"CREATE TABLE {db_name}.distributed (a Int) Engine=Distributed({db_name}, {db_name}, data_table, a)")
node1.query(f"INSERT INTO TABLE {db_name}.distributed VALUES (1), (2)", settings={
"wait_for_async_insert": 1,
"async_insert": async_insert,
"prefer_localhost_replica": prefer_localhost_replica,
})
node1.query(f"SYSTEM SYNC REPLICA ON CLUSTER default {db_name}.data_table")
response = node1.query(f"SELECT a FROM {db_name}.distributed ORDER BY a")
assert response == '1\n2\n'