#!/usr/bin/python3 """ This test checks the the basic functionality of HA Memgraph. It incorporates both leader election and log replication processes. The test proceeds as follows for clusters of size 3 and 5: 1) Start the whole cluster 2) Kill random workers but leave the majority alive 3) Create a single Node 4) Bring dead nodes back to life 5) Kill random workers but leave the majority alive 6) Check if everything is ok with DB state 7) GOTO 1) and repeat 25 times """ import argparse import os import time import subprocess import sys import random SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) PROJECT_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, "..", "..", "..", "..")) # append parent directory sys.path.append(os.path.join(SCRIPT_DIR, "..")) from ha_test import HaTestBase class HaBasicTest(HaTestBase): def execute_step(self, step, node_count): if step == "create": print("Executing create query") client = subprocess.Popen([self.tester_binary, "--step", "create", "--cluster-size", str(self.cluster_size), "--node-count", str(node_count)]) elif step == "count": print("Executing count query") client = subprocess.Popen([self.tester_binary, "--step", "count", "--cluster_size", str(self.cluster_size), "--node-count", str(node_count)]) else: return 0 # Check what happened with query execution. try: code = client.wait(timeout=30) except subprocess.TimeoutExpired as e: print("HA client timed out!") client.kill() return 1 return code def start_workers(self, worker_ids): for wid in worker_ids: print("Starting worker {}".format(wid + 1)) self.start_worker(wid) def kill_workers(self, worker_ids): for wid in worker_ids: print("Killing worker {}".format(wid + 1)) self.kill_worker(wid) def execute(self): self.start_cluster() expected_results = 0 # Make sure at least one node exists. assert self.execute_step("create", expected_results) == 0, \ "Error while executing create query" expected_results = 1 for i in range(20): # Create step partition = random.sample(range(self.cluster_size), random.randint(0, int((self.cluster_size - 1) / 2))) self.kill_workers(partition) assert self.execute_step("create", expected_results) == 0, \ "Error while executing create query" expected_results += 1 self.start_workers(partition) # Check step partition = random.sample(range(self.cluster_size), random.randint(0, int((self.cluster_size - 1) / 2))) self.kill_workers(partition) assert self.execute_step("count", expected_results) == 0, \ "Error while executing count query" self.start_workers(partition) # Check that no data was lost. assert self.execute_step("count", expected_results) == 0, \ "Error while executing count query" def find_correct_path(path): f = os.path.join(PROJECT_DIR, "build", path) if not os.path.exists(f): f = os.path.join(PROJECT_DIR, "build_debug", path) return f if __name__ == "__main__": memgraph_binary = find_correct_path("memgraph_ha") tester_binary = find_correct_path(os.path.join("tests", "integration", "ha", "basic", "tester")) raft_config_file = os.path.join(PROJECT_DIR, "tests", "integration", "ha", "basic", "raft.json") parser = argparse.ArgumentParser() parser.add_argument("--memgraph", default=memgraph_binary) parser.add_argument("--raft_config_file", default=raft_config_file) args = parser.parse_args() for cluster_size in [3, 5]: print("\033[1;36m~~ Executing test with cluster size: %d~~\033[0m" % (cluster_size)) HaBasicTest( args.memgraph, tester_binary, args.raft_config_file, cluster_size) print("\033[1;32m~~ The test finished successfully ~~\033[0m") sys.exit(0)