diff --git a/src/box/vy_write_iterator.c b/src/box/vy_write_iterator.c index 70064c66e50f25a1d03c538c1ccfa121c24ad0cb..01962faa3ef8b348b4338b17875505ce2cf7626a 100644 --- a/src/box/vy_write_iterator.c +++ b/src/box/vy_write_iterator.c @@ -405,6 +405,15 @@ vy_write_iterator_start(struct vy_stmt_stream *vstream) rlist_foreach_entry(src, &stream->src_list, in_src_list) { if (vy_write_iterator_add_src(stream, src) != 0) goto fail; +#ifndef NDEBUG + struct errinj *inj = + errinj(ERRINJ_VY_WRITE_ITERATOR_START_FAIL, ERRINJ_BOOL); + if (inj != NULL && inj->bparam) { + inj->bparam = false; + diag_set(OutOfMemory, 666, "malloc", "struct vy_stmt"); + goto fail; + } +#endif } return 0; fail: diff --git a/src/lib/core/errinj.h b/src/lib/core/errinj.h index f3d42d72a820f60251e7bd2a5476f9e0fd288559..27562b8b50cf3cca8271f4fe191b3dee7e035803 100644 --- a/src/lib/core/errinj.h +++ b/src/lib/core/errinj.h @@ -142,6 +142,7 @@ struct errinj { _(ERRINJ_TXN_COMMIT_ASYNC, ERRINJ_BOOL, {.bparam = false})\ _(ERRINJ_VY_STMT_ALLOC, ERRINJ_INT, {.iparam = -1})\ _(ERRINJ_VY_READ_VIEW_MERGE_FAIL, ERRINJ_BOOL, {.bparam = false})\ + _(ERRINJ_VY_WRITE_ITERATOR_START_FAIL, ERRINJ_BOOL, {.bparam = false})\ ENUM0(errinj_id, ERRINJ_LIST); extern struct errinj errinjs[]; diff --git a/test/box/errinj.result b/test/box/errinj.result index a1c13b58997989f84a06f4aa3b34caabce494b39..12c7e2c085f2301f726ed1046a5b3a5cdcd5be40 100644 --- a/test/box/errinj.result +++ b/test/box/errinj.result @@ -101,6 +101,7 @@ evals - ERRINJ_VY_SQUASH_TIMEOUT: 0 - ERRINJ_VY_STMT_ALLOC: -1 - ERRINJ_VY_TASK_COMPLETE: false + - ERRINJ_VY_WRITE_ITERATOR_START_FAIL: false - ERRINJ_WAL_BREAK_LSN: -1 - ERRINJ_WAL_DELAY: false - ERRINJ_WAL_FALLOCATE: 0 diff --git a/test/vinyl/gh-4864-stmt-alloc-fail-compact.result b/test/vinyl/gh-4864-stmt-alloc-fail-compact.result index 7de141e362effb6cdd3f9e32d858458dced44bd4..6a1f49626b68863cfa17d04d873565e3c0e9ca4d 100644 --- a/test/vinyl/gh-4864-stmt-alloc-fail-compact.result +++ b/test/vinyl/gh-4864-stmt-alloc-fail-compact.result @@ -248,6 +248,75 @@ s:drop() | --- | ... +-- Make sure that there's no extra format unref due to tuple +-- clean-up in the main thread. To achieve this let's sabotage +-- compaction process and delete all tuples: in case ref/unref +-- is the same, format will be deleted alongside with the last +-- tuple. +-- +s = box.schema.space.create('test', {engine = 'vinyl'}) + | --- + | ... +_ = s:create_index('pk', {run_count_per_level = 100, page_size = 128, range_size = 1024}) + | --- + | ... + +dump(true) + | --- + | ... +dump() + | --- + | ... + +compact(1) + | --- + | ... + +dump() + | --- + | ... +assert(s.index.pk:stat().range_count == 1) + | --- + | - true + | ... +assert(s.index.pk:stat().run_count == 2) + | --- + | - true + | ... + +errinj.set('ERRINJ_VY_WRITE_ITERATOR_START_FAIL', true) + | --- + | - ok + | ... +errinj.set("ERRINJ_VY_SCHED_TIMEOUT", 0.1) + | --- + | - ok + | ... +compact(2) + | --- + | ... + +-- Drop is required to unref all tuples. +-- +s:drop() + | --- + | ... +-- After index is dropped, not all tuples are deallocated at once: +-- they may be still referenced (while being pushed) in Lua. So +-- invoke GC explicitly. +-- +_ = collectgarbage("collect") + | --- + | ... + +assert(errinj.get('ERRINJ_VY_WRITE_ITERATOR_START_FAIL') == false) + | --- + | - true + | ... +errinj.set('ERRINJ_VY_WRITE_ITERATOR_START_FAIL', false) + | --- + | - ok + | ... errinj.set("ERRINJ_VY_SCHED_TIMEOUT", 0) | --- | - ok diff --git a/test/vinyl/gh-4864-stmt-alloc-fail-compact.test.lua b/test/vinyl/gh-4864-stmt-alloc-fail-compact.test.lua index eebd1683920bc59ac5003aa812403a423f24e5de..4b3c55505dd4b47c3574fab94659afc9af5d4b4c 100644 --- a/test/vinyl/gh-4864-stmt-alloc-fail-compact.test.lua +++ b/test/vinyl/gh-4864-stmt-alloc-fail-compact.test.lua @@ -111,4 +111,37 @@ assert(errinj.get('ERRINJ_VY_READ_VIEW_MERGE_FAIL') == false) errinj.set('ERRINJ_VY_READ_VIEW_MERGE_FAIL', false) s:drop() +-- Make sure that there's no extra format unref due to tuple +-- clean-up in the main thread. To achieve this let's sabotage +-- compaction process and delete all tuples: in case ref/unref +-- is the same, format will be deleted alongside with the last +-- tuple. +-- +s = box.schema.space.create('test', {engine = 'vinyl'}) +_ = s:create_index('pk', {run_count_per_level = 100, page_size = 128, range_size = 1024}) + +dump(true) +dump() + +compact(1) + +dump() +assert(s.index.pk:stat().range_count == 1) +assert(s.index.pk:stat().run_count == 2) + +errinj.set('ERRINJ_VY_WRITE_ITERATOR_START_FAIL', true) +errinj.set("ERRINJ_VY_SCHED_TIMEOUT", 0.1) +compact(2) + +-- Drop is required to unref all tuples. +-- +s:drop() +-- After index is dropped, not all tuples are deallocated at once: +-- they may be still referenced (while being pushed) in Lua. So +-- invoke GC explicitly. +-- +_ = collectgarbage("collect") + +assert(errinj.get('ERRINJ_VY_WRITE_ITERATOR_START_FAIL') == false) +errinj.set('ERRINJ_VY_WRITE_ITERATOR_START_FAIL', false) errinj.set("ERRINJ_VY_SCHED_TIMEOUT", 0)