diff --git a/changelogs/unreleased/gh-8931-box-info-election-leader-name.md b/changelogs/unreleased/gh-8931-box-info-election-leader-name.md new file mode 100644 index 0000000000000000000000000000000000000000..48ea7bfe7b0a434f0cdc69c57a829a9e31b90861 --- /dev/null +++ b/changelogs/unreleased/gh-8931-box-info-election-leader-name.md @@ -0,0 +1,3 @@ +## feature/box + +* Added the `leader_name` field to `box.info.election` (gh-8931). diff --git a/src/box/lua/info.c b/src/box/lua/info.c index 05bf09389fff919082098053e4dd4e29f2347112..963fe4e2c56184134ccd9bd3737b760ad6c772a7 100644 --- a/src/box/lua/info.c +++ b/src/box/lua/info.c @@ -676,6 +676,14 @@ lbox_info_election(struct lua_State *L) lua_pushinteger(L, raft->leader); lua_setfield(L, -2, "leader"); if (raft_is_enabled(raft)) { + if (raft->leader != 0) { + char *leader_name = replica_by_id(raft->leader)->name; + if (*leader_name == 0) + luaL_pushnull(L); + else + lua_pushstring(L, leader_name); + lua_setfield(L, -2, "leader_name"); + } lua_pushnumber(L, raft_leader_idle(raft)); lua_setfield(L, -2, "leader_idle"); } diff --git a/test/replication-luatest/gh_8931_box_info_election_leader_name_test.lua b/test/replication-luatest/gh_8931_box_info_election_leader_name_test.lua new file mode 100644 index 0000000000000000000000000000000000000000..b14ef6e721400808743d48c6016578a8d19ce4f5 --- /dev/null +++ b/test/replication-luatest/gh_8931_box_info_election_leader_name_test.lua @@ -0,0 +1,83 @@ +local t = require('luatest') +local cluster = require('luatest.replica_set') +local server = require('luatest.server') + +local g = t.group('gh-8931') +-- +-- gh-8931: +-- There is the leader field in the given informational API, but it is easier +-- to a human to understand human-readable names. We have instance names +-- since #5029. Let's show them in this API. +-- +g.before_all(function(cg) + cg.cluster = cluster:new({}) + local cfg = { + replication = { + server.build_listen_uri('node1', cg.cluster.id), + server.build_listen_uri('node2', cg.cluster.id) + }, + election_mode = 'candidate' + } + cg.node1 = cg.cluster:build_and_add_server({alias = 'node1', box_cfg = cfg}) + cg.node2 = cg.cluster:build_and_add_server({alias = 'node2', box_cfg = cfg}) + cg.cluster:start() +end) + +g.before_test('test_leader_has_no_name', function(cg) + cg.node1:wait_until_election_leader_found() + cg.node2:wait_until_election_leader_found() +end) + +g.test_leader_has_no_name = function(cg) + cg.node1:exec(function() + local function is_null(val) + if val == nil and val then + return true + end + return false + end + t.assert(is_null(box.info.election.leader_name)) + end) +end + +g.before_test('test_leader_has_a_name', function(cg) + cg.node1:exec(function() + box.cfg{instance_name = 'node1'} + end) + cg.node2:exec(function() + box.cfg{instance_name = 'node2'} + end) +end) + +g.test_leader_has_a_name = function(cg) + cg.cluster:get_leader():exec(function() + t.assert_equals(box.info.election.leader_name, box.info.name) + end) +end + +g.before_test('test_no_leader', function(cg) + cg.node1:exec(function() + box.cfg{election_mode = 'off'} + end) + cg.node2:exec(function() + box.cfg{election_mode = 'off'} + end) + cg.node1:wait_for_election_state('follower') + cg.node2:wait_for_election_state('follower') +end) + +g.test_no_leader = function(cg) + cg.node1:exec(function() + local function is_nil(val) + if val == nil and not val then + return true + end + return false + end + t.assert(is_nil(box.info.election.leader_name)) + end) +end + +g.after_all(function(cg) + cg.cluster:drop() +end)