diff --git a/src/box/alter.cc b/src/box/alter.cc index 080a72b9f0aa85a92cf0694b6c2c86366db9f7fa..c20b7b3cbebe26f4732a916b1cd704d9b8ebcde1 100644 --- a/src/box/alter.cc +++ b/src/box/alter.cc @@ -1305,18 +1305,25 @@ RebuildIndex::~RebuildIndex() /** TruncateIndex - truncate an index. */ class TruncateIndex: public AlterSpaceOp { + /** id of the index to truncate. */ + uint32_t iid; + /** + * In case TRUNCATE fails, we need to clean up the new + * index data in the engine. + */ + struct index *new_index; public: TruncateIndex(struct alter_space *alter, uint32_t iid) : AlterSpaceOp(alter), iid(iid) {} - /** id of the index to truncate. */ - uint32_t iid; virtual void prepare(struct alter_space *alter); virtual void commit(struct alter_space *alter, int64_t signature); + virtual ~TruncateIndex(); }; void TruncateIndex::prepare(struct alter_space *alter) { + new_index = space_index(alter->new_space, iid); if (iid == 0) { /* * Notify the engine that the primary index @@ -1333,7 +1340,6 @@ TruncateIndex::prepare(struct alter_space *alter) * index was recreated. For example, Vinyl uses this * callback to load indexes during local recovery. */ - struct index *new_index = space_index(alter->new_space, iid); assert(new_index != NULL); space_build_index_xc(alter->new_space, new_index, alter->new_space->format); @@ -1343,10 +1349,17 @@ void TruncateIndex::commit(struct alter_space *alter, int64_t signature) { struct index *old_index = space_index(alter->old_space, iid); - struct index *new_index = space_index(alter->new_space, iid); index_commit_drop(old_index, signature); index_commit_create(new_index, signature); + new_index = NULL; +} + +TruncateIndex::~TruncateIndex() +{ + if (new_index == NULL) + return; + index_abort_create(new_index); } /** diff --git a/test/engine/errinj.result b/test/engine/errinj.result new file mode 100644 index 0000000000000000000000000000000000000000..d244c334a2079272abcfae58229e9c0f792e5fff --- /dev/null +++ b/test/engine/errinj.result @@ -0,0 +1,53 @@ +test_run = require('test_run') +--- +... +inspector = test_run.new() +--- +... +engine = inspector:get_cfg('engine') +--- +... +errinj = box.error.injection +--- +... +-- truncation rollback should not crash +s = box.schema.space.create('truncate_rollback', {engine = engine}) +--- +... +_ = s:create_index('pk') +--- +... +_ = s:create_index('sk', {parts = {1, 'int'}}) +--- +... +for i = 1, 10 do s:replace({i, i}) end +--- +... +errinj.set('ERRINJ_WAL_IO', true) +--- +- ok +... +s:truncate() +--- +- error: Failed to write to disk +... +errinj.set('ERRINJ_WAL_IO', false) +--- +- ok +... +s:select() +--- +- - [1, 1] + - [2, 2] + - [3, 3] + - [4, 4] + - [5, 5] + - [6, 6] + - [7, 7] + - [8, 8] + - [9, 9] + - [10, 10] +... +s:drop() +--- +... diff --git a/test/engine/errinj.test.lua b/test/engine/errinj.test.lua new file mode 100644 index 0000000000000000000000000000000000000000..57f3a962cafe1e84bd6217fc61de11744f2ad141 --- /dev/null +++ b/test/engine/errinj.test.lua @@ -0,0 +1,15 @@ +test_run = require('test_run') +inspector = test_run.new() +engine = inspector:get_cfg('engine') +errinj = box.error.injection + +-- truncation rollback should not crash +s = box.schema.space.create('truncate_rollback', {engine = engine}) +_ = s:create_index('pk') +_ = s:create_index('sk', {parts = {1, 'int'}}) +for i = 1, 10 do s:replace({i, i}) end +errinj.set('ERRINJ_WAL_IO', true) +s:truncate() +errinj.set('ERRINJ_WAL_IO', false) +s:select() +s:drop() diff --git a/test/engine/suite.ini b/test/engine/suite.ini index 3f82a1325a13541cb1d8a1fbee744e2731dbd663..3db02ab6fc53050081b5c51c9286cbfaef0e2520 100644 --- a/test/engine/suite.ini +++ b/test/engine/suite.ini @@ -3,6 +3,7 @@ core = tarantool description = tarantool multiengine tests script = box.lua use_unix_sockets = True +release_disabled = errinj.test.lua config = engine.cfg #disabled = replica_join.test.lua lua_libs = conflict.lua ../box/lua/utils.lua ../box/lua/push.lua