diff --git a/src/box/memtx_space.c b/src/box/memtx_space.c index e7a9dfb3093ca32d9c2a6938328971b487b6c51d..17ae21102bfeed3ebd07f166c355831bd0e86936 100644 --- a/src/box/memtx_space.c +++ b/src/box/memtx_space.c @@ -1268,7 +1268,16 @@ memtx_space_build_index(struct space *src_space, struct index *new_index, trigger_create(&on_replace, memtx_build_on_replace, &state, NULL); - trigger_add(&src_space->on_replace, &on_replace); + /* + * If MVCC is enabled, all concurrent writes that we haven't + * read will be conflicted, so we don't need to set the trigger. + * Moreover, concurrent transaction can be rolled back much + * later than the index will be built so the memtx_ddl_state + * will be invalid in this case since it's allocated on stack + * of this function. + */ + if (!memtx_tx_manager_use_mvcc_engine) + trigger_add(&src_space->on_replace, &on_replace); } /* diff --git a/test/box-luatest/mvcc_ddl_test.lua b/test/box-luatest/mvcc_ddl_test.lua index 2e2cd9c379acf51df1e7ef900b03d3689800a067..24fd45c7fc41078e6c822ff77e89f2a57151dbfa 100644 --- a/test/box-luatest/mvcc_ddl_test.lua +++ b/test/box-luatest/mvcc_ddl_test.lua @@ -43,3 +43,46 @@ g.test_drop_space_many_delete_statements = function(cg) box.internal.memtx_tx_gc(1000) end) end + +-- The test checks if background build of index does not crash when +-- MVCC is enabled +-- gh-10147 +g.test_background_build = function(cg) + cg.server:exec(function() + local txn_proxy = require("test.box.lua.txn_proxy") + local fiber = require('fiber') + + -- Create space with tuples + local s = box.schema.space.create('test') + s:create_index('pk') + for i = 1, 2000 do + s:replace{i} + end + + local index_built = false + local f = fiber.create(function() + s:create_index('sk') + index_built = true + end) + f:set_joinable(true) + + -- Delete the tuples concurrently + local tx1 = txn_proxy:new() + tx1:begin() + for i = 1, 2000 do + local stmt = "box.space.test:delete{" .. i .. "}" + tx1(stmt) + end + + assert(not index_built) + local ok = f:join() + t.assert(ok) + local res = tx1:commit() + -- Must be aborted by DDL + t.assert_equals(res, + {{error = "Transaction has been aborted by conflict"}}) + + -- Collect garbage + box.internal.memtx_tx_gc(1000) + end) +end