Skip to content

Commit

Permalink
replication: allow to pass instance name as bootstrap_leader
Browse files Browse the repository at this point in the history
Make it possible to specify the bootstrap leader via an instance name in
addition to its URI and UUID.

Closes tarantool#8539

@TarantoolBot document
Title: `box.cfg.bootstrap_leader` accepts instance names now

The option `box.cfg.bootstrap_leader`, which specifies the desired
bootstrap leader when bootstrap_strategy is "config" now accepts
instance names.

For example, this is a valid config without replication:
```lua
box.cfg{
    instance_name = 'main-server',
    bootstrap_strategy = 'config',
    bootstrap_leader = 'main-server'
}
```

When `box.cfg` contains some entries in `replication`, the node will
bootstrap from whatever node which has its `box.cfg.instance_name` set
to the same value as specified in `box.cfg.bootstrap_leader`.

This is an addition to tarantool/doc#3432
  • Loading branch information
sergepetrenko committed Jun 1, 2023
1 parent 606e50c commit 7d800d5
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 18 deletions.
15 changes: 15 additions & 0 deletions changelogs/unreleased/gh-8539-bootstrap-leader-name.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## feature/replication

* Added ability to set `bootstrap_leader` configuration option to the instance
name of the desired bootstrap leader:
```lua
box.cfg{
bootstrap_strategy = 'config',
bootstrap_leader = 'leader-name',
replication = {
...
},
...
}
```
(gh-7999, gh-8539).
36 changes: 26 additions & 10 deletions src/box/box.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1326,7 +1326,7 @@ box_check_instance_uuid(struct tt_uuid *uuid)

/** Fetch an optional node name from the config. */
static int
box_check_node_name(const char *cfg_name, char *out)
box_check_node_name(const char *cfg_name, char *out, bool set_diag)
{
const char *name = cfg_gets(cfg_name);
if (name == NULL) {
Expand All @@ -1335,8 +1335,10 @@ box_check_node_name(const char *cfg_name, char *out)
}
/* Nil name is allowed as Lua box.NULL or nil. Not as "". */
if (!node_name_is_valid(name)) {
diag_set(ClientError, ER_CFG, cfg_name,
"expected a valid name");
if (set_diag) {
diag_set(ClientError, ER_CFG, cfg_name,
"expected a valid name");
}
return -1;
}
strlcpy(out, name, NODE_NAME_SIZE_MAX);
Expand All @@ -1346,7 +1348,7 @@ box_check_node_name(const char *cfg_name, char *out)
static int
box_check_instance_name(char *out)
{
return box_check_node_name("instance_name", out);
return box_check_node_name("instance_name", out, true);
}

static int
Expand All @@ -1357,10 +1359,11 @@ box_check_replicaset_uuid(struct tt_uuid *uuid)

/** Check bootstrap_leader option validity. */
static int
box_check_bootstrap_leader(struct uri *uri, struct tt_uuid *uuid)
box_check_bootstrap_leader(struct uri *uri, struct tt_uuid *uuid, char *name)
{
*uuid = uuid_nil;
uri_create(uri, NULL);
*name = '\0';
const char *source = cfg_gets("bootstrap_leader");
enum bootstrap_strategy strategy = box_check_bootstrap_strategy();
if (strategy != BOOTSTRAP_STRATEGY_CONFIG) {
Expand All @@ -1383,21 +1386,23 @@ box_check_bootstrap_leader(struct uri *uri, struct tt_uuid *uuid)
/* Not a uri. Try uuid then. */
if (box_check_uuid(uuid, "bootstrap_leader", false) == 0)
return 0;
if (box_check_node_name("bootstrap_leader", name, false) == 0)
return 0;
diag_set(ClientError, ER_CFG, "bootstrap_leader",
"the value must be either a uri or a uuid");
"the value must be either a uri, a uuid or a name");
return -1;
}

static int
box_check_replicaset_name(char *out)
{
return box_check_node_name("replicaset_name", out);
return box_check_node_name("replicaset_name", out, true);
}

static int
box_check_cluster_name(char *out)
{
return box_check_node_name("cluster_name", out);
return box_check_node_name("cluster_name", out, true);
}

static enum wal_mode
Expand Down Expand Up @@ -1661,6 +1666,7 @@ box_check_config(void)
struct tt_uuid uuid;
struct uri uri;
struct uri_set uri_set;
char name[NODE_NAME_SIZE_MAX];
box_check_say();
box_check_audit();
if (box_check_flightrec() != 0)
Expand Down Expand Up @@ -1696,7 +1702,7 @@ box_check_config(void)
box_check_replication_sync_timeout();
if (box_check_bootstrap_strategy() == BOOTSTRAP_STRATEGY_INVALID)
diag_raise();
if (box_check_bootstrap_leader(&uri, &uuid) != 0)
if (box_check_bootstrap_leader(&uri, &uuid, name) != 0)
diag_raise();
uri_destroy(&uri);
box_check_readahead(cfg_geti("readahead"));
Expand Down Expand Up @@ -1878,7 +1884,8 @@ static int
box_set_bootstrap_leader(void)
{
return box_check_bootstrap_leader(&cfg_bootstrap_leader_uri,
&cfg_bootstrap_leader_uuid);
&cfg_bootstrap_leader_uuid,
cfg_bootstrap_leader_name);
}

/** Persist this instance as the bootstrap leader in _schema space. */
Expand Down Expand Up @@ -4426,6 +4433,15 @@ box_process_vote(struct ballot *ballot)
vclock_copy(&ballot->vclock, &replicaset.vclock);
vclock_copy(&ballot->gc_vclock, &gc.vclock);
ballot->bootstrap_leader_uuid = bootstrap_leader_uuid;
if (*INSTANCE_NAME != '\0') {
strlcpy(ballot->instance_name, INSTANCE_NAME,
NODE_NAME_SIZE_MAX);
} else if (*cfg_instance_name != '\0') {
strlcpy(ballot->instance_name, cfg_instance_name,
NODE_NAME_SIZE_MAX);
} else {
*ballot->instance_name = '\0';
}
int i = 0;
replicaset_foreach(replica) {
if (replica->id != 0)
Expand Down
1 change: 1 addition & 0 deletions src/box/iproto_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ extern const char *iproto_metadata_key_strs[];
_(CAN_LEAD, 0x07) \
_(BOOTSTRAP_LEADER_UUID, 0x08) \
_(REGISTERED_REPLICA_UUIDS, 0x09) \
_(INSTANCE_NAME, 0x10) \

#define IPROTO_BALLOT_KEY_MEMBER(s, v) IPROTO_BALLOT_ ## s = v,

Expand Down
11 changes: 9 additions & 2 deletions src/box/replication.cc
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ int replication_threads = 1;
bool cfg_replication_anon = true;
struct tt_uuid cfg_bootstrap_leader_uuid;
struct uri cfg_bootstrap_leader_uri;
char cfg_bootstrap_leader_name[NODE_NAME_SIZE_MAX];
char cfg_instance_name[NODE_NAME_SIZE_MAX];

struct replicaset replicaset;
Expand Down Expand Up @@ -948,6 +949,10 @@ applier_is_bootstrap_leader(const struct applier *applier)
{
assert(!tt_uuid_is_nil(&applier->uuid));
if (bootstrap_strategy == BOOTSTRAP_STRATEGY_CONFIG) {
if (*cfg_bootstrap_leader_name != '\0') {
return strcmp(applier->ballot.instance_name,
cfg_bootstrap_leader_name) == 0;
}
if (!tt_uuid_is_nil(&cfg_bootstrap_leader_uuid)) {
return tt_uuid_is_equal(&applier->uuid,
&cfg_bootstrap_leader_uuid);
Expand Down Expand Up @@ -1448,8 +1453,10 @@ replicaset_find_join_master_cfg(void)
if (applier_is_bootstrap_leader(applier))
leader = replica;
}
if (leader == NULL && !tt_uuid_is_equal(&cfg_bootstrap_leader_uuid,
&INSTANCE_UUID)) {
if (leader == NULL &&
!tt_uuid_is_equal(&cfg_bootstrap_leader_uuid, &INSTANCE_UUID) &&
(strcmp(cfg_bootstrap_leader_name, cfg_instance_name) != 0 ||
*cfg_bootstrap_leader_name == '\0')) {
tnt_raise(ClientError, ER_CFG, "bootstrap_leader",
"failed to connect to the bootstrap leader");
}
Expand Down
6 changes: 6 additions & 0 deletions src/box/replication.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,12 @@ extern struct tt_uuid cfg_bootstrap_leader_uuid;
*/
extern struct uri cfg_bootstrap_leader_uri;

/**
* The name of the bootstrap leader configured via the bootstrap_leader
* configuration option.
*/
extern char cfg_bootstrap_leader_name[];

/**
* Configured name of this instance. Might be different from the actual name if
* the configuration is not fully applied yet.
Expand Down
18 changes: 16 additions & 2 deletions src/box/xrow.c
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ mp_sizeof_ballot_max(const struct ballot *ballot)
{
int registered_uuids_size = ballot->registered_replica_uuids_size;
return mp_sizeof_map(1) + mp_sizeof_uint(IPROTO_BALLOT) +
mp_sizeof_map(9) + mp_sizeof_uint(IPROTO_BALLOT_IS_RO_CFG) +
mp_sizeof_map(10) + mp_sizeof_uint(IPROTO_BALLOT_IS_RO_CFG) +
mp_sizeof_bool(ballot->is_ro_cfg) +
mp_sizeof_uint(IPROTO_BALLOT_IS_RO) +
mp_sizeof_bool(ballot->is_ro) +
Expand All @@ -560,6 +560,8 @@ mp_sizeof_ballot_max(const struct ballot *ballot)
mp_sizeof_bool(ballot->can_lead) +
mp_sizeof_uint(IPROTO_BALLOT_BOOTSTRAP_LEADER_UUID) +
mp_sizeof_str(UUID_STR_LEN) +
mp_sizeof_uint(IPROTO_BALLOT_INSTANCE_NAME) +
mp_sizeof_str(NODE_NAME_LEN_MAX) +
mp_sizeof_uint(IPROTO_BALLOT_REGISTERED_REPLICA_UUIDS) +
mp_sizeof_array(registered_uuids_size) +
registered_uuids_size * mp_sizeof_str(UUID_STR_LEN);
Expand All @@ -571,7 +573,8 @@ mp_encode_ballot(char *data, const struct ballot *ballot)
{
data = mp_encode_map(data, 1);
data = mp_encode_uint(data, IPROTO_BALLOT);
data = mp_encode_map(data, 9);
bool has_name = *ballot->instance_name != '\0';
data = mp_encode_map(data, has_name ? 10 : 9);
data = mp_encode_uint(data, IPROTO_BALLOT_IS_RO_CFG);
data = mp_encode_bool(data, ballot->is_ro_cfg);
data = mp_encode_uint(data, IPROTO_BALLOT_IS_RO);
Expand All @@ -588,6 +591,10 @@ mp_encode_ballot(char *data, const struct ballot *ballot)
data = mp_encode_bool(data, ballot->can_lead);
data = mp_encode_uint(data, IPROTO_BALLOT_BOOTSTRAP_LEADER_UUID);
data = xrow_encode_uuid(data, &ballot->bootstrap_leader_uuid);
if (has_name) {
data = mp_encode_uint(data, IPROTO_BALLOT_INSTANCE_NAME);
data = mp_encode_str0(data, ballot->instance_name);
}
data = mp_encode_uint(data, IPROTO_BALLOT_REGISTERED_REPLICA_UUIDS);
data = mp_encode_array(data, ballot->registered_replica_uuids_size);
for (int i = 0; i < ballot->registered_replica_uuids_size; i++) {
Expand Down Expand Up @@ -1791,6 +1798,7 @@ xrow_decode_ballot(const struct xrow_header *row, struct ballot *ballot)
ballot->is_booted = true;
vclock_create(&ballot->vclock);
vclock_create(&ballot->gc_vclock);
*ballot->instance_name = '\0';

if (row->bodycnt == 0)
goto err;
Expand Down Expand Up @@ -1889,6 +1897,12 @@ mp_decode_ballot(const char *data, const char *end,
*is_empty = false;
break;
}
case IPROTO_BALLOT_INSTANCE_NAME:
if (xrow_decode_node_name(&data,
ballot->instance_name) != 0) {
return -1;
}
break;
case IPROTO_BALLOT_REGISTERED_REPLICA_UUIDS: {
if (mp_typeof(*data) != MP_ARRAY)
return -1;
Expand Down
1 change: 1 addition & 0 deletions src/box/xrow.h
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ struct ballot {
struct vclock gc_vclock;
/** The uuid of the instance this node considers a bootstrap leader. */
struct tt_uuid bootstrap_leader_uuid;
char instance_name[NODE_NAME_LEN_MAX];
/** Replica uuids registered in the replica set. */
struct tt_uuid registered_replica_uuids[VCLOCK_MAX];
/** Number of replicas registered in the replica set. */
Expand Down
28 changes: 24 additions & 4 deletions test/replication-luatest/bootstrap_strategy_test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -234,10 +234,26 @@ g_config.test_uuid = function(cg)
end)
end

g_config.after_test('test_uuid', function(cg)
cg.server1:stop()
g_config.before_test('test_name', function(cg)
cg.replica_set = replica_set:new{}
cg.server1 = cg.replica_set:build_and_add_server{
alias = 'server1',
box_cfg = {
bootstrap_strategy = 'config',
bootstrap_leader = 'server1name',
instance_name = 'server1name',
replication = nil,
},
}
end)

g_config.test_name = function(cg)
cg.replica_set:start()
t.helpers.retrying({}, cg.server1.exec, cg.server1, function()
t.assert_equals(box.info.status, 'running', 'The server is running')
end)
end

g_config.before_test('test_replication_without_bootstrap_leader', function(cg)
cg.replica_set = replica_set:new{}
cg.server1 = cg.replica_set:build_and_add_server{
Expand Down Expand Up @@ -335,6 +351,7 @@ end)
local g_config_success = t.group('gh-7999-bootstrap-strategy-config-success', {
{leader = 'server3'},
{leader = uuid3},
{leader = 'server3name'},
})

g_config_success.after_each(function(cg)
Expand All @@ -354,6 +371,7 @@ g_config_success.before_test('test_correct_bootstrap_leader', function(cg)
bootstrap_strategy = 'config',
bootstrap_leader = bootstrap_leader,
instance_uuid = uuid1,
instance_name = 'server1name',
replication = {
server.build_listen_uri('server1', cg.replica_set.id),
server.build_listen_uri('server2', cg.replica_set_a.id),
Expand All @@ -367,13 +385,15 @@ g_config_success.before_test('test_correct_bootstrap_leader', function(cg)
box_cfg = {
replicaset_uuid = uuida,
instance_uuid = uuid2,
instance_name = 'server2name',
}
}
cg.server3 = cg.replica_set_b:build_and_add_server{
alias = 'server3',
box_cfg = {
replicaset_uuid = uuidb,
instance_uuid = uuid3,
instance_name = 'server3name',
},
}
end)
Expand Down Expand Up @@ -418,6 +438,7 @@ g_config_success.before_test('test_wait_only_for_leader', function(cg)
box_cfg = {
replicaset_uuid = uuidb,
instance_uuid = uuid3,
instance_name = 'server3name',
},
}
end)
Expand All @@ -437,8 +458,7 @@ g_config_fail.test_bad_uri_or_uuid = function()
{}, -- empty table.
{'a'}, -- non-empty table.
{3301},
'abracadabra', -- neither a URI or a UUID.
'z2345678-1234-1234-1234-12345678', -- not a UUID.
'1z2345678-1234-1234-1234-12345678', -- not a UUID or a name.
}
local errmsg = "Incorrect value for option 'bootstrap_leader':"
for _, leader in pairs(bad_config) do
Expand Down

0 comments on commit 7d800d5

Please sign in to comment.