diff --git a/src/box/vinyl.c b/src/box/vinyl.c index 17d42dfa93c20df2e39cc1b3a644c39741c258f7..b0642d374d6bd7545a2bfcad7e8cf26ea2e015dd 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 2c7c66714ab55c42694b8aba0ac4524e2a53f269..8fde79f019088686a066bf198ef167e845cf281c 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 2ed4800fe2134d58bd8e995751424bd85370deb8..7d5ce23e83a8d0e0041b1711b90cc026db0c3b39 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 2f175580c06214771cd8348ddf33b943bed9c2f1..8a0ed69312bb788d8878e3bdf8a5d74e7f1876c8 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 905173ad9426fa5b7dabffe1a603c412001bafb2..69e2dee0ebdfd37a8b36240f17faf40e908515d5 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 a504c2e04e1eeb96ebb9ad971ee93f714ce8e0a4..576c949b06381581acc684da139d239ac9e2e50c 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 af6257d648abd73a5deff322fda34df92dd1d177..19693bf92d4896f932962f591be881f2b2820de5 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 31239ec0688b2b9cf07ed148b9d17eb36d0e6f0b..8aaa47457dc4a12e12ccd22a97c508b9fba6cd54 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 f3b3b7a24724f2c8782ec5782c5de77e20dda6ca..45eed72ebaaea596c20d7f13bbbb9b5562f88af2 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 4798f5f268117f74af3f085918d59a7ad0a30b2f..ceb59f10bd6e1c5129d5a0d080ef840318af1bfd 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 4f8fce224afbe2f83c976d3f1c43d3bb7821353b..b35335d18cff97e25955b51e1a971fd23ac60890 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 393f531fb01f131775c1d105627731cd61b590d9..87be87018d4e5bc24c25807f8c489c1cbdcd5dea 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 2a5087bf92bd003d412b02bfe13c52dbfdd3f797..425203c432a9c9080a3c9ced2144137cfd70efb9 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 2343a719a153b86c3eb2e766aff26901dcbe3d74..b1b551d0d9349960109dbd61745a3fa3caaec248 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