diff --git a/src/box/box.cc b/src/box/box.cc index a4af9caa02bd1d899767e99a25a3db0a49dca52c..8c9f0b41c64e73de8d6a812b3c3bb5d456e4e713 100644 --- a/src/box/box.cc +++ b/src/box/box.cc @@ -90,10 +90,22 @@ static void process_ro(struct port *port, struct request *request) { if (!iproto_type_is_select(request->type)) - tnt_raise(LoggedError, ER_SECONDARY); + tnt_raise(LoggedError, ER_READONLY); return process_rw(port, request); } +void +box_set_ro(bool ro) +{ + box_process = ro ? process_ro : process_rw; +} + +bool +box_is_ro(void) +{ + return box_process == process_ro; +} + static void recover_row(void *param __attribute__((unused)), struct xrow_header *row) { @@ -237,7 +249,6 @@ box_leave_local_standby_mode(void *data __attribute__((unused))) stat_cleanup(stat_base, IPROTO_TYPE_DML_MAX); box_set_wal_mode(cfg_gets("wal_mode")); - box_process = process_rw; if (recovery_has_remote(recovery)) recovery_follow_remote(recovery); diff --git a/src/box/box.h b/src/box/box.h index 8bb7f8751759d976a8ceb68bf10399583b88fe08..970dad6d78e101a27876755f1fe77989e5535e66 100644 --- a/src/box/box.h +++ b/src/box/box.h @@ -62,7 +62,12 @@ void box_free(void); typedef void (*box_process_func)(struct port *port, struct request *request); /** For read-write operations. */ extern box_process_func box_process; -/** For read-only port. */ + +void +box_set_ro(bool ro); + +bool +box_is_ro(void); /** Non zero if snapshot is in progress. */ extern int snapshot_pid; diff --git a/src/box/cluster.cc b/src/box/cluster.cc index 5f142605a6ae0a8086165eb6824972c92bf60e2c..356f49ff313f4fa86dfe74d4322b4fbe4edf9c21 100644 --- a/src/box/cluster.cc +++ b/src/box/cluster.cc @@ -26,6 +26,7 @@ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ +#include "box.h" #include "cluster.h" #include "recovery.h" #include "exception.h" @@ -65,5 +66,6 @@ cluster_set_server(const tt_uuid *server_uuid, uint32_t server_id) /* Assign local server id */ assert(r->server_id == 0); r->server_id = server_id; + box_set_ro(false); } } diff --git a/src/box/lua/info.cc b/src/box/lua/info.cc index ec13926417ad5f5ae6fd001d809b03f2394dbe47..f1616df17f60b5bb94d5b3490b62e34f65a7ba72 100644 --- a/src/box/lua/info.cc +++ b/src/box/lua/info.cc @@ -59,11 +59,6 @@ lbox_info_recovery_last_update_tstamp(struct lua_State *L) static int lbox_info_server(struct lua_State *L) { - if (recovery->server_id == 0) { - lua_pushnil(L); - return 1; - } - lua_createtable(L, 0, 2); lua_pushliteral(L, "id"); lua_pushinteger(L, recovery->server_id); @@ -72,8 +67,11 @@ lbox_info_server(struct lua_State *L) lua_pushlstring(L, tt_uuid_str(&recovery->server_uuid), UUID_STR_LEN); lua_settable(L, -3); lua_pushliteral(L, "lsn"); - luaL_pushnumber64(L, vclock_get(&recovery->vclock, - recovery->server_id)); + luaL_pushinumber64(L, vclock_get(&recovery->vclock, + recovery->server_id)); + lua_settable(L, -3); + lua_pushliteral(L, "ro"); + lua_pushboolean(L, box_is_ro()); lua_settable(L, -3); return 1; diff --git a/src/errcode.h b/src/errcode.h index 26cbd753527d5ea92dca2abe0a4f2fc3edeeb6c2..b1c472972e6f0d5ba789f2cb95fc281fcd07b622 100644 --- a/src/errcode.h +++ b/src/errcode.h @@ -56,7 +56,7 @@ enum { TNT_ERRMSG_MAX = 512 }; /* 4 */_(ER_TUPLE_NOT_FOUND, 2, "Tuple doesn't exist in index %u") \ /* 5 */_(ER_UNSUPPORTED, 2, "%s does not support %s") \ /* 6 */_(ER_NONMASTER, 2, "Can't modify data on a replication slave. My master is: %s") \ - /* 7 */_(ER_SECONDARY, 2, "Can't modify data upon a request on the secondary port.") \ + /* 7 */_(ER_READONLY, 2, "Can't modify data because this server in read-only mode.") \ /* 8 */_(ER_INJECTION, 2, "Error injection '%s'") \ /* 9 */_(ER_CREATE_SPACE, 2, "Failed to create space %u: %s") \ /* 10 */_(ER_SPACE_EXISTS, 2, "Space '%s' already exists") \ diff --git a/test/box/misc.result b/test/box/misc.result index b2397d2a88830c8510156437ed4eaf6ebc307ca9..9a29bfda137c96b9fb9f4ba52f2fe8c05e0a9e0a 100644 --- a/test/box/misc.result +++ b/test/box/misc.result @@ -182,11 +182,11 @@ end; t; --- - - 'box.error.EXACT_MATCH : 19' - - 'box.error.SECONDARY : 7' + - 'box.error.NO_SUCH_TRIGGER : 34' - 'box.error.CLUSTER_ID_IS_RO : 65' - 'box.error.INDEX_TYPE : 13' - 'box.error.CLUSTER_ID_MISMATCH : 63' - - 'box.error.FIELD_TYPE : 23' + - 'box.error.MEMORY_ISSUE : 2' - 'box.error.KEY_PART_TYPE : 18' - 'box.error.CREATE_FUNCTION : 50' - 'box.error.SOPHIA : 60' @@ -213,7 +213,7 @@ t; - 'box.error.ROLE_EXISTS : 83' - 'box.error.NO_SUCH_ROLE : 82' - 'box.error.NO_ACTIVE_TRANSACTION : 80' - - 'box.error.SPLICE : 25' + - 'box.error.TUPLE_FOUND : 3' - 'box.error.FIELD_TYPE_MISMATCH : 24' - 'box.error.UNSUPPORTED : 5' - 'box.error.INVALID_MSGPACK : 20' @@ -221,7 +221,7 @@ t; - 'box.error.ALTER_SPACE : 12' - 'box.error.ACTIVE_TRANSACTION : 79' - 'box.error.NO_CONNECTION : 77' - - 'box.error.DROP_SPACE : 11' + - 'box.error.FIELD_TYPE : 23' - 'box.error.INVALID_XLOG_NAME : 75' - 'box.error.INVALID_XLOG : 74' - 'box.error.REPLICA_MAX : 73' @@ -235,34 +235,34 @@ t; - 'box.error.INVALID_ORDER : 68' - 'box.error.CFG : 59' - 'box.error.SPACE_FIELD_COUNT : 38' - - 'box.error.SPACE_ACCESS_DENIED : 55' + - 'box.error.UNKNOWN : 0' - 'box.error.NO_SUCH_FIELD : 37' - 'box.error.LOCAL_SERVER_IS_NOT_ACTIVE : 61' - 'box.error.RELOAD_CFG : 58' - 'box.error.PROC_RET : 21' - 'box.error.INJECTION : 8' - - 'box.error.PROC_LUA : 32' + - 'box.error.FUNCTION_MAX : 54' - 'box.error.ILLEGAL_PARAMS : 1' - - 'box.error.TUPLE_NOT_ARRAY : 22' - 'box.error.TUPLE_FORMAT_LIMIT : 16' + - 'box.error.USER_MAX : 56' - 'box.error.INVALID_UUID : 64' - - 'box.error.UNKNOWN : 0' + - 'box.error.SPLICE : 25' - 'box.error.TIMEOUT : 78' - - 'box.error.TUPLE_FOUND : 3' - - 'box.error.MEMORY_ISSUE : 2' - - 'box.error.NO_SUCH_TRIGGER : 34' + - 'box.error.MORE_THAN_ONE_TUPLE : 41' + - 'box.error.NO_SUCH_SPACE : 36' + - 'box.error.PROC_LUA : 32' - 'box.error.UPDATE_FIELD : 29' - 'box.error.ARG_TYPE : 26' - - 'box.error.NO_SUCH_SPACE : 36' - 'box.error.INDEX_FIELD_COUNT : 39' - - 'box.error.MORE_THAN_ONE_TUPLE : 41' + - 'box.error.READONLY : 7' - 'box.error.DROP_PRIMARY_KEY : 17' + - 'box.error.DROP_SPACE : 11' - 'box.error.UNKNOWN_REQUEST_TYPE : 48' - 'box.error.INVALID_XLOG_ORDER : 76' - - 'box.error.FUNCTION_MAX : 54' + - 'box.error.SPACE_ACCESS_DENIED : 55' - 'box.error.NO_SUCH_USER : 45' - - 'box.error.USER_MAX : 56' - 'box.error.UNKNOWN_UPDATE_OP : 28' + - 'box.error.TUPLE_NOT_ARRAY : 22' - 'box.error.NO_SUCH_PROC : 33' - 'box.error.FUNCTION_ACCESS_DENIED : 53' ... diff --git a/test/lib/sql_ast.py b/test/lib/sql_ast.py index 3b0741745ef0322407fda3bda7c365f811ea66a9..f49a42ac6f8bb2ada158b1dcabb4cbfe01bcff38 100644 --- a/test/lib/sql_ast.py +++ b/test/lib/sql_ast.py @@ -25,7 +25,7 @@ ER = { 4: "ER_TUPLE_NOT_FOUND" , 5: "ER_UNSUPPORTED" , 6: "ER_NONMASTER" , - 7: "ER_SECONDARY" , + 7: "ER_READONLY" , 8: "ER_INJECTION" , 9: "ER_CREATE_SPACE" , 10: "ER_SPACE_EXISTS" , diff --git a/test/replication/hot_standby.result b/test/replication/hot_standby.result index 0f939412486f09b1b533d1173cc859b3281fafcb..d2b066d47a98c1c6fc952afcf7743f088c45fd28 100644 --- a/test/replication/hot_standby.result +++ b/test/replication/hot_standby.result @@ -12,7 +12,7 @@ box.schema.user.grant('guest', 'read,write,execute', 'universe') fiber = require('fiber'); --- ... -while box.info.server == nil do fiber.sleep(0.01) end; +while box.info.server.id == 0 do fiber.sleep(0.01) end; --- ... while box.space['_priv']:len() < 1 do fiber.sleep(0.001) end; diff --git a/test/replication/hot_standby.test.lua b/test/replication/hot_standby.test.lua index 82c5165dfdd6a7e70fe23595a3e160e58cf77671..560207fb6b5dd5f2b9495272dc252a75a29a32f8 100644 --- a/test/replication/hot_standby.test.lua +++ b/test/replication/hot_standby.test.lua @@ -9,7 +9,7 @@ box.schema.user.grant('guest', 'read,write,execute', 'universe') --# setopt delimiter ';' --# set connection default, hot_standby, replica fiber = require('fiber'); -while box.info.server == nil do fiber.sleep(0.01) end; +while box.info.server.id == 0 do fiber.sleep(0.01) end; while box.space['_priv']:len() < 1 do fiber.sleep(0.001) end; do local pri_id = '' diff --git a/test/replication/readonly.result b/test/replication/readonly.result new file mode 100644 index 0000000000000000000000000000000000000000..9a3b22b210fa4119b55ef7fd00086806a7e14693 --- /dev/null +++ b/test/replication/readonly.result @@ -0,0 +1,41 @@ +box.schema.user.grant('guest', 'read,write,execute', 'universe') +--- +... +box.info.server.id +--- +- 2 +... +box.info.server.ro +--- +- false +... +box.info.server.lsn +--- +- 0 +... +------------------------------------------------------------- +replica is read-only until receive self server_id in _cluster +------------------------------------------------------------- +box.cfg{replication_source = ""} +--- +... +box.info.server.id +--- +- 0 +... +box.info.server.ro +--- +- true +... +box.info.server.lsn +--- +- -1 +... +space = box.schema.create_space("ro") +--- +- error: Can't modify data because this server in read-only mode. +... +box.info.vclock[2] +--- +- null +... diff --git a/test/replication/readonly.test.py b/test/replication/readonly.test.py new file mode 100644 index 0000000000000000000000000000000000000000..24789c51a77e804d1b4b369e189a7d2e7bc5251c --- /dev/null +++ b/test/replication/readonly.test.py @@ -0,0 +1,46 @@ +import os +from glob import iglob as glob +from lib.tarantool_server import TarantoolServer + +# master server +master = server +master_id = master.get_param('server')['id'] + +master.admin("box.schema.user.grant('guest', 'read,write,execute', 'universe')") + +replica = TarantoolServer(server.ini) +replica.script = 'replication/replica.lua' +replica.vardir = os.path.join(server.vardir, 'replica') +replica.rpl_master = master +replica.deploy() +replica.wait_lsn(master_id, master.get_lsn(master_id)) +replica_id = replica.get_param('server')['id'] +replica.admin('box.info.server.id') +replica.admin('box.info.server.ro') +replica.admin('box.info.server.lsn') +replica.stop() + +print '-------------------------------------------------------------' +print 'replica is read-only until receive self server_id in _cluster' +print '-------------------------------------------------------------' + +# Remove xlog retrived by SUBSCRIBE +filename = str(0).zfill(20) + ".xlog" +wal = os.path.join(replica.vardir, filename) +os.remove(wal) + +# Start replica without master +server.stop() +replica.start() +replica.admin('box.cfg{replication_source = ""}') + +# Check that replica in read-only mode +replica.admin('box.info.server.id') +replica.admin('box.info.server.ro') +replica.admin('box.info.server.lsn') +replica.admin('space = box.schema.create_space("ro")') +replica.admin('box.info.vclock[%d]' % replica_id) + +replica.stop() +replica.cleanup(True) +server.deploy() diff --git a/test/replication/swap.result b/test/replication/swap.result index 5badc7052d0fff463c4f1fca6ffc8953d3e48e84..7264bc985d053c0fb122f80eeda232b9e9e736eb 100644 --- a/test/replication/swap.result +++ b/test/replication/swap.result @@ -4,7 +4,7 @@ box.schema.user.create('test', { password = 'pass123456'}) box.schema.user.grant('test', 'read,write,execute', 'universe') --- ... -while box.info.server == nil do require('fiber').sleep(0.01) end +while box.info.server.id == 0 do require('fiber').sleep(0.01) end --- ... while box.space['_priv']:len() < 1 do require('fiber').sleep(0.01) end diff --git a/test/replication/swap.test.py b/test/replication/swap.test.py index eb3021ebdad958602e7d0454f9309e03a5765cbc..74cd85a7b37e7d7042bb79fc03c6f29b04ba8c02 100644 --- a/test/replication/swap.test.py +++ b/test/replication/swap.test.py @@ -31,7 +31,7 @@ replica = TarantoolServer() replica.script = "replication/replica.lua" replica.vardir = os.path.join(server.vardir, 'replica') replica.deploy() -replica.admin("while box.info.server == nil do require('fiber').sleep(0.01) end") +replica.admin("while box.info.server.id == 0 do require('fiber').sleep(0.01) end") replica.uri = '%s:%s@%s' % (LOGIN, PASSWORD, replica.sql.uri) replica.admin("while box.space['_priv']:len() < 1 do require('fiber').sleep(0.01) end") replica.sql.py_con.authenticate(LOGIN, PASSWORD)