[PATCH v2 1/2] Add function to wait for box.info.ro change

Vladimir Davydov vdavydov.dev at gmail.com
Thu Feb 1 20:35:05 MSK 2018


This patch adds a new Lua function 'box.info.ro.wait(ro, timeout)'.
The new function blocks the caller until 'box.info.ro' equals 'ro'
or timeout passes or the fiber is cancelled.

Needed for #2537
---
 src/box/box.cc         | 21 ++++++++++++++
 src/box/box.h          | 10 +++++++
 src/box/lua/info.c     | 30 +++++++++++++++++++
 test/box/info.result   | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++
 test/box/info.test.lua | 29 +++++++++++++++++++
 5 files changed, 168 insertions(+)

diff --git a/src/box/box.cc b/src/box/box.cc
index c33243a8..1146a053 100644
--- a/src/box/box.cc
+++ b/src/box/box.cc
@@ -103,6 +103,7 @@ static struct gc_consumer *backup_gc;
  */
 static bool is_box_configured = false;
 static bool is_ro = true;
+static fiber_cond ro_cond;
 
 /**
  * The following flag is set if the instance failed to
@@ -193,6 +194,7 @@ void
 box_set_ro(bool ro)
 {
 	is_ro = ro;
+	fiber_cond_broadcast(&ro_cond);
 }
 
 bool
@@ -201,6 +203,21 @@ box_is_ro(void)
 	return is_ro || is_orphan;
 }
 
+int
+box_wait_ro(bool ro, double timeout)
+{
+	double deadline = ev_monotonic_now(loop()) + timeout;
+	while (is_ro != ro) {
+		if (fiber_cond_wait_deadline(&ro_cond, deadline) != 0)
+			return -1;
+		if (fiber_is_cancelled()) {
+			diag_set(FiberIsCancelled);
+			return -1;
+		}
+	}
+	return 0;
+}
+
 void
 box_clear_orphan(void)
 {
@@ -1431,6 +1448,8 @@ box_free(void)
 		wal_thread_stop();
 		identifier_destroy();
 	}
+
+	fiber_cond_destroy(&ro_cond);
 }
 
 static void
@@ -1595,6 +1614,8 @@ tx_prio_cb(struct ev_loop *loop, ev_watcher *watcher, int events)
 void
 box_init(void)
 {
+	fiber_cond_create(&ro_cond);
+
 	user_cache_init();
 	/*
 	 * The order is important: to initialize sessions,
diff --git a/src/box/box.h b/src/box/box.h
index 730cf572..5c87da9d 100644
--- a/src/box/box.h
+++ b/src/box/box.h
@@ -90,6 +90,16 @@ bool
 box_is_ro(void);
 
 /**
+ * Wait until the instance switches to a desired mode.
+ * \param ro wait read-only if set or read-write if unset
+ * \param timeout max time to wait
+ * \retval -1 timeout or fiber is cancelled
+ * \retval 0 success
+ */
+int
+box_wait_ro(bool ro, double timeout);
+
+/**
  * Switch this instance from 'orphan' to 'running' state.
  * Called on initial configuration as soon as this instance
  * synchronizes with enough replicas to form a quorum.
diff --git a/src/box/lua/info.c b/src/box/lua/info.c
index 8e8fd9d9..bab9a4cd 100644
--- a/src/box/lua/info.c
+++ b/src/box/lua/info.c
@@ -222,9 +222,39 @@ lbox_info_signature(struct lua_State *L)
 }
 
 static int
+lbox_info_ro_wait(struct lua_State *L)
+{
+	int index = lua_gettop(L);
+	if (index < 1 || !lua_isboolean(L, 1))
+		return luaL_error(L, "Usage: box.info.ro.wait(true|false[, timeout])");
+	bool ro = lua_toboolean(L, 1);
+	double timeout = TIMEOUT_INFINITY;
+	if (index > 1)
+		timeout = luaL_checknumber(L, 2);
+	if (box_wait_ro(ro, timeout) != 0)
+		return luaT_error(L);
+	return 0;
+}
+
+static int
+lbox_info_ro_index(struct lua_State *L)
+{
+	const char *field = luaL_checkstring(L, -1);
+	if (strcmp(field, "wait") == 0) {
+		lua_pushcfunction(L, lbox_info_ro_wait);
+		return 1;
+	}
+	return 0;
+}
+
+static int
 lbox_info_ro(struct lua_State *L)
 {
 	lua_pushboolean(L, box_is_ro());
+	lua_newtable(L);
+	lua_pushcfunction(L, lbox_info_ro_index);
+	lua_setfield(L, -2, "__index");
+	lua_setmetatable(L, -2);
 	return 1;
 }
 
diff --git a/test/box/info.result b/test/box/info.result
index 600f1799..c63185e5 100644
--- a/test/box/info.result
+++ b/test/box/info.result
@@ -1,3 +1,6 @@
+fiber = require('fiber')
+---
+...
 -- Test Lua from admin console. Whenever producing output,
 -- make sure it's a valid YAML
 box.info.unknown_variable
@@ -119,3 +122,78 @@ box.info().ro == box.info.server.ro
 ---
 - true
 ...
+--
+-- box.info.ro.wait
+--
+box.info.ro.wait() -- error
+---
+- error: 'Usage: box.info.ro.wait(true|false[, timeout])'
+...
+box.info.ro.wait(123) -- error
+---
+- error: 'Usage: box.info.ro.wait(true|false[, timeout])'
+...
+box.info.ro.wait(false, "abc") -- error
+---
+- error: 'bad argument #2 to ''?'' (number expected, got string)'
+...
+box.info.ro -- false
+---
+- false
+...
+box.info.ro.wait(false) -- success
+---
+...
+box.info.ro.wait(true, 0.001) -- timeout
+---
+- error: timed out
+...
+status, err = nil
+---
+...
+f = fiber.create(function() status, err = pcall(box.info.ro.wait, true) end)
+---
+...
+fiber.sleep(0.001)
+---
+...
+f:cancel()
+---
+...
+fiber.sleep(0.001)
+---
+...
+status, err -- fiber is cancelled
+---
+- false
+- fiber is cancelled
+...
+ch = fiber.channel(1)
+---
+...
+_ = fiber.create(function() box.info.ro.wait(true) ch:put(box.info.ro) end)
+---
+...
+fiber.sleep(0.001)
+---
+...
+box.cfg{read_only = true}
+---
+...
+ch:get() -- true
+---
+- true
+...
+_ = fiber.create(function() box.info.ro.wait(false) ch:put(box.info.ro) end)
+---
+...
+fiber.sleep(0.001)
+---
+...
+box.cfg{read_only = false}
+---
+...
+ch:get() -- false
+---
+- false
+...
diff --git a/test/box/info.test.lua b/test/box/info.test.lua
index 4b6de6b1..62da0473 100644
--- a/test/box/info.test.lua
+++ b/test/box/info.test.lua
@@ -1,3 +1,5 @@
+fiber = require('fiber')
+
 -- Test Lua from admin console. Whenever producing output,
 -- make sure it's a valid YAML
 box.info.unknown_variable
@@ -29,3 +31,30 @@ box.info().server.id == box.info.id
 box.info().server.uuid == box.info.uuid
 box.info().server.lsn == box.info.lsn
 box.info().ro == box.info.server.ro
+
+--
+-- box.info.ro.wait
+--
+box.info.ro.wait() -- error
+box.info.ro.wait(123) -- error
+box.info.ro.wait(false, "abc") -- error
+box.info.ro -- false
+box.info.ro.wait(false) -- success
+box.info.ro.wait(true, 0.001) -- timeout
+
+status, err = nil
+f = fiber.create(function() status, err = pcall(box.info.ro.wait, true) end)
+fiber.sleep(0.001)
+f:cancel()
+fiber.sleep(0.001)
+status, err -- fiber is cancelled
+
+ch = fiber.channel(1)
+_ = fiber.create(function() box.info.ro.wait(true) ch:put(box.info.ro) end)
+fiber.sleep(0.001)
+box.cfg{read_only = true}
+ch:get() -- true
+_ = fiber.create(function() box.info.ro.wait(false) ch:put(box.info.ro) end)
+fiber.sleep(0.001)
+box.cfg{read_only = false}
+ch:get() -- false
-- 
2.11.0




More information about the Tarantool-patches mailing list