2020-03-16 21:05:38 +00:00
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
import time
|
|
|
|
from contextlib import contextmanager
|
|
|
|
|
2020-09-16 04:26:10 +00:00
|
|
|
import docker
|
|
|
|
import pytest
|
2020-03-16 21:05:38 +00:00
|
|
|
|
|
|
|
CURRENT_TEST_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
sys.path.insert(0, os.path.dirname(CURRENT_TEST_DIR))
|
|
|
|
from helpers.cluster import ClickHouseCluster
|
|
|
|
from helpers.test_tools import TSV
|
|
|
|
|
2020-12-23 18:59:27 +00:00
|
|
|
COPYING_FAIL_PROBABILITY = 0.33
|
|
|
|
MOVING_FAIL_PROBABILITY = 0.1
|
2020-03-16 21:05:38 +00:00
|
|
|
cluster = None
|
|
|
|
|
2020-09-16 04:26:10 +00:00
|
|
|
|
2020-04-21 17:37:40 +00:00
|
|
|
@pytest.fixture(scope="function")
|
2020-03-16 21:05:38 +00:00
|
|
|
def started_cluster():
|
|
|
|
global cluster
|
|
|
|
try:
|
|
|
|
clusters_schema = {
|
2020-09-16 04:26:10 +00:00
|
|
|
"0": {"0": ["0"]},
|
|
|
|
"1": {"0": ["0"]}
|
2020-03-16 21:05:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
cluster = ClickHouseCluster(__file__)
|
|
|
|
|
2020-10-02 16:54:07 +00:00
|
|
|
for cluster_name, shards in clusters_schema.items():
|
|
|
|
for shard_name, replicas in shards.items():
|
2020-03-16 21:05:38 +00:00
|
|
|
for replica_name in replicas:
|
|
|
|
name = "s{}_{}_{}".format(cluster_name, shard_name, replica_name)
|
|
|
|
cluster.add_instance(name,
|
2020-08-12 08:55:04 +00:00
|
|
|
main_configs=[], user_configs=[],
|
2020-03-16 21:05:38 +00:00
|
|
|
macros={"cluster": cluster_name, "shard": shard_name, "replica": replica_name},
|
|
|
|
with_zookeeper=True)
|
|
|
|
|
|
|
|
cluster.start()
|
|
|
|
yield cluster
|
|
|
|
|
|
|
|
finally:
|
|
|
|
pass
|
|
|
|
cluster.shutdown()
|
|
|
|
|
|
|
|
|
|
|
|
class TaskTrivial:
|
2020-04-21 17:37:40 +00:00
|
|
|
def __init__(self, cluster, use_sample_offset):
|
2020-03-16 21:05:38 +00:00
|
|
|
self.cluster = cluster
|
2020-04-21 17:37:40 +00:00
|
|
|
if use_sample_offset:
|
2020-09-16 04:26:10 +00:00
|
|
|
self.zk_task_path = "/clickhouse-copier/task_trivial_use_sample_offset"
|
2020-04-21 17:37:40 +00:00
|
|
|
else:
|
2020-09-16 04:26:10 +00:00
|
|
|
self.zk_task_path = "/clickhouse-copier/task_trivial"
|
2020-03-16 21:05:38 +00:00
|
|
|
self.copier_task_config = open(os.path.join(CURRENT_TEST_DIR, 'task_trivial.xml'), 'r').read()
|
|
|
|
|
|
|
|
def start(self):
|
|
|
|
source = cluster.instances['s0_0_0']
|
|
|
|
destination = cluster.instances['s1_0_0']
|
|
|
|
|
|
|
|
for node in [source, destination]:
|
|
|
|
node.query("DROP DATABASE IF EXISTS default")
|
2020-09-23 18:28:59 +00:00
|
|
|
node.query("CREATE DATABASE IF NOT EXISTS default")
|
2020-03-16 21:05:38 +00:00
|
|
|
|
|
|
|
source.query("CREATE TABLE trivial (d UInt64, d1 UInt64 MATERIALIZED d+1) "
|
|
|
|
"ENGINE=ReplicatedMergeTree('/clickhouse/tables/source_trivial_cluster/1/trivial', '1') "
|
2020-04-21 17:37:40 +00:00
|
|
|
"PARTITION BY d % 5 ORDER BY (d, sipHash64(d)) SAMPLE BY sipHash64(d) SETTINGS index_granularity = 16")
|
2020-03-16 21:05:38 +00:00
|
|
|
|
2020-09-16 04:26:10 +00:00
|
|
|
source.query("INSERT INTO trivial SELECT * FROM system.numbers LIMIT 1002",
|
|
|
|
settings={"insert_distributed_sync": 1})
|
2020-03-16 21:05:38 +00:00
|
|
|
|
|
|
|
def check(self):
|
|
|
|
source = cluster.instances['s0_0_0']
|
|
|
|
destination = cluster.instances['s1_0_0']
|
|
|
|
|
|
|
|
assert TSV(source.query("SELECT count() FROM trivial")) == TSV("1002\n")
|
|
|
|
assert TSV(destination.query("SELECT count() FROM trivial")) == TSV("1002\n")
|
|
|
|
|
|
|
|
for node in [source, destination]:
|
|
|
|
node.query("DROP TABLE trivial")
|
|
|
|
|
|
|
|
|
|
|
|
def execute_task(task, cmd_options):
|
|
|
|
task.start()
|
|
|
|
|
|
|
|
zk = cluster.get_kazoo_client('zoo1')
|
2020-10-02 16:54:07 +00:00
|
|
|
print("Use ZooKeeper server: {}:{}".format(zk.hosts[0][0], zk.hosts[0][1]))
|
2020-03-16 21:05:38 +00:00
|
|
|
|
|
|
|
zk_task_path = task.zk_task_path
|
|
|
|
zk.ensure_path(zk_task_path)
|
2020-12-30 14:25:55 +00:00
|
|
|
zk.create(zk_task_path + "/description", task.copier_task_config)
|
2020-03-16 21:05:38 +00:00
|
|
|
|
|
|
|
# Run cluster-copier processes on each node
|
|
|
|
docker_api = docker.from_env().api
|
|
|
|
copiers_exec_ids = []
|
|
|
|
|
|
|
|
cmd = ['/usr/bin/clickhouse', 'copier',
|
|
|
|
'--config', '/etc/clickhouse-server/config-copier.xml',
|
|
|
|
'--task-path', zk_task_path,
|
|
|
|
'--base-dir', '/var/log/clickhouse-server/copier']
|
|
|
|
cmd += cmd_options
|
|
|
|
|
2020-12-30 14:25:55 +00:00
|
|
|
print(cmd)
|
2020-03-16 21:05:38 +00:00
|
|
|
|
2020-12-30 14:25:55 +00:00
|
|
|
for instance_name, instance in cluster.instances.items():
|
2020-03-16 21:05:38 +00:00
|
|
|
container = instance.get_docker_handle()
|
|
|
|
exec_id = docker_api.exec_create(container.id, cmd, stderr=True)
|
2020-12-30 14:25:55 +00:00
|
|
|
docker_api.exec_start(exec_id, detach=True)
|
|
|
|
|
2020-03-16 21:05:38 +00:00
|
|
|
copiers_exec_ids.append(exec_id)
|
2020-10-02 16:54:07 +00:00
|
|
|
print("Copier for {} ({}) has started".format(instance.name, instance.ip_address))
|
2020-03-16 21:05:38 +00:00
|
|
|
|
|
|
|
# Wait for copiers stopping and check their return codes
|
2020-12-30 14:25:55 +00:00
|
|
|
for exec_id, instance in zip(copiers_exec_ids, iter(cluster.instances.values())):
|
2020-03-16 21:05:38 +00:00
|
|
|
while True:
|
|
|
|
res = docker_api.exec_inspect(exec_id)
|
|
|
|
if not res['Running']:
|
|
|
|
break
|
2020-12-30 14:25:55 +00:00
|
|
|
time.sleep(1)
|
2020-03-16 21:05:38 +00:00
|
|
|
|
|
|
|
assert res['ExitCode'] == 0, "Instance: {} ({}). Info: {}".format(instance.name, instance.ip_address, repr(res))
|
|
|
|
|
|
|
|
try:
|
|
|
|
task.check()
|
|
|
|
finally:
|
|
|
|
zk.delete(zk_task_path, recursive=True)
|
|
|
|
|
|
|
|
|
|
|
|
# Tests
|
|
|
|
|
|
|
|
|
2020-12-23 18:59:27 +00:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
('use_sample_offset'),
|
|
|
|
[
|
|
|
|
False,
|
|
|
|
True
|
|
|
|
]
|
|
|
|
)
|
2020-04-21 17:37:40 +00:00
|
|
|
def test_trivial_copy(started_cluster, use_sample_offset):
|
|
|
|
if use_sample_offset:
|
|
|
|
execute_task(TaskTrivial(started_cluster, use_sample_offset), ['--experimental-use-sample-offset', '1'])
|
|
|
|
else:
|
2020-12-23 18:59:27 +00:00
|
|
|
print("AAAAA")
|
2020-04-21 17:37:40 +00:00
|
|
|
execute_task(TaskTrivial(started_cluster, use_sample_offset), [])
|
|
|
|
|
2020-09-16 04:26:10 +00:00
|
|
|
|
2020-12-23 18:59:27 +00:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
('use_sample_offset'),
|
|
|
|
[
|
|
|
|
False,
|
|
|
|
True
|
|
|
|
]
|
|
|
|
)
|
2020-04-21 17:37:40 +00:00
|
|
|
def test_trivial_copy_with_copy_fault(started_cluster, use_sample_offset):
|
|
|
|
if use_sample_offset:
|
2020-12-30 14:25:55 +00:00
|
|
|
execute_task(TaskTrivial(started_cluster), ['--copy-fault-probability', str(COPYING_FAIL_PROBABILITY),
|
2020-04-21 17:37:40 +00:00
|
|
|
'--experimental-use-sample-offset', '1'])
|
|
|
|
else:
|
2020-12-30 14:25:55 +00:00
|
|
|
execute_task(TaskTrivial(started_cluster), ['--copy-fault-probability', str(COPYING_FAIL_PROBABILITY)])
|
2020-04-21 17:37:40 +00:00
|
|
|
|
2020-09-16 04:26:10 +00:00
|
|
|
|
2020-12-23 18:59:27 +00:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
('use_sample_offset'),
|
|
|
|
[
|
|
|
|
False,
|
|
|
|
True
|
|
|
|
]
|
|
|
|
)
|
2020-04-21 17:37:40 +00:00
|
|
|
def test_trivial_copy_with_move_fault(started_cluster, use_sample_offset):
|
|
|
|
if use_sample_offset:
|
2020-12-30 14:25:55 +00:00
|
|
|
execute_task(TaskTrivial(started_cluster), ['--move-fault-probability', str(MOVING_FAIL_PROBABILITY),
|
2020-04-21 17:37:40 +00:00
|
|
|
'--experimental-use-sample-offset', '1'])
|
|
|
|
else:
|
2020-12-30 14:25:55 +00:00
|
|
|
execute_task(TaskTrivial(started_cluster), ['--move-fault-probability', str(MOVING_FAIL_PROBABILITY)])
|
2020-12-23 18:59:27 +00:00
|
|
|
|
2020-03-16 21:05:38 +00:00
|
|
|
|
2020-12-23 18:59:27 +00:00
|
|
|
if __name__ == '__main__':
|
|
|
|
with contextmanager(started_cluster)() as cluster:
|
|
|
|
for name, instance in list(cluster.instances.items()):
|
|
|
|
print(name, instance.ip_address)
|
2020-12-24 14:24:51 +00:00
|
|
|
input("Cluster created, press any key to destroy...")
|