[PATCH 02/12] test: split vinyl/errinj
Vladimir Davydov
vdavydov.dev at gmail.com
Tue Jan 15 17:17:11 MSK 2019
This test is huge and takes long to complete. Let's move ddl, tx, and
stat related stuff to separate files.
---
test/vinyl/errinj.result | 1158 --------------------------------------
test/vinyl/errinj.test.lua | 494 ----------------
test/vinyl/errinj_ddl.result | 595 ++++++++++++++++++++
test/vinyl/errinj_ddl.test.lua | 260 +++++++++
test/vinyl/errinj_stat.result | 152 +++++
test/vinyl/errinj_stat.test.lua | 48 ++
test/vinyl/errinj_tx.result | 435 ++++++++++++++
test/vinyl/errinj_tx.test.lua | 194 +++++++
test/vinyl/errinj_vylog.result | 35 ++
test/vinyl/errinj_vylog.test.lua | 18 +
test/vinyl/suite.ini | 2 +-
11 files changed, 1738 insertions(+), 1653 deletions(-)
create mode 100644 test/vinyl/errinj_ddl.result
create mode 100644 test/vinyl/errinj_ddl.test.lua
create mode 100644 test/vinyl/errinj_stat.result
create mode 100644 test/vinyl/errinj_stat.test.lua
create mode 100644 test/vinyl/errinj_tx.result
create mode 100644 test/vinyl/errinj_tx.test.lua
diff --git a/test/vinyl/errinj.result b/test/vinyl/errinj.result
index 23ab845b..a16475f5 100644
--- a/test/vinyl/errinj.result
+++ b/test/vinyl/errinj.result
@@ -1,6 +1,3 @@
---
--- gh-1681: vinyl: crash in vy_rollback on ER_WAL_WRITE
---
test_run = require('test_run').new()
---
...
@@ -17,42 +14,6 @@ errinj.set("ERRINJ_VY_SCHED_TIMEOUT", 0.040)
---
- ok
...
-s = box.schema.space.create('test', {engine='vinyl'})
----
-...
-_ = s:create_index('pk')
----
-...
-function f() box.begin() s:insert{1, 'hi'} s:insert{2, 'bye'} box.commit() end
----
-...
-errinj.set("ERRINJ_WAL_WRITE", true)
----
-- ok
-...
-f()
----
-- error: Failed to write to disk
-...
-s:select{}
----
-- []
-...
-errinj.set("ERRINJ_WAL_WRITE", false)
----
-- ok
-...
-f()
----
-...
-s:select{}
----
-- - [1, 'hi']
- - [2, 'bye']
-...
-s:drop()
----
-...
--
-- Lost data in case of dump error
--
@@ -474,399 +435,6 @@ errinj.set("ERRINJ_VY_SQUASH_TIMEOUT", 0)
---
- ok
...
---https://github.com/tarantool/tarantool/issues/1842
---test error injection
-s = box.schema.space.create('test', {engine='vinyl'})
----
-...
-_ = s:create_index('pk')
----
-...
-s:replace{0, 0}
----
-- [0, 0]
-...
-s:replace{1, 0}
----
-- [1, 0]
-...
-s:replace{2, 0}
----
-- [2, 0]
-...
-errinj.set("ERRINJ_WAL_WRITE", true)
----
-- ok
-...
-s:replace{3, 0}
----
-- error: Failed to write to disk
-...
-s:replace{4, 0}
----
-- error: Failed to write to disk
-...
-s:replace{5, 0}
----
-- error: Failed to write to disk
-...
-s:replace{6, 0}
----
-- error: Failed to write to disk
-...
-errinj.set("ERRINJ_WAL_WRITE", false)
----
-- ok
-...
-s:replace{7, 0}
----
-- [7, 0]
-...
-s:replace{8, 0}
----
-- [8, 0]
-...
-s:select{}
----
-- - [0, 0]
- - [1, 0]
- - [2, 0]
- - [7, 0]
- - [8, 0]
-...
-s:drop()
----
-...
-create_iterator = require('utils').create_iterator
----
-...
---iterator test
-test_run:cmd("setopt delimiter ';'")
----
-- true
-...
-fiber_status = 0
-
-function fiber_func()
- box.begin()
- s:replace{5, 5}
- fiber_status = 1
- local res = {pcall(box.commit) }
- fiber_status = 2
- return unpack(res)
-end;
----
-...
-test_run:cmd("setopt delimiter ''");
----
-- true
-...
-s = box.schema.space.create('test', {engine='vinyl'})
----
-...
-_ = s:create_index('pk')
----
-...
-fiber = require('fiber')
----
-...
-_ = s:replace{0, 0}
----
-...
-_ = s:replace{10, 0}
----
-...
-_ = s:replace{20, 0}
----
-...
-test_run:cmd("setopt delimiter ';'");
----
-- true
-...
-faced_trash = false
-for i = 1,100 do
- errinj.set("ERRINJ_WAL_WRITE", true)
- local f = fiber.create(fiber_func)
- local itr = create_iterator(s, {0}, {iterator='GE'})
- local first = itr.next()
- local second = itr.next()
- if (second[1] ~= 5 and second[1] ~= 10) then faced_trash = true end
- while fiber_status <= 1 do fiber.sleep(0.001) end
- local _,next = pcall(itr.next)
- _,next = pcall(itr.next)
- _,next = pcall(itr.next)
- errinj.set("ERRINJ_WAL_WRITE", false)
- s:delete{5}
-end;
----
-...
-test_run:cmd("setopt delimiter ''");
----
-- true
-...
-faced_trash
----
-- false
-...
-s:drop()
----
-...
--- TX in prepared but not committed state
-s = box.schema.space.create('test', {engine='vinyl'})
----
-...
-_ = s:create_index('pk')
----
-...
-fiber = require('fiber')
----
-...
-txn_proxy = require('txn_proxy')
----
-...
-s:replace{1, "original"}
----
-- [1, 'original']
-...
-s:replace{2, "original"}
----
-- [2, 'original']
-...
-s:replace{3, "original"}
----
-- [3, 'original']
-...
-c0 = txn_proxy.new()
----
-...
-c0:begin()
----
--
-...
-c1 = txn_proxy.new()
----
-...
-c1:begin()
----
--
-...
-c2 = txn_proxy.new()
----
-...
-c2:begin()
----
--
-...
-c3 = txn_proxy.new()
----
-...
-c3:begin()
----
--
-...
---
--- Prepared transactions
---
--- Pause WAL writer to cause all further calls to box.commit() to move
--- transactions into prepared, but not committed yet state.
-errinj.set("ERRINJ_WAL_DELAY", true)
----
-- ok
-...
-lsn = box.info.lsn
----
-...
-c0('s:replace{1, "c0"}')
----
-- - [1, 'c0']
-...
-c0('s:replace{2, "c0"}')
----
-- - [2, 'c0']
-...
-c0('s:replace{3, "c0"}')
----
-- - [3, 'c0']
-...
-_ = fiber.create(c0.commit, c0)
----
-...
-box.info.lsn == lsn
----
-- true
-...
-c1('s:replace{1, "c1"}')
----
-- - [1, 'c1']
-...
-c1('s:replace{2, "c1"}')
----
-- - [2, 'c1']
-...
-_ = fiber.create(c1.commit, c1)
----
-...
-box.info.lsn == lsn
----
-- true
-...
-c3('s:select{1}') -- c1 is visible
----
-- - [[1, 'c1']]
-...
-c2('s:replace{1, "c2"}')
----
-- - [1, 'c2']
-...
-c2('s:replace{3, "c2"}')
----
-- - [3, 'c2']
-...
-_ = fiber.create(c2.commit, c2)
----
-...
-box.info.lsn == lsn
----
-- true
-...
-c3('s:select{1}') -- c1 is visible, c2 is not
----
-- - [[1, 'c1']]
-...
-c3('s:select{2}') -- c1 is visible
----
-- - [[2, 'c1']]
-...
-c3('s:select{3}') -- c2 is not visible
----
-- - [[3, 'c0']]
-...
--- Resume WAL writer and wait until all transactions will been committed
-errinj.set("ERRINJ_WAL_DELAY", false)
----
-- ok
-...
-REQ_COUNT = 7
----
-...
-while box.info.lsn - lsn < REQ_COUNT do fiber.sleep(0.01) end
----
-...
-box.info.lsn == lsn + REQ_COUNT
----
-- true
-...
-c3('s:select{1}') -- c1 is visible, c2 is not
----
-- - [[1, 'c1']]
-...
-c3('s:select{2}') -- c1 is visible
----
-- - [[2, 'c1']]
-...
-c3('s:select{3}') -- c2 is not visible
----
-- - [[3, 'c0']]
-...
-c3:commit()
----
--
-...
-s:drop()
----
-...
---
--- Test mem restoration on a prepared and not commited statement
--- after moving iterator into read view.
---
-space = box.schema.space.create('test', {engine = 'vinyl'})
----
-...
-pk = space:create_index('pk')
----
-...
-space:replace{1}
----
-- [1]
-...
-space:replace{2}
----
-- [2]
-...
-space:replace{3}
----
-- [3]
-...
-last_read = nil
----
-...
-errinj.set("ERRINJ_WAL_DELAY", true)
----
-- ok
-...
-test_run:cmd("setopt delimiter ';'")
----
-- true
-...
--- block until wal_delay = false
--- send iterator to read view
--- flush mem and update index version to trigger iterator restore
-function fill_space()
- box.begin()
- space:replace{1}
- space:replace{2}
- space:replace{3}
- box.commit()
- space:replace{1, 1}
- box.snapshot()
-end;
----
-...
-function iterate_in_read_view()
- local i = create_iterator(space)
- last_read = i.next()
- fiber.sleep(100000)
- last_read = i.next()
-end;
----
-...
-test_run:cmd("setopt delimiter ''");
----
-- true
-...
-f1 = fiber.create(fill_space)
----
-...
--- Prepared transaction is blocked due to wal_delay.
--- Start iterator with vlsn = INT64_MAX
-f2 = fiber.create(iterate_in_read_view)
----
-...
-last_read
----
-- [1]
-...
--- Finish prepared transaction and send to read view the iterator.
-errinj.set("ERRINJ_WAL_DELAY", false)
----
-- ok
-...
-while f1:status() ~= 'dead' do fiber.sleep(0.01) end
----
-...
-f2:wakeup()
----
-...
-while f2:status() ~= 'dead' do fiber.sleep(0.01) end
----
-...
-last_read
----
-- [2]
-...
-space:drop()
----
-...
--
-- Space drop in the middle of dump.
--
@@ -976,38 +544,6 @@ test_run:cmd("cleanup server test")
---
- true
...
---
--- If we logged an index creation in the metadata log before WAL write,
--- WAL failure would result in leaving the index record in vylog forever.
--- Since we use LSN to identify indexes in vylog, retrying index creation
--- would then lead to a duplicate index id in vylog and hence inability
--- to make a snapshot or recover.
---
-s = box.schema.space.create('test', {engine = 'vinyl'})
----
-...
-errinj.set('ERRINJ_WAL_IO', true)
----
-- ok
-...
-_ = s:create_index('pk')
----
-- error: Failed to write to disk
-...
-errinj.set('ERRINJ_WAL_IO', false)
----
-- ok
-...
-_ = s:create_index('pk')
----
-...
-box.snapshot()
----
-- ok
-...
-s:drop()
----
-...
s = box.schema.space.create('test', {engine = 'vinyl'})
---
...
@@ -1392,81 +928,6 @@ test_run:cmd("cleanup server low_quota")
- true
...
--
--- Check that ALTER is abroted if a tuple inserted during space
--- format change does not conform to the new format.
---
-format = {}
----
-...
-format[1] = {name = 'field1', type = 'unsigned'}
----
-...
-format[2] = {name = 'field2', type = 'string', is_nullable = true}
----
-...
-s = box.schema.space.create('test', {engine = 'vinyl', format = format})
----
-...
-_ = s:create_index('pk', {page_size = 16})
----
-...
-pad = string.rep('x', 16)
----
-...
-for i = 101, 200 do s:replace{i, pad} end
----
-...
-box.snapshot()
----
-- ok
-...
-ch = fiber.channel(1)
----
-...
-test_run:cmd("setopt delimiter ';'")
----
-- true
-...
-_ = fiber.create(function()
- fiber.sleep(0.01)
- for i = 1, 100 do
- s:replace{i, box.NULL}
- end
- ch:put(true)
-end);
----
-...
-test_run:cmd("setopt delimiter ''");
----
-- true
-...
-errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0.001)
----
-- ok
-...
-format[2].is_nullable = false
----
-...
-s:format(format) -- must fail
----
-- error: 'Tuple field 2 type does not match one required by operation: expected string'
-...
-errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0)
----
-- ok
-...
-ch:get()
----
-- true
-...
-s:count() -- 200
----
-- 200
-...
-s:drop()
----
-...
---
-- gh-3437: if compaction races with checkpointing, it may remove
-- files needed for backup.
--
@@ -1533,317 +994,6 @@ s:drop()
---
...
--
--- gh-2449: change 'unique' index property from true to false
--- is done without index rebuild.
---
-s = box.schema.space.create('test', { engine = 'vinyl' })
----
-...
-_ = s:create_index('primary')
----
-...
-_ = s:create_index('secondary', {unique = true, parts = {2, 'unsigned'}})
----
-...
-s:insert{1, 10}
----
-- [1, 10]
-...
-box.snapshot()
----
-- ok
-...
-errinj.set("ERRINJ_VY_READ_PAGE", true);
----
-- ok
-...
-s.index.secondary:alter{unique = false} -- ok
----
-...
-s.index.secondary.unique
----
-- false
-...
-s.index.secondary:alter{unique = true} -- error
----
-- error: Error injection 'vinyl page read'
-...
-s.index.secondary.unique
----
-- false
-...
-errinj.set("ERRINJ_VY_READ_PAGE", false);
----
-- ok
-...
-s:insert{2, 10}
----
-- [2, 10]
-...
-s.index.secondary:select(10)
----
-- - [1, 10]
- - [2, 10]
-...
-s:drop()
----
-...
---
--- Check that ALTER is aborted if a tuple inserted during index build
--- doesn't conform to the new format.
---
-s = box.schema.space.create('test', {engine = 'vinyl'})
----
-...
-_ = s:create_index('pk', {page_size = 16})
----
-...
-pad = string.rep('x', 16)
----
-...
-for i = 101, 200 do s:replace{i, i, pad} end
----
-...
-box.snapshot()
----
-- ok
-...
-ch = fiber.channel(1)
----
-...
-test_run:cmd("setopt delimiter ';'")
----
-- true
-...
-_ = fiber.create(function()
- fiber.sleep(0.01)
- for i = 1, 100 do
- s:replace{i}
- end
- ch:put(true)
-end);
----
-...
-test_run:cmd("setopt delimiter ''");
----
-- true
-...
-errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0.001)
----
-- ok
-...
-s:create_index('sk', {parts = {2, 'unsigned'}}) -- must fail
----
-- error: Tuple field 2 required by space format is missing
-...
-errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0)
----
-- ok
-...
-ch:get()
----
-- true
-...
-s:count() -- 200
----
-- 200
-...
-s:drop()
----
-...
---
--- Check that ALTER is aborted if a tuple inserted during index build
--- violates unique constraint.
---
-s = box.schema.space.create('test', {engine = 'vinyl'})
----
-...
-_ = s:create_index('pk', {page_size = 16})
----
-...
-pad = string.rep('x', 16)
----
-...
-for i = 101, 200 do s:replace{i, i, pad} end
----
-...
-box.snapshot()
----
-- ok
-...
-ch = fiber.channel(1)
----
-...
-test_run:cmd("setopt delimiter ';'")
----
-- true
-...
-_ = fiber.create(function()
- fiber.sleep(0.01)
- for i = 1, 100 do
- s:replace{i, i + 1}
- end
- ch:put(true)
-end);
----
-...
-test_run:cmd("setopt delimiter ''");
----
-- true
-...
-errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0.001)
----
-- ok
-...
-s:create_index('sk', {parts = {2, 'unsigned'}}) -- must fail
----
-- error: Duplicate key exists in unique index 'sk' in space 'test'
-...
-errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0)
----
-- ok
-...
-ch:get()
----
-- true
-...
-s:count() -- 200
----
-- 200
-...
-s:drop()
----
-...
---
--- Check that modifications done to the space during the final dump
--- of a newly built index are recovered properly.
---
-s = box.schema.space.create('test', {engine = 'vinyl'})
----
-...
-_ = s:create_index('pk')
----
-...
-for i = 1, 5 do s:replace{i, i} end
----
-...
-errinj.set("ERRINJ_VY_RUN_WRITE_DELAY", true)
----
-- ok
-...
-ch = fiber.channel(1)
----
-...
-_ = fiber.create(function() s:create_index('sk', {parts = {2, 'integer'}}) ch:put(true) end)
----
-...
-fiber.sleep(0.01)
----
-...
-_ = s:delete{1}
----
-...
-_ = s:replace{2, -2}
----
-...
-_ = s:delete{2}
----
-...
-_ = s:replace{3, -3}
----
-...
-_ = s:replace{3, -2}
----
-...
-_ = s:replace{3, -1}
----
-...
-_ = s:delete{3}
----
-...
-_ = s:upsert({3, 3}, {{'=', 2, 1}})
----
-...
-_ = s:upsert({3, 3}, {{'=', 2, 2}})
----
-...
-_ = s:delete{3}
----
-...
-_ = s:replace{4, -1}
----
-...
-_ = s:replace{4, -2}
----
-...
-_ = s:replace{4, -4}
----
-...
-_ = s:upsert({5, 1}, {{'=', 2, 1}})
----
-...
-_ = s:upsert({5, 2}, {{'=', 2, -5}})
----
-...
-_ = s:replace{6, -6}
----
-...
-_ = s:upsert({7, -7}, {{'=', 2, -7}})
----
-...
-errinj.set("ERRINJ_VY_RUN_WRITE_DELAY", false)
----
-- ok
-...
-ch:get()
----
-- true
-...
-s.index.sk:select()
----
-- - [7, -7]
- - [6, -6]
- - [5, -5]
- - [4, -4]
-...
-s.index.sk:stat().memory.rows
----
-- 27
-...
-test_run:cmd('restart server default')
-s = box.space.test
----
-...
-s.index.sk:select()
----
-- - [7, -7]
- - [6, -6]
- - [5, -5]
- - [4, -4]
-...
-s.index.sk:stat().memory.rows
----
-- 27
-...
-box.snapshot()
----
-- ok
-...
-s.index.sk:select()
----
-- - [7, -7]
- - [6, -6]
- - [5, -5]
- - [4, -4]
-...
-s.index.sk:stat().memory.rows
----
-- 0
-...
-s:drop()
----
-...
---
-- Check that tarantool doesn't hang or crash if error
-- occurs while writing a deferred DELETE to WAL.
--
@@ -1926,314 +1076,6 @@ s:drop()
---
...
--
--- gh-3458: check that rw transactions that started before DDL are
--- aborted.
---
-vinyl_cache = box.cfg.vinyl_cache
----
-...
-box.cfg{vinyl_cache = 0}
----
-...
-s1 = box.schema.space.create('test1', {engine = 'vinyl'})
----
-...
-_ = s1:create_index('pk', {page_size = 16})
----
-...
-s2 = box.schema.space.create('test2', {engine = 'vinyl'})
----
-...
-_ = s2:create_index('pk')
----
-...
-pad = string.rep('x', 16)
----
-...
-for i = 101, 200 do s1:replace{i, i, pad} end
----
-...
-box.snapshot()
----
-- ok
-...
-test_run:cmd("setopt delimiter ';'")
----
-- true
-...
-function async_replace(space, tuple, timeout)
- local c = fiber.channel(1)
- fiber.create(function()
- box.begin()
- space:replace(tuple)
- fiber.sleep(timeout)
- local status = pcall(box.commit)
- c:put(status)
- end)
- return c
-end;
----
-...
-test_run:cmd("setopt delimiter ''");
----
-- true
-...
-c1 = async_replace(s1, {1}, 0.01)
----
-...
-c2 = async_replace(s2, {1}, 0.01)
----
-...
-errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0.001)
----
-- ok
-...
-s1:format{{'key', 'unsigned'}, {'value', 'unsigned'}}
----
-...
-errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0)
----
-- ok
-...
-c1:get() -- false (transaction was aborted)
----
-- false
-...
-c2:get() -- true
----
-- true
-...
-s1:get(1) == nil
----
-- true
-...
-s2:get(1) ~= nil
----
-- true
-...
-s1:format()
----
-- [{'name': 'key', 'type': 'unsigned'}, {'name': 'value', 'type': 'unsigned'}]
-...
-s1:format{}
----
-...
-c1 = async_replace(s1, {2}, 0.01)
----
-...
-c2 = async_replace(s2, {2}, 0.01)
----
-...
-errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0.001)
----
-- ok
-...
-_ = s1:create_index('sk', {parts = {2, 'unsigned'}})
----
-...
-errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0)
----
-- ok
-...
-c1:get() -- false (transaction was aborted)
----
-- false
-...
-c2:get() -- true
----
-- true
-...
-s1:get(2) == nil
----
-- true
-...
-s2:get(2) ~= nil
----
-- true
-...
-s1.index.pk:count() == s1.index.sk:count()
----
-- true
-...
-s1:drop()
----
-...
-s2:drop()
----
-...
-box.cfg{vinyl_cache = vinyl_cache}
----
-...
--- Transactions that reached WAL must not be aborted.
-s = box.schema.space.create('test', {engine = 'vinyl'})
----
-...
-_ = s:create_index('pk')
----
-...
-errinj.set('ERRINJ_WAL_DELAY', true)
----
-- ok
-...
-_ = fiber.create(function() s:replace{1} end)
----
-...
-_ = fiber.create(function() fiber.sleep(0.01) errinj.set('ERRINJ_WAL_DELAY', false) end)
----
-...
-fiber.sleep(0)
----
-...
-s:format{{'key', 'unsigned'}, {'value', 'unsigned'}} -- must fail
----
-- error: Tuple field 2 required by space format is missing
-...
-s:select()
----
-- - [1]
-...
-s:truncate()
----
-...
-errinj.set('ERRINJ_WAL_DELAY', true)
----
-- ok
-...
-_ = fiber.create(function() s:replace{1} end)
----
-...
-_ = fiber.create(function() fiber.sleep(0.01) errinj.set('ERRINJ_WAL_DELAY', false) end)
----
-...
-fiber.sleep(0)
----
-...
-s:create_index('sk', {parts = {2, 'unsigned'}})
----
-- error: Tuple field 2 required by space format is missing
-...
-s:select()
----
-- - [1]
-...
-s:drop()
----
-...
---
--- Check disk.compact.queue stat.
---
-test_run:cmd("push filter 'bytes_compressed: .*' to 'bytes_compressed: <bytes_compressed>'")
----
-- true
-...
-s = box.schema.space.create('test', {engine = 'vinyl'})
----
-...
-i = s:create_index('pk', {run_count_per_level = 2})
----
-...
-function dump() for i = 1, 10 do s:replace{i} end box.snapshot() end
----
-...
-dump()
----
-...
-i:stat().disk.compact.queue -- none
----
-- bytes_compressed: <bytes_compressed>
- pages: 0
- rows: 0
- bytes: 0
-...
-i:stat().disk.compact.queue.bytes == box.stat.vinyl().disk.compact.queue
----
-- true
-...
-errinj.set('ERRINJ_VY_COMPACTION_DELAY', true)
----
-- ok
-...
-dump()
----
-...
-dump()
----
-...
-i:stat().disk.compact.queue -- 30 statements
----
-- bytes_compressed: <bytes_compressed>
- pages: 3
- rows: 30
- bytes: 471
-...
-i:stat().disk.compact.queue.bytes == box.stat.vinyl().disk.compact.queue
----
-- true
-...
-dump()
----
-...
-i:stat().disk.compact.queue -- 40 statements
----
-- bytes_compressed: <bytes_compressed>
- pages: 4
- rows: 40
- bytes: 628
-...
-i:stat().disk.compact.queue.bytes == box.stat.vinyl().disk.compact.queue
----
-- true
-...
-dump()
----
-...
-i:stat().disk.compact.queue -- 50 statements
----
-- bytes_compressed: <bytes_compressed>
- pages: 5
- rows: 50
- bytes: 785
-...
-i:stat().disk.compact.queue.bytes == box.stat.vinyl().disk.compact.queue
----
-- true
-...
-box.stat.reset() -- doesn't affect queue size
----
-...
-i:stat().disk.compact.queue -- 50 statements
----
-- bytes_compressed: <bytes_compressed>
- pages: 5
- rows: 50
- bytes: 785
-...
-i:stat().disk.compact.queue.bytes == box.stat.vinyl().disk.compact.queue
----
-- true
-...
-errinj.set('ERRINJ_VY_COMPACTION_DELAY', false)
----
-- ok
-...
-while i:stat().disk.compact.count < 2 do fiber.sleep(0.01) end
----
-...
-i:stat().disk.compact.queue -- none
----
-- bytes_compressed: <bytes_compressed>
- pages: 0
- rows: 0
- bytes: 0
-...
-s:drop()
----
-...
-test_run:cmd("clear filter")
----
-- true
-...
---
-- Check that an instance doesn't crash if a run file needed for
-- joining a replica is corrupted (see gh-3708).
--
diff --git a/test/vinyl/errinj.test.lua b/test/vinyl/errinj.test.lua
index 59e9f81c..d4590fb4 100644
--- a/test/vinyl/errinj.test.lua
+++ b/test/vinyl/errinj.test.lua
@@ -1,21 +1,8 @@
---
--- gh-1681: vinyl: crash in vy_rollback on ER_WAL_WRITE
---
test_run = require('test_run').new()
fio = require('fio')
fiber = require('fiber')
errinj = box.error.injection
errinj.set("ERRINJ_VY_SCHED_TIMEOUT", 0.040)
-s = box.schema.space.create('test', {engine='vinyl'})
-_ = s:create_index('pk')
-function f() box.begin() s:insert{1, 'hi'} s:insert{2, 'bye'} box.commit() end
-errinj.set("ERRINJ_WAL_WRITE", true)
-f()
-s:select{}
-errinj.set("ERRINJ_WAL_WRITE", false)
-f()
-s:select{}
-s:drop()
--
-- Lost data in case of dump error
--
@@ -165,186 +152,6 @@ s:drop() -- index is gone
fiber.sleep(0.05)
errinj.set("ERRINJ_VY_SQUASH_TIMEOUT", 0)
---https://github.com/tarantool/tarantool/issues/1842
---test error injection
-s = box.schema.space.create('test', {engine='vinyl'})
-_ = s:create_index('pk')
-s:replace{0, 0}
-
-s:replace{1, 0}
-s:replace{2, 0}
-errinj.set("ERRINJ_WAL_WRITE", true)
-s:replace{3, 0}
-s:replace{4, 0}
-s:replace{5, 0}
-s:replace{6, 0}
-errinj.set("ERRINJ_WAL_WRITE", false)
-s:replace{7, 0}
-s:replace{8, 0}
-s:select{}
-
-s:drop()
-
-create_iterator = require('utils').create_iterator
-
---iterator test
-test_run:cmd("setopt delimiter ';'")
-
-fiber_status = 0
-
-function fiber_func()
- box.begin()
- s:replace{5, 5}
- fiber_status = 1
- local res = {pcall(box.commit) }
- fiber_status = 2
- return unpack(res)
-end;
-
-test_run:cmd("setopt delimiter ''");
-
-s = box.schema.space.create('test', {engine='vinyl'})
-_ = s:create_index('pk')
-fiber = require('fiber')
-
-_ = s:replace{0, 0}
-_ = s:replace{10, 0}
-_ = s:replace{20, 0}
-
-test_run:cmd("setopt delimiter ';'");
-
-faced_trash = false
-for i = 1,100 do
- errinj.set("ERRINJ_WAL_WRITE", true)
- local f = fiber.create(fiber_func)
- local itr = create_iterator(s, {0}, {iterator='GE'})
- local first = itr.next()
- local second = itr.next()
- if (second[1] ~= 5 and second[1] ~= 10) then faced_trash = true end
- while fiber_status <= 1 do fiber.sleep(0.001) end
- local _,next = pcall(itr.next)
- _,next = pcall(itr.next)
- _,next = pcall(itr.next)
- errinj.set("ERRINJ_WAL_WRITE", false)
- s:delete{5}
-end;
-
-test_run:cmd("setopt delimiter ''");
-
-faced_trash
-
-s:drop()
-
--- TX in prepared but not committed state
-s = box.schema.space.create('test', {engine='vinyl'})
-_ = s:create_index('pk')
-fiber = require('fiber')
-txn_proxy = require('txn_proxy')
-
-s:replace{1, "original"}
-s:replace{2, "original"}
-s:replace{3, "original"}
-
-c0 = txn_proxy.new()
-c0:begin()
-c1 = txn_proxy.new()
-c1:begin()
-c2 = txn_proxy.new()
-c2:begin()
-c3 = txn_proxy.new()
-c3:begin()
-
---
--- Prepared transactions
---
-
--- Pause WAL writer to cause all further calls to box.commit() to move
--- transactions into prepared, but not committed yet state.
-errinj.set("ERRINJ_WAL_DELAY", true)
-lsn = box.info.lsn
-c0('s:replace{1, "c0"}')
-c0('s:replace{2, "c0"}')
-c0('s:replace{3, "c0"}')
-_ = fiber.create(c0.commit, c0)
-box.info.lsn == lsn
-c1('s:replace{1, "c1"}')
-c1('s:replace{2, "c1"}')
-_ = fiber.create(c1.commit, c1)
-box.info.lsn == lsn
-c3('s:select{1}') -- c1 is visible
-c2('s:replace{1, "c2"}')
-c2('s:replace{3, "c2"}')
-_ = fiber.create(c2.commit, c2)
-box.info.lsn == lsn
-c3('s:select{1}') -- c1 is visible, c2 is not
-c3('s:select{2}') -- c1 is visible
-c3('s:select{3}') -- c2 is not visible
-
--- Resume WAL writer and wait until all transactions will been committed
-errinj.set("ERRINJ_WAL_DELAY", false)
-REQ_COUNT = 7
-while box.info.lsn - lsn < REQ_COUNT do fiber.sleep(0.01) end
-box.info.lsn == lsn + REQ_COUNT
-
-c3('s:select{1}') -- c1 is visible, c2 is not
-c3('s:select{2}') -- c1 is visible
-c3('s:select{3}') -- c2 is not visible
-c3:commit()
-
-s:drop()
-
---
--- Test mem restoration on a prepared and not commited statement
--- after moving iterator into read view.
---
-space = box.schema.space.create('test', {engine = 'vinyl'})
-pk = space:create_index('pk')
-space:replace{1}
-space:replace{2}
-space:replace{3}
-
-last_read = nil
-
-errinj.set("ERRINJ_WAL_DELAY", true)
-
-test_run:cmd("setopt delimiter ';'")
-
-function fill_space()
- box.begin()
- space:replace{1}
- space:replace{2}
- space:replace{3}
--- block until wal_delay = false
- box.commit()
--- send iterator to read view
- space:replace{1, 1}
--- flush mem and update index version to trigger iterator restore
- box.snapshot()
-end;
-
-function iterate_in_read_view()
- local i = create_iterator(space)
- last_read = i.next()
- fiber.sleep(100000)
- last_read = i.next()
-end;
-
-test_run:cmd("setopt delimiter ''");
-
-f1 = fiber.create(fill_space)
--- Prepared transaction is blocked due to wal_delay.
--- Start iterator with vlsn = INT64_MAX
-f2 = fiber.create(iterate_in_read_view)
-last_read
--- Finish prepared transaction and send to read view the iterator.
-errinj.set("ERRINJ_WAL_DELAY", false)
-while f1:status() ~= 'dead' do fiber.sleep(0.01) end
-f2:wakeup()
-while f2:status() ~= 'dead' do fiber.sleep(0.01) end
-last_read
-
-space:drop()
-
--
-- Space drop in the middle of dump.
--
@@ -389,21 +196,6 @@ t2 = fiber.time()
t2 - t1 < 1
test_run:cmd("cleanup server test")
---
--- If we logged an index creation in the metadata log before WAL write,
--- WAL failure would result in leaving the index record in vylog forever.
--- Since we use LSN to identify indexes in vylog, retrying index creation
--- would then lead to a duplicate index id in vylog and hence inability
--- to make a snapshot or recover.
---
-s = box.schema.space.create('test', {engine = 'vinyl'})
-errinj.set('ERRINJ_WAL_IO', true)
-_ = s:create_index('pk')
-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'}})
@@ -541,41 +333,6 @@ test_run:cmd("stop server low_quota")
test_run:cmd("cleanup server low_quota")
--
--- Check that ALTER is abroted if a tuple inserted during space
--- format change does not conform to the new format.
---
-format = {}
-format[1] = {name = 'field1', type = 'unsigned'}
-format[2] = {name = 'field2', type = 'string', is_nullable = true}
-s = box.schema.space.create('test', {engine = 'vinyl', format = format})
-_ = s:create_index('pk', {page_size = 16})
-
-pad = string.rep('x', 16)
-for i = 101, 200 do s:replace{i, pad} end
-box.snapshot()
-
-ch = fiber.channel(1)
-test_run:cmd("setopt delimiter ';'")
-_ = fiber.create(function()
- fiber.sleep(0.01)
- for i = 1, 100 do
- s:replace{i, box.NULL}
- end
- ch:put(true)
-end);
-test_run:cmd("setopt delimiter ''");
-
-errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0.001)
-format[2].is_nullable = false
-s:format(format) -- must fail
-errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0)
-
-ch:get()
-
-s:count() -- 200
-s:drop()
-
---
-- gh-3437: if compaction races with checkpointing, it may remove
-- files needed for backup.
--
@@ -604,140 +361,6 @@ box.backup.stop()
s:drop()
--
--- gh-2449: change 'unique' index property from true to false
--- is done without index rebuild.
---
-s = box.schema.space.create('test', { engine = 'vinyl' })
-_ = s:create_index('primary')
-_ = s:create_index('secondary', {unique = true, parts = {2, 'unsigned'}})
-s:insert{1, 10}
-box.snapshot()
-errinj.set("ERRINJ_VY_READ_PAGE", true);
-s.index.secondary:alter{unique = false} -- ok
-s.index.secondary.unique
-s.index.secondary:alter{unique = true} -- error
-s.index.secondary.unique
-errinj.set("ERRINJ_VY_READ_PAGE", false);
-s:insert{2, 10}
-s.index.secondary:select(10)
-s:drop()
-
---
--- Check that ALTER is aborted if a tuple inserted during index build
--- doesn't conform to the new format.
---
-s = box.schema.space.create('test', {engine = 'vinyl'})
-_ = s:create_index('pk', {page_size = 16})
-
-pad = string.rep('x', 16)
-for i = 101, 200 do s:replace{i, i, pad} end
-box.snapshot()
-
-ch = fiber.channel(1)
-test_run:cmd("setopt delimiter ';'")
-_ = fiber.create(function()
- fiber.sleep(0.01)
- for i = 1, 100 do
- s:replace{i}
- end
- ch:put(true)
-end);
-test_run:cmd("setopt delimiter ''");
-
-errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0.001)
-s:create_index('sk', {parts = {2, 'unsigned'}}) -- must fail
-errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0)
-
-ch:get()
-
-s:count() -- 200
-s:drop()
-
---
--- Check that ALTER is aborted if a tuple inserted during index build
--- violates unique constraint.
---
-s = box.schema.space.create('test', {engine = 'vinyl'})
-_ = s:create_index('pk', {page_size = 16})
-
-pad = string.rep('x', 16)
-for i = 101, 200 do s:replace{i, i, pad} end
-box.snapshot()
-
-ch = fiber.channel(1)
-test_run:cmd("setopt delimiter ';'")
-_ = fiber.create(function()
- fiber.sleep(0.01)
- for i = 1, 100 do
- s:replace{i, i + 1}
- end
- ch:put(true)
-end);
-test_run:cmd("setopt delimiter ''");
-
-errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0.001)
-s:create_index('sk', {parts = {2, 'unsigned'}}) -- must fail
-errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0)
-
-ch:get()
-
-s:count() -- 200
-s:drop()
-
---
--- Check that modifications done to the space during the final dump
--- of a newly built index are recovered properly.
---
-s = box.schema.space.create('test', {engine = 'vinyl'})
-_ = s:create_index('pk')
-
-for i = 1, 5 do s:replace{i, i} end
-
-errinj.set("ERRINJ_VY_RUN_WRITE_DELAY", true)
-ch = fiber.channel(1)
-_ = fiber.create(function() s:create_index('sk', {parts = {2, 'integer'}}) ch:put(true) end)
-
-fiber.sleep(0.01)
-
-_ = s:delete{1}
-_ = s:replace{2, -2}
-_ = s:delete{2}
-_ = s:replace{3, -3}
-_ = s:replace{3, -2}
-_ = s:replace{3, -1}
-_ = s:delete{3}
-_ = s:upsert({3, 3}, {{'=', 2, 1}})
-_ = s:upsert({3, 3}, {{'=', 2, 2}})
-_ = s:delete{3}
-_ = s:replace{4, -1}
-_ = s:replace{4, -2}
-_ = s:replace{4, -4}
-_ = s:upsert({5, 1}, {{'=', 2, 1}})
-_ = s:upsert({5, 2}, {{'=', 2, -5}})
-_ = s:replace{6, -6}
-_ = s:upsert({7, -7}, {{'=', 2, -7}})
-
-errinj.set("ERRINJ_VY_RUN_WRITE_DELAY", false)
-ch:get()
-
-s.index.sk:select()
-s.index.sk:stat().memory.rows
-
-test_run:cmd('restart server default')
-
-s = box.space.test
-
-s.index.sk:select()
-s.index.sk:stat().memory.rows
-
-box.snapshot()
-
-s.index.sk:select()
-s.index.sk:stat().memory.rows
-
-s:drop()
-
---
-- Check that tarantool doesn't hang or crash if error
-- occurs while writing a deferred DELETE to WAL.
--
@@ -768,123 +391,6 @@ box.snapshot() -- ok
s:drop()
--
--- gh-3458: check that rw transactions that started before DDL are
--- aborted.
---
-vinyl_cache = box.cfg.vinyl_cache
-box.cfg{vinyl_cache = 0}
-
-s1 = box.schema.space.create('test1', {engine = 'vinyl'})
-_ = s1:create_index('pk', {page_size = 16})
-s2 = box.schema.space.create('test2', {engine = 'vinyl'})
-_ = s2:create_index('pk')
-
-pad = string.rep('x', 16)
-for i = 101, 200 do s1:replace{i, i, pad} end
-box.snapshot()
-
-test_run:cmd("setopt delimiter ';'")
-function async_replace(space, tuple, timeout)
- local c = fiber.channel(1)
- fiber.create(function()
- box.begin()
- space:replace(tuple)
- fiber.sleep(timeout)
- local status = pcall(box.commit)
- c:put(status)
- end)
- return c
-end;
-test_run:cmd("setopt delimiter ''");
-
-c1 = async_replace(s1, {1}, 0.01)
-c2 = async_replace(s2, {1}, 0.01)
-
-errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0.001)
-s1:format{{'key', 'unsigned'}, {'value', 'unsigned'}}
-errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0)
-
-c1:get() -- false (transaction was aborted)
-c2:get() -- true
-
-s1:get(1) == nil
-s2:get(1) ~= nil
-s1:format()
-s1:format{}
-
-c1 = async_replace(s1, {2}, 0.01)
-c2 = async_replace(s2, {2}, 0.01)
-
-errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0.001)
-_ = s1:create_index('sk', {parts = {2, 'unsigned'}})
-errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0)
-
-c1:get() -- false (transaction was aborted)
-c2:get() -- true
-
-s1:get(2) == nil
-s2:get(2) ~= nil
-s1.index.pk:count() == s1.index.sk:count()
-
-s1:drop()
-s2:drop()
-box.cfg{vinyl_cache = vinyl_cache}
-
--- Transactions that reached WAL must not be aborted.
-s = box.schema.space.create('test', {engine = 'vinyl'})
-_ = s:create_index('pk')
-
-errinj.set('ERRINJ_WAL_DELAY', true)
-_ = fiber.create(function() s:replace{1} end)
-_ = fiber.create(function() fiber.sleep(0.01) errinj.set('ERRINJ_WAL_DELAY', false) end)
-
-fiber.sleep(0)
-s:format{{'key', 'unsigned'}, {'value', 'unsigned'}} -- must fail
-s:select()
-s:truncate()
-
-errinj.set('ERRINJ_WAL_DELAY', true)
-_ = fiber.create(function() s:replace{1} end)
-_ = fiber.create(function() fiber.sleep(0.01) errinj.set('ERRINJ_WAL_DELAY', false) end)
-
-fiber.sleep(0)
-s:create_index('sk', {parts = {2, 'unsigned'}})
-s:select()
-s:drop()
-
---
--- Check disk.compact.queue stat.
---
-test_run:cmd("push filter 'bytes_compressed: .*' to 'bytes_compressed: <bytes_compressed>'")
-
-s = box.schema.space.create('test', {engine = 'vinyl'})
-i = s:create_index('pk', {run_count_per_level = 2})
-function dump() for i = 1, 10 do s:replace{i} end box.snapshot() end
-dump()
-i:stat().disk.compact.queue -- none
-i:stat().disk.compact.queue.bytes == box.stat.vinyl().disk.compact.queue
-errinj.set('ERRINJ_VY_COMPACTION_DELAY', true)
-dump()
-dump()
-i:stat().disk.compact.queue -- 30 statements
-i:stat().disk.compact.queue.bytes == box.stat.vinyl().disk.compact.queue
-dump()
-i:stat().disk.compact.queue -- 40 statements
-i:stat().disk.compact.queue.bytes == box.stat.vinyl().disk.compact.queue
-dump()
-i:stat().disk.compact.queue -- 50 statements
-i:stat().disk.compact.queue.bytes == box.stat.vinyl().disk.compact.queue
-box.stat.reset() -- doesn't affect queue size
-i:stat().disk.compact.queue -- 50 statements
-i:stat().disk.compact.queue.bytes == box.stat.vinyl().disk.compact.queue
-errinj.set('ERRINJ_VY_COMPACTION_DELAY', false)
-while i:stat().disk.compact.count < 2 do fiber.sleep(0.01) end
-i:stat().disk.compact.queue -- none
-s:drop()
-
-test_run:cmd("clear filter")
-
---
-- Check that an instance doesn't crash if a run file needed for
-- joining a replica is corrupted (see gh-3708).
--
diff --git a/test/vinyl/errinj_ddl.result b/test/vinyl/errinj_ddl.result
new file mode 100644
index 00000000..4f5d59cd
--- /dev/null
+++ b/test/vinyl/errinj_ddl.result
@@ -0,0 +1,595 @@
+test_run = require('test_run').new()
+---
+...
+fiber = require('fiber')
+---
+...
+errinj = box.error.injection
+---
+...
+--
+-- Check that ALTER is abroted if a tuple inserted during space
+-- format change does not conform to the new format.
+--
+format = {}
+---
+...
+format[1] = {name = 'field1', type = 'unsigned'}
+---
+...
+format[2] = {name = 'field2', type = 'string', is_nullable = true}
+---
+...
+s = box.schema.space.create('test', {engine = 'vinyl', format = format})
+---
+...
+_ = s:create_index('pk', {page_size = 16})
+---
+...
+pad = string.rep('x', 16)
+---
+...
+for i = 101, 200 do s:replace{i, pad} end
+---
+...
+box.snapshot()
+---
+- ok
+...
+ch = fiber.channel(1)
+---
+...
+test_run:cmd("setopt delimiter ';'")
+---
+- true
+...
+_ = fiber.create(function()
+ fiber.sleep(0.01)
+ for i = 1, 100 do
+ s:replace{i, box.NULL}
+ end
+ ch:put(true)
+end);
+---
+...
+test_run:cmd("setopt delimiter ''");
+---
+- true
+...
+errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0.001)
+---
+- ok
+...
+format[2].is_nullable = false
+---
+...
+s:format(format) -- must fail
+---
+- error: 'Tuple field 2 type does not match one required by operation: expected string'
+...
+errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0)
+---
+- ok
+...
+ch:get()
+---
+- true
+...
+s:count() -- 200
+---
+- 200
+...
+s:drop()
+---
+...
+--
+-- gh-2449: change 'unique' index property from true to false
+-- is done without index rebuild.
+--
+s = box.schema.space.create('test', { engine = 'vinyl' })
+---
+...
+_ = s:create_index('primary')
+---
+...
+_ = s:create_index('secondary', {unique = true, parts = {2, 'unsigned'}})
+---
+...
+s:insert{1, 10}
+---
+- [1, 10]
+...
+box.snapshot()
+---
+- ok
+...
+errinj.set("ERRINJ_VY_READ_PAGE", true);
+---
+- ok
+...
+s.index.secondary:alter{unique = false} -- ok
+---
+...
+s.index.secondary.unique
+---
+- false
+...
+s.index.secondary:alter{unique = true} -- error
+---
+- error: Error injection 'vinyl page read'
+...
+s.index.secondary.unique
+---
+- false
+...
+errinj.set("ERRINJ_VY_READ_PAGE", false);
+---
+- ok
+...
+s:insert{2, 10}
+---
+- [2, 10]
+...
+s.index.secondary:select(10)
+---
+- - [1, 10]
+ - [2, 10]
+...
+s:drop()
+---
+...
+--
+-- Check that ALTER is aborted if a tuple inserted during index build
+-- doesn't conform to the new format.
+--
+s = box.schema.space.create('test', {engine = 'vinyl'})
+---
+...
+_ = s:create_index('pk', {page_size = 16})
+---
+...
+pad = string.rep('x', 16)
+---
+...
+for i = 101, 200 do s:replace{i, i, pad} end
+---
+...
+box.snapshot()
+---
+- ok
+...
+ch = fiber.channel(1)
+---
+...
+test_run:cmd("setopt delimiter ';'")
+---
+- true
+...
+_ = fiber.create(function()
+ fiber.sleep(0.01)
+ for i = 1, 100 do
+ s:replace{i}
+ end
+ ch:put(true)
+end);
+---
+...
+test_run:cmd("setopt delimiter ''");
+---
+- true
+...
+errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0.001)
+---
+- ok
+...
+s:create_index('sk', {parts = {2, 'unsigned'}}) -- must fail
+---
+- error: Tuple field 2 required by space format is missing
+...
+errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0)
+---
+- ok
+...
+ch:get()
+---
+- true
+...
+s:count() -- 200
+---
+- 200
+...
+s:drop()
+---
+...
+--
+-- Check that ALTER is aborted if a tuple inserted during index build
+-- violates unique constraint.
+--
+s = box.schema.space.create('test', {engine = 'vinyl'})
+---
+...
+_ = s:create_index('pk', {page_size = 16})
+---
+...
+pad = string.rep('x', 16)
+---
+...
+for i = 101, 200 do s:replace{i, i, pad} end
+---
+...
+box.snapshot()
+---
+- ok
+...
+ch = fiber.channel(1)
+---
+...
+test_run:cmd("setopt delimiter ';'")
+---
+- true
+...
+_ = fiber.create(function()
+ fiber.sleep(0.01)
+ for i = 1, 100 do
+ s:replace{i, i + 1}
+ end
+ ch:put(true)
+end);
+---
+...
+test_run:cmd("setopt delimiter ''");
+---
+- true
+...
+errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0.001)
+---
+- ok
+...
+s:create_index('sk', {parts = {2, 'unsigned'}}) -- must fail
+---
+- error: Duplicate key exists in unique index 'sk' in space 'test'
+...
+errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0)
+---
+- ok
+...
+ch:get()
+---
+- true
+...
+s:count() -- 200
+---
+- 200
+...
+s:drop()
+---
+...
+--
+-- Check that modifications done to the space during the final dump
+-- of a newly built index are recovered properly.
+--
+s = box.schema.space.create('test', {engine = 'vinyl'})
+---
+...
+_ = s:create_index('pk')
+---
+...
+for i = 1, 5 do s:replace{i, i} end
+---
+...
+errinj.set("ERRINJ_VY_RUN_WRITE_DELAY", true)
+---
+- ok
+...
+ch = fiber.channel(1)
+---
+...
+_ = fiber.create(function() s:create_index('sk', {parts = {2, 'integer'}}) ch:put(true) end)
+---
+...
+fiber.sleep(0.01)
+---
+...
+_ = s:delete{1}
+---
+...
+_ = s:replace{2, -2}
+---
+...
+_ = s:delete{2}
+---
+...
+_ = s:replace{3, -3}
+---
+...
+_ = s:replace{3, -2}
+---
+...
+_ = s:replace{3, -1}
+---
+...
+_ = s:delete{3}
+---
+...
+_ = s:upsert({3, 3}, {{'=', 2, 1}})
+---
+...
+_ = s:upsert({3, 3}, {{'=', 2, 2}})
+---
+...
+_ = s:delete{3}
+---
+...
+_ = s:replace{4, -1}
+---
+...
+_ = s:replace{4, -2}
+---
+...
+_ = s:replace{4, -4}
+---
+...
+_ = s:upsert({5, 1}, {{'=', 2, 1}})
+---
+...
+_ = s:upsert({5, 2}, {{'=', 2, -5}})
+---
+...
+_ = s:replace{6, -6}
+---
+...
+_ = s:upsert({7, -7}, {{'=', 2, -7}})
+---
+...
+errinj.set("ERRINJ_VY_RUN_WRITE_DELAY", false)
+---
+- ok
+...
+ch:get()
+---
+- true
+...
+s.index.sk:select()
+---
+- - [7, -7]
+ - [6, -6]
+ - [5, -5]
+ - [4, -4]
+...
+s.index.sk:stat().memory.rows
+---
+- 27
+...
+test_run:cmd('restart server default')
+fiber = require('fiber')
+---
+...
+errinj = box.error.injection
+---
+...
+s = box.space.test
+---
+...
+s.index.sk:select()
+---
+- - [7, -7]
+ - [6, -6]
+ - [5, -5]
+ - [4, -4]
+...
+s.index.sk:stat().memory.rows
+---
+- 27
+...
+box.snapshot()
+---
+- ok
+...
+s.index.sk:select()
+---
+- - [7, -7]
+ - [6, -6]
+ - [5, -5]
+ - [4, -4]
+...
+s.index.sk:stat().memory.rows
+---
+- 0
+...
+s:drop()
+---
+...
+--
+-- gh-3458: check that rw transactions that started before DDL are
+-- aborted.
+--
+vinyl_cache = box.cfg.vinyl_cache
+---
+...
+box.cfg{vinyl_cache = 0}
+---
+...
+s1 = box.schema.space.create('test1', {engine = 'vinyl'})
+---
+...
+_ = s1:create_index('pk', {page_size = 16})
+---
+...
+s2 = box.schema.space.create('test2', {engine = 'vinyl'})
+---
+...
+_ = s2:create_index('pk')
+---
+...
+pad = string.rep('x', 16)
+---
+...
+for i = 101, 200 do s1:replace{i, i, pad} end
+---
+...
+box.snapshot()
+---
+- ok
+...
+test_run:cmd("setopt delimiter ';'")
+---
+- true
+...
+function async_replace(space, tuple, timeout)
+ local c = fiber.channel(1)
+ fiber.create(function()
+ box.begin()
+ space:replace(tuple)
+ fiber.sleep(timeout)
+ local status = pcall(box.commit)
+ c:put(status)
+ end)
+ return c
+end;
+---
+...
+test_run:cmd("setopt delimiter ''");
+---
+- true
+...
+c1 = async_replace(s1, {1}, 0.01)
+---
+...
+c2 = async_replace(s2, {1}, 0.01)
+---
+...
+errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0.001)
+---
+- ok
+...
+s1:format{{'key', 'unsigned'}, {'value', 'unsigned'}}
+---
+...
+errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0)
+---
+- ok
+...
+c1:get() -- false (transaction was aborted)
+---
+- false
+...
+c2:get() -- true
+---
+- true
+...
+s1:get(1) == nil
+---
+- true
+...
+s2:get(1) ~= nil
+---
+- true
+...
+s1:format()
+---
+- [{'name': 'key', 'type': 'unsigned'}, {'name': 'value', 'type': 'unsigned'}]
+...
+s1:format{}
+---
+...
+c1 = async_replace(s1, {2}, 0.01)
+---
+...
+c2 = async_replace(s2, {2}, 0.01)
+---
+...
+errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0.001)
+---
+- ok
+...
+_ = s1:create_index('sk', {parts = {2, 'unsigned'}})
+---
+...
+errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0)
+---
+- ok
+...
+c1:get() -- false (transaction was aborted)
+---
+- false
+...
+c2:get() -- true
+---
+- true
+...
+s1:get(2) == nil
+---
+- true
+...
+s2:get(2) ~= nil
+---
+- true
+...
+s1.index.pk:count() == s1.index.sk:count()
+---
+- true
+...
+s1:drop()
+---
+...
+s2:drop()
+---
+...
+box.cfg{vinyl_cache = vinyl_cache}
+---
+...
+-- Transactions that reached WAL must not be aborted.
+s = box.schema.space.create('test', {engine = 'vinyl'})
+---
+...
+_ = s:create_index('pk')
+---
+...
+errinj.set('ERRINJ_WAL_DELAY', true)
+---
+- ok
+...
+_ = fiber.create(function() s:replace{1} end)
+---
+...
+_ = fiber.create(function() fiber.sleep(0.01) errinj.set('ERRINJ_WAL_DELAY', false) end)
+---
+...
+fiber.sleep(0)
+---
+...
+s:format{{'key', 'unsigned'}, {'value', 'unsigned'}} -- must fail
+---
+- error: Tuple field 2 required by space format is missing
+...
+s:select()
+---
+- - [1]
+...
+s:truncate()
+---
+...
+errinj.set('ERRINJ_WAL_DELAY', true)
+---
+- ok
+...
+_ = fiber.create(function() s:replace{1} end)
+---
+...
+_ = fiber.create(function() fiber.sleep(0.01) errinj.set('ERRINJ_WAL_DELAY', false) end)
+---
+...
+fiber.sleep(0)
+---
+...
+s:create_index('sk', {parts = {2, 'unsigned'}})
+---
+- error: Tuple field 2 required by space format is missing
+...
+s:select()
+---
+- - [1]
+...
+s:drop()
+---
+...
diff --git a/test/vinyl/errinj_ddl.test.lua b/test/vinyl/errinj_ddl.test.lua
new file mode 100644
index 00000000..0948bc3d
--- /dev/null
+++ b/test/vinyl/errinj_ddl.test.lua
@@ -0,0 +1,260 @@
+test_run = require('test_run').new()
+fiber = require('fiber')
+errinj = box.error.injection
+
+--
+-- Check that ALTER is abroted if a tuple inserted during space
+-- format change does not conform to the new format.
+--
+format = {}
+format[1] = {name = 'field1', type = 'unsigned'}
+format[2] = {name = 'field2', type = 'string', is_nullable = true}
+s = box.schema.space.create('test', {engine = 'vinyl', format = format})
+_ = s:create_index('pk', {page_size = 16})
+
+pad = string.rep('x', 16)
+for i = 101, 200 do s:replace{i, pad} end
+box.snapshot()
+
+ch = fiber.channel(1)
+test_run:cmd("setopt delimiter ';'")
+_ = fiber.create(function()
+ fiber.sleep(0.01)
+ for i = 1, 100 do
+ s:replace{i, box.NULL}
+ end
+ ch:put(true)
+end);
+test_run:cmd("setopt delimiter ''");
+
+errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0.001)
+format[2].is_nullable = false
+s:format(format) -- must fail
+errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0)
+
+ch:get()
+
+s:count() -- 200
+s:drop()
+
+--
+-- gh-2449: change 'unique' index property from true to false
+-- is done without index rebuild.
+--
+s = box.schema.space.create('test', { engine = 'vinyl' })
+_ = s:create_index('primary')
+_ = s:create_index('secondary', {unique = true, parts = {2, 'unsigned'}})
+s:insert{1, 10}
+box.snapshot()
+errinj.set("ERRINJ_VY_READ_PAGE", true);
+s.index.secondary:alter{unique = false} -- ok
+s.index.secondary.unique
+s.index.secondary:alter{unique = true} -- error
+s.index.secondary.unique
+errinj.set("ERRINJ_VY_READ_PAGE", false);
+s:insert{2, 10}
+s.index.secondary:select(10)
+s:drop()
+
+--
+-- Check that ALTER is aborted if a tuple inserted during index build
+-- doesn't conform to the new format.
+--
+s = box.schema.space.create('test', {engine = 'vinyl'})
+_ = s:create_index('pk', {page_size = 16})
+
+pad = string.rep('x', 16)
+for i = 101, 200 do s:replace{i, i, pad} end
+box.snapshot()
+
+ch = fiber.channel(1)
+test_run:cmd("setopt delimiter ';'")
+_ = fiber.create(function()
+ fiber.sleep(0.01)
+ for i = 1, 100 do
+ s:replace{i}
+ end
+ ch:put(true)
+end);
+test_run:cmd("setopt delimiter ''");
+
+errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0.001)
+s:create_index('sk', {parts = {2, 'unsigned'}}) -- must fail
+errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0)
+
+ch:get()
+
+s:count() -- 200
+s:drop()
+
+--
+-- Check that ALTER is aborted if a tuple inserted during index build
+-- violates unique constraint.
+--
+s = box.schema.space.create('test', {engine = 'vinyl'})
+_ = s:create_index('pk', {page_size = 16})
+
+pad = string.rep('x', 16)
+for i = 101, 200 do s:replace{i, i, pad} end
+box.snapshot()
+
+ch = fiber.channel(1)
+test_run:cmd("setopt delimiter ';'")
+_ = fiber.create(function()
+ fiber.sleep(0.01)
+ for i = 1, 100 do
+ s:replace{i, i + 1}
+ end
+ ch:put(true)
+end);
+test_run:cmd("setopt delimiter ''");
+
+errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0.001)
+s:create_index('sk', {parts = {2, 'unsigned'}}) -- must fail
+errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0)
+
+ch:get()
+
+s:count() -- 200
+s:drop()
+
+--
+-- Check that modifications done to the space during the final dump
+-- of a newly built index are recovered properly.
+--
+s = box.schema.space.create('test', {engine = 'vinyl'})
+_ = s:create_index('pk')
+
+for i = 1, 5 do s:replace{i, i} end
+
+errinj.set("ERRINJ_VY_RUN_WRITE_DELAY", true)
+ch = fiber.channel(1)
+_ = fiber.create(function() s:create_index('sk', {parts = {2, 'integer'}}) ch:put(true) end)
+
+fiber.sleep(0.01)
+
+_ = s:delete{1}
+_ = s:replace{2, -2}
+_ = s:delete{2}
+_ = s:replace{3, -3}
+_ = s:replace{3, -2}
+_ = s:replace{3, -1}
+_ = s:delete{3}
+_ = s:upsert({3, 3}, {{'=', 2, 1}})
+_ = s:upsert({3, 3}, {{'=', 2, 2}})
+_ = s:delete{3}
+_ = s:replace{4, -1}
+_ = s:replace{4, -2}
+_ = s:replace{4, -4}
+_ = s:upsert({5, 1}, {{'=', 2, 1}})
+_ = s:upsert({5, 2}, {{'=', 2, -5}})
+_ = s:replace{6, -6}
+_ = s:upsert({7, -7}, {{'=', 2, -7}})
+
+errinj.set("ERRINJ_VY_RUN_WRITE_DELAY", false)
+ch:get()
+
+s.index.sk:select()
+s.index.sk:stat().memory.rows
+
+test_run:cmd('restart server default')
+
+fiber = require('fiber')
+errinj = box.error.injection
+
+s = box.space.test
+
+s.index.sk:select()
+s.index.sk:stat().memory.rows
+
+box.snapshot()
+
+s.index.sk:select()
+s.index.sk:stat().memory.rows
+
+s:drop()
+
+--
+-- gh-3458: check that rw transactions that started before DDL are
+-- aborted.
+--
+vinyl_cache = box.cfg.vinyl_cache
+box.cfg{vinyl_cache = 0}
+
+s1 = box.schema.space.create('test1', {engine = 'vinyl'})
+_ = s1:create_index('pk', {page_size = 16})
+s2 = box.schema.space.create('test2', {engine = 'vinyl'})
+_ = s2:create_index('pk')
+
+pad = string.rep('x', 16)
+for i = 101, 200 do s1:replace{i, i, pad} end
+box.snapshot()
+
+test_run:cmd("setopt delimiter ';'")
+function async_replace(space, tuple, timeout)
+ local c = fiber.channel(1)
+ fiber.create(function()
+ box.begin()
+ space:replace(tuple)
+ fiber.sleep(timeout)
+ local status = pcall(box.commit)
+ c:put(status)
+ end)
+ return c
+end;
+test_run:cmd("setopt delimiter ''");
+
+c1 = async_replace(s1, {1}, 0.01)
+c2 = async_replace(s2, {1}, 0.01)
+
+errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0.001)
+s1:format{{'key', 'unsigned'}, {'value', 'unsigned'}}
+errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0)
+
+c1:get() -- false (transaction was aborted)
+c2:get() -- true
+
+s1:get(1) == nil
+s2:get(1) ~= nil
+s1:format()
+s1:format{}
+
+c1 = async_replace(s1, {2}, 0.01)
+c2 = async_replace(s2, {2}, 0.01)
+
+errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0.001)
+_ = s1:create_index('sk', {parts = {2, 'unsigned'}})
+errinj.set("ERRINJ_VY_READ_PAGE_TIMEOUT", 0)
+
+c1:get() -- false (transaction was aborted)
+c2:get() -- true
+
+s1:get(2) == nil
+s2:get(2) ~= nil
+s1.index.pk:count() == s1.index.sk:count()
+
+s1:drop()
+s2:drop()
+box.cfg{vinyl_cache = vinyl_cache}
+
+-- Transactions that reached WAL must not be aborted.
+s = box.schema.space.create('test', {engine = 'vinyl'})
+_ = s:create_index('pk')
+
+errinj.set('ERRINJ_WAL_DELAY', true)
+_ = fiber.create(function() s:replace{1} end)
+_ = fiber.create(function() fiber.sleep(0.01) errinj.set('ERRINJ_WAL_DELAY', false) end)
+
+fiber.sleep(0)
+s:format{{'key', 'unsigned'}, {'value', 'unsigned'}} -- must fail
+s:select()
+s:truncate()
+
+errinj.set('ERRINJ_WAL_DELAY', true)
+_ = fiber.create(function() s:replace{1} end)
+_ = fiber.create(function() fiber.sleep(0.01) errinj.set('ERRINJ_WAL_DELAY', false) end)
+
+fiber.sleep(0)
+s:create_index('sk', {parts = {2, 'unsigned'}})
+s:select()
+s:drop()
diff --git a/test/vinyl/errinj_stat.result b/test/vinyl/errinj_stat.result
new file mode 100644
index 00000000..fac56cee
--- /dev/null
+++ b/test/vinyl/errinj_stat.result
@@ -0,0 +1,152 @@
+test_run = require('test_run').new()
+---
+...
+-- Since we store LSNs in data files, the data size may differ
+-- from run to run. Deploy a new server to make sure it will be
+-- the same so that we can check it.
+test_run:cmd('create server test with script = "vinyl/stat.lua"')
+---
+- true
+...
+test_run:cmd('start server test')
+---
+- true
+...
+test_run:cmd('switch test')
+---
+- true
+...
+-- Compressed data size depends on the zstd version so let's
+-- filter it out.
+test_run:cmd("push filter 'bytes_compressed: .*' to 'bytes_compressed: <bytes_compressed>'")
+---
+- true
+...
+fiber = require('fiber')
+---
+...
+errinj = box.error.injection
+---
+...
+--
+-- Check disk.compact.queue stat.
+--
+s = box.schema.space.create('test', {engine = 'vinyl'})
+---
+...
+i = s:create_index('pk', {run_count_per_level = 2})
+---
+...
+function dump() for i = 1, 10 do s:replace{i} end box.snapshot() end
+---
+...
+dump()
+---
+...
+i:stat().disk.compact.queue -- none
+---
+- bytes_compressed: <bytes_compressed>
+ pages: 0
+ rows: 0
+ bytes: 0
+...
+i:stat().disk.compact.queue.bytes == box.stat.vinyl().disk.compact.queue
+---
+- true
+...
+errinj.set('ERRINJ_VY_COMPACTION_DELAY', true)
+---
+- ok
+...
+dump()
+---
+...
+dump()
+---
+...
+i:stat().disk.compact.queue -- 30 statements
+---
+- bytes_compressed: <bytes_compressed>
+ pages: 3
+ rows: 30
+ bytes: 411
+...
+i:stat().disk.compact.queue.bytes == box.stat.vinyl().disk.compact.queue
+---
+- true
+...
+dump()
+---
+...
+i:stat().disk.compact.queue -- 40 statements
+---
+- bytes_compressed: <bytes_compressed>
+ pages: 4
+ rows: 40
+ bytes: 548
+...
+i:stat().disk.compact.queue.bytes == box.stat.vinyl().disk.compact.queue
+---
+- true
+...
+dump()
+---
+...
+i:stat().disk.compact.queue -- 50 statements
+---
+- bytes_compressed: <bytes_compressed>
+ pages: 5
+ rows: 50
+ bytes: 685
+...
+i:stat().disk.compact.queue.bytes == box.stat.vinyl().disk.compact.queue
+---
+- true
+...
+box.stat.reset() -- doesn't affect queue size
+---
+...
+i:stat().disk.compact.queue -- 50 statements
+---
+- bytes_compressed: <bytes_compressed>
+ pages: 5
+ rows: 50
+ bytes: 685
+...
+i:stat().disk.compact.queue.bytes == box.stat.vinyl().disk.compact.queue
+---
+- true
+...
+errinj.set('ERRINJ_VY_COMPACTION_DELAY', false)
+---
+- ok
+...
+while i:stat().disk.compact.count < 2 do fiber.sleep(0.01) end
+---
+...
+i:stat().disk.compact.queue -- none
+---
+- bytes_compressed: <bytes_compressed>
+ pages: 0
+ rows: 0
+ bytes: 0
+...
+s:drop()
+---
+...
+test_run:cmd("clear filter")
+---
+- true
+...
+test_run:cmd('switch default')
+---
+- true
+...
+test_run:cmd('stop server test')
+---
+- true
+...
+test_run:cmd('cleanup server test')
+---
+- true
+...
diff --git a/test/vinyl/errinj_stat.test.lua b/test/vinyl/errinj_stat.test.lua
new file mode 100644
index 00000000..1e0e63ae
--- /dev/null
+++ b/test/vinyl/errinj_stat.test.lua
@@ -0,0 +1,48 @@
+test_run = require('test_run').new()
+
+-- Since we store LSNs in data files, the data size may differ
+-- from run to run. Deploy a new server to make sure it will be
+-- the same so that we can check it.
+test_run:cmd('create server test with script = "vinyl/stat.lua"')
+test_run:cmd('start server test')
+test_run:cmd('switch test')
+
+-- Compressed data size depends on the zstd version so let's
+-- filter it out.
+test_run:cmd("push filter 'bytes_compressed: .*' to 'bytes_compressed: <bytes_compressed>'")
+
+fiber = require('fiber')
+errinj = box.error.injection
+
+--
+-- Check disk.compact.queue stat.
+--
+s = box.schema.space.create('test', {engine = 'vinyl'})
+i = s:create_index('pk', {run_count_per_level = 2})
+function dump() for i = 1, 10 do s:replace{i} end box.snapshot() end
+dump()
+i:stat().disk.compact.queue -- none
+i:stat().disk.compact.queue.bytes == box.stat.vinyl().disk.compact.queue
+errinj.set('ERRINJ_VY_COMPACTION_DELAY', true)
+dump()
+dump()
+i:stat().disk.compact.queue -- 30 statements
+i:stat().disk.compact.queue.bytes == box.stat.vinyl().disk.compact.queue
+dump()
+i:stat().disk.compact.queue -- 40 statements
+i:stat().disk.compact.queue.bytes == box.stat.vinyl().disk.compact.queue
+dump()
+i:stat().disk.compact.queue -- 50 statements
+i:stat().disk.compact.queue.bytes == box.stat.vinyl().disk.compact.queue
+box.stat.reset() -- doesn't affect queue size
+i:stat().disk.compact.queue -- 50 statements
+i:stat().disk.compact.queue.bytes == box.stat.vinyl().disk.compact.queue
+errinj.set('ERRINJ_VY_COMPACTION_DELAY', false)
+while i:stat().disk.compact.count < 2 do fiber.sleep(0.01) end
+i:stat().disk.compact.queue -- none
+s:drop()
+
+test_run:cmd("clear filter")
+test_run:cmd('switch default')
+test_run:cmd('stop server test')
+test_run:cmd('cleanup server test')
diff --git a/test/vinyl/errinj_tx.result b/test/vinyl/errinj_tx.result
new file mode 100644
index 00000000..a7583b2e
--- /dev/null
+++ b/test/vinyl/errinj_tx.result
@@ -0,0 +1,435 @@
+test_run = require('test_run').new()
+---
+...
+fiber = require('fiber')
+---
+...
+txn_proxy = require('txn_proxy')
+---
+...
+create_iterator = require('utils').create_iterator
+---
+...
+errinj = box.error.injection
+---
+...
+--
+-- gh-1681: vinyl: crash in vy_rollback on ER_WAL_WRITE
+--
+s = box.schema.space.create('test', {engine='vinyl'})
+---
+...
+_ = s:create_index('pk')
+---
+...
+function f() box.begin() s:insert{1, 'hi'} s:insert{2, 'bye'} box.commit() end
+---
+...
+errinj.set("ERRINJ_WAL_WRITE", true)
+---
+- ok
+...
+f()
+---
+- error: Failed to write to disk
+...
+s:select{}
+---
+- []
+...
+errinj.set("ERRINJ_WAL_WRITE", false)
+---
+- ok
+...
+f()
+---
+...
+s:select{}
+---
+- - [1, 'hi']
+ - [2, 'bye']
+...
+s:drop()
+---
+...
+--https://github.com/tarantool/tarantool/issues/1842
+--test error injection
+s = box.schema.space.create('test', {engine='vinyl'})
+---
+...
+_ = s:create_index('pk')
+---
+...
+s:replace{0, 0}
+---
+- [0, 0]
+...
+s:replace{1, 0}
+---
+- [1, 0]
+...
+s:replace{2, 0}
+---
+- [2, 0]
+...
+errinj.set("ERRINJ_WAL_WRITE", true)
+---
+- ok
+...
+s:replace{3, 0}
+---
+- error: Failed to write to disk
+...
+s:replace{4, 0}
+---
+- error: Failed to write to disk
+...
+s:replace{5, 0}
+---
+- error: Failed to write to disk
+...
+s:replace{6, 0}
+---
+- error: Failed to write to disk
+...
+errinj.set("ERRINJ_WAL_WRITE", false)
+---
+- ok
+...
+s:replace{7, 0}
+---
+- [7, 0]
+...
+s:replace{8, 0}
+---
+- [8, 0]
+...
+s:select{}
+---
+- - [0, 0]
+ - [1, 0]
+ - [2, 0]
+ - [7, 0]
+ - [8, 0]
+...
+s:drop()
+---
+...
+--iterator test
+test_run:cmd("setopt delimiter ';'")
+---
+- true
+...
+fiber_status = 0
+
+function fiber_func()
+ box.begin()
+ s:replace{5, 5}
+ fiber_status = 1
+ local res = {pcall(box.commit) }
+ fiber_status = 2
+ return unpack(res)
+end;
+---
+...
+test_run:cmd("setopt delimiter ''");
+---
+- true
+...
+s = box.schema.space.create('test', {engine='vinyl'})
+---
+...
+_ = s:create_index('pk')
+---
+...
+_ = s:replace{0, 0}
+---
+...
+_ = s:replace{10, 0}
+---
+...
+_ = s:replace{20, 0}
+---
+...
+test_run:cmd("setopt delimiter ';'");
+---
+- true
+...
+faced_trash = false
+for i = 1,100 do
+ errinj.set("ERRINJ_WAL_WRITE", true)
+ local f = fiber.create(fiber_func)
+ local itr = create_iterator(s, {0}, {iterator='GE'})
+ local first = itr.next()
+ local second = itr.next()
+ if (second[1] ~= 5 and second[1] ~= 10) then faced_trash = true end
+ while fiber_status <= 1 do fiber.sleep(0.001) end
+ local _,next = pcall(itr.next)
+ _,next = pcall(itr.next)
+ _,next = pcall(itr.next)
+ errinj.set("ERRINJ_WAL_WRITE", false)
+ s:delete{5}
+end;
+---
+...
+test_run:cmd("setopt delimiter ''");
+---
+- true
+...
+faced_trash
+---
+- false
+...
+s:drop()
+---
+...
+-- TX in prepared but not committed state
+s = box.schema.space.create('test', {engine='vinyl'})
+---
+...
+_ = s:create_index('pk')
+---
+...
+s:replace{1, "original"}
+---
+- [1, 'original']
+...
+s:replace{2, "original"}
+---
+- [2, 'original']
+...
+s:replace{3, "original"}
+---
+- [3, 'original']
+...
+c0 = txn_proxy.new()
+---
+...
+c0:begin()
+---
+-
+...
+c1 = txn_proxy.new()
+---
+...
+c1:begin()
+---
+-
+...
+c2 = txn_proxy.new()
+---
+...
+c2:begin()
+---
+-
+...
+c3 = txn_proxy.new()
+---
+...
+c3:begin()
+---
+-
+...
+--
+-- Prepared transactions
+--
+-- Pause WAL writer to cause all further calls to box.commit() to move
+-- transactions into prepared, but not committed yet state.
+errinj.set("ERRINJ_WAL_DELAY", true)
+---
+- ok
+...
+lsn = box.info.lsn
+---
+...
+c0('s:replace{1, "c0"}')
+---
+- - [1, 'c0']
+...
+c0('s:replace{2, "c0"}')
+---
+- - [2, 'c0']
+...
+c0('s:replace{3, "c0"}')
+---
+- - [3, 'c0']
+...
+_ = fiber.create(c0.commit, c0)
+---
+...
+box.info.lsn == lsn
+---
+- true
+...
+c1('s:replace{1, "c1"}')
+---
+- - [1, 'c1']
+...
+c1('s:replace{2, "c1"}')
+---
+- - [2, 'c1']
+...
+_ = fiber.create(c1.commit, c1)
+---
+...
+box.info.lsn == lsn
+---
+- true
+...
+c3('s:select{1}') -- c1 is visible
+---
+- - [[1, 'c1']]
+...
+c2('s:replace{1, "c2"}')
+---
+- - [1, 'c2']
+...
+c2('s:replace{3, "c2"}')
+---
+- - [3, 'c2']
+...
+_ = fiber.create(c2.commit, c2)
+---
+...
+box.info.lsn == lsn
+---
+- true
+...
+c3('s:select{1}') -- c1 is visible, c2 is not
+---
+- - [[1, 'c1']]
+...
+c3('s:select{2}') -- c1 is visible
+---
+- - [[2, 'c1']]
+...
+c3('s:select{3}') -- c2 is not visible
+---
+- - [[3, 'c0']]
+...
+-- Resume WAL writer and wait until all transactions will been committed
+errinj.set("ERRINJ_WAL_DELAY", false)
+---
+- ok
+...
+REQ_COUNT = 7
+---
+...
+while box.info.lsn - lsn < REQ_COUNT do fiber.sleep(0.01) end
+---
+...
+box.info.lsn == lsn + REQ_COUNT
+---
+- true
+...
+c3('s:select{1}') -- c1 is visible, c2 is not
+---
+- - [[1, 'c1']]
+...
+c3('s:select{2}') -- c1 is visible
+---
+- - [[2, 'c1']]
+...
+c3('s:select{3}') -- c2 is not visible
+---
+- - [[3, 'c0']]
+...
+c3:commit()
+---
+-
+...
+s:drop()
+---
+...
+--
+-- Test mem restoration on a prepared and not commited statement
+-- after moving iterator into read view.
+--
+space = box.schema.space.create('test', {engine = 'vinyl'})
+---
+...
+pk = space:create_index('pk')
+---
+...
+space:replace{1}
+---
+- [1]
+...
+space:replace{2}
+---
+- [2]
+...
+space:replace{3}
+---
+- [3]
+...
+last_read = nil
+---
+...
+errinj.set("ERRINJ_WAL_DELAY", true)
+---
+- ok
+...
+test_run:cmd("setopt delimiter ';'")
+---
+- true
+...
+-- block until wal_delay = false
+-- send iterator to read view
+-- flush mem and update index version to trigger iterator restore
+function fill_space()
+ box.begin()
+ space:replace{1}
+ space:replace{2}
+ space:replace{3}
+ box.commit()
+ space:replace{1, 1}
+ box.snapshot()
+end;
+---
+...
+function iterate_in_read_view()
+ local i = create_iterator(space)
+ last_read = i.next()
+ fiber.sleep(100000)
+ last_read = i.next()
+end;
+---
+...
+test_run:cmd("setopt delimiter ''");
+---
+- true
+...
+f1 = fiber.create(fill_space)
+---
+...
+-- Prepared transaction is blocked due to wal_delay.
+-- Start iterator with vlsn = INT64_MAX
+f2 = fiber.create(iterate_in_read_view)
+---
+...
+last_read
+---
+- [1]
+...
+-- Finish prepared transaction and send to read view the iterator.
+errinj.set("ERRINJ_WAL_DELAY", false)
+---
+- ok
+...
+while f1:status() ~= 'dead' do fiber.sleep(0.01) end
+---
+...
+f2:wakeup()
+---
+...
+while f2:status() ~= 'dead' do fiber.sleep(0.01) end
+---
+...
+last_read
+---
+- [2]
+...
+space:drop()
+---
+...
diff --git a/test/vinyl/errinj_tx.test.lua b/test/vinyl/errinj_tx.test.lua
new file mode 100644
index 00000000..cf83b326
--- /dev/null
+++ b/test/vinyl/errinj_tx.test.lua
@@ -0,0 +1,194 @@
+test_run = require('test_run').new()
+fiber = require('fiber')
+txn_proxy = require('txn_proxy')
+create_iterator = require('utils').create_iterator
+errinj = box.error.injection
+
+--
+-- gh-1681: vinyl: crash in vy_rollback on ER_WAL_WRITE
+--
+s = box.schema.space.create('test', {engine='vinyl'})
+_ = s:create_index('pk')
+function f() box.begin() s:insert{1, 'hi'} s:insert{2, 'bye'} box.commit() end
+errinj.set("ERRINJ_WAL_WRITE", true)
+f()
+s:select{}
+errinj.set("ERRINJ_WAL_WRITE", false)
+f()
+s:select{}
+s:drop()
+
+--https://github.com/tarantool/tarantool/issues/1842
+--test error injection
+s = box.schema.space.create('test', {engine='vinyl'})
+_ = s:create_index('pk')
+s:replace{0, 0}
+
+s:replace{1, 0}
+s:replace{2, 0}
+errinj.set("ERRINJ_WAL_WRITE", true)
+s:replace{3, 0}
+s:replace{4, 0}
+s:replace{5, 0}
+s:replace{6, 0}
+errinj.set("ERRINJ_WAL_WRITE", false)
+s:replace{7, 0}
+s:replace{8, 0}
+s:select{}
+
+s:drop()
+
+--iterator test
+test_run:cmd("setopt delimiter ';'")
+
+fiber_status = 0
+
+function fiber_func()
+ box.begin()
+ s:replace{5, 5}
+ fiber_status = 1
+ local res = {pcall(box.commit) }
+ fiber_status = 2
+ return unpack(res)
+end;
+
+test_run:cmd("setopt delimiter ''");
+
+s = box.schema.space.create('test', {engine='vinyl'})
+_ = s:create_index('pk')
+
+_ = s:replace{0, 0}
+_ = s:replace{10, 0}
+_ = s:replace{20, 0}
+
+test_run:cmd("setopt delimiter ';'");
+
+faced_trash = false
+for i = 1,100 do
+ errinj.set("ERRINJ_WAL_WRITE", true)
+ local f = fiber.create(fiber_func)
+ local itr = create_iterator(s, {0}, {iterator='GE'})
+ local first = itr.next()
+ local second = itr.next()
+ if (second[1] ~= 5 and second[1] ~= 10) then faced_trash = true end
+ while fiber_status <= 1 do fiber.sleep(0.001) end
+ local _,next = pcall(itr.next)
+ _,next = pcall(itr.next)
+ _,next = pcall(itr.next)
+ errinj.set("ERRINJ_WAL_WRITE", false)
+ s:delete{5}
+end;
+
+test_run:cmd("setopt delimiter ''");
+
+faced_trash
+
+s:drop()
+
+-- TX in prepared but not committed state
+s = box.schema.space.create('test', {engine='vinyl'})
+_ = s:create_index('pk')
+
+s:replace{1, "original"}
+s:replace{2, "original"}
+s:replace{3, "original"}
+
+c0 = txn_proxy.new()
+c0:begin()
+c1 = txn_proxy.new()
+c1:begin()
+c2 = txn_proxy.new()
+c2:begin()
+c3 = txn_proxy.new()
+c3:begin()
+
+--
+-- Prepared transactions
+--
+
+-- Pause WAL writer to cause all further calls to box.commit() to move
+-- transactions into prepared, but not committed yet state.
+errinj.set("ERRINJ_WAL_DELAY", true)
+lsn = box.info.lsn
+c0('s:replace{1, "c0"}')
+c0('s:replace{2, "c0"}')
+c0('s:replace{3, "c0"}')
+_ = fiber.create(c0.commit, c0)
+box.info.lsn == lsn
+c1('s:replace{1, "c1"}')
+c1('s:replace{2, "c1"}')
+_ = fiber.create(c1.commit, c1)
+box.info.lsn == lsn
+c3('s:select{1}') -- c1 is visible
+c2('s:replace{1, "c2"}')
+c2('s:replace{3, "c2"}')
+_ = fiber.create(c2.commit, c2)
+box.info.lsn == lsn
+c3('s:select{1}') -- c1 is visible, c2 is not
+c3('s:select{2}') -- c1 is visible
+c3('s:select{3}') -- c2 is not visible
+
+-- Resume WAL writer and wait until all transactions will been committed
+errinj.set("ERRINJ_WAL_DELAY", false)
+REQ_COUNT = 7
+while box.info.lsn - lsn < REQ_COUNT do fiber.sleep(0.01) end
+box.info.lsn == lsn + REQ_COUNT
+
+c3('s:select{1}') -- c1 is visible, c2 is not
+c3('s:select{2}') -- c1 is visible
+c3('s:select{3}') -- c2 is not visible
+c3:commit()
+
+s:drop()
+
+--
+-- Test mem restoration on a prepared and not commited statement
+-- after moving iterator into read view.
+--
+space = box.schema.space.create('test', {engine = 'vinyl'})
+pk = space:create_index('pk')
+space:replace{1}
+space:replace{2}
+space:replace{3}
+
+last_read = nil
+
+errinj.set("ERRINJ_WAL_DELAY", true)
+
+test_run:cmd("setopt delimiter ';'")
+
+function fill_space()
+ box.begin()
+ space:replace{1}
+ space:replace{2}
+ space:replace{3}
+-- block until wal_delay = false
+ box.commit()
+-- send iterator to read view
+ space:replace{1, 1}
+-- flush mem and update index version to trigger iterator restore
+ box.snapshot()
+end;
+
+function iterate_in_read_view()
+ local i = create_iterator(space)
+ last_read = i.next()
+ fiber.sleep(100000)
+ last_read = i.next()
+end;
+
+test_run:cmd("setopt delimiter ''");
+
+f1 = fiber.create(fill_space)
+-- Prepared transaction is blocked due to wal_delay.
+-- Start iterator with vlsn = INT64_MAX
+f2 = fiber.create(iterate_in_read_view)
+last_read
+-- Finish prepared transaction and send to read view the iterator.
+errinj.set("ERRINJ_WAL_DELAY", false)
+while f1:status() ~= 'dead' do fiber.sleep(0.01) end
+f2:wakeup()
+while f2:status() ~= 'dead' do fiber.sleep(0.01) end
+last_read
+
+space:drop()
diff --git a/test/vinyl/errinj_vylog.result b/test/vinyl/errinj_vylog.result
index 9ced03df..0e3b79c4 100644
--- a/test/vinyl/errinj_vylog.result
+++ b/test/vinyl/errinj_vylog.result
@@ -5,6 +5,41 @@ fiber = require('fiber')
---
...
--
+-- Check that DDL operations are logged in vylog only after successful
+-- WAL write.
+--
+-- If we logged an index creation in the metadata log before WAL write,
+-- WAL failure would result in leaving the index record in vylog forever.
+-- Since we use LSN to identify indexes in vylog, retrying index creation
+-- would then lead to a duplicate index id in vylog and hence inability
+-- to make a snapshot or recover.
+--
+s = box.schema.space.create('test', {engine = 'vinyl'})
+---
+...
+box.error.injection.set('ERRINJ_WAL_IO', true)
+---
+- ok
+...
+_ = s:create_index('pk')
+---
+- error: Failed to write to disk
+...
+box.error.injection.set('ERRINJ_WAL_IO', false)
+---
+- ok
+...
+_ = s:create_index('pk')
+---
+...
+box.snapshot()
+---
+- ok
+...
+s:drop()
+---
+...
+--
-- Check that an error to commit a new run to vylog does not
-- break vinyl permanently.
--
diff --git a/test/vinyl/errinj_vylog.test.lua b/test/vinyl/errinj_vylog.test.lua
index c1fd517d..ce9e12e5 100644
--- a/test/vinyl/errinj_vylog.test.lua
+++ b/test/vinyl/errinj_vylog.test.lua
@@ -2,6 +2,24 @@ test_run = require('test_run').new()
fiber = require('fiber')
--
+-- Check that DDL operations are logged in vylog only after successful
+-- WAL write.
+--
+-- If we logged an index creation in the metadata log before WAL write,
+-- WAL failure would result in leaving the index record in vylog forever.
+-- Since we use LSN to identify indexes in vylog, retrying index creation
+-- would then lead to a duplicate index id in vylog and hence inability
+-- to make a snapshot or recover.
+--
+s = box.schema.space.create('test', {engine = 'vinyl'})
+box.error.injection.set('ERRINJ_WAL_IO', true)
+_ = s:create_index('pk')
+box.error.injection.set('ERRINJ_WAL_IO', false)
+_ = s:create_index('pk')
+box.snapshot()
+s:drop()
+
+--
-- Check that an error to commit a new run to vylog does not
-- break vinyl permanently.
--
diff --git a/test/vinyl/suite.ini b/test/vinyl/suite.ini
index ace53e7f..d2a194d8 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_gc.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
config = suite.cfg
lua_libs = suite.lua stress.lua large.lua txn_proxy.lua ../box/lua/utils.lua
use_unix_sockets = True
--
2.11.0
More information about the Tarantool-patches
mailing list