[Tarantool-patches] [PATCH v5] memtx: fix out of memory handling for rtree

Olga Arkhangelskaia arkholga at tarantool.org
Wed Dec 25 17:35:57 MSK 2019


When tarantool tries to recover rtree from a snapshot and memtx_memory value
is lower than it has been when the snapshot was created, server suffers from
segmentation fault. This happens because there is no out of memory error
handling in rtree lib. In another words, we do not check the result of
malloc operation.
The execution flow in case of recovery uses different way and the secondary
keys are build in batches. That way has no checks and reservations.
The patch adds memtx_rtree_index_reserve implementation to make sure that any
memory allocation in rtree will fail. Although this gives us no additional
optimization as in case of memtx_tree, the memory reservation prevents
tarantool from segmentation fault. If there is not enough memory to be reserved
server will fail gently with the "Failed to allocate" error message.

Closes #4619
---
Branch: https://github.com/tarantool/tarantool/tree/OKriw/gh-RTREE-doesnt-handle-OOM-properly
Issue:https://github.com/tarantool/tarantool/issues/4619

v1:https://lists.tarantool.org/pipermail/tarantool-patches/2019-November/012391.html
v2:https://lists.tarantool.org/pipermail/tarantool-patches/2019-December/012958.html
v3:https://lists.tarantool.org/pipermail/tarantool-patches/2019-December/013182.html
v4:https://lists.tarantool.org/pipermail/tarantool-patches/2019-December/013230.html

Changes in v2:
- changed way of error handling, now we reserve pages before go to rtree lib
- changed test
- changed commit msg

Changes in v3:
- added memtx_rtree_build_next function
- memory reservation is moved to memtx_rtree_build_next

Changes in v4:
- added index reservation in build_next
- added memtx_rtree_reserve implementation

Changes in v5:
- added error injection
- test is placed in errorinj.test.lua
- test is much faster now
- fixed indentation errors 

 src/box/index.cc           |    2 +
 src/box/memtx_engine.h     |   12 +
 src/box/memtx_rtree.c      |   19 +-
 src/box/memtx_space.c      |   12 -
 src/lib/core/errinj.h      |    1 +
 test/box/errinj.result     | 2712 +++++++++++++++++++-----------------
 test/box/errinj.test.lua   |   27 +
 test/box/lua/cfg_rtree.lua |    8 +
 8 files changed, 1524 insertions(+), 1269 deletions(-)
 create mode 100644 test/box/lua/cfg_rtree.lua

diff --git a/src/box/index.cc b/src/box/index.cc
index 4e4867118..62cb2ab96 100644
--- a/src/box/index.cc
+++ b/src/box/index.cc
@@ -733,6 +733,8 @@ int
 generic_index_build_next(struct index *index, struct tuple *tuple)
 {
 	struct tuple *unused;
+	if (index_reserve(index, 0) != 0)
+        return -1;
 	return index_replace(index, NULL, tuple, DUP_INSERT, &unused);
 }
 
diff --git a/src/box/memtx_engine.h b/src/box/memtx_engine.h
index f562c66df..8b380bf3c 100644
--- a/src/box/memtx_engine.h
+++ b/src/box/memtx_engine.h
@@ -87,6 +87,18 @@ enum memtx_recovery_state {
 /** Memtx extents pool, available to statistics. */
 extern struct mempool memtx_index_extent_pool;
 
+enum memtx_reserve_extents_num {
+	/**
+	 * This number is calculated based on the
+	 * max (realistic) number of insertions
+	 * a deletion from a B-tree or an R-tree
+	 * can lead to, and, as a result, the max
+	 * number of new block allocations.
+	 */
+	RESERVE_EXTENTS_BEFORE_DELETE = 8,
+	RESERVE_EXTENTS_BEFORE_REPLACE = 16
+};
+
 /**
  * The size of the biggest memtx iterator. Used with
  * mempool_create. This is the size of the block that will be
diff --git a/src/box/memtx_rtree.c b/src/box/memtx_rtree.c
index 8badad797..a5cae8f45 100644
--- a/src/box/memtx_rtree.c
+++ b/src/box/memtx_rtree.c
@@ -242,6 +242,23 @@ memtx_rtree_index_replace(struct index *base, struct tuple *old_tuple,
 	return 0;
 }
 
+static int
+memtx_rtree_index_reserve(struct index *base, uint32_t size_hint)
+{
+	/*
+	 * In case of rtree we use reserve to make sure that memory allocation
+	 * will not fail durig any operationin rtree, because there is no
+	 * error handling in rtree lib.
+	 */
+	(void)size_hint;
+	ERROR_INJECT(ERRINJ_INDEX_RESERVE, {
+		diag_set(OutOfMemory, MEMTX_EXTENT_SIZE, "slab allocator", "memtx extent");
+		return -1;
+	});
+	struct memtx_engine *memtx = (struct memtx_engine *)base->engine;
+	return memtx_index_extent_reserve(memtx, RESERVE_EXTENTS_BEFORE_REPLACE);
+}
+
 static struct iterator *
 memtx_rtree_index_create_iterator(struct index *base,  enum iterator_type type,
 				  const char *key, uint32_t part_count)
@@ -333,7 +350,7 @@ static const struct index_vtab memtx_rtree_index_vtab = {
 	/* .compact = */ generic_index_compact,
 	/* .reset_stat = */ generic_index_reset_stat,
 	/* .begin_build = */ generic_index_begin_build,
-	/* .reserve = */ generic_index_reserve,
+	/* .reserve = */ memtx_rtree_index_reserve,
 	/* .build_next = */ generic_index_build_next,
 	/* .end_build = */ generic_index_end_build,
 };
diff --git a/src/box/memtx_space.c b/src/box/memtx_space.c
index 6ef84e045..da20b9196 100644
--- a/src/box/memtx_space.c
+++ b/src/box/memtx_space.c
@@ -103,18 +103,6 @@ memtx_space_replace_no_keys(struct space *space, struct tuple *old_tuple,
 	return -1;
 }
 
-enum {
-	/**
-	 * This number is calculated based on the
-	 * max (realistic) number of insertions
-	 * a deletion from a B-tree or an R-tree
-	 * can lead to, and, as a result, the max
-	 * number of new block allocations.
-	 */
-	RESERVE_EXTENTS_BEFORE_DELETE = 8,
-	RESERVE_EXTENTS_BEFORE_REPLACE = 16
-};
-
 /**
  * A short-cut version of replace() used during bulk load
  * from snapshot.
diff --git a/src/lib/core/errinj.h b/src/lib/core/errinj.h
index 672da2119..9ee9b5699 100644
--- a/src/lib/core/errinj.h
+++ b/src/lib/core/errinj.h
@@ -135,6 +135,7 @@ struct errinj {
 	_(ERRINJ_COIO_SENDFILE_CHUNK, ERRINJ_INT, {.iparam = -1}) \
 	_(ERRINJ_SWIM_FD_ONLY, ERRINJ_BOOL, {.bparam = false}) \
 	_(ERRINJ_DYN_MODULE_COUNT, ERRINJ_INT, {.iparam = 0}) \
+	_(ERRINJ_INDEX_RESERVE, ERRINJ_BOOL, {.bparam = false})\
 
 ENUM0(errinj_id, ERRINJ_LIST);
 extern struct errinj errinjs[];
diff --git a/test/box/errinj.result b/test/box/errinj.result
index babe36b1b..4c5a56ee3 100644
--- a/test/box/errinj.result
+++ b/test/box/errinj.result
@@ -1,1783 +1,1983 @@
+-- test-run result file version 2
 -- Test that recovery had been completed without errors
 test_run = require('test_run').new()
----
-...
+ | ---
+ | ...
 test_run:cmd("restart server default")
+ | 
 box.error.last() == nil
----
-- true
-...
+ | ---
+ | - true
+ | ...
+
 errinj = box.error.injection
----
-...
+ | ---
+ | ...
 net_box = require('net.box')
----
-...
+ | ---
+ | ...
+
 space = box.schema.space.create('tweedledum')
----
-...
+ | ---
+ | ...
 index = space:create_index('primary', { type = 'hash' })
----
-...
+ | ---
+ | ...
+
 errinj.info()
----
-- ERRINJ_VY_RUN_WRITE_STMT_TIMEOUT:
-    state: 0
-  ERRINJ_WAL_WRITE:
-    state: false
-  ERRINJ_RELAY_BREAK_LSN:
-    state: -1
-  ERRINJ_HTTPC_EXECUTE:
-    state: false
-  ERRINJ_VYRUN_DATA_READ:
-    state: false
-  ERRINJ_SWIM_FD_ONLY:
-    state: false
-  ERRINJ_SQL_NAME_NORMALIZATION:
-    state: false
-  ERRINJ_VY_SCHED_TIMEOUT:
-    state: 0
-  ERRINJ_COIO_SENDFILE_CHUNK:
-    state: -1
-  ERRINJ_HTTP_RESPONSE_ADD_WAIT:
-    state: false
-  ERRINJ_WAL_WRITE_PARTIAL:
-    state: -1
-  ERRINJ_VY_GC:
-    state: false
-  ERRINJ_WAL_DELAY:
-    state: false
-  ERRINJ_INDEX_ALLOC:
-    state: false
-  ERRINJ_WAL_WRITE_EOF:
-    state: false
-  ERRINJ_WAL_SYNC:
-    state: false
-  ERRINJ_BUILD_INDEX:
-    state: -1
-  ERRINJ_BUILD_INDEX_DELAY:
-    state: false
-  ERRINJ_VY_RUN_FILE_RENAME:
-    state: false
-  ERRINJ_VY_COMPACTION_DELAY:
-    state: false
-  ERRINJ_VY_DUMP_DELAY:
-    state: false
-  ERRINJ_VY_DELAY_PK_LOOKUP:
-    state: false
-  ERRINJ_VY_TASK_COMPLETE:
-    state: false
-  ERRINJ_PORT_DUMP:
-    state: false
-  ERRINJ_WAL_BREAK_LSN:
-    state: -1
-  ERRINJ_WAL_IO:
-    state: false
-  ERRINJ_WAL_FALLOCATE:
-    state: 0
-  ERRINJ_DYN_MODULE_COUNT:
-    state: 0
-  ERRINJ_VY_INDEX_FILE_RENAME:
-    state: false
-  ERRINJ_TUPLE_FORMAT_COUNT:
-    state: -1
-  ERRINJ_TUPLE_ALLOC:
-    state: false
-  ERRINJ_VY_RUN_WRITE_DELAY:
-    state: false
-  ERRINJ_VY_READ_PAGE:
-    state: false
-  ERRINJ_RELAY_REPORT_INTERVAL:
-    state: 0
-  ERRINJ_VY_LOG_FILE_RENAME:
-    state: false
-  ERRINJ_VY_READ_PAGE_TIMEOUT:
-    state: 0
-  ERRINJ_XLOG_META:
-    state: false
-  ERRINJ_SIO_READ_MAX:
-    state: -1
-  ERRINJ_SNAP_COMMIT_DELAY:
-    state: false
-  ERRINJ_WAL_WRITE_DISK:
-    state: false
-  ERRINJ_SNAP_WRITE_DELAY:
-    state: false
-  ERRINJ_LOG_ROTATE:
-    state: false
-  ERRINJ_VY_RUN_WRITE:
-    state: false
-  ERRINJ_CHECK_FORMAT_DELAY:
-    state: false
-  ERRINJ_VY_LOG_FLUSH_DELAY:
-    state: false
-  ERRINJ_RELAY_FINAL_JOIN:
-    state: false
-  ERRINJ_REPLICA_JOIN_DELAY:
-    state: false
-  ERRINJ_RELAY_FINAL_SLEEP:
-    state: false
-  ERRINJ_VY_RUN_DISCARD:
-    state: false
-  ERRINJ_WAL_ROTATE:
-    state: false
-  ERRINJ_RELAY_EXIT_DELAY:
-    state: 0
-  ERRINJ_VY_POINT_ITER_WAIT:
-    state: false
-  ERRINJ_MEMTX_DELAY_GC:
-    state: false
-  ERRINJ_IPROTO_TX_DELAY:
-    state: false
-  ERRINJ_XLOG_READ:
-    state: -1
-  ERRINJ_TUPLE_FIELD:
-    state: false
-  ERRINJ_XLOG_GARBAGE:
-    state: false
-  ERRINJ_VY_INDEX_DUMP:
-    state: -1
-  ERRINJ_VY_READ_PAGE_DELAY:
-    state: false
-  ERRINJ_TESTING:
-    state: false
-  ERRINJ_RELAY_SEND_DELAY:
-    state: false
-  ERRINJ_VY_SQUASH_TIMEOUT:
-    state: 0
-  ERRINJ_VY_LOG_FLUSH:
-    state: false
-  ERRINJ_RELAY_TIMEOUT:
-    state: 0
-...
+ | ---
+ | - ERRINJ_VY_RUN_WRITE_STMT_TIMEOUT:
+ |     state: 0
+ |   ERRINJ_WAL_BREAK_LSN:
+ |     state: -1
+ |   ERRINJ_VYRUN_DATA_READ:
+ |     state: false
+ |   ERRINJ_VY_SCHED_TIMEOUT:
+ |     state: 0
+ |   ERRINJ_HTTP_RESPONSE_ADD_WAIT:
+ |     state: false
+ |   ERRINJ_WAL_WRITE_EOF:
+ |     state: false
+ |   ERRINJ_BUILD_INDEX_DELAY:
+ |     state: false
+ |   ERRINJ_VY_DELAY_PK_LOOKUP:
+ |     state: false
+ |   ERRINJ_VY_POINT_ITER_WAIT:
+ |     state: false
+ |   ERRINJ_WAL_IO:
+ |     state: false
+ |   ERRINJ_VY_INDEX_FILE_RENAME:
+ |     state: false
+ |   ERRINJ_TUPLE_FORMAT_COUNT:
+ |     state: -1
+ |   ERRINJ_TUPLE_ALLOC:
+ |     state: false
+ |   ERRINJ_VY_RUN_FILE_RENAME:
+ |     state: false
+ |   ERRINJ_VY_READ_PAGE:
+ |     state: false
+ |   ERRINJ_RELAY_REPORT_INTERVAL:
+ |     state: 0
+ |   ERRINJ_RELAY_BREAK_LSN:
+ |     state: -1
+ |   ERRINJ_XLOG_META:
+ |     state: false
+ |   ERRINJ_SNAP_COMMIT_DELAY:
+ |     state: false
+ |   ERRINJ_VY_RUN_WRITE:
+ |     state: false
+ |   ERRINJ_BUILD_INDEX:
+ |     state: -1
+ |   ERRINJ_RELAY_FINAL_JOIN:
+ |     state: false
+ |   ERRINJ_REPLICA_JOIN_DELAY:
+ |     state: false
+ |   ERRINJ_LOG_ROTATE:
+ |     state: false
+ |   ERRINJ_MEMTX_DELAY_GC:
+ |     state: false
+ |   ERRINJ_XLOG_GARBAGE:
+ |     state: false
+ |   ERRINJ_VY_READ_PAGE_DELAY:
+ |     state: false
+ |   ERRINJ_INDEX_RESERVE:
+ |     state: false
+ |   ERRINJ_SWIM_FD_ONLY:
+ |     state: false
+ |   ERRINJ_WAL_WRITE:
+ |     state: false
+ |   ERRINJ_HTTPC_EXECUTE:
+ |     state: false
+ |   ERRINJ_SQL_NAME_NORMALIZATION:
+ |     state: false
+ |   ERRINJ_WAL_WRITE_PARTIAL:
+ |     state: -1
+ |   ERRINJ_VY_GC:
+ |     state: false
+ |   ERRINJ_WAL_DELAY:
+ |     state: false
+ |   ERRINJ_XLOG_READ:
+ |     state: -1
+ |   ERRINJ_WAL_SYNC:
+ |     state: false
+ |   ERRINJ_VY_TASK_COMPLETE:
+ |     state: false
+ |   ERRINJ_PORT_DUMP:
+ |     state: false
+ |   ERRINJ_COIO_SENDFILE_CHUNK:
+ |     state: -1
+ |   ERRINJ_DYN_MODULE_COUNT:
+ |     state: 0
+ |   ERRINJ_SIO_READ_MAX:
+ |     state: -1
+ |   ERRINJ_RELAY_TIMEOUT:
+ |     state: 0
+ |   ERRINJ_VY_DUMP_DELAY:
+ |     state: false
+ |   ERRINJ_VY_SQUASH_TIMEOUT:
+ |     state: 0
+ |   ERRINJ_VY_LOG_FLUSH_DELAY:
+ |     state: false
+ |   ERRINJ_RELAY_SEND_DELAY:
+ |     state: false
+ |   ERRINJ_VY_COMPACTION_DELAY:
+ |     state: false
+ |   ERRINJ_VY_LOG_FILE_RENAME:
+ |     state: false
+ |   ERRINJ_VY_RUN_DISCARD:
+ |     state: false
+ |   ERRINJ_WAL_ROTATE:
+ |     state: false
+ |   ERRINJ_VY_READ_PAGE_TIMEOUT:
+ |     state: 0
+ |   ERRINJ_VY_INDEX_DUMP:
+ |     state: -1
+ |   ERRINJ_TUPLE_FIELD:
+ |     state: false
+ |   ERRINJ_SNAP_WRITE_DELAY:
+ |     state: false
+ |   ERRINJ_IPROTO_TX_DELAY:
+ |     state: false
+ |   ERRINJ_RELAY_EXIT_DELAY:
+ |     state: 0
+ |   ERRINJ_RELAY_FINAL_SLEEP:
+ |     state: false
+ |   ERRINJ_WAL_WRITE_DISK:
+ |     state: false
+ |   ERRINJ_CHECK_FORMAT_DELAY:
+ |     state: false
+ |   ERRINJ_TESTING:
+ |     state: false
+ |   ERRINJ_VY_RUN_WRITE_DELAY:
+ |     state: false
+ |   ERRINJ_WAL_FALLOCATE:
+ |     state: 0
+ |   ERRINJ_VY_LOG_FLUSH:
+ |     state: false
+ |   ERRINJ_INDEX_ALLOC:
+ |     state: false
+ | ...
 errinj.set("some-injection", true)
----
-- 'error: can''t find error injection ''some-injection'''
-...
+ | ---
+ | - 'error: can''t find error injection ''some-injection'''
+ | ...
 errinj.set("some-injection") -- check error
----
-- 'error: can''t find error injection ''some-injection'''
-...
+ | ---
+ | - 'error: can''t find error injection ''some-injection'''
+ | ...
 space:select{222444}
----
-- []
-...
+ | ---
+ | - []
+ | ...
 errinj.set("ERRINJ_TESTING", true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 space:select{222444}
----
-- error: Error injection 'ERRINJ_TESTING'
-...
+ | ---
+ | - error: Error injection 'ERRINJ_TESTING'
+ | ...
 errinj.set("ERRINJ_TESTING", false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
+
 -- Check how well we handle a failed log write
 errinj.set("ERRINJ_WAL_IO", true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 space:insert{1}
----
-- error: Failed to write to disk
-...
+ | ---
+ | - error: Failed to write to disk
+ | ...
 space:get{1}
----
-...
+ | ---
+ | ...
 errinj.set("ERRINJ_WAL_IO", false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 space:insert{1}
----
-- [1]
-...
+ | ---
+ | - [1]
+ | ...
 errinj.set("ERRINJ_WAL_IO", true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 space:update(1, {{'=', 2, 2}})
----
-- error: Failed to write to disk
-...
+ | ---
+ | - error: Failed to write to disk
+ | ...
 space:get{1}
----
-- [1]
-...
+ | ---
+ | - [1]
+ | ...
 space:get{2}
----
-...
+ | ---
+ | ...
 errinj.set("ERRINJ_WAL_IO", false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 space:update(1, {{'=', 2, 2}})
----
-- [1, 2]
-...
+ | ---
+ | - [1, 2]
+ | ...
 space:truncate()
----
-...
+ | ---
+ | ...
+
 -- Check that WAL vclock isn't promoted on failed write.
 lsn1 = box.info.vclock[box.info.id]
----
-...
+ | ---
+ | ...
 errinj.set("ERRINJ_WAL_WRITE_PARTIAL", 0)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 space:insert{1}
----
-- error: Failed to write to disk
-...
+ | ---
+ | - error: Failed to write to disk
+ | ...
 errinj.set("ERRINJ_WAL_WRITE_PARTIAL", -1)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 space:insert{1}
----
-- [1]
-...
+ | ---
+ | - [1]
+ | ...
 -- Check vclock was promoted only one time
 box.info.vclock[box.info.id] == lsn1 + 1
----
-- true
-...
+ | ---
+ | - true
+ | ...
 errinj.set("ERRINJ_WAL_WRITE_PARTIAL", 0)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 space:update(1, {{'=', 2, 2}})
----
-- error: Failed to write to disk
-...
+ | ---
+ | - error: Failed to write to disk
+ | ...
 space:get{1}
----
-- [1]
-...
+ | ---
+ | - [1]
+ | ...
 errinj.set("ERRINJ_WAL_WRITE_PARTIAL", -1)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 space:update(1, {{'=', 2, 2}})
----
-- [1, 2]
-...
+ | ---
+ | - [1, 2]
+ | ...
 -- Check vclock was promoted only two times
 box.info.vclock[box.info.id] == lsn1 + 2
----
-- true
-...
+ | ---
+ | - true
+ | ...
 space:truncate()
----
-...
+ | ---
+ | ...
+
 -- Check a failed log rotation
 errinj.set("ERRINJ_WAL_ROTATE", true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 space:insert{1}
----
-- error: Failed to write to disk
-...
+ | ---
+ | - error: Failed to write to disk
+ | ...
 space:get{1}
----
-...
+ | ---
+ | ...
 errinj.set("ERRINJ_WAL_ROTATE", false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 space:insert{1}
----
-- [1]
-...
+ | ---
+ | - [1]
+ | ...
 errinj.set("ERRINJ_WAL_ROTATE", true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 space:update(1, {{'=', 2, 2}})
----
-- error: Failed to write to disk
-...
+ | ---
+ | - error: Failed to write to disk
+ | ...
 space:get{1}
----
-- [1]
-...
+ | ---
+ | - [1]
+ | ...
 space:get{2}
----
-...
+ | ---
+ | ...
 errinj.set("ERRINJ_WAL_ROTATE", false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 space:update(1, {{'=', 2, 2}})
----
-- [1, 2]
-...
+ | ---
+ | - [1, 2]
+ | ...
 space:get{1}
----
-- [1, 2]
-...
+ | ---
+ | - [1, 2]
+ | ...
 space:get{2}
----
-...
+ | ---
+ | ...
 space:truncate()
----
-...
+ | ---
+ | ...
+
 space:drop()
----
-...
+ | ---
+ | ...
+
 -- Check how well we handle a failed log write in DDL
 s_disabled = box.schema.space.create('disabled')
----
-...
+ | ---
+ | ...
 s_withindex = box.schema.space.create('withindex')
----
-...
+ | ---
+ | ...
 index1 = s_withindex:create_index('primary', { type = 'hash' })
----
-...
+ | ---
+ | ...
 s_withdata = box.schema.space.create('withdata')
----
-...
+ | ---
+ | ...
 index2 = s_withdata:create_index('primary', { type = 'tree' })
----
-...
+ | ---
+ | ...
 s_withdata:insert{1, 2, 3, 4, 5}
----
-- [1, 2, 3, 4, 5]
-...
+ | ---
+ | - [1, 2, 3, 4, 5]
+ | ...
 s_withdata:insert{4, 5, 6, 7, 8}
----
-- [4, 5, 6, 7, 8]
-...
+ | ---
+ | - [4, 5, 6, 7, 8]
+ | ...
 index3 = s_withdata:create_index('secondary', { type = 'hash', parts = {2, 'unsigned', 3, 'unsigned' }})
----
-...
+ | ---
+ | ...
 errinj.set("ERRINJ_WAL_IO", true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 test = box.schema.space.create('test')
----
-- error: Failed to write to disk
-...
+ | ---
+ | - error: Failed to write to disk
+ | ...
 s_disabled:create_index('primary', { type = 'hash' })
----
-- error: Failed to write to disk
-...
+ | ---
+ | - error: Failed to write to disk
+ | ...
 s_disabled.enabled
----
-- false
-...
+ | ---
+ | - false
+ | ...
 s_disabled:insert{0}
----
-- error: 'No index #0 is defined in space ''disabled'''
-...
+ | ---
+ | - error: 'No index #0 is defined in space ''disabled'''
+ | ...
 s_withindex:create_index('secondary', { type = 'tree', parts = { 2, 'unsigned'} })
----
-- error: Failed to write to disk
-...
+ | ---
+ | - error: Failed to write to disk
+ | ...
 s_withindex.index.secondary
----
-- null
-...
+ | ---
+ | - null
+ | ...
 s_withdata.index.secondary:drop()
----
-- error: Failed to write to disk
-...
+ | ---
+ | - error: Failed to write to disk
+ | ...
 s_withdata.index.secondary.unique
----
-- true
-...
+ | ---
+ | - true
+ | ...
 s_withdata:drop()
----
-- error: Failed to write to disk
-...
+ | ---
+ | - error: Failed to write to disk
+ | ...
 box.space['withdata'].enabled
----
-- true
-...
+ | ---
+ | - true
+ | ...
 index4 = s_withdata:create_index('another', { type = 'tree', parts = { 5, 'unsigned' }, unique = false})
----
-- error: Failed to write to disk
-...
+ | ---
+ | - error: Failed to write to disk
+ | ...
 s_withdata.index.another
----
-- null
-...
+ | ---
+ | - null
+ | ...
 errinj.set("ERRINJ_WAL_IO", false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 test = box.schema.space.create('test')
----
-...
+ | ---
+ | ...
 index5 = s_disabled:create_index('primary', { type = 'hash' })
----
-...
+ | ---
+ | ...
 s_disabled.enabled
----
-- true
-...
+ | ---
+ | - true
+ | ...
 s_disabled:insert{0}
----
-- [0]
-...
+ | ---
+ | - [0]
+ | ...
 index6 = s_withindex:create_index('secondary', { type = 'tree', parts = { 2, 'unsigned'} })
----
-...
+ | ---
+ | ...
 s_withindex.index.secondary.unique
----
-- true
-...
+ | ---
+ | - true
+ | ...
 s_withdata.index.secondary:drop()
----
-...
+ | ---
+ | ...
 s_withdata.index.secondary
----
-- null
-...
+ | ---
+ | - null
+ | ...
 s_withdata:drop()
----
-...
+ | ---
+ | ...
 box.space['withdata']
----
-- null
-...
+ | ---
+ | - null
+ | ...
 index7 = s_withdata:create_index('another', { type = 'tree', parts = { 5, 'unsigned' }, unique = false})
----
-- error: Space 'withdata' does not exist
-...
+ | ---
+ | - error: Space 'withdata' does not exist
+ | ...
 s_withdata.index.another
----
-- null
-...
+ | ---
+ | - null
+ | ...
 test:drop()
----
-...
+ | ---
+ | ...
 s_disabled:drop()
----
-...
+ | ---
+ | ...
 s_withindex:drop()
----
-...
+ | ---
+ | ...
+
 -- Check transaction rollback when out of memory
 env = require('test_run')
----
-...
+ | ---
+ | ...
 test_run = env.new()
----
-...
+ | ---
+ | ...
+
 s = box.schema.space.create('s')
----
-...
+ | ---
+ | ...
 _ = s:create_index('pk')
----
-...
+ | ---
+ | ...
 errinj.set("ERRINJ_TUPLE_ALLOC", true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 s:auto_increment{}
----
-- error: Failed to allocate 16 bytes in slab allocator for memtx_tuple
-...
+ | ---
+ | - error: Failed to allocate 16 bytes in slab allocator for memtx_tuple
+ | ...
 s:select{}
----
-- []
-...
+ | ---
+ | - []
+ | ...
 s:auto_increment{}
----
-- error: Failed to allocate 16 bytes in slab allocator for memtx_tuple
-...
+ | ---
+ | - error: Failed to allocate 16 bytes in slab allocator for memtx_tuple
+ | ...
 s:select{}
----
-- []
-...
+ | ---
+ | - []
+ | ...
 s:auto_increment{}
----
-- error: Failed to allocate 16 bytes in slab allocator for memtx_tuple
-...
+ | ---
+ | - error: Failed to allocate 16 bytes in slab allocator for memtx_tuple
+ | ...
 s:select{}
----
-- []
-...
+ | ---
+ | - []
+ | ...
 test_run:cmd("setopt delimiter ';'")
----
-- true
-...
+ | ---
+ | - true
+ | ...
 box.begin()
     s:insert{1}
 box.commit();
----
-- error: Failed to allocate 16 bytes in slab allocator for memtx_tuple
-...
+ | ---
+ | - error: Failed to allocate 16 bytes in slab allocator for memtx_tuple
+ | ...
 box.rollback();
----
-...
+ | ---
+ | ...
 s:select{};
----
-- []
-...
+ | ---
+ | - []
+ | ...
 box.begin()
     s:insert{1}
     s:insert{2}
 box.commit();
----
-- error: Failed to allocate 16 bytes in slab allocator for memtx_tuple
-...
+ | ---
+ | - error: Failed to allocate 16 bytes in slab allocator for memtx_tuple
+ | ...
 s:select{};
----
-- []
-...
+ | ---
+ | - []
+ | ...
 box.rollback();
----
-...
+ | ---
+ | ...
 box.begin()
     pcall(s.insert, s, {1})
     s:insert{2}
 box.commit();
----
-- error: Failed to allocate 16 bytes in slab allocator for memtx_tuple
-...
+ | ---
+ | - error: Failed to allocate 16 bytes in slab allocator for memtx_tuple
+ | ...
 s:select{};
----
-- []
-...
+ | ---
+ | - []
+ | ...
 box.rollback();
----
-...
+ | ---
+ | ...
 errinj.set("ERRINJ_TUPLE_ALLOC", false);
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 box.begin()
     s:insert{1}
     errinj.set("ERRINJ_TUPLE_ALLOC", true)
     s:insert{2}
 box.commit();
----
-- error: Failed to allocate 16 bytes in slab allocator for memtx_tuple
-...
+ | ---
+ | - error: Failed to allocate 16 bytes in slab allocator for memtx_tuple
+ | ...
 errinj.set("ERRINJ_TUPLE_ALLOC", false);
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 box.rollback();
----
-...
+ | ---
+ | ...
 s:select{};
----
-- []
-...
+ | ---
+ | - []
+ | ...
 box.begin()
     s:insert{1}
     errinj.set("ERRINJ_TUPLE_ALLOC", true)
     pcall(s.insert, s, {2})
 box.commit();
----
-...
+ | ---
+ | ...
 s:select{};
----
-- - [1]
-...
+ | ---
+ | - - [1]
+ | ...
 box.rollback();
----
-...
+ | ---
+ | ...
+
 test_run:cmd("setopt delimiter ''");
----
-- true
-...
+ | ---
+ | - true
+ | ...
 errinj.set("ERRINJ_TUPLE_ALLOC", false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
+
 s:drop()
----
-...
+ | ---
+ | ...
 s = box.schema.space.create('test')
----
-...
+ | ---
+ | ...
 _ = s:create_index('test', {parts = {1, 'unsigned', 3, 'unsigned', 5, 'unsigned'}})
----
-...
+ | ---
+ | ...
 s:insert{1, 2, 3, 4, 5, 6}
----
-- [1, 2, 3, 4, 5, 6]
-...
+ | ---
+ | - [1, 2, 3, 4, 5, 6]
+ | ...
 t = s:select{}[1]
----
-...
+ | ---
+ | ...
 errinj.set("ERRINJ_TUPLE_FIELD", true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 tostring(t[1]) .. tostring(t[2]) ..tostring(t[3]) .. tostring(t[4]) .. tostring(t[5]) .. tostring(t[6])
----
-- 1nil3nil5nil
-...
+ | ---
+ | - 1nil3nil5nil
+ | ...
 errinj.set("ERRINJ_TUPLE_FIELD", false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 tostring(t[1]) .. tostring(t[2]) ..tostring(t[3]) .. tostring(t[4]) .. tostring(t[5]) .. tostring(t[6])
----
-- '123456'
-...
+ | ---
+ | - '123456'
+ | ...
+
 s:drop()
----
-...
+ | ---
+ | ...
 s = box.schema.space.create('test')
----
-...
+ | ---
+ | ...
 _ = s:create_index('test', {parts = {2, 'unsigned', 4, 'unsigned', 6, 'unsigned'}})
----
-...
+ | ---
+ | ...
 s:insert{1, 2, 3, 4, 5, 6}
----
-- [1, 2, 3, 4, 5, 6]
-...
+ | ---
+ | - [1, 2, 3, 4, 5, 6]
+ | ...
 t = s:select{}[1]
----
-...
+ | ---
+ | ...
 errinj.set("ERRINJ_TUPLE_FIELD", true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 tostring(t[1]) .. tostring(t[2]) ..tostring(t[3]) .. tostring(t[4]) .. tostring(t[5]) .. tostring(t[6])
----
-- 12nil4nil6
-...
+ | ---
+ | - 12nil4nil6
+ | ...
 errinj.set("ERRINJ_TUPLE_FIELD", false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 tostring(t[1]) .. tostring(t[2]) ..tostring(t[3]) .. tostring(t[4]) .. tostring(t[5]) .. tostring(t[6])
----
-- '123456'
-...
+ | ---
+ | - '123456'
+ | ...
+
 -- Cleanup
 s:drop()
----
-...
+ | ---
+ | ...
+
 --
 -- gh-2046: don't store offsets for sequential multi-parts keys
 --
 s = box.schema.space.create('test')
----
-...
+ | ---
+ | ...
 _ = s:create_index('seq2', { parts = { 1, 'unsigned', 2, 'unsigned' }})
----
-...
+ | ---
+ | ...
 _ = s:create_index('seq3', { parts = { 1, 'unsigned', 2, 'unsigned', 3, 'unsigned' }})
----
-...
+ | ---
+ | ...
 _ = s:create_index('seq5', { parts = { 1, 'unsigned', 2, 'unsigned', 3, 'unsigned', 4, 'scalar', 5, 'number' }})
----
-...
+ | ---
+ | ...
 _ = s:create_index('rnd1', { parts = { 3, 'unsigned' }})
----
-...
+ | ---
+ | ...
+
 errinj.set("ERRINJ_TUPLE_FIELD", true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 tuple = s:insert({1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
----
-...
+ | ---
+ | ...
 tuple
----
-- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
-...
+ | ---
+ | - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ | ...
 tuple[1] -- not-null, always accessible
----
-- 1
-...
+ | ---
+ | - 1
+ | ...
 tuple[2] -- null, doesn't have offset
----
-- null
-...
+ | ---
+ | - null
+ | ...
 tuple[3] -- not null, has offset
----
-- 3
-...
+ | ---
+ | - 3
+ | ...
 tuple[4] -- null, doesn't have offset
----
-- null
-...
+ | ---
+ | - null
+ | ...
 tuple[5] -- null, doesn't have offset
----
-- null
-...
+ | ---
+ | - null
+ | ...
 s.index.seq2:select({1})
----
-- - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
-...
+ | ---
+ | - - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ | ...
 s.index.seq2:select({1, 2})
----
-- - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
-...
+ | ---
+ | - - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ | ...
 s.index.seq3:select({1})
----
-- - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
-...
+ | ---
+ | - - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ | ...
 s.index.seq3:select({1, 2, 3})
----
-- - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
-...
+ | ---
+ | - - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ | ...
 s.index.seq5:select({1})
----
-- - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
-...
+ | ---
+ | - - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ | ...
 s.index.seq5:select({1, 2, 3, 4, 5})
----
-- - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
-...
+ | ---
+ | - - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ | ...
 s.index.rnd1:select({3})
----
-- - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
-...
+ | ---
+ | - - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ | ...
 errinj.set("ERRINJ_TUPLE_FIELD", false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 s:drop()
----
-...
+ | ---
+ | ...
+
 space = box.schema.space.create('test')
----
-...
+ | ---
+ | ...
 _ = space:create_index('pk')
----
-...
+ | ---
+ | ...
 errinj.set("ERRINJ_WAL_WRITE", true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 space:insert{1}
----
-- error: Failed to write to disk
-...
+ | ---
+ | - error: Failed to write to disk
+ | ...
 errinj.set("ERRINJ_WAL_WRITE", false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
+
 errinj.set("ERRINJ_WAL_WRITE_DISK", true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 _ = space:insert{1, require'digest'.urandom(192 * 1024)}
----
-- error: Failed to write to disk
-...
+ | ---
+ | - error: Failed to write to disk
+ | ...
 errinj.set("ERRINJ_WAL_WRITE_DISK", false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
+
 _ = space:insert{1}
----
-...
+ | ---
+ | ...
 errinj.set("ERRINJ_WAL_WRITE", true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 box.snapshot()
----
-- error: Error injection 'xlog write injection'
-...
+ | ---
+ | - error: Error injection 'xlog write injection'
+ | ...
 errinj.set("ERRINJ_WAL_WRITE", false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 space:drop()
----
-...
+ | ---
+ | ...
+
 --test space:bsize() in case of memory error
 utils = dofile('utils.lua')
----
-...
+ | ---
+ | ...
 s = box.schema.space.create('space_bsize')
----
-...
+ | ---
+ | ...
 idx = s:create_index('primary')
----
-...
+ | ---
+ | ...
+
 for i = 1, 13 do s:insert{ i, string.rep('x', i) } end
----
-...
+ | ---
+ | ...
+
 s:bsize()
----
-- 130
-...
+ | ---
+ | - 130
+ | ...
 utils.space_bsize(s)
----
-- 130
-...
+ | ---
+ | - 130
+ | ...
+
 errinj.set("ERRINJ_TUPLE_ALLOC", true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
+
 s:replace{1, "test"}
----
-- error: Failed to allocate 21 bytes in slab allocator for memtx_tuple
-...
+ | ---
+ | - error: Failed to allocate 21 bytes in slab allocator for memtx_tuple
+ | ...
 s:bsize()
----
-- 130
-...
+ | ---
+ | - 130
+ | ...
 utils.space_bsize(s)
----
-- 130
-...
+ | ---
+ | - 130
+ | ...
+
 s:update({1}, {{'=', 3, '!'}})
----
-- error: Failed to allocate 20 bytes in slab allocator for memtx_tuple
-...
+ | ---
+ | - error: Failed to allocate 20 bytes in slab allocator for memtx_tuple
+ | ...
 s:bsize()
----
-- 130
-...
+ | ---
+ | - 130
+ | ...
 utils.space_bsize(s)
----
-- 130
-...
+ | ---
+ | - 130
+ | ...
+
 errinj.set("ERRINJ_TUPLE_ALLOC", false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
+
 s:drop()
----
-...
+ | ---
+ | ...
+
 space = box.schema.space.create('test')
----
-...
+ | ---
+ | ...
 index1 = space:create_index('primary')
----
-...
+ | ---
+ | ...
 fiber = require'fiber'
----
-...
+ | ---
+ | ...
 ch = fiber.channel(1)
----
-...
+ | ---
+ | ...
+
 test_run:cmd('setopt delimiter ";"')
----
-- true
-...
+ | ---
+ | - true
+ | ...
 function test()
   errinj.set('ERRINJ_WAL_WRITE_DISK', true)
   pcall(box.space.test.replace, box.space.test, {1, 1})
   errinj.set('ERRINJ_WAL_WRITE_DISK', false)
   ch:put(true)
 end ;
----
-...
+ | ---
+ | ...
+
 function run()
   fiber.create(test)
   box.snapshot()
 end ;
----
-...
+ | ---
+ | ...
+
 test_run:cmd('setopt delimiter ""');
----
-- true
-...
+ | ---
+ | - true
+ | ...
+
 -- Port_dump can fail.
+
 box.schema.user.grant('guest', 'read', 'space', '_space')
----
-...
+ | ---
+ | ...
+
 cn = net_box.connect(box.cfg.listen)
----
-...
+ | ---
+ | ...
 cn:ping()
----
-- true
-...
+ | ---
+ | - true
+ | ...
 errinj.set('ERRINJ_PORT_DUMP', true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 ok, ret = pcall(cn.space._space.select, cn.space._space)
----
-...
+ | ---
+ | ...
 assert(not ok)
----
-- true
-...
+ | ---
+ | - true
+ | ...
 assert(string.match(tostring(ret), 'Failed to allocate'))
----
-- Failed to allocate
-...
+ | ---
+ | - Failed to allocate
+ | ...
 errinj.set('ERRINJ_PORT_DUMP', false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 cn:close()
----
-...
+ | ---
+ | ...
 box.schema.user.revoke('guest', 'read', 'space', '_space')
----
-...
+ | ---
+ | ...
+
 run()
----
-- error: Can't start a checkpoint while in cascading rollback
-...
+ | ---
+ | - error: Can't start a checkpoint while in cascading rollback
+ | ...
 ch:get()
----
-- true
-...
+ | ---
+ | - true
+ | ...
+
 box.space.test:select()
----
-- []
-...
+ | ---
+ | - []
+ | ...
 test_run:cmd('restart server default')
+ | 
 box.space.test:select()
----
-- []
-...
+ | ---
+ | - []
+ | ...
 box.space.test:drop()
----
-...
+ | ---
+ | ...
+
 errinj = box.error.injection
----
-...
+ | ---
+ | ...
 net_box = require('net.box')
----
-...
+ | ---
+ | ...
 fiber = require'fiber'
----
-...
+ | ---
+ | ...
+
 s = box.schema.space.create('test')
----
-...
+ | ---
+ | ...
 _ = s:create_index('pk')
----
-...
+ | ---
+ | ...
+
 ch = fiber.channel(2)
----
-...
+ | ---
+ | ...
+
 test_run:cmd("setopt delimiter ';'")
----
-- true
-...
+ | ---
+ | - true
+ | ...
 function test(tuple)
    ch:put({pcall(s.replace, s, tuple)})
 end;
----
-...
+ | ---
+ | ...
 test_run:cmd("setopt delimiter ''");
----
-- true
-...
+ | ---
+ | - true
+ | ...
+
 errinj.set("ERRINJ_WAL_WRITE", true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 _ = {fiber.create(test, {1, 2, 3}), fiber.create(test, {3, 4, 5})}
----
-...
+ | ---
+ | ...
+
 {ch:get(), ch:get()}
----
-- - - false
-    - Failed to write to disk
-  - - false
-    - Failed to write to disk
-...
+ | ---
+ | - - - false
+ |     - Failed to write to disk
+ |   - - false
+ |     - Failed to write to disk
+ | ...
 errinj.set("ERRINJ_WAL_WRITE", false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 s:drop()
----
-...
+ | ---
+ | ...
+
 -- rebuild some secondary indexes if the primary was changed
 s = box.schema.space.create('test')
----
-...
+ | ---
+ | ...
 i1 = s:create_index('i1', {parts = {1, 'unsigned'}})
----
-...
+ | ---
+ | ...
 --i2 = s:create_index('i2', {parts = {5, 'unsigned'}, unique = false})
 --i3 = s:create_index('i3', {parts = {6, 'unsigned'}, unique = false})
 i2 = i1 i3 = i1
----
-...
+ | ---
+ | ...
+
 _ = s:insert{1, 4, 3, 4, 10, 10}
----
-...
+ | ---
+ | ...
 _ = s:insert{2, 3, 1, 2, 10, 10}
----
-...
+ | ---
+ | ...
 _ = s:insert{3, 2, 2, 1, 10, 10}
----
-...
+ | ---
+ | ...
 _ = s:insert{4, 1, 4, 3, 10, 10}
----
-...
+ | ---
+ | ...
+
 i1:select{}
----
-- - [1, 4, 3, 4, 10, 10]
-  - [2, 3, 1, 2, 10, 10]
-  - [3, 2, 2, 1, 10, 10]
-  - [4, 1, 4, 3, 10, 10]
-...
+ | ---
+ | - - [1, 4, 3, 4, 10, 10]
+ |   - [2, 3, 1, 2, 10, 10]
+ |   - [3, 2, 2, 1, 10, 10]
+ |   - [4, 1, 4, 3, 10, 10]
+ | ...
 i2:select{}
----
-- - [1, 4, 3, 4, 10, 10]
-  - [2, 3, 1, 2, 10, 10]
-  - [3, 2, 2, 1, 10, 10]
-  - [4, 1, 4, 3, 10, 10]
-...
+ | ---
+ | - - [1, 4, 3, 4, 10, 10]
+ |   - [2, 3, 1, 2, 10, 10]
+ |   - [3, 2, 2, 1, 10, 10]
+ |   - [4, 1, 4, 3, 10, 10]
+ | ...
 i3:select{}
----
-- - [1, 4, 3, 4, 10, 10]
-  - [2, 3, 1, 2, 10, 10]
-  - [3, 2, 2, 1, 10, 10]
-  - [4, 1, 4, 3, 10, 10]
-...
+ | ---
+ | - - [1, 4, 3, 4, 10, 10]
+ |   - [2, 3, 1, 2, 10, 10]
+ |   - [3, 2, 2, 1, 10, 10]
+ |   - [4, 1, 4, 3, 10, 10]
+ | ...
+
 i1:alter({parts={2, 'unsigned'}})
----
-...
+ | ---
+ | ...
+
 _ = collectgarbage('collect')
----
-...
+ | ---
+ | ...
 i1:select{}
----
-- - [4, 1, 4, 3, 10, 10]
-  - [3, 2, 2, 1, 10, 10]
-  - [2, 3, 1, 2, 10, 10]
-  - [1, 4, 3, 4, 10, 10]
-...
+ | ---
+ | - - [4, 1, 4, 3, 10, 10]
+ |   - [3, 2, 2, 1, 10, 10]
+ |   - [2, 3, 1, 2, 10, 10]
+ |   - [1, 4, 3, 4, 10, 10]
+ | ...
 i2:select{}
----
-- - [4, 1, 4, 3, 10, 10]
-  - [3, 2, 2, 1, 10, 10]
-  - [2, 3, 1, 2, 10, 10]
-  - [1, 4, 3, 4, 10, 10]
-...
+ | ---
+ | - - [4, 1, 4, 3, 10, 10]
+ |   - [3, 2, 2, 1, 10, 10]
+ |   - [2, 3, 1, 2, 10, 10]
+ |   - [1, 4, 3, 4, 10, 10]
+ | ...
 i3:select{}
----
-- - [4, 1, 4, 3, 10, 10]
-  - [3, 2, 2, 1, 10, 10]
-  - [2, 3, 1, 2, 10, 10]
-  - [1, 4, 3, 4, 10, 10]
-...
+ | ---
+ | - - [4, 1, 4, 3, 10, 10]
+ |   - [3, 2, 2, 1, 10, 10]
+ |   - [2, 3, 1, 2, 10, 10]
+ |   - [1, 4, 3, 4, 10, 10]
+ | ...
+
 box.error.injection.set('ERRINJ_BUILD_INDEX', i2.id)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
+
 i1:alter{parts = {3, "unsigned"}}
----
-- error: Error injection 'build index'
-...
+ | ---
+ | - error: Error injection 'build index'
+ | ...
+
 _ = collectgarbage('collect')
----
-...
+ | ---
+ | ...
 i1:select{}
----
-- - [4, 1, 4, 3, 10, 10]
-  - [3, 2, 2, 1, 10, 10]
-  - [2, 3, 1, 2, 10, 10]
-  - [1, 4, 3, 4, 10, 10]
-...
+ | ---
+ | - - [4, 1, 4, 3, 10, 10]
+ |   - [3, 2, 2, 1, 10, 10]
+ |   - [2, 3, 1, 2, 10, 10]
+ |   - [1, 4, 3, 4, 10, 10]
+ | ...
 i2:select{}
----
-- - [4, 1, 4, 3, 10, 10]
-  - [3, 2, 2, 1, 10, 10]
-  - [2, 3, 1, 2, 10, 10]
-  - [1, 4, 3, 4, 10, 10]
-...
+ | ---
+ | - - [4, 1, 4, 3, 10, 10]
+ |   - [3, 2, 2, 1, 10, 10]
+ |   - [2, 3, 1, 2, 10, 10]
+ |   - [1, 4, 3, 4, 10, 10]
+ | ...
 i3:select{}
----
-- - [4, 1, 4, 3, 10, 10]
-  - [3, 2, 2, 1, 10, 10]
-  - [2, 3, 1, 2, 10, 10]
-  - [1, 4, 3, 4, 10, 10]
-...
+ | ---
+ | - - [4, 1, 4, 3, 10, 10]
+ |   - [3, 2, 2, 1, 10, 10]
+ |   - [2, 3, 1, 2, 10, 10]
+ |   - [1, 4, 3, 4, 10, 10]
+ | ...
+
 box.error.injection.set('ERRINJ_BUILD_INDEX', i3.id)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
+
 i1:alter{parts = {4, "unsigned"}}
----
-- error: Error injection 'build index'
-...
+ | ---
+ | - error: Error injection 'build index'
+ | ...
+
 _ = collectgarbage('collect')
----
-...
+ | ---
+ | ...
 i1:select{}
----
-- - [4, 1, 4, 3, 10, 10]
-  - [3, 2, 2, 1, 10, 10]
-  - [2, 3, 1, 2, 10, 10]
-  - [1, 4, 3, 4, 10, 10]
-...
+ | ---
+ | - - [4, 1, 4, 3, 10, 10]
+ |   - [3, 2, 2, 1, 10, 10]
+ |   - [2, 3, 1, 2, 10, 10]
+ |   - [1, 4, 3, 4, 10, 10]
+ | ...
 i2:select{}
----
-- - [4, 1, 4, 3, 10, 10]
-  - [3, 2, 2, 1, 10, 10]
-  - [2, 3, 1, 2, 10, 10]
-  - [1, 4, 3, 4, 10, 10]
-...
+ | ---
+ | - - [4, 1, 4, 3, 10, 10]
+ |   - [3, 2, 2, 1, 10, 10]
+ |   - [2, 3, 1, 2, 10, 10]
+ |   - [1, 4, 3, 4, 10, 10]
+ | ...
 i3:select{}
----
-- - [4, 1, 4, 3, 10, 10]
-  - [3, 2, 2, 1, 10, 10]
-  - [2, 3, 1, 2, 10, 10]
-  - [1, 4, 3, 4, 10, 10]
-...
+ | ---
+ | - - [4, 1, 4, 3, 10, 10]
+ |   - [3, 2, 2, 1, 10, 10]
+ |   - [2, 3, 1, 2, 10, 10]
+ |   - [1, 4, 3, 4, 10, 10]
+ | ...
+
 box.error.injection.set('ERRINJ_BUILD_INDEX', -1)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
+
 s:drop()
----
-...
+ | ---
+ | ...
+
 --
 -- Do not rebuild index if the only change is a key part type
 -- compatible change.
 --
 s = box.schema.space.create('test')
----
-...
+ | ---
+ | ...
 pk = s:create_index('pk')
----
-...
+ | ---
+ | ...
 sk = s:create_index('sk', {parts = {2, 'unsigned'}})
----
-...
+ | ---
+ | ...
 s:replace{1, 1}
----
-- [1, 1]
-...
+ | ---
+ | - [1, 1]
+ | ...
 box.error.injection.set('ERRINJ_BUILD_INDEX', sk.id)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 sk:alter({parts = {2, 'number'}})
----
-...
+ | ---
+ | ...
 box.error.injection.set('ERRINJ_BUILD_INDEX', -1)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 s:drop()
----
-...
+ | ---
+ | ...
+
 --
 -- gh-3255: iproto can crash and discard responses, if a network
 -- is saturated, and DML yields too long on commit.
 --
+
 s = box.schema.space.create('test')
----
-...
+ | ---
+ | ...
 _ = s:create_index('pk')
----
-...
+ | ---
+ | ...
 box.schema.user.grant('guest', 'read,write,alter', 'space', 'test')
----
-...
+ | ---
+ | ...
 c = net_box.connect(box.cfg.listen)
----
-...
+ | ---
+ | ...
+
 ch = fiber.channel(200)
----
-...
+ | ---
+ | ...
 errinj.set("ERRINJ_IPROTO_TX_DELAY", true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 for i = 1, 100 do fiber.create(function() for j = 1, 10 do c.space.test:replace{1} end ch:put(true) end) end
----
-...
+ | ---
+ | ...
 for i = 1, 100 do fiber.create(function() for j = 1, 10 do c.space.test:select() end ch:put(true) end) end
----
-...
+ | ---
+ | ...
 for i = 1, 200 do ch:get() end
----
-...
+ | ---
+ | ...
 errinj.set("ERRINJ_IPROTO_TX_DELAY", false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
+
 s:drop()
----
-...
+ | ---
+ | ...
+
 --
 -- gh-3325: do not cancel already sent requests, when a schema
 -- change is detected.
 --
+
 box.schema.user.grant('guest', 'execute', 'universe')
----
-...
+ | ---
+ | ...
+
 s = box.schema.create_space('test')
----
-...
+ | ---
+ | ...
 pk = s:create_index('pk')
----
-...
+ | ---
+ | ...
+
 box.schema.user.grant('guest', 'read,write,alter', 'space', 'test')
----
-...
+ | ---
+ | ...
 box.schema.user.grant('guest', 'create', 'space')
----
-...
+ | ---
+ | ...
 box.schema.user.grant('guest', 'write', 'space', '_index')
----
-...
+ | ---
+ | ...
 s:replace{1, 1}
----
-- [1, 1]
-...
+ | ---
+ | - [1, 1]
+ | ...
 cn = net_box.connect(box.cfg.listen)
----
-...
+ | ---
+ | ...
 errinj.set("ERRINJ_WAL_DELAY", true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 ok = nil
----
-...
+ | ---
+ | ...
 err = nil
----
-...
+ | ---
+ | ...
 test_run:cmd('setopt delimiter ";"')
----
-- true
-...
+ | ---
+ | - true
+ | ...
 f = fiber.create(function()
   local str = 'box.space.test:create_index("sk", {parts = {{2, "integer"}}})'
   ok, err = pcall(cn.eval, cn, str)
 end)
 test_run:cmd('setopt delimiter ""');
----
-...
+ | ---
+ | ...
 cn.space.test:get{1}
----
-- [1, 1]
-...
+ | ---
+ | - [1, 1]
+ | ...
 errinj.set("ERRINJ_WAL_DELAY", false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 while ok == nil do fiber.sleep(0.01) end
----
-...
+ | ---
+ | ...
 ok, err
----
-- true
-- null
-...
+ | ---
+ | - true
+ | - null
+ | ...
 cn:close()
----
-...
+ | ---
+ | ...
 s:drop()
----
-...
+ | ---
+ | ...
 box.schema.user.revoke('guest', 'execute', 'universe')
----
-...
+ | ---
+ | ...
 box.schema.user.revoke('guest', 'create', 'space')
----
-...
+ | ---
+ | ...
 box.schema.user.revoke('guest', 'write', 'space', '_index')
----
-...
+ | ---
+ | ...
 --
 -- If message memory pool is used up, stop the connection, until
 -- the pool has free memory.
 --
 started = 0
----
-...
+ | ---
+ | ...
 finished = 0
----
-...
+ | ---
+ | ...
 continue = false
----
-...
+ | ---
+ | ...
 test_run:cmd('setopt delimiter ";"')
----
-- true
-...
+ | ---
+ | - true
+ | ...
 function long_poll_f()
     started = started + 1
     f = fiber.self()
     while not continue do fiber.sleep(0.01) end
     finished = finished + 1
 end;
----
-...
+ | ---
+ | ...
+
 box.schema.func.create('long_poll_f');
----
-...
+ | ---
+ | ...
 box.schema.user.grant('guest', 'execute', 'function', 'long_poll_f');
----
-...
+ | ---
+ | ...
+
 test_run:cmd('setopt delimiter ""');
----
-- true
-...
+ | ---
+ | - true
+ | ...
 cn = net_box.connect(box.cfg.listen)
----
-...
+ | ---
+ | ...
 function long_poll() cn:call('long_poll_f') end
----
-...
+ | ---
+ | ...
 _ = fiber.create(long_poll)
----
-...
+ | ---
+ | ...
 while started ~= 1 do fiber.sleep(0.01) end
----
-...
+ | ---
+ | ...
 -- Simulate OOM for new requests.
 errinj.set("ERRINJ_TESTING", true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 -- This request tries to allocate memory for request data and
 -- fails. This stops the connection until an existing
 -- request is finished.
 log = require('log')
----
-...
+ | ---
+ | ...
 -- Fill the log with garbage to not accidentally read log messages
 -- produced by a previous test.
 log.info(string.rep('a', 1000))
----
-...
+ | ---
+ | ...
 _ = fiber.create(long_poll)
----
-...
+ | ---
+ | ...
 while not test_run:grep_log('default', 'can not allocate memory for a new message', 1000) do fiber.sleep(0.01) end
----
-...
+ | ---
+ | ...
 test_run:grep_log('default', 'stopping input on connection', 1000) ~= nil
----
-- true
-...
+ | ---
+ | - true
+ | ...
 started == 1
----
-- true
-...
+ | ---
+ | - true
+ | ...
 continue = true
----
-...
+ | ---
+ | ...
 errinj.set("ERRINJ_TESTING", false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 -- Ensure that when memory is available again, the pending
 -- request is executed.
 while finished ~= 2 do fiber.sleep(0.01) end
----
-...
+ | ---
+ | ...
 cn:close()
----
-...
+ | ---
+ | ...
+
 box.schema.user.revoke('guest', 'execute', 'function', 'long_poll_f')
----
-...
+ | ---
+ | ...
 box.schema.func.drop('long_poll_f')
----
-...
+ | ---
+ | ...
 --
 -- gh-3289: drop/truncate leaves the space in inconsistent
 -- state if WAL write fails.
 --
 s = box.schema.space.create('test')
----
-...
+ | ---
+ | ...
 _ = s:create_index('pk')
----
-...
+ | ---
+ | ...
 for i = 1, 10 do s:replace{i} end
----
-...
+ | ---
+ | ...
 errinj.set('ERRINJ_WAL_IO', true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 s:drop()
----
-- error: Failed to write to disk
-...
+ | ---
+ | - error: Failed to write to disk
+ | ...
 s:truncate()
----
-- error: Failed to write to disk
-...
+ | ---
+ | - error: Failed to write to disk
+ | ...
 s:drop()
----
-- error: Failed to write to disk
-...
+ | ---
+ | - error: Failed to write to disk
+ | ...
 s:truncate()
----
-- error: Failed to write to disk
-...
+ | ---
+ | - error: Failed to write to disk
+ | ...
 errinj.set('ERRINJ_WAL_IO', false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 for i = 1, 10 do s:replace{i + 10} end
----
-...
+ | ---
+ | ...
 s:select()
----
-- - [1]
-  - [2]
-  - [3]
-  - [4]
-  - [5]
-  - [6]
-  - [7]
-  - [8]
-  - [9]
-  - [10]
-  - [11]
-  - [12]
-  - [13]
-  - [14]
-  - [15]
-  - [16]
-  - [17]
-  - [18]
-  - [19]
-  - [20]
-...
+ | ---
+ | - - [1]
+ |   - [2]
+ |   - [3]
+ |   - [4]
+ |   - [5]
+ |   - [6]
+ |   - [7]
+ |   - [8]
+ |   - [9]
+ |   - [10]
+ |   - [11]
+ |   - [12]
+ |   - [13]
+ |   - [14]
+ |   - [15]
+ |   - [16]
+ |   - [17]
+ |   - [18]
+ |   - [19]
+ |   - [20]
+ | ...
 s:drop()
----
-...
+ | ---
+ | ...
+
 --
 -- gh-3432: check that deletion of temporary tuples is not delayed
 -- if snapshot is in progress.
 --
 test_run:cmd("create server test with script='box/lua/cfg_memory.lua'")
----
-- true
-...
+ | ---
+ | - true
+ | ...
 test_run:cmd(string.format("start server test with args='%d'", 100 * 1024 * 1024))
----
-- true
-...
+ | ---
+ | - true
+ | ...
 test_run:cmd("switch test")
----
-- true
-...
+ | ---
+ | - true
+ | ...
+
 fiber = require('fiber')
----
-...
+ | ---
+ | ...
+
 -- Create a persistent space.
 _ = box.schema.space.create('test')
----
-...
+ | ---
+ | ...
 _ = box.space.test:create_index('pk')
----
-...
+ | ---
+ | ...
 for i = 1, 100 do box.space.test:insert{i} end
----
-...
+ | ---
+ | ...
+
 -- Create a temporary space.
 count = 500
----
-...
+ | ---
+ | ...
 pad = string.rep('x', 100 * 1024)
----
-...
+ | ---
+ | ...
 _ = box.schema.space.create('tmp', {temporary = true})
----
-...
+ | ---
+ | ...
 _ = box.space.tmp:create_index('pk')
----
-...
+ | ---
+ | ...
 for i = 1, count do box.space.tmp:insert{i, pad} end
----
-...
+ | ---
+ | ...
+
 -- Start background snapshot.
 c = fiber.channel(1)
----
-...
+ | ---
+ | ...
 box.error.injection.set('ERRINJ_SNAP_WRITE_DELAY', true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 _ = fiber.create(function() box.snapshot() c:put(true) end)
----
-...
+ | ---
+ | ...
+
 -- Overwrite data stored in the temporary space while snapshot
 -- is in progress to make sure that tuples stored in it are freed
 -- immediately.
 for i = 1, count do box.space.tmp:delete{i} end
----
-...
+ | ---
+ | ...
 _ = collectgarbage('collect')
----
-...
+ | ---
+ | ...
 for i = 1, count do box.space.tmp:insert{i, pad} end
----
-...
+ | ---
+ | ...
+
 box.error.injection.set('ERRINJ_SNAP_WRITE_DELAY', false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 c:get()
----
-- true
-...
+ | ---
+ | - true
+ | ...
+
 box.space.tmp:drop()
----
-...
+ | ---
+ | ...
 box.space.test:drop()
----
-...
+ | ---
+ | ...
+
 test_run:cmd("switch default")
----
-- true
-...
+ | ---
+ | - true
+ | ...
 test_run:cmd("stop server test")
----
-- true
-...
+ | ---
+ | - true
+ | ...
 test_run:cmd("cleanup server test")
----
-- true
-...
+ | ---
+ | - true
+ | ...
+
 --
 -- gh-3406: check that incomplete files got cleaned up after restart.
 --
 fio = require('fio')
----
-...
+ | ---
+ | ...
 fiber = require('fiber')
----
-...
+ | ---
+ | ...
+
 -- Check that snap.inprogress files are removed.
 _ = box.schema.space.create('test')
----
-...
+ | ---
+ | ...
 _ = box.space.test:create_index('primary')
----
-...
+ | ---
+ | ...
 for i = 1, 10 do box.space.test:insert{i} end
----
-...
+ | ---
+ | ...
+
 errinj.set('ERRINJ_SNAP_WRITE_DELAY', true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 _ = fiber.create(function() box.snapshot() end)
----
-...
+ | ---
+ | ...
 path = fio.pathjoin(box.cfg.memtx_dir, '*.snap.inprogress')
----
-...
+ | ---
+ | ...
 while #fio.glob(path) == 0 do fiber.sleep(0.001) end
----
-...
+ | ---
+ | ...
 #fio.glob(path) > 0
----
-- true
-...
+ | ---
+ | - true
+ | ...
+
 test_run:cmd('restart server default')
+ | 
+
 fio = require('fio')
----
-...
+ | ---
+ | ...
 fiber = require('fiber')
----
-...
+ | ---
+ | ...
 errinj = box.error.injection
----
-...
+ | ---
+ | ...
+
 #fio.glob(fio.pathjoin(box.cfg.memtx_dir, "*.snap.inprogress")) == 0
----
-- true
-...
+ | ---
+ | - true
+ | ...
 box.space.test:drop()
----
-...
+ | ---
+ | ...
+
 -- Check that run.inprogress, index.inprogress, and vylog.inprogress
 -- files are removed.
 _ = box.schema.space.create('test', {engine = 'vinyl'})
----
-...
+ | ---
+ | ...
 _ = box.space.test:create_index('primary')
----
-...
+ | ---
+ | ...
+
 errinj.set('ERRINJ_VY_LOG_FILE_RENAME', true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 box.snapshot()
----
-- error: Error injection 'vinyl log file rename'
-...
+ | ---
+ | - error: Error injection 'vinyl log file rename'
+ | ...
 errinj.set('ERRINJ_VY_LOG_FILE_RENAME', false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
+
 errinj.set('ERRINJ_VY_GC', true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 errinj.set('ERRINJ_VY_SCHED_TIMEOUT', 0.001)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
+
 errinj.set('ERRINJ_VY_RUN_FILE_RENAME', true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 box.space.test:insert{1}
----
-- [1]
-...
+ | ---
+ | - [1]
+ | ...
 box.snapshot() -- error
----
-- error: Error injection 'vinyl run file rename'
-...
+ | ---
+ | - error: Error injection 'vinyl run file rename'
+ | ...
 errinj.set('ERRINJ_VY_RUN_FILE_RENAME', false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
+
 -- Wait for the scheduler to unthrottle.
 repeat fiber.sleep(0.001) until pcall(box.snapshot)
----
-...
+ | ---
+ | ...
+
 errinj.set('ERRINJ_VY_INDEX_FILE_RENAME', true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 box.space.test:insert{2}
----
-- [2]
-...
+ | ---
+ | - [2]
+ | ...
 box.snapshot() -- error
----
-- error: Error injection 'vinyl index file rename'
-...
+ | ---
+ | - error: Error injection 'vinyl index file rename'
+ | ...
 errinj.set('ERRINJ_VY_INDEX_FILE_RENAME', false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
+
 errinj.set('ERRINJ_VY_SCHED_TIMEOUT', 0)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 errinj.set('ERRINJ_VY_GC', false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
+
 test_run:cmd('restart server default')
+ | 
+
 fio = require('fio')
----
-...
+ | ---
+ | ...
 #fio.glob(fio.pathjoin(box.cfg.vinyl_dir, '*.vylog.inprogress')) == 0
----
-- true
-...
+ | ---
+ | - true
+ | ...
 #fio.glob(fio.pathjoin(box.cfg.vinyl_dir, box.space.test.id, 0, '*.run.inprogress')) == 0
----
-- true
-...
+ | ---
+ | - true
+ | ...
 #fio.glob(fio.pathjoin(box.cfg.vinyl_dir, box.space.test.id, 0, '*.index.inprogress')) == 0
----
-- true
-...
+ | ---
+ | - true
+ | ...
+
 box.space.test:drop()
----
-...
+ | ---
+ | ...
+
 -- gh-4276 - check grant privilege rollback
 _ = box.schema.user.create('testg')
----
-...
+ | ---
+ | ...
 _ = box.schema.space.create('testg'):create_index('pk')
----
-...
+ | ---
+ | ...
+
 box.error.injection.set('ERRINJ_WAL_IO', true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 -- the grant operation above fails and test hasn't any space test permissions
 box.schema.user.grant('testg', 'read,write', 'space', 'testg')
----
-- error: Failed to write to disk
-...
+ | ---
+ | - error: Failed to write to disk
+ | ...
 -- switch user and check they couldn't select
 box.session.su('testg')
----
-...
+ | ---
+ | ...
 box.space.testg:select()
----
-- error: Read access to space 'testg' is denied for user 'testg'
-...
+ | ---
+ | - error: Read access to space 'testg' is denied for user 'testg'
+ | ...
 box.session.su('admin')
----
-...
+ | ---
+ | ...
 box.error.injection.set('ERRINJ_WAL_IO', false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 box.schema.user.drop('testg')
----
-...
+ | ---
+ | ...
 box.space.testg:drop()
----
-...
+ | ---
+ | ...
+
 --
 -- Errinj:get().
 --
 box.error.injection.get('bad name')
----
-- 'error: can''t find error injection ''bad name'''
-...
+ | ---
+ | - 'error: can''t find error injection ''bad name'''
+ | ...
 box.error.injection.set('ERRINJ_WAL_IO', true)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 box.error.injection.get('ERRINJ_WAL_IO')
----
-- true
-...
+ | ---
+ | - true
+ | ...
 box.error.injection.set('ERRINJ_WAL_IO', false)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 box.error.injection.get('ERRINJ_WAL_IO')
----
-- false
-...
+ | ---
+ | - false
+ | ...
 box.error.injection.set('ERRINJ_TUPLE_FORMAT_COUNT', 20)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 box.error.injection.get('ERRINJ_TUPLE_FORMAT_COUNT')
----
-- 20
-...
+ | ---
+ | - 20
+ | ...
 box.error.injection.set('ERRINJ_TUPLE_FORMAT_COUNT', -1)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 box.error.injection.get('ERRINJ_TUPLE_FORMAT_COUNT')
----
-- -1
-...
+ | ---
+ | - -1
+ | ...
 box.error.injection.set('ERRINJ_RELAY_TIMEOUT', 0.5)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 box.error.injection.get('ERRINJ_RELAY_TIMEOUT')
----
-- 0.5
-...
+ | ---
+ | - 0.5
+ | ...
 box.error.injection.set('ERRINJ_RELAY_TIMEOUT', 0)
----
-- ok
-...
+ | ---
+ | - ok
+ | ...
 box.error.injection.get('ERRINJ_RELAY_TIMEOUT')
----
-- 0
-...
+ | ---
+ | - 0
+ | ...
+
+--
+-- gh-4619-RTREE-doesn't-handle-OOM-properly
+--
+test_run:cmd('create server rtree with script = "box/lua/cfg_rtree.lua"')
+ | ---
+ | - true
+ | ...
+test_run:cmd("start server rtree")
+ | ---
+ | - true
+ | ...
+test_run:cmd('switch rtree')
+ | ---
+ | - true
+ | ...
+math = require("math")
+ | ---
+ | ...
+rtreespace = box.schema.create_space('rtree', {if_not_exists = true})
+ | ---
+ | ...
+rtreespace:create_index('pk', {if_not_exists = true})
+ | ---
+ | - unique: true
+ |   parts:
+ |   - type: unsigned
+ |     is_nullable: false
+ |     fieldno: 1
+ |   id: 0
+ |   space_id: 512
+ |   type: TREE
+ |   name: pk
+ | ...
+rtreespace:create_index('target', {type='rtree', dimension = 3, parts={2, 'array'},unique = false, if_not_exists = true,})
+ | ---
+ | - parts:
+ |   - type: array
+ |     is_nullable: false
+ |     fieldno: 2
+ |   dimension: 3
+ |   id: 1
+ |   type: RTREE
+ |   space_id: 512
+ |   name: target
+ | ...
+count = 10
+ | ---
+ | ...
+for i = 1, count do box.space.rtree:insert{i, {(i + 1) -\
+    math.floor((i + 1)/7000) * 7000, (i + 2) - math.floor((i + 2)/7000) * 7000,\
+    (i + 3) - math.floor((i + 3)/7000) * 7000}} end
+ | ---
+ | ...
+rtreespace:count()
+ | ---
+ | - 10
+ | ...
+box.snapshot()
+ | ---
+ | - ok
+ | ...
+test_run:cmd('switch default')
+ | ---
+ | - true
+ | ...
+test_run:cmd("stop server rtree")
+ | ---
+ | - true
+ | ...
+test_run:cmd("start server rtree with crash_expected=True")
+ | ---
+ | - false
+ | ...
+fio = require('fio')
+ | ---
+ | ...
+fh = fio.open(fio.pathjoin(fio.cwd(), 'cfg_rtree.log'), {'O_RDONLY'})
+ | ---
+ | ...
+size = fh:seek(0, 'SEEK_END')
+ | ---
+ | ...
+fh:seek(-256, 'SEEK_END') ~= nil
+ | ---
+ | - true
+ | ...
+line = fh:read(256)
+ | ---
+ | ...
+fh:close()
+ | ---
+ | - true
+ | ...
+string.match(line, 'Failed to allocate') ~= nil
+ | ---
+ | - true
+ | ...
diff --git a/test/box/errinj.test.lua b/test/box/errinj.test.lua
index 03c088677..598e7da33 100644
--- a/test/box/errinj.test.lua
+++ b/test/box/errinj.test.lua
@@ -620,3 +620,30 @@ box.error.injection.set('ERRINJ_RELAY_TIMEOUT', 0.5)
 box.error.injection.get('ERRINJ_RELAY_TIMEOUT')
 box.error.injection.set('ERRINJ_RELAY_TIMEOUT', 0)
 box.error.injection.get('ERRINJ_RELAY_TIMEOUT')
+
+--
+-- gh-4619-RTREE-doesn't-handle-OOM-properly
+--
+test_run:cmd('create server rtree with script = "box/lua/cfg_rtree.lua"')
+test_run:cmd("start server rtree")
+test_run:cmd('switch rtree')
+math = require("math")
+rtreespace = box.schema.create_space('rtree', {if_not_exists = true})
+rtreespace:create_index('pk', {if_not_exists = true})
+rtreespace:create_index('target', {type='rtree', dimension = 3, parts={2, 'array'},unique = false, if_not_exists = true,})
+count = 10
+for i = 1, count do box.space.rtree:insert{i, {(i + 1) -\
+    math.floor((i + 1)/7000) * 7000, (i + 2) - math.floor((i + 2)/7000) * 7000,\
+    (i + 3) - math.floor((i + 3)/7000) * 7000}} end
+rtreespace:count()
+box.snapshot()
+test_run:cmd('switch default')
+test_run:cmd("stop server rtree")
+test_run:cmd("start server rtree with crash_expected=True")
+fio = require('fio')
+fh = fio.open(fio.pathjoin(fio.cwd(), 'cfg_rtree.log'), {'O_RDONLY'})
+size = fh:seek(0, 'SEEK_END')
+fh:seek(-256, 'SEEK_END') ~= nil
+line = fh:read(256)
+fh:close()
+string.match(line, 'Failed to allocate') ~= nil
diff --git a/test/box/lua/cfg_rtree.lua b/test/box/lua/cfg_rtree.lua
new file mode 100644
index 000000000..f2d32ef7d
--- /dev/null
+++ b/test/box/lua/cfg_rtree.lua
@@ -0,0 +1,8 @@
+#!/usr/bin/env tarantool
+os = require('os')
+box.error.injection.set("ERRINJ_INDEX_RESERVE", true)
+box.cfg{
+    listen              = os.getenv("LISTEN"),
+}
+require('console').listen(os.getenv('ADMIN'))
+box.schema.user.grant('guest', 'read,write,execute', 'universe')
-- 
2.17.2 (Apple Git-113)



More information about the Tarantool-patches mailing list