[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