diff --git a/src/box/alter.cc b/src/box/alter.cc index c02531e87960c23b25dea7a8d2ff0bdf7faa41c7..2e70333a1db5fb5c9d1199a2a571e388d7864a65 100644 --- a/src/box/alter.cc +++ b/src/box/alter.cc @@ -1635,8 +1635,22 @@ on_commit_dd_cluster(struct trigger *trigger, void *event) (void) trigger; struct txn_stmt *stmt = txn_stmt((struct txn *) event); struct tuple *new_tuple = stmt->new_tuple; + + if (new_tuple == NULL) { + uint32_t old_id = tuple_field_u32(stmt->old_tuple, 0); + cluster_del_server(old_id); + return; + } uint32_t id = tuple_field_u32(new_tuple, 0); tt_uuid uuid = tuple_field_uuid(new_tuple, 1); + struct tuple *old_tuple = stmt->old_tuple; + if (old_tuple != NULL) { + uint32_t old_id = tuple_field_u32(old_tuple, 0); + if (id != old_id) { + /* box.space._cluster:update(old, {{'=', 1, new}} */ + cluster_del_server(old_id); + } + } cluster_set_server(&uuid, id); } @@ -1670,8 +1684,10 @@ on_replace_dd_cluster(struct trigger *trigger, void *event) txn_check_autocommit(txn, "Space _cluster"); struct txn_stmt *stmt = txn_stmt(txn); struct tuple *new_tuple = stmt->new_tuple; - if (new_tuple == NULL) - tnt_raise(ClientError, ER_SERVER_ID_IS_RO); + if (new_tuple == NULL) { + trigger_add(&txn->on_commit, &commit_cluster_trigger); + return; + } /* Check fields */ uint32_t server_id = tuple_field_u32(new_tuple, 0); @@ -1682,14 +1698,6 @@ on_replace_dd_cluster(struct trigger *trigger, void *event) if (tt_uuid_is_nil(&server_uuid)) tnt_raise(ClientError, ER_INVALID_UUID, tt_uuid_str(&server_uuid)); - struct tuple *old_tuple = stmt->old_tuple; - if (old_tuple != NULL) { - /* server_id is read-only, other fields can be changed */ - uint32_t old_server_id = tuple_field_u32(old_tuple, 0); - if (server_id != old_server_id) - tnt_raise(ClientError, ER_SERVER_ID_IS_RO, - (unsigned) server_id); - } trigger_add(&txn->on_commit, &commit_cluster_trigger); } diff --git a/src/box/cluster.cc b/src/box/cluster.cc index 356f49ff313f4fa86dfe74d4322b4fbe4edf9c21..031ecff6a681226a6b34d9a7a5087fd710f22cd2 100644 --- a/src/box/cluster.cc +++ b/src/box/cluster.cc @@ -69,3 +69,14 @@ cluster_set_server(const tt_uuid *server_uuid, uint32_t server_id) box_set_ro(false); } } + +void +cluster_del_server(uint32_t server_id) +{ + struct recovery_state *r = recovery; + vclock_del_server(&r->vclock, server_id); + if (r->server_id == server_id) { + r->server_id = 0; + box_set_ro(true); + } +} diff --git a/src/box/cluster.h b/src/box/cluster.h index b98e2ef1b1d2e3d623c65bce6aa17553854c0160..15ce75f038a1e819822a62275ac2f5ceb6ab220a 100644 --- a/src/box/cluster.h +++ b/src/box/cluster.h @@ -105,6 +105,9 @@ cserver_id_is_reserved(uint32_t id) void cluster_set_server(const tt_uuid *server_uuid, uint32_t id); +void +cluster_del_server(uint32_t server_id); + /** }}} **/ #endif diff --git a/src/errcode.h b/src/errcode.h index 41436381a39e11f5cbbf1b8ed3aa02bbef6cd61f..3c1b8fd6b5533065e80d98b6734aa0bf33e1c962 100644 --- a/src/errcode.h +++ b/src/errcode.h @@ -115,7 +115,7 @@ enum { TNT_ERRMSG_MAX = 512 }; /* 63 */_(ER_CLUSTER_ID_MISMATCH, 2, "Cluster id of the replica %s doesn't match cluster id of the master %s") \ /* 64 */_(ER_INVALID_UUID, 2, "Invalid UUID: %s") \ /* 65 */_(ER_CLUSTER_ID_IS_RO, 2, "Can't reset cluster id: it is already assigned") \ - /* 66 */_(ER_SERVER_ID_IS_RO, 2, "Can't reset server id") \ + /* 66 */_(ER_RESERVED66, 2, "Reserved66") \ /* 67 */_(ER_SERVER_ID_IS_RESERVED, 2, "Can't initialize server id with a reserved value %u") \ /* 68 */_(ER_INVALID_ORDER, 2, "Invalid LSN order for server %u: previous LSN = %llu, new lsn = %llu") \ /* 69 */_(ER_MISSING_REQUEST_FIELD, 2, "Missing mandatory field '%s' in request") \ diff --git a/test/box/misc.result b/test/box/misc.result index 5a65842d9dcf5f3287cc162a0fe7840e3e94167c..b58402743425ee14bad1524e63dee097f671a491 100644 --- a/test/box/misc.result +++ b/test/box/misc.result @@ -197,7 +197,6 @@ t; - 'box.error.NO_SUCH_FUNCTION : 51' - 'box.error.TUPLE_NOT_FOUND : 4' - 'box.error.DROP_USER : 44' - - 'box.error.SERVER_ID_IS_RO : 66' - 'box.error.CROSS_ENGINE_TRANSACTION : 81' - 'box.error.MODIFY_INDEX : 14' - 'box.error.PASSWORD_MISMATCH : 47' diff --git a/test/replication/cluster.result b/test/replication/cluster.result index efd3543df86a852b77e870b0f8e630b23e4610df..8be88a719587137e3452a009ac329aa811736783 100644 --- a/test/replication/cluster.result +++ b/test/replication/cluster.result @@ -1,3 +1,6 @@ +------------------------------------------------------------- +gh-434: Assertion if replace _cluster tuple +------------------------------------------------------------- box.space._cluster:replace{1, '8c7ff474-65f9-4abe-81a4-a3e1019bb1ae'} --- - [1, '8c7ff474-65f9-4abe-81a4-a3e1019bb1ae'] @@ -22,15 +25,109 @@ box.info.server.uuid --- - 8c7ff474-65f9-4abe-81a4-a3e1019bb1ae ... -box.space._cluster:delete(1) +box.space._cluster:replace{1, require('uuid').NULL:str()} --- -- error: Can't reset server id +- error: 'Invalid UUID: 00000000-0000-0000-0000-000000000000' ... -box.space._cluster:update(1, {{'=', 1, 10}}) +------------------------------------------------------------- +gh-527: update vclock on delete from box.space._cluster +------------------------------------------------------------- +box.schema.user.grant('guest', 'read,write,execute', 'universe') --- -- error: Can't reset server id ... -box.space._cluster:replace{1, require('uuid').NULL:str()} +box.space._schema:insert{"test", 48} --- -- error: 'Invalid UUID: 00000000-0000-0000-0000-000000000000' +- ['test', 48] +... +box.info.server.id +--- +- 2 +... +box.info.server.ro +--- +- false +... +box.info.server.lsn +--- +- 1 +... +box.info.vclock[2] +--- +- 1 +... +box.space._cluster:delete{2} +--- +- [2, '<replica uuid>'] +... +box.info.server.id +--- +- 0 +... +box.info.server.ro +--- +- true +... +box.info.server.lsn +--- +- -1 +... +box.info.vclock[2] +--- +- null +... +box.space._schema:replace{"test", 48} +--- +- error: Can't modify data because this server in read-only mode. +... +box.space._cluster:insert{10, "<replica uuid>"} +--- +- [10, '<replica uuid>'] +... +box.info.server.id +--- +- 10 +... +box.info.server.ro +--- +- false +... +box.info.server.lsn +--- +- 0 +... +box.info.vclock[2] +--- +- null +... +box.info.vclock[10] +--- +- 0 +... +box.space._cluster:update(10, {{'=', 1, 11}}) +--- +- [11, '<replica uuid>'] +... +box.info.server.id +--- +- 11 +... +box.info.server.ro +--- +- false +... +box.info.server.lsn +--- +- 0 +... +box.info.vclock[2] +--- +- null +... +box.info.vclock[10] +--- +- null +... +box.info.vclock[11] +--- +- 0 ... diff --git a/test/replication/cluster.test.py b/test/replication/cluster.test.py index b909a00082b2c665a038f96b30b6f7ccf375214a..b8b902da4dc662dbbaa09c4aee1e00105a85b85c 100644 --- a/test/replication/cluster.test.py +++ b/test/replication/cluster.test.py @@ -1,9 +1,12 @@ +import os +import sys import re import yaml +from lib.tarantool_server import TarantoolServer -# -# gh-434: Assertion if replace _cluster tuple -# +print '-------------------------------------------------------------' +print 'gh-434: Assertion if replace _cluster tuple' +print '-------------------------------------------------------------' new_uuid = '8c7ff474-65f9-4abe-81a4-a3e1019bb1ae' @@ -36,13 +39,71 @@ server.restart() server.admin("box.info.server.uuid") -# Can't reset server id -server.admin("box.space._cluster:delete(1)") -server.admin("box.space._cluster:update(1, {{'=', 1, 10}})") - # Invalid UUID server.admin("box.space._cluster:replace{1, require('uuid').NULL:str()}") # Cleanup server.stop() server.deploy() + +print '-------------------------------------------------------------' +print 'gh-527: update vclock on delete from box.space._cluster' +print '-------------------------------------------------------------' + +# 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_uuid = replica.get_param('server')['uuid'] +sys.stdout.push_filter(replica_uuid, '<replica uuid>') +replica.admin('box.space._schema:insert{"test", 48}') + +replica.admin('box.info.server.id') +replica.admin('box.info.server.ro') +replica.admin('box.info.server.lsn') # 1 +replica.admin('box.info.vclock[%d]' % replica_id) + +master.admin('box.space._cluster:delete{%d}' % replica_id) +replica.wait_lsn(master_id, master.get_lsn(master_id)) +replica.admin('box.info.server.id') +replica.admin('box.info.server.ro') +replica.admin('box.info.server.lsn') # -1 +replica.admin('box.info.vclock[%d]' % replica_id) +# replica is read-only +replica.admin('box.space._schema:replace{"test", 48}') + +replica_id2 = 10 +master.admin('box.space._cluster:insert{%d, "%s"}' % + (replica_id2, replica_uuid)) +replica.wait_lsn(master_id, master.get_lsn(master_id)) +replica.admin('box.info.server.id') +replica.admin('box.info.server.ro') +replica.admin('box.info.server.lsn') # 0 +replica.admin('box.info.vclock[%d]' % replica_id) +replica.admin('box.info.vclock[%d]' % replica_id2) + +replica_id3 = 11 +server.admin("box.space._cluster:update(%d, {{'=', 1, %d}})" % + (replica_id2, replica_id3)) +replica.wait_lsn(master_id, master.get_lsn(master_id)) +replica.admin('box.info.server.id') +replica.admin('box.info.server.ro') +replica.admin('box.info.server.lsn') # 0 +replica.admin('box.info.vclock[%d]' % replica_id) +replica.admin('box.info.vclock[%d]' % replica_id2) +replica.admin('box.info.vclock[%d]' % replica_id3) + +sys.stdout.pop_filter() + +# Cleanup +server.stop() +server.deploy()