diff --git a/docs/en/operations/clickhouse-keeper.md b/docs/en/operations/clickhouse-keeper.md index 0324f742988..c5a569a64f5 100644 --- a/docs/en/operations/clickhouse-keeper.md +++ b/docs/en/operations/clickhouse-keeper.md @@ -57,7 +57,7 @@ Internal coordination settings are located in the `..` section and contain servers description. @@ -126,7 +126,7 @@ clickhouse keeper --config /etc/your_path_to_config/config.xml ClickHouse Keeper also provides 4lw commands which are almost the same with Zookeeper. Each command is composed of four letters such as `mntr`, `stat` etc. There are some more interesting commands: `stat` gives some general information about the server and connected clients, while `srvr` and `cons` give extended details on server and connections respectively. -The 4lw commands has a white list configuration `four_letter_word_white_list` which has default value `conf,cons,crst,envi,ruok,srst,srvr,stat,wchs,dirs,mntr,isro,rcvr,apiv,csnp,lgif`. +The 4lw commands has a white list configuration `four_letter_word_white_list` which has default value `conf,cons,crst,envi,ruok,srst,srvr,stat,wchs,dirs,mntr,isro,rcvr,apiv,csnp,lgif,rqld`. You can issue the commands to ClickHouse Keeper via telnet or nc, at the client port. @@ -328,6 +328,12 @@ target_committed_log_idx 101 last_snapshot_idx 50 ``` +- `rqld`: Request to become new leader. Return `Sent leadership request to leader.` if request sent or `Failed to send leadership request to leader.` if request not sent. Note that if node is already leader it will not send the request. + +``` +Sent leadership request to leader. +``` + ## Migration from ZooKeeper {#migration-from-zookeeper} Seamlessly migration from ZooKeeper to ClickHouse Keeper is impossible you have to stop your ZooKeeper cluster, convert data and start ClickHouse Keeper. `clickhouse-keeper-converter` tool allows converting ZooKeeper logs and snapshots to ClickHouse Keeper snapshot. It works only with ZooKeeper > 3.4. Steps for migration: diff --git a/src/Coordination/FourLetterCommand.cpp b/src/Coordination/FourLetterCommand.cpp index 82123dc8218..9b4e6a3cf10 100644 --- a/src/Coordination/FourLetterCommand.cpp +++ b/src/Coordination/FourLetterCommand.cpp @@ -142,6 +142,9 @@ void FourLetterCommandFactory::registerCommands(KeeperDispatcher & keeper_dispat FourLetterCommandPtr log_info_command = std::make_shared(keeper_dispatcher); factory.registerCommand(log_info_command); + FourLetterCommandPtr request_leader_command = std::make_shared(keeper_dispatcher); + factory.registerCommand(request_leader_command); + factory.initializeAllowList(keeper_dispatcher); factory.setInitialize(true); } @@ -507,4 +510,9 @@ String LogInfoCommand::run() return ret.str(); } +String RequestLeaderCommand::run() +{ + return keeper_dispatcher.requestLeader() ? "Sent leadership request to leader." : "Failed to send leadership request to leader."; +} + } diff --git a/src/Coordination/FourLetterCommand.h b/src/Coordination/FourLetterCommand.h index a8801474bb0..8a8aacf7a3a 100644 --- a/src/Coordination/FourLetterCommand.h +++ b/src/Coordination/FourLetterCommand.h @@ -364,4 +364,17 @@ struct LogInfoCommand : public IFourLetterCommand ~LogInfoCommand() override = default; }; +/// Request to be leader. +struct RequestLeaderCommand : public IFourLetterCommand +{ + explicit RequestLeaderCommand(KeeperDispatcher & keeper_dispatcher_) + : IFourLetterCommand(keeper_dispatcher_) + { + } + + String name() override { return "rqld"; } + String run() override; + ~RequestLeaderCommand() override = default; +}; + } diff --git a/src/Coordination/KeeperDispatcher.h b/src/Coordination/KeeperDispatcher.h index 84345ca1ff5..632e5e65e5f 100644 --- a/src/Coordination/KeeperDispatcher.h +++ b/src/Coordination/KeeperDispatcher.h @@ -215,6 +215,12 @@ public: { return server->getKeeperLogInfo(); } + + /// Request to be leader. + bool requestLeader() + { + return server->requestLeader(); + } }; } diff --git a/src/Coordination/KeeperServer.cpp b/src/Coordination/KeeperServer.cpp index 487d0dc4cc3..f312dbe2e77 100644 --- a/src/Coordination/KeeperServer.cpp +++ b/src/Coordination/KeeperServer.cpp @@ -932,4 +932,9 @@ KeeperLogInfo KeeperServer::getKeeperLogInfo() return log_info; } +bool KeeperServer::requestLeader() +{ + return raft_instance->request_leadership(); +} + } diff --git a/src/Coordination/KeeperServer.h b/src/Coordination/KeeperServer.h index 192c8f470b1..feadf3bb7ce 100644 --- a/src/Coordination/KeeperServer.h +++ b/src/Coordination/KeeperServer.h @@ -135,6 +135,8 @@ public: uint64_t createSnapshot(); KeeperLogInfo getKeeperLogInfo(); + + bool requestLeader(); }; } diff --git a/tests/integration/test_keeper_four_word_command/test.py b/tests/integration/test_keeper_four_word_command/test.py index 4559904f8b7..526a4185bd2 100644 --- a/tests/integration/test_keeper_four_word_command/test.py +++ b/tests/integration/test_keeper_four_word_command/test.py @@ -641,3 +641,22 @@ def test_cmd_lgif(started_cluster): assert int(result["last_snapshot_idx"]) >= 1 finally: destroy_zk_client(zk) + + +def test_cmd_rqld(started_cluster): + for node in [node1, node2, node3]: + stat = keeper_utils.send_4lw_cmd(cluster, node, cmd="stat") + stats = [n for n in stat.split("\n") if "=" not in n] + reader = csv.reader(stats, delimiter=":") + result = {} + + for row in reader: + if len(row) != 0: + result[row[0].strip()] = row[1].strip() + + data = keeper_utils.send_4lw_cmd(cluster, node, cmd="rqld") + + if result["Mode"] == "leader": + assert data == "Failed to send leadership request to leader." + else: + assert data == "Sent leadership request to leader."