[Tarantool-patches] [PATCH v2 12/11] error: introduce box.info.ro_reason
Vladislav Shpilevoy
v.shpilevoy at tarantool.org
Sat Nov 13 02:25:43 MSK 2021
Follow-up #5568
@TarantoolBot document
Title: box.info.ro_reason
The new `box.info` field - `ro_reason` - is `nil` on a writable
instance. On a read-only instance `box.info.ro == true` it reports
an error reason. Currently the list is
- `'election'` - `box.cfg.election_mode` is not `'off'` and this
instance is not a leader. See `box.info.election` for details.
- `'synchro'` - the synchro queue is owned by some other instance.
For details see `box.info.synchro`.
- `'config'` - `box.cfg.read_only` is true;
- `'orphan'` - the instance is in orphan state.
---
diff --git a/changelogs/unreleased/gh-5568-readonly-reason.md b/changelogs/unreleased/gh-5568-readonly-reason.md
index f3a2db986..d2985eede 100644
--- a/changelogs/unreleased/gh-5568-readonly-reason.md
+++ b/changelogs/unreleased/gh-5568-readonly-reason.md
@@ -1,4 +1,6 @@
## feature/core
* Error objects with the code `box.error.READONLY` now have additional fields
- explaining why the error happened (gh-5568).
+ explaining why the error happened.
+ Also there is a new field `box.info.ro_reason`. It is `nil` on a writable
+ instance, but reports a reason when `box.info.ro` is true (gh-5568).
diff --git a/src/box/box.cc b/src/box/box.cc
index f776c82c1..c3bdff2f5 100644
--- a/src/box/box.cc
+++ b/src/box/box.cc
@@ -174,6 +174,20 @@ box_update_ro_summary(void)
fiber_cond_broadcast(&ro_cond);
}
+const char *
+box_ro_reason(void)
+{
+ if (raft_is_ro(box_raft()))
+ return "election";
+ if (txn_limbo_is_ro(&txn_limbo))
+ return "synchro";
+ if (is_ro)
+ return "config";
+ if (is_orphan)
+ return "orphan";
+ assert(false);
+}
+
static int
box_check_writable(void)
{
diff --git a/src/box/box.h b/src/box/box.h
index 14278a294..83fdba332 100644
--- a/src/box/box.h
+++ b/src/box/box.h
@@ -152,6 +152,13 @@ box_do_set_orphan(bool orphan);
void
box_update_ro_summary(void);
+/**
+ * Get the reason why the instance is read only if it is. Can't be called on a
+ * writable instance.
+ */
+const char *
+box_ro_reason(void);
+
/**
* Iterate over all spaces and save them to the
* snapshot file.
diff --git a/src/box/lua/info.c b/src/box/lua/info.c
index 6e13041b2..cc0790077 100644
--- a/src/box/lua/info.c
+++ b/src/box/lua/info.c
@@ -321,6 +321,16 @@ lbox_info_ro(struct lua_State *L)
return 1;
}
+static int
+lbox_info_ro_reason(struct lua_State *L)
+{
+ if (box_is_ro())
+ lua_pushstring(L, box_ro_reason());
+ else
+ lua_pushnil(L);
+ return 1;
+}
+
/*
* Tarantool 1.6.x compat
*/
@@ -635,6 +645,7 @@ static const struct luaL_Reg lbox_info_dynamic_meta[] = {
{"signature", lbox_info_signature},
{"vclock", lbox_info_vclock},
{"ro", lbox_info_ro},
+ {"ro_reason", lbox_info_ro_reason},
{"replication", lbox_info_replication},
{"replication_anon", lbox_info_replication_anon},
{"status", lbox_info_status},
diff --git a/test/replication-luatest/gh_5568_read_only_reason_test.lua b/test/replication-luatest/gh_5568_read_only_reason_test.lua
index 5e67029c0..13682cbba 100644
--- a/test/replication-luatest/gh_5568_read_only_reason_test.lua
+++ b/test/replication-luatest/gh_5568_read_only_reason_test.lua
@@ -53,6 +53,10 @@ local read_only_msg = "Can't modify data on a read-only instance - "
-- Read-only because of box.cfg{read_only = true}.
--
g.test_read_only_reason_cfg = function(g)
+ t.assert_equals(g.master:exec(function()
+ return box.info.ro_reason
+ end), nil, "no ro reason");
+
local ok, err = g.master:exec(function()
box.cfg{read_only = true}
local ok, err = pcall(box.schema.create_space, 'test')
@@ -61,6 +65,9 @@ g.test_read_only_reason_cfg = function(g)
t.assert(not ok, 'fail ddl')
t.assert_str_contains(err.message, read_only_msg..
'box.cfg.read_only is true')
+ t.assert_equals(g.master:exec(function()
+ return box.info.ro_reason
+ end), "config", "ro reason config");
t.assert_covers(err, {
reason = 'state',
state = 'read_only',
@@ -93,6 +100,9 @@ g.test_read_only_reason_orphan = function(g)
end, {fake_uri})
t.assert(not ok, 'fail ddl')
t.assert_str_contains(err.message, read_only_msg..'it is an orphan')
+ t.assert_equals(g.master:exec(function()
+ return box.info.ro_reason
+ end), "orphan", "ro reason orphan");
t.assert_covers(err, {
reason = 'state',
state = 'orphan',
@@ -124,6 +134,9 @@ g.test_read_only_reason_election_no_leader = function(g)
t.assert(err.term, 'has term')
t.assert_str_contains(err.message, read_only_msg..('state is election '..
'%s with term %s'):format(err.state, err.term))
+ t.assert_equals(g.master:exec(function()
+ return box.info.ro_reason
+ end), "election", "ro reason election");
t.assert_covers(err, {
reason = 'election',
state = 'follower',
@@ -158,6 +171,9 @@ g.test_read_only_reason_election_has_leader = function(g)
t.assert_str_contains(err.message, read_only_msg..('state is election '..
'%s with term %s, leader is %s (%s)'):format(
err.state, err.term, err.leader_id, err.leader_uuid))
+ t.assert_equals(g.replica:exec(function()
+ return box.info.ro_reason
+ end), "election", "ro reason election");
t.assert_covers(err, {
reason = 'election',
state = 'follower',
@@ -200,6 +216,9 @@ g.test_read_only_reason_synchro = function(g)
t.assert_str_contains(err.message, read_only_msg..('synchro queue with '..
'term %s belongs to %s (%s)'):format(err.term,
err.queue_owner_id, err.queue_owner_uuid))
+ t.assert_equals(g.replica:exec(function()
+ return box.info.ro_reason
+ end), "synchro", "ro reason synchro");
t.assert_covers(err, {
reason = 'synchro',
queue_owner_id = g.master:instance_id(),
@@ -258,6 +277,9 @@ g.test_read_only_reason_election_has_leader_no_uuid = function(g)
t.assert_str_contains(err.message, read_only_msg..('state is election %s '..
'with term %s, leader is %s'):format(err.state,
err.term, err.leader_id))
+ t.assert_equals(g.replica:exec(function()
+ return box.info.ro_reason
+ end), "election", "ro reason election");
t.assert_covers(err, {
reason = 'election',
state = 'follower',
@@ -295,6 +317,9 @@ g.test_read_only_reason_synchro_no_uuid = function(g)
t.assert_str_contains(err.message, read_only_msg..('synchro queue with '..
'term %s belongs to %s'):format(err.term,
err.queue_owner_id))
+ t.assert_equals(g.replica:exec(function()
+ return box.info.ro_reason
+ end), "synchro", "ro reason synchro");
t.assert_covers(err, {
reason = 'synchro',
queue_owner_id = leader_id,
More information about the Tarantool-patches
mailing list