From a31c2c10347cbd131395ebdd93d64069923f5286 Mon Sep 17 00:00:00 2001 From: Vladimir Davydov <vdavydov.dev@gmail.com> Date: Tue, 19 Dec 2017 15:11:28 +0300 Subject: [PATCH] vinyl: force read view in iterator in autocommit mode Every iteration over a secondary index tracks a point in the transaction manager (due to lookup in the primary index). As a result, if the user calls 'select' or 'pairs' over a huge data set, it will consume a lot of memory due to this tracked points, even if the user doesn't uses transactions. To mitigate this, let's send all read only transactions to read view immediately so that tracking is disabled completely during iteration. Note, with this patch select() called outside a transaction doesn't populate the cache any more, but it seems to be OK as caching large select() requests results in cache thrashing. Closes #2534 --- src/box/vinyl.c | 123 +++++++++++++++++++------------ src/box/vy_tx.c | 12 +-- src/box/vy_tx.h | 17 +++-- test/engine/iterator.result | 16 ++-- test/engine/iterator.test.lua | 8 ++ test/vinyl/cache.result | 22 +++--- test/vinyl/cache.test.lua | 12 +-- test/vinyl/errinj.result | 8 ++ test/vinyl/errinj.test.lua | 5 ++ test/vinyl/iterator.result | 110 +++++++++++---------------- test/vinyl/partial_dump.result | 17 ++++- test/vinyl/partial_dump.test.lua | 13 +++- test/vinyl/tx_gap_lock.result | 43 +++++++++++ test/vinyl/tx_gap_lock.test.lua | 15 ++++ 14 files changed, 270 insertions(+), 151 deletions(-) diff --git a/src/box/vinyl.c b/src/box/vinyl.c index 17d42dfa93..b0642d374d 100644 --- a/src/box/vinyl.c +++ b/src/box/vinyl.c @@ -232,21 +232,25 @@ struct vinyl_iterator { struct vy_env *env; /** Vinyl index this iterator is for. */ struct vy_index *index; + /** Active transaction or NULL if autocommit. */ + struct vy_tx *tx; /** - * Points either to tx_autocommit for autocommit mode - * or to a multi-statement transaction active when the - * iterator was created. + * Read view to use for the iteration. Points either to + * tx->read_view or to rv_autocommit if autocommit. */ - struct vy_tx *tx; + const struct vy_read_view **rv; /** Search key. */ struct tuple *key; /** Vinyl read iterator. */ struct vy_read_iterator iterator; /** - * Built-in transaction created when iterator is opened - * in autocommit mode. + * If an iterator is open in autocommit mode, we create + * a read view for it immediately so as not to waste + * memory on tracking reads for conflict resolution. + * The following member points to the read view used by + * the iterator. */ - struct vy_tx tx_autocommit; + struct vy_read_view *rv_autocommit; /** Trigger invoked when tx ends to close the iterator. */ struct trigger on_tx_destroy; }; @@ -2811,6 +2815,16 @@ vinyl_engine_end_recovery(struct engine *engine) vy_gc(e, e->recovery, VY_GC_INCOMPLETE, INT64_MAX); vy_recovery_delete(e->recovery); e->recovery = NULL; + /* + * During WAL recovery we skip WAL rows that have + * already been dumped to disk so the LSN last seen + * by the transaction manager after WAL recovery is + * complete may be less than the newest LSN actually + * stored on disk. Update the LSN to make sure that + * the iterator sees all data when open in a read + * view. + */ + e->xm->lsn = vclock_sum(e->recovery_vclock); e->recovery_vclock = NULL; e->status = VINYL_ONLINE; vy_quota_set_limit(&e->quota, e->memory); @@ -3716,15 +3730,6 @@ vy_squash_schedule(struct vy_index *index, struct tuple *stmt, void *arg) /* {{{ Cursor */ -static void -vinyl_iterator_on_tx_destroy(struct trigger *trigger, void *event) -{ - (void)event; - struct vinyl_iterator *it = container_of(trigger, - struct vinyl_iterator, on_tx_destroy); - it->tx = NULL; -} - static int vinyl_iterator_last(struct iterator *base, struct tuple **ret) { @@ -3741,18 +3746,11 @@ vinyl_iterator_close(struct vinyl_iterator *it) it->index = NULL; tuple_unref(it->key); it->key = NULL; - if (it->tx == &it->tx_autocommit) { - /* - * Rollback the automatic transaction. - * Use vy_tx_destroy() so as not to spoil - * the statistics of rollbacks issued by - * user transactions. - */ - vy_tx_destroy(it->tx); - } else { - trigger_clear(&it->on_tx_destroy); - } it->tx = NULL; + it->rv = NULL; + trigger_clear(&it->on_tx_destroy); + if (it->rv_autocommit != NULL) + tx_manager_destroy_read_view(it->env->xm, it->rv_autocommit); it->base.next = vinyl_iterator_last; } @@ -3763,12 +3761,8 @@ vinyl_iterator_next(struct iterator *base, struct tuple **ret) struct vinyl_iterator *it = (struct vinyl_iterator *)base; struct tuple *tuple; - if (it->tx == NULL) { - diag_set(ClientError, ER_CURSOR_NO_TRANSACTION); - goto fail; - } - if (it->tx->state == VINYL_TX_ABORT || it->tx->read_view->is_aborted) { - diag_set(ClientError, ER_READ_VIEW_ABORTED); + if ((*it->rv)->is_aborted || + (it->tx != NULL && it->tx->state == VINYL_TX_ABORT)) { goto fail; } @@ -3784,17 +3778,17 @@ vinyl_iterator_next(struct iterator *base, struct tuple **ret) if (it->index->id > 0) { /* Get the full tuple from the primary index. */ - if (vy_index_get(it->index->pk, it->tx, - vy_tx_read_view(it->tx), + if (vy_index_get(it->index->pk, it->tx, it->rv, tuple, &tuple) != 0) goto fail; } else { tuple_ref(tuple); } *ret = tuple_bless(tuple); - tuple_unref(*ret); + tuple_unref(tuple); if (*ret == NULL) goto fail; + return 0; fail: vinyl_iterator_close(it); @@ -3806,11 +3800,30 @@ vinyl_iterator_free(struct iterator *base) { assert(base->free == vinyl_iterator_free); struct vinyl_iterator *it = (struct vinyl_iterator *)base; - if (base->next != vinyl_iterator_last) + if (it->index != NULL) vinyl_iterator_close(it); mempool_free(&it->env->iterator_pool, it); } +static int +vinyl_iterator_no_tx(struct iterator *base, struct tuple **ret) +{ + (void)base; + (void)ret; + diag_set(ClientError, ER_CURSOR_NO_TRANSACTION); + return -1; +} + +static void +vinyl_iterator_on_tx_destroy(struct trigger *trigger, void *event) +{ + (void)event; + struct vinyl_iterator *it = container_of(trigger, + struct vinyl_iterator, on_tx_destroy); + vinyl_iterator_close(it); + it->base.next = vinyl_iterator_no_tx; +} + static struct iterator * vinyl_index_create_iterator(struct index *base, enum iterator_type type, const char *key, uint32_t part_count) @@ -3821,20 +3834,18 @@ vinyl_index_create_iterator(struct index *base, enum iterator_type type, if (type > ITER_GT) { diag_set(UnsupportedIndexFeature, base->def, "requested iterator type"); - return NULL; + goto err; } struct vinyl_iterator *it = mempool_alloc(&env->iterator_pool); if (it == NULL) { diag_set(OutOfMemory, sizeof(struct vinyl_iterator), "mempool", "struct vinyl_iterator"); - return NULL; + goto err; } it->key = vy_stmt_new_select(index->env->key_format, key, part_count); - if (it->key == NULL) { - mempool_free(&env->iterator_pool, it); - return NULL; - } + if (it->key == NULL) + goto err_key; iterator_create(&it->base, base); it->base.next = vinyl_iterator_next; @@ -3844,6 +3855,11 @@ vinyl_index_create_iterator(struct index *base, enum iterator_type type, it->index = index; vy_index_ref(index); + it->rv_autocommit = NULL; + trigger_create(&it->on_tx_destroy, + vinyl_iterator_on_tx_destroy, NULL, NULL); + + const struct vy_read_view **rv; struct vy_tx *tx = in_txn() ? in_txn()->engine_tx : NULL; assert(tx == NULL || tx->state == VINYL_TX_READY); if (tx != NULL) { @@ -3851,18 +3867,27 @@ vinyl_index_create_iterator(struct index *base, enum iterator_type type, * Register a trigger that will abort this iterator * when the transaction ends. */ - trigger_create(&it->on_tx_destroy, - vinyl_iterator_on_tx_destroy, NULL, NULL); trigger_add(&tx->on_destroy, &it->on_tx_destroy); + rv = (const struct vy_read_view **)&tx->read_view; } else { - tx = &it->tx_autocommit; - vy_tx_create(env->xm, tx); + it->rv_autocommit = tx_manager_read_view(env->xm); + if (it->rv_autocommit == NULL) + goto err_rv; + rv = (const struct vy_read_view **)&it->rv_autocommit; } it->tx = tx; + it->rv = rv; - vy_read_iterator_open(&it->iterator, index, tx, type, it->key, - (const struct vy_read_view **)&tx->read_view); + vy_read_iterator_open(&it->iterator, index, tx, type, it->key, rv); return (struct iterator *)it; + +err_rv: + vy_index_unref(index); + tuple_unref(it->key); +err_key: + mempool_free(&env->iterator_pool, it); +err: + return NULL; } static int diff --git a/src/box/vy_tx.c b/src/box/vy_tx.c index 2c7c66714a..8fde79f019 100644 --- a/src/box/vy_tx.c +++ b/src/box/vy_tx.c @@ -133,8 +133,7 @@ tx_manager_delete(struct tx_manager *xm) free(xm); } -/** Create or reuse an instance of a read view. */ -static struct vy_read_view * +struct vy_read_view * tx_manager_read_view(struct tx_manager *xm) { struct vy_read_view *rv; @@ -177,8 +176,7 @@ tx_manager_read_view(struct tx_manager *xm) return rv; } -/** Dereference and possibly destroy a read view. */ -static void +void tx_manager_destroy_read_view(struct tx_manager *xm, const struct vy_read_view *read_view) { @@ -277,7 +275,8 @@ vy_tx_read_set_free_cb(vy_tx_read_set_t *read_set, return NULL; } -void +/** Initialize a tx object. */ +static void vy_tx_create(struct tx_manager *xm, struct vy_tx *tx) { stailq_create(&tx->log); @@ -292,7 +291,8 @@ vy_tx_create(struct tx_manager *xm, struct vy_tx *tx) rlist_create(&tx->on_destroy); } -void +/** Destroy a tx object. */ +static void vy_tx_destroy(struct vy_tx *tx) { trigger_run(&tx->on_destroy, NULL); diff --git a/src/box/vy_tx.h b/src/box/vy_tx.h index 2ed4800fe2..7d5ce23e83 100644 --- a/src/box/vy_tx.h +++ b/src/box/vy_tx.h @@ -257,6 +257,15 @@ tx_manager_new(void); void tx_manager_delete(struct tx_manager *xm); +/** Create or reuse an instance of a read view. */ +struct vy_read_view * +tx_manager_read_view(struct tx_manager *xm); + +/** Dereference and possibly destroy a read view. */ +void +tx_manager_destroy_read_view(struct tx_manager *xm, + const struct vy_read_view *read_view); + /* * Determine the lowest possible vlsn, i.e. the level below * which the history could be compacted. @@ -269,14 +278,6 @@ tx_manager_delete(struct tx_manager *xm); int64_t tx_manager_vlsn(struct tx_manager *xm); -/** Initialize a tx object. */ -void -vy_tx_create(struct tx_manager *xm, struct vy_tx *tx); - -/** Destroy a tx object. */ -void -vy_tx_destroy(struct vy_tx *tx); - /** Begin a new transaction. */ struct vy_tx * vy_tx_begin(struct tx_manager *xm); diff --git a/test/engine/iterator.result b/test/engine/iterator.result index 2f175580c0..8a0ed69312 100644 --- a/test/engine/iterator.result +++ b/test/engine/iterator.result @@ -4016,17 +4016,21 @@ r - [3, 2] - [3, 3] ... -itr1,itr2,itr3 = s:pairs({2}, {iterator = 'REQ'}) +-- In memtx an iterator opened in autocommit mode works in +-- the read-committed isolation level while in vinyl it +-- creates a read view. To make sure the result is the same +-- for both engines, start a transaction. +inspector:cmd("setopt delimiter ';'") --- +- true ... +box.begin() +itr1,itr2,itr3 = s:pairs({2}, {iterator = 'REQ'}) s:replace{2, 4} ---- -- [2, 4] -... r = {} ---- -... for k,v in itr1,itr2,itr3 do table.insert(r, v) end +box.commit() +inspector:cmd("setopt delimiter ''"); --- ... r diff --git a/test/engine/iterator.test.lua b/test/engine/iterator.test.lua index 905173ad94..69e2dee0eb 100644 --- a/test/engine/iterator.test.lua +++ b/test/engine/iterator.test.lua @@ -337,10 +337,18 @@ r = {} for k,v in itr1,itr2,itr3 do table.insert(r, v) end r +-- In memtx an iterator opened in autocommit mode works in +-- the read-committed isolation level while in vinyl it +-- creates a read view. To make sure the result is the same +-- for both engines, start a transaction. +inspector:cmd("setopt delimiter ';'") +box.begin() itr1,itr2,itr3 = s:pairs({2}, {iterator = 'REQ'}) s:replace{2, 4} r = {} for k,v in itr1,itr2,itr3 do table.insert(r, v) end +box.commit() +inspector:cmd("setopt delimiter ''"); r r = nil diff --git a/test/vinyl/cache.result b/test/vinyl/cache.result index a504c2e04e..576c949b06 100644 --- a/test/vinyl/cache.result +++ b/test/vinyl/cache.result @@ -460,6 +460,8 @@ s:drop() --- ... -- Same test w/o begin/end +-- +-- Note, select() does not update cache in autcommit mode, see gh-2534. s = box.schema.space.create('test', {engine = 'vinyl'}) --- ... @@ -585,9 +587,9 @@ s:select{1} - [1, 3, 1, ''] - [1, 4, 1, ''] ... -stat_changed() -- cache hit, false +stat_changed() -- cache miss, true --- -- false +- true ... s:select{} --- @@ -619,9 +621,9 @@ s:select{} - [2, 4, 2, ''] - [3, 3, 4] ... -stat_changed() -- cache hit, false +stat_changed() -- cache miss, true --- -- false +- true ... s:drop() --- @@ -703,9 +705,9 @@ s:get{1, 2} --- - [1, 2, 1, ''] ... -stat_changed() -- cache hit, false +stat_changed() -- cache miss, true --- -- false +- true ... s:select{1} --- @@ -714,9 +716,9 @@ s:select{1} - [1, 3, 1, ''] - [1, 4, 1, ''] ... -stat_changed() -- cache hit, false +stat_changed() -- cache miss, true --- -- false +- true ... s:select{} --- @@ -731,9 +733,9 @@ s:select{} - [2, 4, 2, ''] - [3, 3, 4] ... -stat_changed() -- cache hit, false +stat_changed() -- cache miss, true --- -- false +- true ... s:drop() --- diff --git a/test/vinyl/cache.test.lua b/test/vinyl/cache.test.lua index af6257d648..19693bf92d 100644 --- a/test/vinyl/cache.test.lua +++ b/test/vinyl/cache.test.lua @@ -166,6 +166,8 @@ pk:max() s:drop() -- Same test w/o begin/end +-- +-- Note, select() does not update cache in autcommit mode, see gh-2534. s = box.schema.space.create('test', {engine = 'vinyl'}) pk = s:create_index('pk') @@ -216,13 +218,13 @@ s:select{1} stat_changed() -- cache miss, true s:select{1} -stat_changed() -- cache hit, false +stat_changed() -- cache miss, true s:select{} stat_changed() -- cache miss, true s:select{} -stat_changed() -- cache hit, false +stat_changed() -- cache miss, true s:drop() @@ -249,13 +251,13 @@ s:select{} stat_changed() -- cache miss, true s:get{1, 2} -stat_changed() -- cache hit, false +stat_changed() -- cache miss, true s:select{1} -stat_changed() -- cache hit, false +stat_changed() -- cache miss, true s:select{} -stat_changed() -- cache hit, false +stat_changed() -- cache miss, true s:drop() diff --git a/test/vinyl/errinj.result b/test/vinyl/errinj.result index 31239ec068..8aaa47457d 100644 --- a/test/vinyl/errinj.result +++ b/test/vinyl/errinj.result @@ -791,10 +791,12 @@ end; --- ... function iterate_in_read_view() + box.begin() local i = create_iterator(space) last_read = i.next() fiber.sleep(100000) last_read = i.next() + box.commit() end; --- ... @@ -1149,6 +1151,9 @@ s:select{0} --- - - [0, 0] ... +box.begin() +--- +... errinj.set("ERRINJ_WAL_DELAY", true) --- - ok @@ -1183,6 +1188,9 @@ value --- - [2, 0] ... +box.commit() +--- +... s:drop() --- ... diff --git a/test/vinyl/errinj.test.lua b/test/vinyl/errinj.test.lua index f3b3b7a247..45eed72eba 100644 --- a/test/vinyl/errinj.test.lua +++ b/test/vinyl/errinj.test.lua @@ -309,10 +309,12 @@ function fill_space() end; function iterate_in_read_view() + box.begin() local i = create_iterator(space) last_read = i.next() fiber.sleep(100000) last_read = i.next() + box.commit() end; test_run:cmd("setopt delimiter ''"); @@ -450,6 +452,7 @@ box.snapshot() s:replace{0, 0} s:select{0} +box.begin() errinj.set("ERRINJ_WAL_DELAY", true) wait_replace = true _ = fiber.create(function() s:replace{1, 1} wait_replace = false end) @@ -460,4 +463,6 @@ errinj.set("ERRINJ_WAL_DELAY", false) while wait_replace do fiber.sleep(0.01) end state, value = gen(param, state) value +box.commit() + s:drop() diff --git a/test/vinyl/iterator.result b/test/vinyl/iterator.result index 4798f5f268..ceb59f10bd 100644 --- a/test/vinyl/iterator.result +++ b/test/vinyl/iterator.result @@ -126,7 +126,9 @@ space:select{} ... iterate_over(iter_obj) --- -- [] +- 0: [1] + 1: [2] + 2: [3] ... -- 3) create iterator within test case space:insert({1}) @@ -174,7 +176,9 @@ space:select{} ... iterate_over(iter_obj) --- -- [] +- 0: [1, 1] + 1: [2, 2] + 2: [3, 3] ... -- -- UPSERT followed by DELETE @@ -237,7 +241,9 @@ space:select{} ... iterate_over(iter_obj) --- -- [] +- 0: [1] + 1: [2] + 2: [3] ... -- 3) create iterator within test case space:upsert({1}, {{'!', 2, 1}}) @@ -267,7 +273,7 @@ space:select{} ... iterate_over(iter_obj) --- -- [] +- 0: [3] ... -- -- UPSERT followed by UPSERT @@ -302,9 +308,7 @@ space:select{} ... iterate_over(iter_obj) --- -- 0: [1, 1] - 1: [2, 2] - 2: [3, 3] +- [] ... space:truncate() --- @@ -339,9 +343,9 @@ space:select{} ... iterate_over(iter_obj) --- -- 0: [1, 1] - 1: [2, 2] - 2: [3, 3] +- 0: [1] + 1: [2] + 2: [3] ... space:truncate() --- @@ -377,8 +381,8 @@ space:select{} iterate_over(iter_obj) --- - 0: [1, 1] - 1: [2, 2] - 2: [3, 3] + 1: [2] + 2: [3] ... space:truncate() --- @@ -419,9 +423,7 @@ space:select{} ... iterate_over(iter_obj) --- -- 0: [1, 10] - 1: [2, 20] - 2: [3, 30] +- [] ... space:truncate() --- @@ -459,9 +461,9 @@ space:select{} ... iterate_over(iter_obj) --- -- 0: [1, 10] - 1: [2, 20] - 2: [3, 30] +- 0: [1] + 1: [2] + 2: [3] ... space:truncate() --- @@ -501,7 +503,7 @@ iterate_over(iter_obj) --- - 0: [1, 10] 1: [2, 20] - 2: [3, 30] + 2: [3] ... space:truncate() --- @@ -542,9 +544,7 @@ space:select{} ... iterate_over(iter_obj) --- -- 0: [1, 1, 10] - 1: [2, 2, 20] - 2: [3, 3, 30] +- [] ... space:truncate() --- @@ -582,9 +582,9 @@ space:select{} ... iterate_over(iter_obj) --- -- 0: [1, 1, 10] - 1: [2, 2, 20] - 2: [3, 3, 30] +- 0: [1, 10] + 1: [2, 20] + 2: [3, 30] ... space:truncate() --- @@ -623,8 +623,8 @@ space:select{} iterate_over(iter_obj) --- - 0: [1, 1, 10] - 1: [2, 2, 20] - 2: [3, 3, 30] + 1: [2, 20] + 2: [3, 30] ... space:truncate() --- @@ -696,7 +696,9 @@ space:select{} ... iterate_over(iter_obj) --- -- [] +- 0: [1, 10] + 1: [2, 20] + 2: [3, 30] ... -- 3) create iterator within test case space:replace({1, 10}) @@ -729,7 +731,7 @@ space:select{} ... iterate_over(iter_obj) --- -- [] +- 0: [3, 30] ... -- -- DELETE followed by REPLACE @@ -779,9 +781,9 @@ space:select{} ... iterate_over(iter_obj) --- -- 0: [1] - 1: [2] - 2: [3] +- 0: [1, 10] + 1: [2, 20] + 2: [3, 30] ... space:truncate() --- @@ -831,9 +833,7 @@ space:select{} ... iterate_over(iter_obj) --- -- 0: [1] - 1: [2] - 2: [3] +- [] ... space:truncate() --- @@ -885,7 +885,6 @@ iterate_over(iter_obj) --- - 0: [1] 1: [2] - 2: [3] ... space:truncate() --- @@ -929,9 +928,7 @@ space:select{} ... iterate_over(iter_obj) --- -- 0: [1, 10] - 1: [2, 20] - 2: [3, 30] +- [] ... space:truncate() --- @@ -972,9 +969,9 @@ space:select{} ... iterate_over(iter_obj) --- -- 0: [1, 10] - 1: [2, 20] - 2: [3, 30] +- 0: [1] + 1: [2] + 2: [3] ... space:truncate() --- @@ -1017,7 +1014,7 @@ iterate_over(iter_obj) --- - 0: [1, 10] 1: [2, 20] - 2: [3, 30] + 2: [3] ... space:truncate() --- @@ -1046,9 +1043,7 @@ space:select{} ... iterate_over(iter_obj) --- -- 0: [1] - 1: [2] - 2: [3] +- [] ... space:truncate() --- @@ -1075,8 +1070,6 @@ space:select{} iterate_over(iter_obj) --- - 0: [1] - 1: [2] - 2: [3] ... space:truncate() --- @@ -1136,9 +1129,7 @@ space:select{} ... iterate_over(iter_obj) --- -- 0: [1] - 1: [2] - 2: [3] +- [] ... space:truncate() --- @@ -1169,7 +1160,6 @@ iterate_over(iter_obj) --- - 0: [1] 1: [2] - 2: [3] ... space:truncate() --- @@ -1447,8 +1437,6 @@ box.snapshot() iterate_over(iter_obj_sp2) --- - 0: [5] - 1: [6] - 2: [8] ... space2:truncate() --- @@ -1461,8 +1449,6 @@ iterate_over(iter_obj_sp3) --- - 0: [4] 1: [5] - 2: [6] - 3: [8] ... space3:truncate() --- @@ -1529,7 +1515,7 @@ space:select{} ... iterate_over(iter_obj) --- -- 0: [1, 2, 3] +- [] ... iter_obj2 = create_iterator(idx2, 2, {iterator = 'EQ'}) --- @@ -1539,7 +1525,7 @@ space:delete({1}) ... iterate_over(iter_obj2) --- -- [] +- 0: [1, 2, 3] ... space:truncate() --- @@ -1604,15 +1590,11 @@ space:select{} iterate_over(iter_obj) --- - 0: [3, 8, 1] - 1: [4, 16, -1] - 2: [5, 32, -10] - 3: [6, 64, -10] ... iterate_over(iter_obj2) --- - 0: [4, 16, -1] - 1: [6, 64, -10] - 2: [5, 32, -10] + 1: [5, 32, -10] ... iterate_over(iter_obj3) --- @@ -1726,12 +1708,10 @@ iterate_over(iter_obj) --- - 0: [1, 1, 1] 1: [2, 2, 2] - 2: [3, 3, 3] ... iterate_over(iter_obj2) --- - 0: [2, 2, 2] - 1: [3, 3, 3] ... box.commit() --- diff --git a/test/vinyl/partial_dump.result b/test/vinyl/partial_dump.result index 4f8fce224a..b35335d18c 100644 --- a/test/vinyl/partial_dump.result +++ b/test/vinyl/partial_dump.result @@ -61,6 +61,20 @@ box.error.injection.set('ERRINJ_VY_INDEX_DUMP', -1) - ok ... test_run:cmd('restart server default') +test_run:cmd("setopt delimiter ';'") +--- +- true +... +function tuple_equal(t1, t2) + if #t1 ~= #t2 then return false end + for i = 1, #t1 do + if t1[i] ~= t2[i] then return false end + end + return true +end +test_run:cmd("setopt delimiter ''"); +--- +... INDEX_COUNT = box.cfg.vinyl_write_threads * 3 --- ... @@ -91,7 +105,8 @@ for i = 1, INDEX_COUNT - 1 do bad_index = i end for _, v in s.index[i]:pairs() do - if v ~= s:get(v[1]) then + local v2 = s:get(v[1]) + if not v2 or not tuple_equal(v, v2) then bad_index = i end end diff --git a/test/vinyl/partial_dump.test.lua b/test/vinyl/partial_dump.test.lua index 393f531fb0..87be87018d 100644 --- a/test/vinyl/partial_dump.test.lua +++ b/test/vinyl/partial_dump.test.lua @@ -37,6 +37,16 @@ box.error.injection.set('ERRINJ_VY_INDEX_DUMP', -1) test_run:cmd('restart server default') +test_run:cmd("setopt delimiter ';'") +function tuple_equal(t1, t2) + if #t1 ~= #t2 then return false end + for i = 1, #t1 do + if t1[i] ~= t2[i] then return false end + end + return true +end +test_run:cmd("setopt delimiter ''"); + INDEX_COUNT = box.cfg.vinyl_write_threads * 3 assert(INDEX_COUNT < 100) @@ -50,7 +60,8 @@ for i = 1, INDEX_COUNT - 1 do bad_index = i end for _, v in s.index[i]:pairs() do - if v ~= s:get(v[1]) then + local v2 = s:get(v[1]) + if not v2 or not tuple_equal(v, v2) then bad_index = i end end diff --git a/test/vinyl/tx_gap_lock.result b/test/vinyl/tx_gap_lock.result index 2a5087bf92..425203c432 100644 --- a/test/vinyl/tx_gap_lock.result +++ b/test/vinyl/tx_gap_lock.result @@ -1362,6 +1362,49 @@ test_run:cmd("setopt delimiter ''"); - true ... ---------------------------------------------------------------- +-- gh-2534: Iterator doesn't track reads in autocommit mode. +---------------------------------------------------------------- +s = box.schema.space.create('test', {engine = 'vinyl'}) +--- +... +_ = s:create_index('pk', {parts = {1, 'unsigned'}}) +--- +... +_ = s:create_index('sk', {parts = {2, 'unsigned'}}) +--- +... +for i = 1, 100 do s:insert{i, i} end +--- +... +gen, param, state = s.index.sk:pairs() +--- +... +for i = 1, 100 do state, value = gen(param, state) end +--- +... +value +--- +- [100, 100] +... +stat = box.info.vinyl() +--- +... +stat.tx.transactions -- 0 +--- +- 0 +... +stat.tx.read_views -- 1 +--- +- 1 +... +stat.tx.gap_locks -- 0 +--- +- 0 +... +s:drop() +--- +... +---------------------------------------------------------------- c = nil --- ... diff --git a/test/vinyl/tx_gap_lock.test.lua b/test/vinyl/tx_gap_lock.test.lua index 2343a719a1..b1b551d0d9 100644 --- a/test/vinyl/tx_gap_lock.test.lua +++ b/test/vinyl/tx_gap_lock.test.lua @@ -507,6 +507,21 @@ s:drop(); test_run:cmd("setopt delimiter ''"); ---------------------------------------------------------------- +-- gh-2534: Iterator doesn't track reads in autocommit mode. +---------------------------------------------------------------- +s = box.schema.space.create('test', {engine = 'vinyl'}) +_ = s:create_index('pk', {parts = {1, 'unsigned'}}) +_ = s:create_index('sk', {parts = {2, 'unsigned'}}) +for i = 1, 100 do s:insert{i, i} end +gen, param, state = s.index.sk:pairs() +for i = 1, 100 do state, value = gen(param, state) end +value +stat = box.info.vinyl() +stat.tx.transactions -- 0 +stat.tx.read_views -- 1 +stat.tx.gap_locks -- 0 +s:drop() +---------------------------------------------------------------- c = nil c1 = nil -- GitLab