[PATCH v4 1/2] Add Lua helpers to wait for server to switch to/from ro mode

Vladimir Davydov vdavydov.dev at gmail.com
Sat Feb 10 19:48:59 MSK 2018


This patch adds two new Lua function, box.ctl.wait_ro() and
box.ctl.wait_rw(), that block the current fiber until the
server switches to read-only or read-write mode, respectively.
Both functions take the timeout as an optional argument.

Needed for #2537
---
 src/box/CMakeLists.txt |   1 +
 src/box/box.cc         |  21 ++++++++++
 src/box/box.h          |  10 +++++
 src/box/lua/ctl.c      |  78 +++++++++++++++++++++++++++++++++++
 src/box/lua/ctl.h      |  48 ++++++++++++++++++++++
 src/box/lua/init.c     |   2 +
 test/box/info.result   | 107 +++++++++++++++++++++++++++++++++++++++++++++++++
 test/box/info.test.lua |  38 ++++++++++++++++++
 test/box/misc.result   |   1 +
 9 files changed, 306 insertions(+)
 create mode 100644 src/box/lua/ctl.c
 create mode 100644 src/box/lua/ctl.h

diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index bdbbbb07..e420fe3e 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -122,6 +122,7 @@ add_library(box STATIC
     lua/misc.cc
     lua/info.c
     lua/stat.c
+    lua/ctl.c
     lua/error.cc
     lua/session.c
     lua/net_box.c
diff --git a/src/box/box.cc b/src/box/box.cc
index 97b57792..e724de39 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)
 {
@@ -1440,6 +1457,8 @@ box_free(void)
 		wal_thread_stop();
 		identifier_destroy();
 	}
+
+	fiber_cond_destroy(&ro_cond);
 }
 
 static void
@@ -1604,6 +1623,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/ctl.c b/src/box/lua/ctl.c
new file mode 100644
index 00000000..9a105ed5
--- /dev/null
+++ b/src/box/lua/ctl.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2010-2018, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "box/lua/ctl.h"
+
+#include <tarantool_ev.h>
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+#include "lua/utils.h"
+
+#include "box/box.h"
+
+static int
+lbox_ctl_wait_ro(struct lua_State *L)
+{
+	int index = lua_gettop(L);
+	double timeout = TIMEOUT_INFINITY;
+	if (index > 0)
+		timeout = luaL_checknumber(L, 1);
+	if (box_wait_ro(true, timeout) != 0)
+		return luaT_error(L);
+	return 0;
+}
+
+static int
+lbox_ctl_wait_rw(struct lua_State *L)
+{
+	int index = lua_gettop(L);
+	double timeout = TIMEOUT_INFINITY;
+	if (index > 0)
+		timeout = luaL_checknumber(L, 1);
+	if (box_wait_ro(false, timeout) != 0)
+		return luaT_error(L);
+	return 0;
+}
+
+static const struct luaL_Reg lbox_ctl_lib[] = {
+	{"wait_ro", lbox_ctl_wait_ro},
+	{"wait_rw", lbox_ctl_wait_rw},
+	{NULL, NULL}
+};
+
+void
+box_lua_ctl_init(struct lua_State *L)
+{
+	luaL_register_module(L, "box.ctl", lbox_ctl_lib);
+	lua_pop(L, 1);
+}
diff --git a/src/box/lua/ctl.h b/src/box/lua/ctl.h
new file mode 100644
index 00000000..e7c2edd1
--- /dev/null
+++ b/src/box/lua/ctl.h
@@ -0,0 +1,48 @@
+#ifndef INCLUDES_TARANTOOL_LUA_CTL_H
+#define INCLUDES_TARANTOOL_LUA_CTL_H
+
+/*
+ * Copyright 2010-2018, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+struct lua_State;
+
+void
+box_lua_ctl_init(struct lua_State *L);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif /* defined(__cplusplus) */
+
+#endif /* INCLUDES_TARANTOOL_LUA_CTL_H */
diff --git a/src/box/lua/init.c b/src/box/lua/init.c
index 22ecb033..75477584 100644
--- a/src/box/lua/init.c
+++ b/src/box/lua/init.c
@@ -52,6 +52,7 @@
 #include "box/lua/misc.h"
 #include "box/lua/stat.h"
 #include "box/lua/info.h"
+#include "box/lua/ctl.h"
 #include "box/lua/session.h"
 #include "box/lua/net_box.h"
 #include "box/lua/cfg.h"
@@ -243,6 +244,7 @@ box_lua_init(struct lua_State *L)
 	box_lua_misc_init(L);
 	box_lua_info_init(L);
 	box_lua_stat_init(L);
+	box_lua_ctl_init(L);
 	box_lua_session_init(L);
 	box_lua_xlog_init(L);
 	luaopen_net_box(L);
diff --git a/test/box/info.result b/test/box/info.result
index 600f1799..88ec15bc 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,107 @@ box.info().ro == box.info.server.ro
 ---
 - true
 ...
+--
+-- box.ctl.wait_ro and box.ctl.wait_rw
+--
+box.ctl.wait_ro("abc") -- invalid argument
+---
+- error: 'bad argument #1 to ''?'' (number expected, got string)'
+...
+box.ctl.wait_rw("def") -- invalid argument
+---
+- error: 'bad argument #1 to ''?'' (number expected, got string)'
+...
+box.info.ro -- false
+---
+- false
+...
+box.ctl.wait_rw() -- success
+---
+...
+box.ctl.wait_ro(0.001) -- timeout
+---
+- error: timed out
+...
+box.cfg{read_only = true}
+---
+...
+box.ctl.wait_ro() -- success
+---
+...
+box.ctl.wait_rw(0.001) -- timeout
+---
+- error: timed out
+...
+status, err = nil
+---
+...
+f = fiber.create(function() status, err = pcall(box.ctl.wait_rw) end)
+---
+...
+fiber.sleep(0.001)
+---
+...
+f:cancel()
+---
+...
+while f:status() ~= 'dead' do fiber.sleep(0.001) end
+---
+...
+status, err -- fiber is cancelled
+---
+- false
+- fiber is cancelled
+...
+box.cfg{read_only = false}
+---
+...
+status, err = nil
+---
+...
+f = fiber.create(function() status, err = pcall(box.ctl.wait_ro) end)
+---
+...
+fiber.sleep(0.001)
+---
+...
+f:cancel()
+---
+...
+while f:status() ~= 'dead' do fiber.sleep(0.001) end
+---
+...
+status, err -- fiber is cancelled
+---
+- false
+- fiber is cancelled
+...
+ch = fiber.channel(1)
+---
+...
+_ = fiber.create(function() box.ctl.wait_ro() ch:put(box.info.ro) end)
+---
+...
+fiber.sleep(0.001)
+---
+...
+box.cfg{read_only = true}
+---
+...
+ch:get() -- true
+---
+- true
+...
+_ = fiber.create(function() box.ctl.wait_rw() 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..60ee3216 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,39 @@ 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.ctl.wait_ro and box.ctl.wait_rw
+--
+box.ctl.wait_ro("abc") -- invalid argument
+box.ctl.wait_rw("def") -- invalid argument
+box.info.ro -- false
+box.ctl.wait_rw() -- success
+box.ctl.wait_ro(0.001) -- timeout
+box.cfg{read_only = true}
+box.ctl.wait_ro() -- success
+box.ctl.wait_rw(0.001) -- timeout
+
+status, err = nil
+f = fiber.create(function() status, err = pcall(box.ctl.wait_rw) end)
+fiber.sleep(0.001)
+f:cancel()
+while f:status() ~= 'dead' do fiber.sleep(0.001) end
+status, err -- fiber is cancelled
+box.cfg{read_only = false}
+status, err = nil
+f = fiber.create(function() status, err = pcall(box.ctl.wait_ro) end)
+fiber.sleep(0.001)
+f:cancel()
+while f:status() ~= 'dead' do fiber.sleep(0.001) end
+status, err -- fiber is cancelled
+
+ch = fiber.channel(1)
+_ = fiber.create(function() box.ctl.wait_ro() ch:put(box.info.ro) end)
+fiber.sleep(0.001)
+box.cfg{read_only = true}
+ch:get() -- true
+_ = fiber.create(function() box.ctl.wait_rw() ch:put(box.info.ro) end)
+fiber.sleep(0.001)
+box.cfg{read_only = false}
+ch:get() -- false
diff --git a/test/box/misc.result b/test/box/misc.result
index a2b77eb5..cd3af7f9 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -61,6 +61,7 @@ t
   - begin
   - cfg
   - commit
+  - ctl
   - error
   - index
   - info
-- 
2.11.0




More information about the Tarantool-patches mailing list