[PATCH v2 4/4] Introduce replica local spaces

Vladimir Davydov vdavydov.dev at gmail.com
Mon Jul 9 18:40:12 MSK 2018


This patch introduces a new space option, group_id, which defines how
the space is replicated. If it is 0 (default), the space is replicated
throughout the entire cluster. If it is 1, the space is replica local,
i.e. all changes made to it are invisible to other replicas in the
cluster. Currently, no other value is permitted, but in future we will
use this option for setting up arbitrary replication groups in a
cluster. The option can only be set on space creation and cannot be
altered.

Technically, to support this feature, we introduce a new header key,
IPROTO_GROUP_ID, which is set to the space group id for all rows
corresponding to a space, both in xlog and in snap. Relay won't send
snapshot rows whose group_id is 1. As for xlog rows, they are
transformed to IPROTO_NOP so as to promote vclock on replicas without
any actual data modification.

The feature is currently supported for memtx spaces only, but it should
be easy to implement it for vinyl spaces as well.

@TarantoolBot document
Title: Document new space option - group_id
Group identifier defines how the space is replicated. Currently, only
two values are permitted: 0 and 1. If space.group_id is 0, changes made
to the space are replicated throughout the entire cluster. If it is 1,
then the space is replica local, i.e. changes made to it are invisible
to other replicas in the cluster.

Closes #3443
---
 src/box/alter.cc                       |  13 +++
 src/box/errcode.h                      |   1 +
 src/box/iproto_constants.c             |   4 +-
 src/box/iproto_constants.h             |   1 +
 src/box/lua/schema.lua                 |   8 +-
 src/box/lua/space.cc                   |   5 +
 src/box/lua/xlog.c                     |   5 +
 src/box/memtx_engine.c                 |   8 +-
 src/box/relay.cc                       |  17 +++-
 src/box/replication.h                  |  14 +++
 src/box/space.h                        |   7 ++
 src/box/space_def.c                    |   2 +
 src/box/space_def.h                    |   5 +
 src/box/txn.c                          |   1 +
 src/box/vinyl.c                        |   5 +
 src/box/xrow.c                         |   9 ++
 src/box/xrow.h                         |   1 +
 test/box/misc.result                   |   5 +-
 test/engine/iterator.result            |   2 +-
 test/replication/local_spaces.result   | 174 +++++++++++++++++++++++++++++++++
 test/replication/local_spaces.test.lua |  63 ++++++++++++
 test/replication/suite.cfg             |   1 +
 test/vinyl/ddl.result                  |   5 +
 test/vinyl/ddl.test.lua                |   3 +
 24 files changed, 348 insertions(+), 11 deletions(-)
 create mode 100644 test/replication/local_spaces.result
 create mode 100644 test/replication/local_spaces.test.lua

diff --git a/src/box/alter.cc b/src/box/alter.cc
index 07dee7d9..9d114b86 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -560,6 +560,15 @@ space_def_new_from_tuple(struct tuple *tuple, uint32_t errcode,
 	}
 	struct space_opts opts;
 	space_opts_decode(&opts, space_opts);
+	/*
+	 * Currently, only predefined replication groups
+	 * are supported.
+	 */
+	if (opts.group_id != GROUP_DEFAULT &&
+	    opts.group_id != GROUP_LOCAL) {
+		tnt_raise(ClientError, ER_NO_SUCH_GROUP,
+			  int2str(opts.group_id));
+	}
 	struct space_def *def =
 		space_def_new_xc(id, uid, exact_field_count, name, name_len,
 				 engine_name, engine_name_len, &opts, fields,
@@ -1631,6 +1640,10 @@ on_replace_dd_space(struct trigger * /* trigger */, void *event)
 			tnt_raise(ClientError, ER_ALTER_SPACE,
 				  space_name(old_space),
 				  "can not change space engine");
+		if (def->opts.group_id != space_group_id(old_space))
+			tnt_raise(ClientError, ER_ALTER_SPACE,
+				  space_name(old_space),
+				  "group id is immutable");
 		/*
 		 * Allow change of space properties, but do it
 		 * in WAL-error-safe mode.
diff --git a/src/box/errcode.h b/src/box/errcode.h
index f3987cf0..3d5f66af 100644
--- a/src/box/errcode.h
+++ b/src/box/errcode.h
@@ -207,6 +207,7 @@ struct errcode_record {
 	/*152 */_(ER_NULLABLE_PRIMARY,		"Primary index of the space '%s' can not contain nullable parts") \
 	/*153 */_(ER_NULLABLE_MISMATCH,		"Field %d is %s in space format, but %s in index parts") \
 	/*154 */_(ER_TRANSACTION_YIELD,		"Transaction has been aborted by a fiber yield") \
+	/*155 */_(ER_NO_SUCH_GROUP,		"Replication group '%s' does not exist") \
 
 /*
  * !IMPORTANT! Please follow instructions at start of the file
diff --git a/src/box/iproto_constants.c b/src/box/iproto_constants.c
index 5c1d3a31..3bc965bd 100644
--- a/src/box/iproto_constants.c
+++ b/src/box/iproto_constants.c
@@ -40,10 +40,10 @@ const unsigned char iproto_key_type[IPROTO_KEY_MAX] =
 		/* 0x04 */	MP_DOUBLE, /* IPROTO_TIMESTAMP */
 		/* 0x05 */	MP_UINT,   /* IPROTO_SCHEMA_VERSION */
 		/* 0x06 */	MP_UINT,   /* IPROTO_SERVER_VERSION */
+		/* 0x07 */	MP_UINT,   /* IPROTO_GROUP_ID */
 	/* }}} */
 
 	/* {{{ unused */
-		/* 0x07 */	MP_UINT,
 		/* 0x08 */	MP_UINT,
 		/* 0x09 */	MP_UINT,
 		/* 0x0a */	MP_UINT,
@@ -133,7 +133,7 @@ const char *iproto_key_strs[IPROTO_KEY_MAX] = {
 	"timestamp",        /* 0x04 */
 	"schema version",   /* 0x05 */
 	"server version",   /* 0x06 */
-	NULL,               /* 0x07 */
+	"group id",         /* 0x07 */
 	NULL,               /* 0x08 */
 	NULL,               /* 0x09 */
 	NULL,               /* 0x0a */
diff --git a/src/box/iproto_constants.h b/src/box/iproto_constants.h
index d1320de7..ccbf2da5 100644
--- a/src/box/iproto_constants.h
+++ b/src/box/iproto_constants.h
@@ -58,6 +58,7 @@ enum iproto_key {
 	IPROTO_TIMESTAMP = 0x04,
 	IPROTO_SCHEMA_VERSION = 0x05,
 	IPROTO_SERVER_VERSION = 0x06,
+	IPROTO_GROUP_ID = 0x07,
 	/* Leave a gap for other keys in the header. */
 	IPROTO_SPACE_ID = 0x10,
 	IPROTO_INDEX_ID = 0x11,
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index dc18f71b..356f05b6 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -418,6 +418,7 @@ box.schema.space.create = function(name, options)
         field_count = 'number',
         user = 'string, number',
         format = 'table',
+        group_id = 'number',
         temporary = 'boolean',
     }
     local options_defaults = {
@@ -461,6 +462,7 @@ box.schema.space.create = function(name, options)
     format = update_format(format)
     -- filter out global parameters from the options array
     local space_options = setmap({
+        group_id = options.group_id,
         temporary = options.temporary and true or nil,
     })
     _space:insert{id, uid, name, options.engine, options.field_count,
@@ -2369,7 +2371,11 @@ local function box_space_mt(tab)
     for k,v in pairs(tab) do
         -- skip system spaces and views
         if type(k) == 'string' and #k > 0 and k:sub(1,1) ~= '_' then
-            t[k] = { engine = v.engine, temporary = v.temporary }
+            t[k] = {
+                engine = v.engine,
+                group_id = v.group_id,
+                temporary = v.temporary,
+            }
         end
     end
     return t
diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
index 52438275..5a38a2ac 100644
--- a/src/box/lua/space.cc
+++ b/src/box/lua/space.cc
@@ -160,6 +160,11 @@ lbox_fillspace(struct lua_State *L, struct space *space, int i)
 	lua_pushnumber(L, space_id(space));
 	lua_settable(L, i);
 
+	/* space.group_id */
+	lua_pushstring(L, "group_id");
+	lua_pushnumber(L, space_group_id(space));
+	lua_settable(L, i);
+
 	/* space.is_temp */
 	lua_pushstring(L, "temporary");
 	lua_pushboolean(L, space_is_temporary(space));
diff --git a/src/box/lua/xlog.c b/src/box/lua/xlog.c
index 2271c829..3c7cab38 100644
--- a/src/box/lua/xlog.c
+++ b/src/box/lua/xlog.c
@@ -211,6 +211,11 @@ lbox_xlog_parser_iterate(struct lua_State *L)
 		lua_pushinteger(L, row.replica_id);
 		lua_settable(L, -3); /* replica_id */
 	}
+	if (row.group_id != 0) {
+		lbox_xlog_pushkey(L, iproto_key_name(IPROTO_GROUP_ID));
+		lua_pushinteger(L, row.group_id);
+		lua_settable(L, -3); /* group_id */
+	}
 	if (row.tm != 0) {
 		lbox_xlog_pushkey(L, iproto_key_name(IPROTO_TIMESTAMP));
 		lua_pushnumber(L, row.tm);
diff --git a/src/box/memtx_engine.c b/src/box/memtx_engine.c
index 74c6be8d..c210afbe 100644
--- a/src/box/memtx_engine.c
+++ b/src/box/memtx_engine.c
@@ -555,19 +555,20 @@ checkpoint_write_row(struct xlog *l, struct xrow_header *row)
 }
 
 static int
-checkpoint_write_tuple(struct xlog *l, uint32_t space_id,
+checkpoint_write_tuple(struct xlog *l, struct space *space,
 		       const char *data, uint32_t size)
 {
 	struct request_replace_body body;
 	body.m_body = 0x82; /* map of two elements. */
 	body.k_space_id = IPROTO_SPACE_ID;
 	body.m_space_id = 0xce; /* uint32 */
-	body.v_space_id = mp_bswap_u32(space_id);
+	body.v_space_id = mp_bswap_u32(space_id(space));
 	body.k_tuple = IPROTO_TUPLE;
 
 	struct xrow_header row;
 	memset(&row, 0, sizeof(struct xrow_header));
 	row.type = IPROTO_INSERT;
+	row.group_id = space_group_id(space);
 
 	row.bodycnt = 2;
 	row.body[0].iov_base = &body;
@@ -692,8 +693,7 @@ checkpoint_f(va_list ap)
 		struct snapshot_iterator *it = entry->iterator;
 		for (data = it->next(it, &size); data != NULL;
 		     data = it->next(it, &size)) {
-			if (checkpoint_write_tuple(&snap,
-					space_id(entry->space),
+			if (checkpoint_write_tuple(&snap, entry->space,
 					data, size) != 0) {
 				xlog_close(&snap, false);
 				return -1;
diff --git a/src/box/relay.cc b/src/box/relay.cc
index c91e5aed..75c3d56a 100644
--- a/src/box/relay.cc
+++ b/src/box/relay.cc
@@ -620,7 +620,12 @@ static void
 relay_send_initial_join_row(struct xstream *stream, struct xrow_header *row)
 {
 	struct relay *relay = container_of(stream, struct relay, stream);
-	relay_send(relay, row);
+	/*
+	 * Ignore replica local requests as we don't need to promote
+	 * vclock while sending a snapshot.
+	 */
+	if (row->group_id != GROUP_LOCAL)
+		relay_send(relay, row);
 }
 
 /** Send a single row to the client. */
@@ -630,6 +635,16 @@ relay_send_row(struct xstream *stream, struct xrow_header *packet)
 	struct relay *relay = container_of(stream, struct relay, stream);
 	assert(iproto_type_is_dml(packet->type));
 	/*
+	 * Transform replica local requests to IPROTO_NOP so as to
+	 * promote vclock on the replica without actually modifying
+	 * any data.
+	 */
+	if (packet->group_id == GROUP_LOCAL) {
+		packet->type = IPROTO_NOP;
+		packet->group_id = GROUP_DEFAULT;
+		packet->bodycnt = 0;
+	}
+	/*
 	 * We're feeding a WAL, thus responding to SUBSCRIBE request.
 	 * In that case, only send a row if it is not from the same replica
 	 * (i.e. don't send replica's own rows back) or if this row is
diff --git a/src/box/replication.h b/src/box/replication.h
index fdf995c3..6956837a 100644
--- a/src/box/replication.h
+++ b/src/box/replication.h
@@ -96,6 +96,20 @@ extern "C" {
 
 struct gc_consumer;
 
+/** Predefined replication group identifiers. */
+enum {
+	/**
+	 * Default replication group: changes made to the space
+	 * are replicated throughout the entire cluster.
+	 */
+	GROUP_DEFAULT = 0,
+	/**
+	 * Replica local space: changes made to the space are
+	 * not replicated.
+	 */
+	GROUP_LOCAL = 1,
+};
+
 static const int REPLICATION_CONNECT_QUORUM_ALL = INT_MAX;
 
 /**
diff --git a/src/box/space.h b/src/box/space.h
index 074e2462..ae32e6df 100644
--- a/src/box/space.h
+++ b/src/box/space.h
@@ -198,6 +198,13 @@ space_is_temporary(struct space *space)
 	return space->def->opts.is_temporary;
 }
 
+/** Return replication group id of a space. */
+static inline bool
+space_group_id(struct space *space)
+{
+	return space->def->opts.group_id;
+}
+
 void
 space_run_triggers(struct space *space, bool yesno);
 
diff --git a/src/box/space_def.c b/src/box/space_def.c
index ff35cb20..6243c2c4 100644
--- a/src/box/space_def.c
+++ b/src/box/space_def.c
@@ -34,10 +34,12 @@
 #include "error.h"
 
 const struct space_opts space_opts_default = {
+	/* .group_id = */ 0,
 	/* .is_temporary = */ false,
 };
 
 const struct opt_def space_opts_reg[] = {
+	OPT_DEF("group_id", OPT_UINT32, struct space_opts, group_id),
 	OPT_DEF("temporary", OPT_BOOL, struct space_opts, is_temporary),
 	OPT_END,
 };
diff --git a/src/box/space_def.h b/src/box/space_def.h
index 6cee6ad8..f66417d9 100644
--- a/src/box/space_def.h
+++ b/src/box/space_def.h
@@ -41,6 +41,11 @@ extern "C" {
 
 /** Space options */
 struct space_opts {
+	/**
+	 * Replication group identifier. Defines how changes
+	 * made to a space are replicated.
+	 */
+	uint32_t group_id;
         /**
 	 * The space is a temporary:
 	 * - it is empty at server start
diff --git a/src/box/txn.c b/src/box/txn.c
index 80e5463d..619780f3 100644
--- a/src/box/txn.c
+++ b/src/box/txn.c
@@ -61,6 +61,7 @@ txn_add_redo(struct txn_stmt *stmt, struct request *request)
 	/* Initialize members explicitly to save time on memset() */
 	row->type = request->type;
 	row->replica_id = 0;
+	row->group_id = stmt->space != NULL ? space_group_id(stmt->space) : 0;
 	row->lsn = 0;
 	row->sync = 0;
 	row->tm = 0;
diff --git a/src/box/vinyl.c b/src/box/vinyl.c
index 08a83bb5..2d11fc8d 100644
--- a/src/box/vinyl.c
+++ b/src/box/vinyl.c
@@ -595,6 +595,11 @@ vinyl_engine_check_space_def(struct space_def *def)
 			 def->name, "engine does not support temporary flag");
 		return -1;
 	}
+	if (def->opts.group_id != 0) {
+		diag_set(ClientError, ER_ALTER_SPACE,
+			 def->name, "engine does not support group id");
+		return -1;
+	}
 	return 0;
 }
 
diff --git a/src/box/xrow.c b/src/box/xrow.c
index 64d845f7..11316906 100644
--- a/src/box/xrow.c
+++ b/src/box/xrow.c
@@ -97,6 +97,9 @@ error:
 		case IPROTO_REPLICA_ID:
 			header->replica_id = mp_decode_uint(pos);
 			break;
+		case IPROTO_GROUP_ID:
+			header->group_id = mp_decode_uint(pos);
+			break;
 		case IPROTO_LSN:
 			header->lsn = mp_decode_uint(pos);
 			break;
@@ -178,6 +181,12 @@ xrow_header_encode(const struct xrow_header *header, uint64_t sync,
 		map_size++;
 	}
 
+	if (header->group_id) {
+		d = mp_encode_uint(d, IPROTO_GROUP_ID);
+		d = mp_encode_uint(d, header->group_id);
+		map_size++;
+	}
+
 	if (header->lsn) {
 		d = mp_encode_uint(d, IPROTO_LSN);
 		d = mp_encode_uint(d, header->lsn);
diff --git a/src/box/xrow.h b/src/box/xrow.h
index 1bb5f103..92ea3c97 100644
--- a/src/box/xrow.h
+++ b/src/box/xrow.h
@@ -57,6 +57,7 @@ struct xrow_header {
 
 	uint32_t type;
 	uint32_t replica_id;
+	uint32_t group_id;
 	uint64_t sync;
 	int64_t lsn; /* LSN must be signed for correct comparison */
 	double tm;
diff --git a/test/box/misc.result b/test/box/misc.result
index 5c390ae8..9209c33a 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -398,8 +398,9 @@ t;
   - 'box.error.UPDATE_ARG_TYPE : 26'
   - 'box.error.CROSS_ENGINE_TRANSACTION : 81'
   - 'box.error.FORMAT_MISMATCH_INDEX_PART : 27'
-  - 'box.error.FUNCTION_TX_ACTIVE : 30'
   - 'box.error.injection : table: <address>
+  - 'box.error.FUNCTION_TX_ACTIVE : 30'
+  - 'box.error.ITERATOR_TYPE : 72'
   - 'box.error.TRANSACTION_YIELD : 154'
   - 'box.error.NO_SUCH_ENGINE : 57'
   - 'box.error.COMMIT_IN_SUB_STMT : 122'
@@ -449,7 +450,7 @@ t;
   - 'box.error.COMPRESSION : 119'
   - 'box.error.INVALID_ORDER : 68'
   - 'box.error.UNKNOWN : 0'
-  - 'box.error.ITERATOR_TYPE : 72'
+  - 'box.error.NO_SUCH_GROUP : 155'
   - 'box.error.TUPLE_FORMAT_LIMIT : 16'
   - 'box.error.DROP_PRIMARY_KEY : 17'
   - 'box.error.NULLABLE_PRIMARY : 152'
diff --git a/test/engine/iterator.result b/test/engine/iterator.result
index 05d892df..10097edb 100644
--- a/test/engine/iterator.result
+++ b/test/engine/iterator.result
@@ -4213,7 +4213,7 @@ s:replace{35}
 ...
 state, value = gen(param,state)
 ---
-- error: 'builtin/box/schema.lua:1032: usage: next(param, state)'
+- error: 'builtin/box/schema.lua:1034: usage: next(param, state)'
 ...
 value
 ---
diff --git a/test/replication/local_spaces.result b/test/replication/local_spaces.result
new file mode 100644
index 00000000..0eb5a2f6
--- /dev/null
+++ b/test/replication/local_spaces.result
@@ -0,0 +1,174 @@
+env = require('test_run')
+---
+...
+test_run = env.new()
+---
+...
+--
+-- gh-3443: Check that changes done to spaces marked as local
+-- are not replicated, but vclock is still promoted.
+--
+-- Currently, there are only two replication groups:
+-- 0 (global) and 1 (local)
+box.schema.space.create('test', {group_id = 2}) -- error
+---
+- error: Replication group '2' does not exist
+...
+s1 = box.schema.space.create('test1')
+---
+...
+_ = s1:create_index('pk')
+---
+...
+s2 = box.schema.space.create('test2', {group_id = 1})
+---
+...
+_ = s2:create_index('pk')
+---
+...
+s1.group_id
+---
+- 0
+...
+s2.group_id
+---
+- 1
+...
+-- group_id is immutable
+box.space._space:update(s1.id, {{'=', 6, {group_id = 1}}}) -- error
+---
+- error: 'Can''t modify space ''test1'': group id is immutable'
+...
+box.space._space:update(s2.id, {{'=', 6, {group_id = 0}}}) -- error
+---
+- error: 'Can''t modify space ''test2'': group id is immutable'
+...
+_ = s1:insert{1}
+---
+...
+_ = s2:insert{1}
+---
+...
+box.snapshot()
+---
+- ok
+...
+_ = s1:insert{2}
+---
+...
+_ = s2:insert{2}
+---
+...
+box.schema.user.grant('guest', 'replication')
+---
+...
+test_run:cmd("create server replica with rpl_master=default, script='replication/replica.lua'")
+---
+- true
+...
+test_run:cmd("start server replica")
+---
+- true
+...
+test_run:cmd("switch replica")
+---
+- true
+...
+box.space.test1.group_id
+---
+- 0
+...
+box.space.test2.group_id
+---
+- 1
+...
+box.space.test1:select()
+---
+- - [1]
+  - [2]
+...
+box.space.test2:select()
+---
+- []
+...
+for i = 1, 3 do box.space.test2:insert{i, i} end
+---
+...
+test_run:cmd("switch default")
+---
+- true
+...
+_ = s1:insert{3}
+---
+...
+_ = s2:insert{3}
+---
+...
+vclock = test_run:get_vclock('default')
+---
+...
+_ = test_run:wait_vclock('replica', vclock)
+---
+...
+test_run:cmd("switch replica")
+---
+- true
+...
+box.space.test1:select()
+---
+- - [1]
+  - [2]
+  - [3]
+...
+box.space.test2:select()
+---
+- - [1, 1]
+  - [2, 2]
+  - [3, 3]
+...
+test_run:cmd("restart server replica")
+box.space.test1:select()
+---
+- - [1]
+  - [2]
+  - [3]
+...
+box.space.test2:select()
+---
+- - [1, 1]
+  - [2, 2]
+  - [3, 3]
+...
+test_run:cmd("switch default")
+---
+- true
+...
+test_run:cmd("stop server replica")
+---
+- true
+...
+test_run:cmd("cleanup server replica")
+---
+- true
+...
+box.schema.user.revoke('guest', 'replication')
+---
+...
+s1:select()
+---
+- - [1]
+  - [2]
+  - [3]
+...
+s2:select()
+---
+- - [1]
+  - [2]
+  - [3]
+...
+s1:drop()
+---
+...
+s2:drop()
+---
+...
diff --git a/test/replication/local_spaces.test.lua b/test/replication/local_spaces.test.lua
new file mode 100644
index 00000000..700c1413
--- /dev/null
+++ b/test/replication/local_spaces.test.lua
@@ -0,0 +1,63 @@
+env = require('test_run')
+test_run = env.new()
+
+--
+-- gh-3443: Check that changes done to spaces marked as local
+-- are not replicated, but vclock is still promoted.
+--
+
+-- Currently, there are only two replication groups:
+-- 0 (global) and 1 (local)
+box.schema.space.create('test', {group_id = 2}) -- error
+
+s1 = box.schema.space.create('test1')
+_ = s1:create_index('pk')
+s2 = box.schema.space.create('test2', {group_id = 1})
+_ = s2:create_index('pk')
+s1.group_id
+s2.group_id
+
+-- group_id is immutable
+box.space._space:update(s1.id, {{'=', 6, {group_id = 1}}}) -- error
+box.space._space:update(s2.id, {{'=', 6, {group_id = 0}}}) -- error
+
+_ = s1:insert{1}
+_ = s2:insert{1}
+box.snapshot()
+_ = s1:insert{2}
+_ = s2:insert{2}
+
+box.schema.user.grant('guest', 'replication')
+test_run:cmd("create server replica with rpl_master=default, script='replication/replica.lua'")
+test_run:cmd("start server replica")
+
+test_run:cmd("switch replica")
+box.space.test1.group_id
+box.space.test2.group_id
+box.space.test1:select()
+box.space.test2:select()
+for i = 1, 3 do box.space.test2:insert{i, i} end
+
+test_run:cmd("switch default")
+_ = s1:insert{3}
+_ = s2:insert{3}
+vclock = test_run:get_vclock('default')
+_ = test_run:wait_vclock('replica', vclock)
+
+test_run:cmd("switch replica")
+box.space.test1:select()
+box.space.test2:select()
+test_run:cmd("restart server replica")
+box.space.test1:select()
+box.space.test2:select()
+
+test_run:cmd("switch default")
+test_run:cmd("stop server replica")
+test_run:cmd("cleanup server replica")
+box.schema.user.revoke('guest', 'replication')
+
+s1:select()
+s2:select()
+
+s1:drop()
+s2:drop()
diff --git a/test/replication/suite.cfg b/test/replication/suite.cfg
index 95e94e5a..283edcad 100644
--- a/test/replication/suite.cfg
+++ b/test/replication/suite.cfg
@@ -6,6 +6,7 @@
     "wal_off.test.lua": {},
     "hot_standby.test.lua": {},
     "rebootstrap.test.lua": {},
+    "local_spaces.test.lua": {},
     "*": {
         "memtx": {"engine": "memtx"},
         "vinyl": {"engine": "vinyl"}
diff --git a/test/vinyl/ddl.result b/test/vinyl/ddl.result
index 16ee7097..5f385123 100644
--- a/test/vinyl/ddl.result
+++ b/test/vinyl/ddl.result
@@ -44,6 +44,11 @@ space:create_index('pk', {bloom_fpr = 1.1})
 space:drop()
 ---
 ...
+-- vinyl does not support replica local spaces
+space = box.schema.space.create('test', {engine = 'vinyl', group_id = 1})
+---
+- error: 'Can''t modify space ''test'': engine does not support group id'
+...
 -- space secondary index create
 space = box.schema.space.create('test', { engine = 'vinyl' })
 ---
diff --git a/test/vinyl/ddl.test.lua b/test/vinyl/ddl.test.lua
index 95dd5a11..3894f003 100644
--- a/test/vinyl/ddl.test.lua
+++ b/test/vinyl/ddl.test.lua
@@ -12,6 +12,9 @@ space:create_index('pk', {bloom_fpr = 0})
 space:create_index('pk', {bloom_fpr = 1.1})
 space:drop()
 
+-- vinyl does not support replica local spaces
+space = box.schema.space.create('test', {engine = 'vinyl', group_id = 1})
+
 -- space secondary index create
 space = box.schema.space.create('test', { engine = 'vinyl' })
 index1 = space:create_index('primary')
-- 
2.11.0




More information about the Tarantool-patches mailing list