Tarantool development patches archive
 help / color / mirror / Atom feed
* [Tarantool-patches] [PATCH v5 0/2] fiber: Handle stack madvise/mprotect errors
@ 2020-02-05 22:06 Cyrill Gorcunov
  2020-02-05 22:06 ` [Tarantool-patches] [PATCH v5 1/2] fiber: use diag_ logger in fiber_madvise/mprotect failures Cyrill Gorcunov
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Cyrill Gorcunov @ 2020-02-05 22:06 UTC (permalink / raw)
  To: tml

issue https://github.com/tarantool/tarantool/issues/4722
branch gorcunov/gh-4722-mprotect-diag-error-5

In v5:
 - merged errinj together with test into the patch itself
 - on guard page restore we print error and continue instead
   of immeadiate exit

Cyrill Gorcunov (2):
  fiber: use diag_ logger in fiber_madvise/mprotect failures
  fiber: leak slab if unable to bring prots back

 src/lib/core/errinj.h        |    2 +
 src/lib/core/fiber.c         |   92 +-
 test/box/errinj.result       | 2616 ++++++++++++++++++----------------
 test/unit/CMakeLists.txt     |    4 +
 test/unit/fiber_stack.cc     |   83 ++
 test/unit/fiber_stack.result |    6 +
 test/unit/suite.ini          |    2 +-
 7 files changed, 1530 insertions(+), 1275 deletions(-)
 create mode 100644 test/unit/fiber_stack.cc
 create mode 100644 test/unit/fiber_stack.result

-- 
2.20.1

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [Tarantool-patches] [PATCH v5 1/2] fiber: use diag_ logger in fiber_madvise/mprotect failures
  2020-02-05 22:06 [Tarantool-patches] [PATCH v5 0/2] fiber: Handle stack madvise/mprotect errors Cyrill Gorcunov
@ 2020-02-05 22:06 ` Cyrill Gorcunov
  2020-02-06 11:10   ` Alexander Turenko
  2020-02-05 22:06 ` [Tarantool-patches] [PATCH v5 2/2] fiber: leak slab if unable to bring prots back Cyrill Gorcunov
  2020-02-06 11:20 ` [Tarantool-patches] [PATCH v5 0/2] fiber: Handle stack madvise/mprotect errors Alexander Turenko
  2 siblings, 1 reply; 6+ messages in thread
From: Cyrill Gorcunov @ 2020-02-05 22:06 UTC (permalink / raw)
  To: tml

Both madvise and mprotect calls can fail due to various
reasons, mostly because of lack of free memory in the
system.

We log such cases via say_x helpers but this is not enough.
In particular tarantool/memcached relies on diag error to be
set to detect an error condition:

 | expire_fiber = fiber_new(name, memcached_expire_loop);
 | const box_error_t *err = box_error_last();
 | if (err) {
 |	say_error("Can't start the expire fiber");
 |	say_error("%s", box_error_message(err));
 |	return -1;
 | }

Thus lets use diag_set() helper here and instead of macros
use inline functions for better readability.

Fixes #4722

Reported-by: Alexander Turenko <alexander.turenko@tarantool.org>
Signed-off-by: Cyrill Gorcunov <gorcunov@gmail.com>
---
 src/lib/core/errinj.h        |    2 +
 src/lib/core/fiber.c         |   84 +-
 test/box/errinj.result       | 2616 ++++++++++++++++++----------------
 test/unit/CMakeLists.txt     |    4 +
 test/unit/fiber_stack.cc     |   83 ++
 test/unit/fiber_stack.result |    6 +
 test/unit/suite.ini          |    2 +-
 7 files changed, 1523 insertions(+), 1274 deletions(-)
 create mode 100644 test/unit/fiber_stack.cc
 create mode 100644 test/unit/fiber_stack.result

diff --git a/src/lib/core/errinj.h b/src/lib/core/errinj.h
index 672da2119..ed0cba903 100644
--- a/src/lib/core/errinj.h
+++ b/src/lib/core/errinj.h
@@ -135,6 +135,8 @@ 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_FIBER_MADVISE, ERRINJ_BOOL, {.bparam = false}) \
+	_(ERRINJ_FIBER_MPROTECT, ERRINJ_INT, {.iparam = -1})
 
 ENUM0(errinj_id, ERRINJ_LIST);
 extern struct errinj errinjs[];
diff --git a/src/lib/core/fiber.c b/src/lib/core/fiber.c
index 00ae8cded..57e4cb6ef 100644
--- a/src/lib/core/fiber.c
+++ b/src/lib/core/fiber.c
@@ -41,6 +41,7 @@
 #include "assoc.h"
 #include "memory.h"
 #include "trigger.h"
+#include "errinj.h"
 
 #if ENABLE_FIBER_TOP
 #include <x86intrin.h> /* __rdtscp() */
@@ -173,21 +174,40 @@ static int (*fiber_invoke)(fiber_func f, va_list ap);
 #define ASAN_FINISH_SWITCH_FIBER(var_name)
 #endif
 
-#define fiber_madvise(addr, len, advice)				\
-({									\
-	int err = madvise((addr), (len), (advice));			\
-	if (err)							\
-		say_syserror("madvise");				\
-	err;								\
-})
-
-#define fiber_mprotect(addr, len, prot)					\
-({									\
-	int err = mprotect((addr), (len), (prot));			\
-	if (err)							\
-		say_syserror("mprotect");				\
-	err;								\
-})
+static inline int
+fiber_madvise(void *addr, size_t len, int advice)
+{
+	int rc = 0;
+
+	ERROR_INJECT(ERRINJ_FIBER_MADVISE, {
+		errno = ENOMEM;
+		rc = -1;
+	});
+
+	if (rc != 0 || madvise(addr, len, advice) != 0) {
+		diag_set(SystemError, "fiber madvise failed");
+		return -1;
+	}
+	return 0;
+}
+
+static inline int
+fiber_mprotect(void *addr, size_t len, int prot)
+{
+	int rc = 0;
+
+	struct errinj *inj = errinj(ERRINJ_FIBER_MPROTECT, ERRINJ_INT);
+	if (inj != NULL && inj->iparam == prot) {
+		errno = ENOMEM;
+		rc = -1;
+	}
+
+	if (rc != 0 || mprotect(addr, len, prot) != 0) {
+		diag_set(SystemError, "fiber mprotect failed");
+		return -1;
+	}
+	return 0;
+}
 
 #if ENABLE_FIBER_TOP
 static __thread bool fiber_top_enabled = false;
@@ -951,6 +971,12 @@ fiber_stack_recycle(struct fiber *fiber)
 		start = page_align_up(fiber->stack_watermark);
 		end = fiber->stack + fiber->stack_size;
 	}
+
+	/*
+	 * Ignore errors on MADV_DONTNEED because this is
+	 * just a hint for OS and not critical for
+	 * functionality.
+	 */
 	fiber_madvise(start, end - start, MADV_DONTNEED);
 	stack_put_watermark(fiber->stack_watermark);
 }
@@ -969,7 +995,9 @@ fiber_stack_watermark_create(struct fiber *fiber)
 
 	/*
 	 * We don't expect the whole stack usage in regular
-	 * loads, let's try to minimize rss pressure.
+	 * loads, let's try to minimize rss pressure. But do
+	 * not exit if MADV_DONTNEED failed, it is just a hint
+	 * for OS, not critical one.
 	 */
 	fiber_madvise(fiber->stack, fiber->stack_size, MADV_DONTNEED);
 
@@ -1007,6 +1035,8 @@ fiber_stack_watermark_create(struct fiber *fiber)
 static void
 fiber_stack_destroy(struct fiber *fiber, struct slab_cache *slabc)
 {
+	static const int mprotect_flags = PROT_READ | PROT_WRITE;
+
 	if (fiber->stack != NULL) {
 		VALGRIND_STACK_DEREGISTER(fiber->stack_id);
 #if ENABLE_ASAN
@@ -1017,7 +1047,22 @@ fiber_stack_destroy(struct fiber *fiber, struct slab_cache *slabc)
 			guard = page_align_down(fiber->stack - page_size);
 		else
 			guard = page_align_up(fiber->stack + fiber->stack_size);
-		fiber_mprotect(guard, page_size, PROT_READ | PROT_WRITE);
+
+		if (fiber_mprotect(guard, page_size, mprotect_flags) != 0) {
+			/*
+			 * FIXME: We need some intelligent handling:
+			 * say put this slab into a queue and retry
+			 * to setup the original protection back in
+			 * background.
+			 *
+			 * Note that in case if we're called from
+			 * fiber_stack_create() the @mprotect_flags is
+			 * the same as the slab been created with, so
+			 * calling mprotect for VMA with same flags
+			 * won't fail.
+			 */
+			diag_log();
+		}
 		slab_put(slabc, fiber->stack_slab);
 	}
 }
@@ -1064,6 +1109,11 @@ fiber_stack_create(struct fiber *fiber, struct slab_cache *slabc,
 						  fiber->stack_size);
 
 	if (fiber_mprotect(guard, page_size, PROT_NONE)) {
+		/*
+		 * Write an error into the log since a guard
+		 * page is critical for functionality.
+		 */
+		diag_log();
 		fiber_stack_destroy(fiber, slabc);
 		return -1;
 	}
diff --git a/test/box/errinj.result b/test/box/errinj.result
index babe36b1b..c58e1d2e8 100644
--- a/test/box/errinj.result
+++ b/test/box/errinj.result
@@ -1,1783 +1,1887 @@
+-- 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_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_FIBER_MPROTECT:
+ |     state: -1
+ |   ERRINJ_FIBER_MADVISE:
+ |     state: false
+ |   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
+ | ...
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
index 4a57597e9..212a02d36 100644
--- a/test/unit/CMakeLists.txt
+++ b/test/unit/CMakeLists.txt
@@ -74,6 +74,10 @@ add_executable(fiber.test fiber.cc)
 set_source_files_properties(fiber.cc PROPERTIES COMPILE_FLAGS -O0)
 target_link_libraries(fiber.test core unit)
 
+add_executable(fiber_stack.test fiber_stack.cc)
+set_source_files_properties(fiber_stack.cc PROPERTIES COMPILE_FLAGS -O0)
+target_link_libraries(fiber_stack.test core unit)
+
 if (NOT ENABLE_GCOV)
     # This test is known to be broken with GCOV
     add_executable(guard.test guard.cc)
diff --git a/test/unit/fiber_stack.cc b/test/unit/fiber_stack.cc
new file mode 100644
index 000000000..b402e55d5
--- /dev/null
+++ b/test/unit/fiber_stack.cc
@@ -0,0 +1,83 @@
+#include "memory.h"
+#include "fiber.h"
+#include "unit.h"
+#include "trivia/util.h"
+#include "errinj.h"
+
+static struct fiber_attr default_attr;
+
+static int
+noop_f(va_list ap)
+{
+	return 0;
+}
+
+static void
+fiber_stack_fail_test(void)
+{
+	struct errinj *inj;
+	struct fiber *fiber;
+
+	header();
+
+	/*
+        * Set non-default stack size to prevent reusing of an
+        * existing fiber.
+        */
+	struct fiber_attr *fiber_attr = fiber_attr_new();
+	fiber_attr_setstacksize(fiber_attr, default_attr.stack_size * 2);
+
+	/*
+        * Clear the fiber's diagnostics area to check that failed
+        * fiber_new() sets an error.
+        */
+	diag_clear(diag_get());
+
+	/*
+	 * Check guard page setup via mprotect. We can't test the fiber
+	 * destroy path since it clears fiber's diag.
+	 */
+	inj = errinj(ERRINJ_FIBER_MPROTECT, ERRINJ_INT);
+	inj->iparam = PROT_NONE;
+	fiber = fiber_new_ex("test_mprotect", fiber_attr, noop_f);
+	inj->iparam = -1;
+
+	ok(fiber == NULL, "mprotect: failed to setup fiber guard page");
+	ok(diag_get() != NULL, "mprotect: diag is armed after error");
+
+	/*
+	 * Check madvise. We can't test the fiber destroy
+	 * path since it is cleaning error.
+	 */
+	diag_clear(diag_get());
+	inj = errinj(ERRINJ_FIBER_MADVISE, ERRINJ_BOOL);
+	inj->bparam = true;
+	fiber = fiber_new_ex("test_madvise", fiber_attr, noop_f);
+	inj->bparam = false;
+
+	ok(fiber != NULL, "madvise: non critical error on madvise hint");
+	ok(diag_get() != NULL, "madvise: diag is armed after error");
+
+	footer();
+}
+
+static int
+main_f(va_list ap)
+{
+	fiber_stack_fail_test();
+	ev_break(loop(), EVBREAK_ALL);
+	return 0;
+}
+
+int main()
+{
+	memory_init();
+	fiber_init(fiber_cxx_invoke);
+	fiber_attr_create(&default_attr);
+	struct fiber *main = fiber_new_xc("main", main_f);
+	fiber_wakeup(main);
+	ev_run(loop(), 0);
+	fiber_free();
+	memory_free();
+	return 0;
+}
diff --git a/test/unit/fiber_stack.result b/test/unit/fiber_stack.result
new file mode 100644
index 000000000..af10ab4f1
--- /dev/null
+++ b/test/unit/fiber_stack.result
@@ -0,0 +1,6 @@
+	*** fiber_stack_fail_test ***
+ok 1 - mprotect: failed to setup fiber guard page
+ok 2 - mprotect: diag is armed after error
+ok 3 - madvise: non critical error on madvise hint
+ok 4 - madvise: diag is armed after error
+	*** fiber_stack_fail_test: done ***
diff --git a/test/unit/suite.ini b/test/unit/suite.ini
index 89c8499fc..d429c95e9 100644
--- a/test/unit/suite.ini
+++ b/test/unit/suite.ini
@@ -1,5 +1,5 @@
 [default]
 core = unittest
 description = unit tests
-release_disabled = swim_errinj.test
+release_disabled = fiber_stack.test swim_errinj.test
 is_parallel = True
-- 
2.20.1

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [Tarantool-patches] [PATCH v5 2/2] fiber: leak slab if unable to bring prots back
  2020-02-05 22:06 [Tarantool-patches] [PATCH v5 0/2] fiber: Handle stack madvise/mprotect errors Cyrill Gorcunov
  2020-02-05 22:06 ` [Tarantool-patches] [PATCH v5 1/2] fiber: use diag_ logger in fiber_madvise/mprotect failures Cyrill Gorcunov
@ 2020-02-05 22:06 ` Cyrill Gorcunov
  2020-02-06 11:10   ` Alexander Turenko
  2020-02-06 11:20 ` [Tarantool-patches] [PATCH v5 0/2] fiber: Handle stack madvise/mprotect errors Alexander Turenko
  2 siblings, 1 reply; 6+ messages in thread
From: Cyrill Gorcunov @ 2020-02-05 22:06 UTC (permalink / raw)
  To: tml

In case if we unable to revert guard page back to
read|write we should never use such slab again.

Initially I thought of just put panic here and
exit but it is too destructive. I think better
print an error and continue. If node admin ignore
this message then one moment at future there won't
be slab left for use and creating new fibers get
prohibited.

In future (hopefully near one) we plan to drop
guard pages to prevent VMA fracturing and use
stack marks instead.

Signed-off-by: Cyrill Gorcunov <gorcunov@gmail.com>
---
 src/lib/core/fiber.c | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/src/lib/core/fiber.c b/src/lib/core/fiber.c
index 57e4cb6ef..5298541d4 100644
--- a/src/lib/core/fiber.c
+++ b/src/lib/core/fiber.c
@@ -1055,15 +1055,21 @@ fiber_stack_destroy(struct fiber *fiber, struct slab_cache *slabc)
 			 * to setup the original protection back in
 			 * background.
 			 *
+			 * For now lets keep such slab referenced and
+			 * leaked: if mprotect failed we must not allow
+			 * to reuse such slab with PROT_NONE'ed page
+			 * inside.
+			 *
 			 * Note that in case if we're called from
 			 * fiber_stack_create() the @mprotect_flags is
 			 * the same as the slab been created with, so
 			 * calling mprotect for VMA with same flags
 			 * won't fail.
 			 */
-			diag_log();
-		}
-		slab_put(slabc, fiber->stack_slab);
+			say_syserror("fiber: Can't put guard page to slab. "
+				     "Leak %zu bytes", (size_t)fiber->stack_size);
+		} else
+			slab_put(slabc, fiber->stack_slab);
 	}
 }
 
-- 
2.20.1

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [Tarantool-patches] [PATCH v5 1/2] fiber: use diag_ logger in fiber_madvise/mprotect failures
  2020-02-05 22:06 ` [Tarantool-patches] [PATCH v5 1/2] fiber: use diag_ logger in fiber_madvise/mprotect failures Cyrill Gorcunov
@ 2020-02-06 11:10   ` Alexander Turenko
  0 siblings, 0 replies; 6+ messages in thread
From: Alexander Turenko @ 2020-02-06 11:10 UTC (permalink / raw)
  To: Cyrill Gorcunov; +Cc: tml

Everything looks okay expect a couple of minor comments.

WBR, Alexader Turenko.

> fiber: use diag_ logger in fiber_madvise/mprotect failures

We usually try to fit a commit header within 50 symbols (this is however
should not be very strong requirement, IMHO). Say,

 | fiber: set diagnostic at mprotect/madvise failure

> diff --git a/test/box/errinj.result b/test/box/errinj.result
> index babe36b1b..c58e1d2e8 100644
> --- a/test/box/errinj.result
> +++ b/test/box/errinj.result
> @@ -1,1783 +1,1887 @@
> +-- test-run result file version 2

Please, don't change format of the result file. See [1], section 'New
result file format'.

In brief, it means to do

$ (cd test && ./test-run.py box/errinj.test.lua && mv box/errinj.{reject,result})

instead of

$ (cd test && rm box/errinj.result && ./test-run.py box/errinj.test.lua)

I was asked to fix it several times and so I will bring the issue [2]
into QA team attention at Friday's status meeting.

[1]: https://github.com/tarantool/test-run/commit/a04b5b096c607172ce4fc86a84e3531c9f3a7304
[2]: https://github.com/tarantool/test-run/issues/194

> diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
> index 4a57597e9..212a02d36 100644
> --- a/test/unit/CMakeLists.txt
> +++ b/test/unit/CMakeLists.txt
> @@ -74,6 +74,10 @@ add_executable(fiber.test fiber.cc)
>  set_source_files_properties(fiber.cc PROPERTIES COMPILE_FLAGS -O0)
>  target_link_libraries(fiber.test core unit)
>  
> +add_executable(fiber_stack.test fiber_stack.cc)
> +set_source_files_properties(fiber_stack.cc PROPERTIES COMPILE_FLAGS -O0)
> +target_link_libraries(fiber_stack.test core unit)
> +
>  if (NOT ENABLE_GCOV)
>      # This test is known to be broken with GCOV
>      add_executable(guard.test guard.cc)
> diff --git a/test/unit/fiber_stack.cc b/test/unit/fiber_stack.cc
> new file mode 100644
> index 000000000..b402e55d5
> --- /dev/null
> +++ b/test/unit/fiber_stack.cc
> @@ -0,0 +1,83 @@
> +#include "memory.h"
> +#include "fiber.h"
> +#include "unit.h"
> +#include "trivia/util.h"
> +#include "errinj.h"
> +
> +static struct fiber_attr default_attr;
> +
> +static int
> +noop_f(va_list ap)
> +{
> +	return 0;
> +}
> +
> +static void
> +fiber_stack_fail_test(void)
> +{
> +	struct errinj *inj;
> +	struct fiber *fiber;
> +
> +	header();
> +
> +	/*
> +        * Set non-default stack size to prevent reusing of an
> +        * existing fiber.
> +        */

Space/tab indent mix.

> +	struct fiber_attr *fiber_attr = fiber_attr_new();
> +	fiber_attr_setstacksize(fiber_attr, default_attr.stack_size * 2);
> +
> +	/*
> +        * Clear the fiber's diagnostics area to check that failed
> +        * fiber_new() sets an error.
> +        */

Same.

> diff --git a/test/unit/fiber_stack.result b/test/unit/fiber_stack.result
> new file mode 100644
> index 000000000..af10ab4f1
> --- /dev/null
> +++ b/test/unit/fiber_stack.result
> @@ -0,0 +1,6 @@
> +       *** fiber_stack_fail_test ***
> +ok 1 - mprotect: failed to setup fiber guard page
> +ok 2 - mprotect: diag is armed after error
> +ok 3 - madvise: non critical error on madvise hint
> +ok 4 - madvise: diag is armed after error
> +       *** fiber_stack_fail_test: done ***

It fails like so for me:

[001] Test failed! Result content mismatch:
[001] --- unit/fiber_stack.result	Thu Feb  6 10:36:02 2020
[001] +++ unit/fiber_stack.reject	Thu Feb  6 13:29:15 2020
[001] @@ -1,3 +1,4 @@
[001] +SystemError fiber mprotect failed: Cannot allocate memory
[001]  	*** fiber_stack_fail_test ***
[001]  ok 1 - mprotect: failed to setup fiber guard page
[001]  ok 2 - mprotect: diag is armed after error

It seems test-run mixes stdout and stderr for unit tests.

There is the common problem: 'release_disabled' tests are not run at all
for 'core = app' and 'core = unittest' test suites. I filed [3] for
this. It means for now that you should verify your test locally w/o
release_disabled, but then put it to release_disabled back before push.

[3]: https://github.com/tarantool/test-run/issues/199

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [Tarantool-patches] [PATCH v5 2/2] fiber: leak slab if unable to bring prots back
  2020-02-05 22:06 ` [Tarantool-patches] [PATCH v5 2/2] fiber: leak slab if unable to bring prots back Cyrill Gorcunov
@ 2020-02-06 11:10   ` Alexander Turenko
  0 siblings, 0 replies; 6+ messages in thread
From: Alexander Turenko @ 2020-02-06 11:10 UTC (permalink / raw)
  To: Cyrill Gorcunov; +Cc: tml

Okay except one style comment.

WBR, Alexander TUrenko.

On Thu, Feb 06, 2020 at 01:06:24AM +0300, Cyrill Gorcunov wrote:
> In case if we unable to revert guard page back to
> read|write we should never use such slab again.
> 
> Initially I thought of just put panic here and
> exit but it is too destructive. I think better
> print an error and continue. If node admin ignore

I agree.

> this message then one moment at future there won't
> be slab left for use and creating new fibers get
> prohibited.
> 
> In future (hopefully near one) we plan to drop
> guard pages to prevent VMA fracturing and use
> stack marks instead.
> 
> Signed-off-by: Cyrill Gorcunov <gorcunov@gmail.com>
> ---
>  src/lib/core/fiber.c | 12 +++++++++---
>  1 file changed, 9 insertions(+), 3 deletions(-)
> 
> diff --git a/src/lib/core/fiber.c b/src/lib/core/fiber.c
> index 57e4cb6ef..5298541d4 100644
> --- a/src/lib/core/fiber.c
> +++ b/src/lib/core/fiber.c
> @@ -1055,15 +1055,21 @@ fiber_stack_destroy(struct fiber *fiber, struct slab_cache *slabc)
>  			 * to setup the original protection back in
>  			 * background.
>  			 *
> +			 * For now lets keep such slab referenced and
> +			 * leaked: if mprotect failed we must not allow
> +			 * to reuse such slab with PROT_NONE'ed page
> +			 * inside.
> +			 *
>  			 * Note that in case if we're called from
>  			 * fiber_stack_create() the @mprotect_flags is
>  			 * the same as the slab been created with, so
>  			 * calling mprotect for VMA with same flags
>  			 * won't fail.
>  			 */
> -			diag_log();
> -		}
> -		slab_put(slabc, fiber->stack_slab);
> +			say_syserror("fiber: Can't put guard page to slab. "
> +				     "Leak %zu bytes", (size_t)fiber->stack_size);
> +		} else
> +			slab_put(slabc, fiber->stack_slab);

Usual way (AFAIK) is to use braces around both branches or neither of
them. Our code style mentions it, see [1] (from words 'Do not
unnecessarily use braces').

AFAIS there are exceptions across our code, but rules are rules :)

[1]: https://www.tarantool.io/en/doc/1.10/dev_guide/c_style_guide/

>  	}
>  }
>  
> -- 
> 2.20.1
> 

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [Tarantool-patches] [PATCH v5 0/2] fiber: Handle stack madvise/mprotect errors
  2020-02-05 22:06 [Tarantool-patches] [PATCH v5 0/2] fiber: Handle stack madvise/mprotect errors Cyrill Gorcunov
  2020-02-05 22:06 ` [Tarantool-patches] [PATCH v5 1/2] fiber: use diag_ logger in fiber_madvise/mprotect failures Cyrill Gorcunov
  2020-02-05 22:06 ` [Tarantool-patches] [PATCH v5 2/2] fiber: leak slab if unable to bring prots back Cyrill Gorcunov
@ 2020-02-06 11:20 ` Alexander Turenko
  2 siblings, 0 replies; 6+ messages in thread
From: Alexander Turenko @ 2020-02-06 11:20 UTC (permalink / raw)
  To: Cyrill Gorcunov; +Cc: tml

LGTM (except several minor points; fixes for them are obvious and it is
not necessary to re-review the patchset with me).

Please, fix those points and send the patch to Vlad for the second
review.

WBR, Alexander Turenko.

On Thu, Feb 06, 2020 at 01:06:22AM +0300, Cyrill Gorcunov wrote:
> issue https://github.com/tarantool/tarantool/issues/4722
> branch gorcunov/gh-4722-mprotect-diag-error-5
> 
> In v5:
>  - merged errinj together with test into the patch itself
>  - on guard page restore we print error and continue instead
>    of immeadiate exit
> 
> Cyrill Gorcunov (2):
>   fiber: use diag_ logger in fiber_madvise/mprotect failures
>   fiber: leak slab if unable to bring prots back
> 
>  src/lib/core/errinj.h        |    2 +
>  src/lib/core/fiber.c         |   92 +-
>  test/box/errinj.result       | 2616 ++++++++++++++++++----------------
>  test/unit/CMakeLists.txt     |    4 +
>  test/unit/fiber_stack.cc     |   83 ++
>  test/unit/fiber_stack.result |    6 +
>  test/unit/suite.ini          |    2 +-
>  7 files changed, 1530 insertions(+), 1275 deletions(-)
>  create mode 100644 test/unit/fiber_stack.cc
>  create mode 100644 test/unit/fiber_stack.result
> 
> -- 
> 2.20.1
> 

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2020-02-06 11:20 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-02-05 22:06 [Tarantool-patches] [PATCH v5 0/2] fiber: Handle stack madvise/mprotect errors Cyrill Gorcunov
2020-02-05 22:06 ` [Tarantool-patches] [PATCH v5 1/2] fiber: use diag_ logger in fiber_madvise/mprotect failures Cyrill Gorcunov
2020-02-06 11:10   ` Alexander Turenko
2020-02-05 22:06 ` [Tarantool-patches] [PATCH v5 2/2] fiber: leak slab if unable to bring prots back Cyrill Gorcunov
2020-02-06 11:10   ` Alexander Turenko
2020-02-06 11:20 ` [Tarantool-patches] [PATCH v5 0/2] fiber: Handle stack madvise/mprotect errors Alexander Turenko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox