diff --git a/src/box/vy_lsm.c b/src/box/vy_lsm.c index aa4bce9eb34e0337c995725b181c924df99c85c7..97767772495ae45fb3f610a14a4e9ffcd454adce 100644 --- a/src/box/vy_lsm.c +++ b/src/box/vy_lsm.c @@ -1120,7 +1120,7 @@ vy_lsm_split_range(struct vy_lsm *lsm, struct vy_range *range) /* Split a range in two parts. */ const int n_parts = 2; - + struct vy_range *parts[2] = { NULL, NULL }; /* * Determine new ranges' boundaries. */ @@ -1140,7 +1140,7 @@ vy_lsm_split_range(struct vy_lsm *lsm, struct vy_range *range) * the old range's runs for them. */ struct vy_slice *slice, *new_slice; - struct vy_range *part, *parts[2] = {NULL, }; + struct vy_range *part = NULL; for (int i = 0; i < n_parts; i++) { part = vy_range_new(vy_log_next_id(), keys[i], keys[i + 1], lsm->cmp_def); diff --git a/src/box/vy_stmt.c b/src/box/vy_stmt.c index 518a24f43d7048acc439c4e6872e1552a652b1b9..adc3ba452f52e988db77e8874bbfbb7c728cb3d9 100644 --- a/src/box/vy_stmt.c +++ b/src/box/vy_stmt.c @@ -167,6 +167,16 @@ vy_stmt_alloc(struct tuple_format *format, uint32_t data_offset, uint32_t bsize) error_log(diag_last_error(diag_get())); return NULL; } +#ifndef NDEBUG + struct errinj *inj = errinj(ERRINJ_VY_STMT_ALLOC, ERRINJ_INT); + if (inj != NULL && inj->iparam >= 0) { + if (inj->iparam-- == 0) { + diag_set(OutOfMemory, total_size, "malloc", + "struct vy_stmt"); + return NULL; + } + } +#endif struct tuple *tuple = malloc(total_size); if (unlikely(tuple == NULL)) { diag_set(OutOfMemory, total_size, "malloc", "struct vy_stmt"); diff --git a/src/lib/core/errinj.h b/src/lib/core/errinj.h index 7577ed11a48bb378eed06513f78a96c6ef05d705..2391a739a92f7bf45e8373a1f312ef6e8f999f2f 100644 --- a/src/lib/core/errinj.h +++ b/src/lib/core/errinj.h @@ -140,6 +140,7 @@ struct errinj { _(ERRINJ_RELAY_FASTER_THAN_TX, ERRINJ_BOOL, {.bparam = false}) \ _(ERRINJ_INDEX_RESERVE, ERRINJ_BOOL, {.bparam = false})\ _(ERRINJ_TXN_COMMIT_ASYNC, ERRINJ_BOOL, {.bparam = false})\ + _(ERRINJ_VY_STMT_ALLOC, ERRINJ_INT, {.iparam = -1})\ ENUM0(errinj_id, ERRINJ_LIST); extern struct errinj errinjs[]; diff --git a/test/box/errinj.result b/test/box/errinj.result index de877b7087d1909bf6696a14bca1c6cd4983a97d..1906dff5b3737e6e65820d6b47e6d7d2327594eb 100644 --- a/test/box/errinj.result +++ b/test/box/errinj.result @@ -98,6 +98,7 @@ evals - ERRINJ_VY_RUN_WRITE_STMT_TIMEOUT: 0 - ERRINJ_VY_SCHED_TIMEOUT: 0 - ERRINJ_VY_SQUASH_TIMEOUT: 0 + - ERRINJ_VY_STMT_ALLOC: -1 - ERRINJ_VY_TASK_COMPLETE: false - ERRINJ_WAL_BREAK_LSN: -1 - ERRINJ_WAL_DELAY: false diff --git a/test/vinyl/gh-4864-stmt-alloc-fail-compact.result b/test/vinyl/gh-4864-stmt-alloc-fail-compact.result new file mode 100644 index 0000000000000000000000000000000000000000..1afc02befd2120ee7fba3508824810a237a6c0a2 --- /dev/null +++ b/test/vinyl/gh-4864-stmt-alloc-fail-compact.result @@ -0,0 +1,123 @@ +-- test-run result file version 2 +test_run = require('test_run').new() + | --- + | ... +fiber = require('fiber') + | --- + | ... +digest = require('digest') + | --- + | ... + +s = box.schema.space.create('test', {engine = 'vinyl'}) + | --- + | ... +_ = s:create_index('pk', {run_count_per_level = 100, page_size = 128, range_size = 1024}) + | --- + | ... + +test_run:cmd("setopt delimiter ';'") + | --- + | - true + | ... +function dump(big) + local step = big and 1 or 5 + for i = 1, 20, step do + s:replace{i, digest.urandom(1000)} + end + box.snapshot() +end; + | --- + | ... + +function compact() + s.index.pk:compact() + repeat + fiber.sleep(0.001) + local info = s.index.pk:stat() + until info.range_count == info.run_count +end; + | --- + | ... +test_run:cmd("setopt delimiter ''"); + | --- + | - true + | ... + +-- The first run should be big enough to prevent major compaction +-- on the next dump, because run_count_per_level is ignored on the +-- last level. +-- +dump(true) + | --- + | ... +dump() + | --- + | ... +assert(s.index.pk:stat().range_count == 1) + | --- + | - true + | ... +assert(s.index.pk:stat().run_count == 2) + | --- + | - true + | ... + +compact() + | --- + | ... +assert(s.index.pk:stat().range_count == 1) + | --- + | - true + | ... +assert(s.index.pk:stat().run_count == 1) + | --- + | - true + | ... + +dump() + | --- + | ... +assert(s.index.pk:stat().range_count == 1) + | --- + | - true + | ... +assert(s.index.pk:stat().run_count == 2) + | --- + | - true + | ... + +errinj = box.error.injection + | --- + | ... +errinj.set('ERRINJ_VY_STMT_ALLOC', 0) + | --- + | - ok + | ... +-- Should finish successfully despite vy_stmt_alloc() failure. +-- Still split_range() fails, as a result we get one range +-- instead two. +-- +compact() + | --- + | ... +assert(s.index.pk:stat().range_count == 1) + | --- + | - true + | ... +assert(s.index.pk:stat().run_count == 1) + | --- + | - true + | ... +assert(errinj.get('ERRINJ_VY_STMT_ALLOC') == -1) + | --- + | - true + | ... +errinj.set('ERRINJ_VY_STMT_ALLOC', -1) + | --- + | - ok + | ... + +s:drop() + | --- + | ... diff --git a/test/vinyl/gh-4864-stmt-alloc-fail-compact.test.lua b/test/vinyl/gh-4864-stmt-alloc-fail-compact.test.lua new file mode 100644 index 0000000000000000000000000000000000000000..bf70bdf752f3aacfacb75f8e3a076e96798bb9ec --- /dev/null +++ b/test/vinyl/gh-4864-stmt-alloc-fail-compact.test.lua @@ -0,0 +1,55 @@ +test_run = require('test_run').new() +fiber = require('fiber') +digest = require('digest') + +s = box.schema.space.create('test', {engine = 'vinyl'}) +_ = s:create_index('pk', {run_count_per_level = 100, page_size = 128, range_size = 1024}) + +test_run:cmd("setopt delimiter ';'") +function dump(big) + local step = big and 1 or 5 + for i = 1, 20, step do + s:replace{i, digest.urandom(1000)} + end + box.snapshot() +end; + +function compact() + s.index.pk:compact() + repeat + fiber.sleep(0.001) + local info = s.index.pk:stat() + until info.range_count == info.run_count +end; +test_run:cmd("setopt delimiter ''"); + +-- The first run should be big enough to prevent major compaction +-- on the next dump, because run_count_per_level is ignored on the +-- last level. +-- +dump(true) +dump() +assert(s.index.pk:stat().range_count == 1) +assert(s.index.pk:stat().run_count == 2) + +compact() +assert(s.index.pk:stat().range_count == 1) +assert(s.index.pk:stat().run_count == 1) + +dump() +assert(s.index.pk:stat().range_count == 1) +assert(s.index.pk:stat().run_count == 2) + +errinj = box.error.injection +errinj.set('ERRINJ_VY_STMT_ALLOC', 0) +-- Should finish successfully despite vy_stmt_alloc() failure. +-- Still split_range() fails, as a result we get one range +-- instead two. +-- +compact() +assert(s.index.pk:stat().range_count == 1) +assert(s.index.pk:stat().run_count == 1) +assert(errinj.get('ERRINJ_VY_STMT_ALLOC') == -1) +errinj.set('ERRINJ_VY_STMT_ALLOC', -1) + +s:drop() diff --git a/test/vinyl/suite.ini b/test/vinyl/suite.ini index c8bc270f3cb63cb783edf4c9353e0ba9fabec309..aa3cb1eb94a37427b99af677165971ee40c0b02b 100644 --- a/test/vinyl/suite.ini +++ b/test/vinyl/suite.ini @@ -2,7 +2,7 @@ core = tarantool description = vinyl integration tests script = vinyl.lua -release_disabled = errinj.test.lua errinj_ddl.test.lua errinj_gc.test.lua errinj_stat.test.lua errinj_tx.test.lua errinj_vylog.test.lua partial_dump.test.lua quota_timeout.test.lua recovery_quota.test.lua replica_rejoin.test.lua +release_disabled = errinj.test.lua errinj_ddl.test.lua errinj_gc.test.lua errinj_stat.test.lua errinj_tx.test.lua errinj_vylog.test.lua partial_dump.test.lua quota_timeout.test.lua recovery_quota.test.lua replica_rejoin.test.lua gh-4864-stmt-alloc-fail-compact.test.lua config = suite.cfg lua_libs = suite.lua stress.lua large.lua txn_proxy.lua ../box/lua/utils.lua use_unix_sockets = True