diff --git a/src/box/vy_tx.c b/src/box/vy_tx.c index a0430fdcf3cfe049fe9642a1e531a6b821f3e430..8967fd2f20744e86dd45dfcf449457027393f027 100644 --- a/src/box/vy_tx.c +++ b/src/box/vy_tx.c @@ -589,9 +589,26 @@ vy_tx_rollback_after_prepare(struct vy_tx *tx) struct tx_manager *xm = tx->xm; - /* Expect cascading rollback in the reverse order. */ - assert(xm->last_prepared_tx == tx); - xm->last_prepared_tx = NULL; + /* + * There are two reasons of rollback_after_prepare: + * 1) Fail in the middle of vy_tx_prepare call. + * 2) Cascading rollback after WAL fail. + * + * If a TX is the latest prepared TX and the it is rollbacked, + * it's certainly the case (2) and we should set xm->last_prepared_tx + * to the previous prepared TX, if any. + * But doesn't know the previous TX. + * On the other hand we may expect that cascading rollback will + * concern all the prepared TXs, all of them will be rollbacked + * and xm->last_prepared_tx must be set to NULL in the end. + * Thus we can set xm->last_prepared_tx to NULL now and it will be + * correct in the end of the cascading rollback. + * + * We must not change xm->last_prepared_tx in all other cases, + * it will be changed by the corresponding TX. + */ + if (xm->last_prepared_tx == tx) + xm->last_prepared_tx = NULL; struct txv *v; stailq_foreach_entry(v, &tx->log, next_in_log) { diff --git a/test/vinyl/errinj.result b/test/vinyl/errinj.result index 40d797527228fd97f3fc2c99c2f883b79c5d1f0b..1752bc220b4123eaac0d9651899f1cd740d9c227 100644 --- a/test/vinyl/errinj.result +++ b/test/vinyl/errinj.result @@ -878,3 +878,33 @@ box.snapshot() s:drop() --- ... +s = box.schema.space.create('test', {engine = 'vinyl'}) +--- +... +_ = s:create_index('i1', {parts = {1, 'unsigned'}}) +--- +... +c = 10 +--- +... +errinj.set("ERRINJ_WAL_WRITE_DISK", true) +--- +- ok +... +for i = 1,10 do fiber.create(function() pcall(s.replace, s, {i}) c = c - 1 end) end +--- +... +while c ~= 0 do fiber.sleep(0.001) end +--- +... +s:select{} +--- +- [] +... +errinj.set("ERRINJ_WAL_WRITE_DISK", false) +--- +- ok +... +s:drop() +--- +... diff --git a/test/vinyl/errinj.test.lua b/test/vinyl/errinj.test.lua index bddc2936b6f730206639942a4caee212650e68d5..3a599218f55c79a81ac1d2d149ace6edc04ad278 100644 --- a/test/vinyl/errinj.test.lua +++ b/test/vinyl/errinj.test.lua @@ -353,3 +353,15 @@ errinj.set('ERRINJ_WAL_IO', false) _ = s:create_index('pk') box.snapshot() s:drop() + +s = box.schema.space.create('test', {engine = 'vinyl'}) +_ = s:create_index('i1', {parts = {1, 'unsigned'}}) + +c = 10 +errinj.set("ERRINJ_WAL_WRITE_DISK", true) +for i = 1,10 do fiber.create(function() pcall(s.replace, s, {i}) c = c - 1 end) end +while c ~= 0 do fiber.sleep(0.001) end +s:select{} +errinj.set("ERRINJ_WAL_WRITE_DISK", false) + +s:drop() diff --git a/test/vinyl/gh.result b/test/vinyl/gh.result index 15570d6118f1c995db79043e0b87d9dc8cb95580..2ca16889e7092a6ec89eb6761c9d89da10e479d7 100644 --- a/test/vinyl/gh.result +++ b/test/vinyl/gh.result @@ -640,3 +640,17 @@ s.index.i1:count() == s.index.i2:count() s:drop() --- ... +-- https://github.com/tarantool/tarantool/issues/2588 +s = box.schema.space.create('vinyl', { engine = 'vinyl' }) +--- +... +i = box.space.vinyl:create_index('primary') +--- +... +s:replace({1, string.rep('x', 10 * 1024 * 1024)}) +--- +- error: Failed to allocate 10485799 bytes in lsregion_alloc for mem_stmt +... +s:drop() +--- +... diff --git a/test/vinyl/gh.test.lua b/test/vinyl/gh.test.lua index c14bab432303f4dd8f03fff89fa9c342742ab853..9756d153b8e10c98fa69c68bd73bfc76f9915d75 100644 --- a/test/vinyl/gh.test.lua +++ b/test/vinyl/gh.test.lua @@ -266,3 +266,9 @@ test_run:cmd("setopt delimiter ''"); s.index.i1:count() == s.index.i2:count() s:drop() + +-- https://github.com/tarantool/tarantool/issues/2588 +s = box.schema.space.create('vinyl', { engine = 'vinyl' }) +i = box.space.vinyl:create_index('primary') +s:replace({1, string.rep('x', 10 * 1024 * 1024)}) +s:drop()