Skip to content

Assertion `has_optional_parts || (field_a != NULL && field_b != NULL)' failed. #10081

@ligurio

Description

@ligurio

Bug description

  • OS: Linux
  • OS Version: 22.04
  • Architecture: amd64

Tarantool 3.2.0-entrypoint-56-g05d03a1c58
Target: Linux-x86_64-Debug
Build options: cmake . -DCMAKE_INSTALL_PREFIX=/usr/local -DENABLE_BACKTRACE=TRUE
Compiler: GNU-11.4.0
C_FLAGS: -fexceptions -funwind-tables -fasynchronous-unwind-tables -fno-common -msse2 -Wformat -Wformat-security -Werror=format-security -fstack-protector-strong -fPIC -fmacro-prefix-map=/home/sergeyb/sources/MRG/tarantool=. -std=c11 -Wall -Wextra -Wno-gnu-alignof-expression -fno-gnu89-inline -Wno-cast-function-type -Werror -g -ggdb -O0
CXX_FLAGS: -fexceptions -funwind-tables -fasynchronous-unwind-tables -fno-common -msse2 -Wformat -Wformat-security -Werror=format-security -fstack-protector-strong -fPIC -fmacro-prefix-map=/home/sergeyb/sources/MRG/tarantool=. -std=c++11 -Wall -Wextra -Wno-invalid-offsetof -Wno-gnu-alignof-expression -Wno-cast-function-type -Werror -g -ggdb -O0

Steps to reproduce

Build Tarantool with enabled debug option:

$ cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
$ cmake --build build/ --parallel

Execute Lua script vinyl.lua.

vinyl.lua
--[[ Тест для vinyl позволяет случайным образом выставлять error injections,
генерировать операции с индексами, генерировать операции с данными, настройки
vinyl в box.cfg. Все случайные операции и настройки зависят от seed, который
генерируется в самом начале теста.

Usage: taskset 0xef ./tarantool vinyl.lua
]]

local TEST_DURATION = 30*60 -- Seconds.
local NUM_SP = 70
local NUM_TUPLES = 1000
local SEED = 10000
local N_OPS_IN_TX = 10
local MAX_KEY = 10000

--

local fiber = require('fiber')
local log = require('log')
local math = require('math')
local json = require('json')

local decimal = require('decimal')
local uuid = require('uuid')

local seed = SEED or os.time()
seed = seed or math.randomseed(seed)
log.info(string.format("RANDOM SEED %d", seed))

local function trace(event, line) -- luacheck: no unused
    local s = debug.getinfo(2).short_src
    if s == 'vinyl.lua' then
        log.info(s .. ":" .. line)
    end
end

-- https://www.lua.org/pil/23.2.html
if os.getenv('DEV') then
    debug.sethook(trace, "l")
end

local function dict_keys(t)
    assert(next(t) ~= nil)
    local keys = {}
    for k, _ in pairs(t) do
        table.insert(keys, k)
    end
	return keys
end

-- local function rand_char()
--     return string.char(math.random(97, 97 + 25))
-- end

local function random_elem(t)
    assert(type(t) == 'table')
    assert(next(t) ~= nil)

    local n = table.getn(t)
    local idx = math.random(1, n)
    return t[idx]
end

-- TODO: Obtain error injections dynamically:
-- box.error.injection.info()
-- box.error.injection.get('ERRINJ_SNAP_SKIP_ALL_ROWS')
-- box.error.injection.set('ERRINJ_SNAP_SKIP_ALL_ROWS', true)
-- Source: src/lib/core/errinj.h
local errinj_set = {
    ['ERRINJ_APPLIER_DESTROY_DELAY'] = 'boolean',
    ['ERRINJ_APPLIER_READ_TX_ROW_DELAY'] = 'boolean',
    ['ERRINJ_APPLIER_SLOW_ACK'] = 'boolean',
    ['ERRINJ_APPLIER_STOP_DELAY'] = 'boolean',
    ['ERRINJ_BUILD_INDEX'] = 'int',
    ['ERRINJ_BUILD_INDEX_DELAY'] = 'boolean',
    ['ERRINJ_BUILD_INDEX_ON_ROLLBACK_ALLOC'] = 'boolean',
    ['ERRINJ_BUILD_INDEX_TIMEOUT'] = 'double',
    ['ERRINJ_CHECK_FORMAT_DELAY'] = 'boolean',
    ['ERRINJ_COIO_SENDFILE_CHUNK'] = 'int',
    ['ERRINJ_COIO_WRITE_CHUNK'] = 'boolean',
    ['ERRINJ_DYN_MODULE_COUNT'] = 'int',
    ['ERRINJ_ENGINE_JOIN_DELAY'] = 'boolean',
    ['ERRINJ_FIBER_MADVISE'] = 'boolean',
    ['ERRINJ_FIBER_MPROTECT'] = 'int',
    ['ERRINJ_FLIGHTREC_RECREATE_RENAME'] = 'boolean',
    ['ERRINJ_FLIGHTREC_LOG_DELAY'] = 'double',
    ['ERRINJ_HTTPC_EXECUTE'] = 'boolean',
    ['ERRINJ_HTTP_RESPONSE_ADD_WAIT'] = 'boolean',
    ['ERRINJ_INDEX_ALLOC'] = 'boolean',
    ['ERRINJ_INDEX_RESERVE'] = 'boolean',
    ['ERRINJ_INDEX_ITERATOR_NEW'] = 'boolean',
    ['ERRINJ_HASH_INDEX_REPLACE'] = 'boolean',
    ['ERRINJ_IPROTO_CFG_LISTEN'] = 'boolean',
    ['ERRINJ_IPROTO_DISABLE_ID'] = 'boolean',
    ['ERRINJ_IPROTO_DISABLE_WATCH'] = 'boolean',
    ['ERRINJ_IPROTO_FLIP_FEATURE'] = 'int',
    ['ERRINJ_IPROTO_SET_VERSION'] = 'int',
    ['ERRINJ_IPROTO_TX_DELAY'] = 'boolean',
    ['ERRINJ_IPROTO_WRITE_ERROR_DELAY'] = 'boolean',
    ['ERRINJ_LOG_ROTATE'] = 'boolean',
    ['ERRINJ_MEMTX_DELAY_GC'] = 'boolean',
    ['ERRINJ_NETBOX_DISABLE_ID'] = 'boolean',
    ['ERRINJ_NETBOX_FLIP_FEATURE'] = 'int',
    ['ERRINJ_NETBOX_IO_DELAY'] = 'boolean',
    ['ERRINJ_NETBOX_IO_ERROR'] = 'boolean',
    ['ERRINJ_RAFT_WAIT_TERM_PERSISTED_DELAY'] = 'boolean',
    ['ERRINJ_RELAY_BREAK_LSN'] = 'int',
    ['ERRINJ_RELAY_EXIT_DELAY'] = 'double',
    ['ERRINJ_RELAY_FASTER_THAN_TX'] = 'boolean',
    ['ERRINJ_RELAY_FINAL_JOIN'] = 'boolean',
    ['ERRINJ_RELAY_FINAL_SLEEP'] = 'boolean',
    ['ERRINJ_RELAY_FROM_TX_DELAY'] = 'boolean',
    ['ERRINJ_RELAY_REPORT_INTERVAL'] = 'double',
    ['ERRINJ_RELAY_SEND_DELAY'] = 'boolean',
    ['ERRINJ_RELAY_TIMEOUT'] = 'double',
    ['ERRINJ_RELAY_WAL_START_DELAY'] = 'boolean',
    ['ERRINJ_REPLICASET_VCLOCK'] = 'boolean',
    ['ERRINJ_REPLICA_JOIN_DELAY'] = 'boolean',
    ['ERRINJ_SIGILL_MAIN_THREAD'] = 'boolean',
    ['ERRINJ_SIGILL_NONMAIN_THREAD'] = 'boolean',
    ['ERRINJ_SIO_READ_MAX'] = 'int',
    ['ERRINJ_SNAP_COMMIT_DELAY'] = 'boolean',
    ['ERRINJ_SNAP_COMMIT_FAIL'] = 'boolean',
    ['ERRINJ_SNAP_SKIP_ALL_ROWS'] = 'boolean',
    ['ERRINJ_SNAP_SKIP_DDL_ROWS'] = 'boolean',
    ['ERRINJ_SNAP_WRITE_DELAY'] = 'boolean',
    ['ERRINJ_SNAP_WRITE_CORRUPTED_INSERT_ROW'] = 'boolean',
    ['ERRINJ_SNAP_WRITE_INVALID_SYSTEM_ROW'] = 'boolean',
    ['ERRINJ_SNAP_WRITE_MISSING_SPACE_ROW'] = 'boolean',
    ['ERRINJ_SNAP_WRITE_TIMEOUT'] = 'double',
    ['ERRINJ_SNAP_WRITE_UNKNOWN_ROW_TYPE'] = 'boolean',
    ['ERRINJ_SPACE_UPGRADE_DELAY'] = 'boolean',
    ['ERRINJ_SWIM_FD_ONLY'] = 'boolean',
    ['ERRINJ_TESTING'] = 'boolean',
    ['ERRINJ_TUPLE_ALLOC'] = 'boolean',
    ['ERRINJ_TUPLE_FIELD'] = 'boolean',
    -- https://github.com/tarantool/tarantool/issues/10033
    -- ['ERRINJ_TUPLE_FIELD_COUNT_LIMIT'] = 'int',
    ['ERRINJ_TUPLE_FORMAT_COUNT'] = 'int',
    ['ERRINJ_TX_DELAY_PRIO_ENDPOINT'] = 'double',
    ['ERRINJ_TXN_COMMIT_ASYNC'] = 'boolean',
    ['ERRINJ_TXN_LIMBO_BEGIN_DELAY'] = 'boolean',
    ['ERRINJ_VYRUN_DATA_READ'] = 'boolean',
    ['ERRINJ_VY_COMPACTION_DELAY'] = 'boolean',
    ['ERRINJ_VY_DELAY_PK_LOOKUP'] = 'boolean',
    ['ERRINJ_VY_DUMP_DELAY'] = 'boolean',
    ['ERRINJ_VY_GC'] = 'boolean',
    ['ERRINJ_VY_INDEX_DUMP'] = 'int',
    ['ERRINJ_VY_INDEX_FILE_RENAME'] = 'boolean',
    ['ERRINJ_VY_LOG_FILE_RENAME'] = 'boolean',
    ['ERRINJ_VY_LOG_FLUSH'] = 'boolean',
    ['ERRINJ_VY_POINT_ITER_WAIT'] = 'boolean',
    ['ERRINJ_VY_QUOTA_DELAY'] = 'boolean',
    ['ERRINJ_VY_READ_PAGE'] = 'boolean',
    ['ERRINJ_VY_READ_PAGE_DELAY'] = 'boolean',
    ['ERRINJ_VY_READ_PAGE_TIMEOUT'] = 'double',
    ['ERRINJ_VY_READ_VIEW_MERGE_FAIL'] = 'boolean',
    ['ERRINJ_VY_RUN_DISCARD'] = 'boolean',
    ['ERRINJ_VY_RUN_FILE_RENAME'] = 'boolean',
    ['ERRINJ_VY_RUN_OPEN'] = 'int',
    ['ERRINJ_VY_RUN_WRITE'] = 'boolean',
    ['ERRINJ_VY_RUN_WRITE_DELAY'] = 'boolean',
    ['ERRINJ_VY_RUN_WRITE_STMT_TIMEOUT'] = 'double',
    ['ERRINJ_VY_SCHED_TIMEOUT'] = 'double',
    ['ERRINJ_VY_SQUASH_TIMEOUT'] = 'double',
    ['ERRINJ_VY_STMT_ALLOC'] = 'int',
    ['ERRINJ_VY_TASK_COMPLETE'] = 'boolean',
    ['ERRINJ_VY_WRITE_ITERATOR_START_FAIL'] = 'boolean',
    ['ERRINJ_WAIT_QUORUM_COUNT'] = 'int',
    ['ERRINJ_WAL_BREAK_LSN'] = 'int',
    ['ERRINJ_WAL_DELAY'] = 'boolean',
    ['ERRINJ_WAL_DELAY_COUNTDOWN'] = 'int',
    ['ERRINJ_WAL_FALLOCATE'] = 'int',
    ['ERRINJ_WAL_IO'] = 'boolean',
    ['ERRINJ_WAL_IO_COUNTDOWN'] = 'int',
    ['ERRINJ_WAL_ROTATE'] = 'boolean',
    ['ERRINJ_WAL_SYNC'] = 'boolean',
    ['ERRINJ_WAL_SYNC_DELAY'] = 'boolean',
    ['ERRINJ_WAL_WRITE'] = 'boolean',
    ['ERRINJ_WAL_WRITE_COUNT'] = 'int',
    ['ERRINJ_WAL_WRITE_DISK'] = 'boolean',
    ['ERRINJ_WAL_WRITE_EOF'] = 'boolean',
    ['ERRINJ_WAL_WRITE_PARTIAL'] = 'int',
    ['ERRINJ_XLOG_GARBAGE'] = 'boolean',
    ['ERRINJ_XLOG_META'] = 'boolean',
    ['ERRINJ_XLOG_READ'] = 'int',
    ['ERRINJ_XLOG_RENAME_DELAY'] = 'boolean',
    ['ERRINJ_XLOG_WRITE_CORRUPTED_BODY'] = 'boolean',
    ['ERRINJ_XLOG_WRITE_CORRUPTED_HEADER'] = 'boolean',
    ['ERRINJ_XLOG_WRITE_INVALID_BODY'] = 'boolean',
    ['ERRINJ_XLOG_WRITE_INVALID_HEADER'] = 'boolean',
    ['ERRINJ_XLOG_WRITE_INVALID_KEY'] = 'boolean',
    ['ERRINJ_XLOG_WRITE_INVALID_VALUE'] = 'boolean',
    ['ERRINJ_XLOG_WRITE_UNKNOWN_KEY'] = 'boolean',
    ['ERRINJ_XLOG_WRITE_UNKNOWN_TYPE'] = 'boolean',
}

-- Forward declaration.
local generate_dml
local generate_ddl
local index_create

local tx_op = {
    ['TX_COMMIT'] = function() box.rollback() end,
    ['TX_ROLLBACK'] = function() box.commit() end,
    ['TX_NOOP'] = function() end,
}

local function generate_tx(space)
    log.info("GENERATE_TX")
    if box.is_in_txn() then
        local tx_op_name = random_elem(dict_keys(tx_op))
	    local fn = tx_op[tx_op_name]
	    assert(type(fn) == 'function')
	    pcall(fn)
    else
        box.begin()
            for _ = 1, N_OPS_IN_TX do
                generate_dml(space)
                generate_ddl(space)
            end
        box.commit()
    end
end

-- Iterator types for TREE indexes.
-- https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_index/pairs/#box-index-iterator-types
local iter_type = {
    'ALL',
    'EQ',
    'GE',
    'GT',
    'LE',
    'LT',
    'REQ',
}

local function generate_select(space)
    local select_opts = {
        iterator = random_elem(iter_type),
        -- The maximum number of tuples.
        limit = math.random(100, 500),
        -- The number of tuples to skip.
        offset = math.random(100),
        -- A tuple or the position of a tuple (tuple_pos) after
        -- which select starts the search.
        after = box.NULL,
        -- If true, the select method returns the position of
        -- the last selected tuple as the second value.
        fetch_pos = random_elem({true, false}),
    }
    log.info(select_opts)
    local key = math.random(MAX_KEY)
    log.info('SELECT ' .. key)
    space:select(key, select_opts)
end

local function generate_delete(space)
    local key = math.random(MAX_KEY)
    log.info('DELETE ' .. key)
    space:delete(key)
end

local function generate_insert(space)
    local key = math.random(MAX_KEY)
    if space:get(key) ~= nil then
        return
    end
    log.info('INSERT ' .. key)
    pcall(space.insert, space, {
        key,
		math.random(MAX_KEY),
        math.random(MAX_KEY),
        math.random(MAX_KEY),
	})
end

local tuple_op = {
    '+', -- numeric.
    '-', -- numeric.
    '&', -- numeric.
    '|', -- numeric.
    '^', -- numeric.
    '!', -- for insertion of a new field.
    '#', -- for deletion.
    '=', -- for assignment.
    -- ':', for string splice.
}

local function generate_upsert(space)
    local tuple = { math.random(MAX_KEY), math.random(MAX_KEY) }
    space:upsert(tuple, {
        { random_elem(tuple_op), math.random(2), math.random(1000) },
        { random_elem(tuple_op), math.random(2), math.random(1000) }
    })
end

local function generate_update(space)
    local count = space:count()
    local key = math.random(count)
    log.info('UPDATE ' .. key)
	space:update(key, {
        { random_elem(tuple_op), math.random(2), math.random(1000) },
        { random_elem(tuple_op), math.random(2), math.random(1000) },
    })
end

local function generate_replace(space)
    local key = math.random(MAX_KEY)
    log.info('REPLACE ' .. key)
    space:replace({key, math.random(MAX_KEY)})
end

local function generate_bsize(space)
    space:bsize()
end

local function generate_len(space)
    space:len()
end

local function generate_truncate(space)
    space:truncate()
end

local DEFAULT_FORMAT = {
    {name = '1', type = 'any'},
    {name = '2', type = 'unsigned'},
    {name = '3', type = 'string'},
    {name = '4', type = 'number'},
    {name = '5', type = 'double'},
    {name = '6', type = 'integer'},
    {name = '7', type = 'boolean'},
    {name = '8', type = 'decimal'},
    {name = '9', type = 'uuid'},
    {name = 'a', type = 'scalar'},
    {name = 'b', type = 'array'},
    {name = 'c', type = 'map'}
}

local function generate_format(space)
    -- https://www.tarantool.io/ru/doc/latest/reference/reference_lua/box_space/format/
    space:format(DEFAULT_FORMAT)
end

local function init_space(space)
    log.info('CREATING TUPLES')
    for _ = 1, NUM_TUPLES do
        box.begin()
            for _ = 1, N_OPS_IN_TX do
                generate_insert(space)
            end
        box.commit()
    end
    local dump_watermark = 70000
    while box.stat.vinyl().memory.level0 < dump_watermark do
        generate_insert(space)
    end
end

local function setup()
    log.info("SETUP")
    -- TODO: https://www.tarantool.io/en/doc/2.3/reference/configuration/
    box.cfg{
        memtx_memory = 1024*1024,
        vinyl_cache = math.random(0, 10),
        vinyl_bloom_fpr = math.random(50) / 100,
        vinyl_max_tuple_size = math.random(0, 100000),
        vinyl_memory = 10*1024*1024,
        -- vinyl_page_size = math.random(1, 10),
        -- vinyl_range_size = math.random(1, 10),
        vinyl_run_size_ratio = math.random(2, 5),
        vinyl_run_count_per_level = math.random(1, 10),
        vinyl_read_threads = math.random(2, 10),
        vinyl_write_threads = math.random(2, 10),
        vinyl_timeout = math.random(1, 5),
        wal_mode = random_elem({'write', 'fsync'}),
        wal_max_size = math.random(1024 * 1024 * 1024),
        checkpoint_interval = math.random(1*60*60),
        checkpoint_count = math.random(5),
        checkpoint_wal_threshold = math.random(10^18),
    }
    log.info('FINISH BOX.CFG')

    log.info('create a space')
    local space = box.schema.space.create('test', { engine = 'vinyl' })
    index_create(space)
    index_create(space)

    init_space(space)
    log.info('FINISH SETUP')
    return space
end

local function cleanup()
   log.info("CLEANUP")
   os.execute('rm -rf *.snap *.xlog *.vylog 51*')
end

local function teardown(space)
   log.info("TEARDOWN")
   space:drop()
   cleanup()
end

local dml_ops = {
    ['DELETE_OP'] = generate_delete,
    ['INSERT_OP'] = generate_insert,
    ['SELECT_OP'] = generate_select,
    ['REPLACE_OP'] = generate_replace,
    ['UPDATE_OP'] = generate_update,
    ['UPSERT_OP'] = generate_upsert,
    ['BSIZE_OP'] = generate_bsize,
    ['LEN_OP'] = generate_len,
    ['TRUNCATE_OP'] = generate_truncate,
    ['FORMAT_OP'] = generate_format,
}

generate_dml = function(space)
    local op_name = random_elem(dict_keys(dml_ops))
    log.info(("GENERATE DML: %s"):format(op_name))
	local fn = dml_ops[op_name]
	assert(type(fn) == 'function')
	local ok, err = pcall(fn, space)
	if ok ~= true then
        log.info('ERROR: ' .. err)
	end
end

-- https://www.tarantool.io/en/doc/latest/concepts/data_model/indexes/
local function index_opts(space)
    assert(space ~= nil)
    -- TODO: generate random 'parts' by specified space format.
    local opts = {
        unique = random_elem({true, false}),
        if_not_exists = false,
        -- sequence,
        -- func,
        -- page_size,
        -- range_size,
        -- run_count_per_level,
        -- run_size_ratio,
    }

    -- TODO: RTREE, BITSET
    opts.type = random_elem({'TREE', 'HASH'})
    if space.engine == 'memtx' then
        opts.hint = random_elem({true, false})
    end

    if space.engine == 'vinyl' then
        opts.bloom_fpr = math.random(50) / 100
    end

    -- TODO: index_opts.parts

    if opts.type == 'RTREE' then
        -- TODO: dimension (RTREE only)
    end
    if opts.type == 'RTREE' then
        -- TODO: distance (RTREE only)
    end

    return opts
end

function index_create(space)
    local idx_name = 'idx_' .. math.random(100)
    if space.index[idx_name] ~= nil then
        space.index[idx_name]:drop()
    end
    local opts = index_opts(space)
    -- FIXME
    opts.type = 'TREE'
    local ok, err = pcall(space.create_index, space, idx_name, opts)
    if ok ~= true then
        local msg = ('ERROR: %s (%s)'):format(err, json.encode(opts))
        log.info(msg)
    end
end

local function index_drop(space)
    if not space.enabled then return end
    local idx = random_elem(space.index)
    if idx ~= nil then idx:drop() end
end

local function index_alter(space)
    if not space.enabled then return end
    local idx = random_elem(space.index)
    local opts = index_opts(space)
    -- Option is not relevant.
    opts.if_not_exists = nil
    if idx ~= nil then idx:alter(opts) end
end

local function index_compact(space)
    if not space.enabled then return end
    local idx = random_elem(space.index)
    if idx ~= nil then idx:compact() end
end

local function index_max(space)
    if not space.enabled then return end
    local idx = random_elem(space.index)
    if idx ~= nil then idx:max() end
end

local function index_min(space)
    if not space.enabled then return end
    local idx = random_elem(space.index)
    if idx ~= nil then idx:min() end
end

local function index_random(space)
    if not space.enabled then return end
    local idx = random_elem(space.index)
    if idx ~= nil and idx.type ~= 'TREE' then idx:random() end
end

local function index_rename(space)
    if not space.enabled then return end
    local idx = random_elem(space.index)
    -- FIXME: Use random string instead.
    if idx ~= nil then idx:rename('XXX') end
end

local function index_stat(space)
    if not space.enabled then return end
    local idx = random_elem(space.index)
    if idx ~= nil then idx:stat() end
end

local ddl_ops = {
    INDEX_ALTER = index_alter,
    INDEX_COMPACT = index_compact,
    INDEX_CREATE = index_create,
    -- INDEX_DROP = index_drop,
    -- INDEX_GET = index_get,
    -- INDEX_SELECT = index_select,
    INDEX_MIN = index_min,
    INDEX_MAX = index_max,
    INDEX_RANDOM = index_random,
    -- INDEX_COUNT = index_count,
    -- INDEX_UPDATE = index_update,
    -- INDEX_DELETE = index_delete,
    INDEX_RENAME = index_rename,
    INDEX_STAT = index_stat,
}

generate_ddl = function(space)
    local op_name = random_elem(dict_keys(ddl_ops))
    log.info(("GENERATE DDL: %s"):format(op_name))
	local fn = ddl_ops[op_name]
	assert(type(fn) == 'function')
	local ok, err = pcall(fn, space)
	if ok ~= true then
        log.info('ERROR: ' .. err)
	end
end

local function set_err_injection()
    local errinj_name = random_elem(dict_keys(errinj_set))
	local t = errinj_set[errinj_name]

    local errinj_val_enable = true
    local errinj_val_disable = false
    if t == 'double' then
        errinj_val_enable = math.random(0, 50)
        errinj_val_disable = 0
    end
    if t == 'int' then
        errinj_val_enable = math.random(0, 50)
        errinj_val_disable = -1
    end

    local pause_time = math.random(1, 10)

    log.info(string.format("ENABLE RANDOM ERROR INJECTION: %s -> %s",
                           errinj_name, tostring(errinj_val_enable)))
    local ok, err
    ok, err = pcall(box.error.injection.set, errinj_name, errinj_val_enable)
    if ok ~= true then
        log.info(err)
    end
    fiber.sleep(pause_time)
    log.info(string.format("DISABLE RANDOM ERROR INJECTION: %s -> %s",
                           errinj_name, tostring(errinj_val_disable)))
    ok, err = pcall(box.error.injection.set, errinj_name, errinj_val_disable)
    if ok ~= true then
        log.info('ERR: ' .. err)
    end
end

-- https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_stat/vinyl/
local function print_stat(space)
    log.info("PRINT STATISTICS")
    local stat = box.stat.vinyl()
    log.info(string.format('STATISTICS: transactions: %d, tx memory: %d',
                           stat.tx.transactions, stat.memory.tx))

    stat = space.index.secondary:stat()
    log.info(string.format('STATISTICS: memory rows %d bytes %d',
                           stat.memory.rows, stat.memory.bytes))
end

local function main()
    local fibers = {}

    cleanup()
    local space = setup()
    fiber.sleep(1)

    local f
    for i = 1, NUM_SP do
        f = fiber.new(function()
            log.info('START DML ' .. i)
            local start = os.clock()
            while os.clock() - start < TEST_DURATION do
                generate_dml(space)
                fiber.sleep(0.01)
            end
        end)
        f:set_joinable(true)
        f:name('DML_' .. i)
        table.insert(fibers, f)
    end

    for i = 1, NUM_SP do
        f = fiber.new(function()
            log.info('START TX ' .. i)
            local start = os.clock()
            while os.clock() - start < TEST_DURATION do
                local ok, err = pcall(generate_tx, space)
                if ok ~= true then
                    log.info('TX: ' .. err)
                end
                fiber.sleep(0.01)
            end
        end)
        f:set_joinable(true)
        f:name('TX_' .. i)
        table.insert(fibers, f)
    end

    for i = 1, NUM_SP do
        f = fiber.new(function()
            log.info('START DDL ' .. i)
            local start = os.clock()
            while os.clock() - start < TEST_DURATION do
                generate_ddl(space)
                fiber.sleep(math.random(15, 30))
            end
        end)
        f:set_joinable(true)
        f:name('DDL_' .. i)
        table.insert(fibers, f)
    end

    f = fiber.new(function()
        local start = os.clock()
        while os.clock() - start < TEST_DURATION do
            local in_progress = box.info.gc().checkpoint_is_in_progress
            if not in_progress then
                box.snapshot()
            end
            fiber.sleep(math.random(30, 60))
        end
    end)
    f:set_joinable(true)
    f:name('SNAPSHOTS')
    table.insert(fibers, f)

    f = fiber.new(function()
        local start = os.clock()
        while os.clock() - start < TEST_DURATION do
            set_err_injection()
            fiber.sleep(math.random(10, 30))
        end
    end)
    f:set_joinable(true)
    f:name('ERRINJ')
    table.insert(fibers, f)

    for _, fb in ipairs(fibers) do
        local ok, errmsg = fiber.join(fb)
        assert(ok == true)
        assert(errmsg == nil)
    end

    teardown(space)
end

main()

require('console').start()

Actual behavior

2024-05-31 15:14:12.819 [196789] main/128/DML_15/vinyl I> GENERATE DML: FORMAT_OP
tarantool: ./src/box/tuple_compare.cc:750: int tuple_compare_slowpath(tuple*, hint_t, tuple*, hint_t, key_def*) [with bool is_nullable = false; bool has_optional_parts = false; bool has_json_paths = false; bool is_multikey = false; bool has_desc_parts = false; hint_t = long unsigned int]: Assertion `has_optional_parts || (field_a != NULL && field_b != NULL)' failed.
Aborted (core dumped)

Expected behavior

no crash

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingcrashduplicateThis issue or pull request already existsfuzzingvinyl

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions