* [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