[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