diff --git a/changelogs/unreleased/gh-5522-vy-tx-isolation-level.md b/changelogs/unreleased/gh-5522-vy-tx-isolation-level.md
new file mode 100644
index 0000000000000000000000000000000000000000..df63925d312a81d439852e51af6c535d7cbc3b31
--- /dev/null
+++ b/changelogs/unreleased/gh-5522-vy-tx-isolation-level.md
@@ -0,0 +1,5 @@
+## feature/vinyl
+
+* Added support of transaction isolation levels for the Vinyl engine.
+  The `txn_isolation` option passed to `box.begin()` now has the same
+  effect for Vinyl and memtx (gh-5522).
diff --git a/src/box/vinyl.c b/src/box/vinyl.c
index 80821b70a81a44eb28e775da71ffd66c46e45f29..cc2ae6b368509b77bee071d0c64a1e87f66282ec 100644
--- a/src/box/vinyl.c
+++ b/src/box/vinyl.c
@@ -2424,7 +2424,7 @@ vinyl_engine_begin(struct engine *engine, struct txn *txn)
 {
 	struct vy_env *env = vy_env(engine);
 	assert(txn->engine_tx == NULL);
-	txn->engine_tx = vy_tx_begin(env->xm);
+	txn->engine_tx = vy_tx_begin(env->xm, txn->isolation);
 	if (txn->engine_tx == NULL)
 		return -1;
 	return 0;
@@ -3045,7 +3045,7 @@ vinyl_engine_prepare_join(struct engine *engine, void **arg)
 		return -1;
 	}
 	rlist_create(&ctx->entries);
-	ctx->rv = vy_tx_manager_read_view(env->xm);
+	ctx->rv = vy_tx_manager_read_view(env->xm, /*plsn=*/INT64_MAX);
 	if (ctx->rv == NULL) {
 		free(ctx);
 		return -1;
diff --git a/src/box/vy_point_lookup.c b/src/box/vy_point_lookup.c
index 8d73328f253385fe4e85344767b01188aea2a74e..c4e4b6cf683e1f444936c047471890ee77b818c3 100644
--- a/src/box/vy_point_lookup.c
+++ b/src/box/vy_point_lookup.c
@@ -74,12 +74,14 @@ vy_point_lookup_scan_txw(struct vy_lsm *lsm, struct vy_tx *tx,
  */
 static int
 vy_point_lookup_scan_cache(struct vy_lsm *lsm, const struct vy_read_view **rv,
-			   struct vy_entry key, struct vy_history *history)
+			   bool is_prepared_ok, struct vy_entry key,
+			   struct vy_history *history)
 {
 	lsm->cache.stat.lookup++;
 	struct vy_entry entry = vy_cache_get(&lsm->cache, key);
 
-	if (entry.stmt == NULL || vy_stmt_lsn(entry.stmt) > (*rv)->vlsn)
+	if (entry.stmt == NULL || vy_stmt_lsn(entry.stmt) > (*rv)->vlsn ||
+	    (!is_prepared_ok && vy_stmt_is_prepared(entry.stmt)))
 		return 0;
 
 	vy_stmt_counter_acct_tuple(&lsm->cache.stat.get, entry.stmt);
@@ -92,16 +94,18 @@ vy_point_lookup_scan_cache(struct vy_lsm *lsm, const struct vy_read_view **rv,
  */
 static int
 vy_point_lookup_scan_mem(struct vy_lsm *lsm, struct vy_mem *mem,
-			 const struct vy_read_view **rv,
-			 struct vy_entry key, struct vy_history *history)
+			 const struct vy_read_view **rv, bool is_prepared_ok,
+			 struct vy_entry key, struct vy_history *history,
+			 int64_t *min_skipped_plsn)
 {
 	struct vy_mem_iterator mem_itr;
 	vy_mem_iterator_open(&mem_itr, &lsm->stat.memory.iterator,
-			     mem, ITER_EQ, key, rv, /*is_prepared_ok=*/true);
+			     mem, ITER_EQ, key, rv, is_prepared_ok);
 	struct vy_history mem_history;
 	vy_history_create(&mem_history, &lsm->env->history_node_pool);
 	int rc = vy_mem_iterator_next(&mem_itr, &mem_history);
 	vy_history_splice(history, &mem_history);
+	*min_skipped_plsn = MIN(*min_skipped_plsn, mem_itr.min_skipped_plsn);
 	vy_mem_iterator_close(&mem_itr);
 	return rc;
 
@@ -112,17 +116,31 @@ vy_point_lookup_scan_mem(struct vy_lsm *lsm, struct vy_mem *mem,
  * Add found statements to the history list up to terminal statement.
  */
 static int
-vy_point_lookup_scan_mems(struct vy_lsm *lsm, const struct vy_read_view **rv,
+vy_point_lookup_scan_mems(struct vy_lsm *lsm, struct vy_tx *tx,
+			  const struct vy_read_view **rv, bool is_prepared_ok,
 			  struct vy_entry key, struct vy_history *history)
 {
 	assert(lsm->mem != NULL);
-	int rc = vy_point_lookup_scan_mem(lsm, lsm->mem, rv, key, history);
+	int64_t min_skipped_plsn = INT64_MAX;
+	if (vy_point_lookup_scan_mem(lsm, lsm->mem, rv, is_prepared_ok,
+				     key, history, &min_skipped_plsn) != 0)
+		return -1;
 	struct vy_mem *mem;
 	rlist_foreach_entry(mem, &lsm->sealed, in_sealed) {
-		if (rc != 0 || vy_history_is_terminal(history))
-			return rc;
-
-		rc = vy_point_lookup_scan_mem(lsm, mem, rv, key, history);
+		if (vy_history_is_terminal(history))
+			break;
+		if (vy_point_lookup_scan_mem(lsm, mem, rv, is_prepared_ok,
+					     key, history,
+					     &min_skipped_plsn) != 0)
+			return -1;
+	}
+	if (tx != NULL && min_skipped_plsn != INT64_MAX) {
+		if (vy_tx_send_to_read_view(tx, min_skipped_plsn) != 0)
+			return -1;
+		if (tx->state == VINYL_TX_ABORT) {
+			diag_set(ClientError, ER_TRANSACTION_CONFLICT);
+			return -1;
+		}
 	}
 	return 0;
 }
@@ -214,12 +232,14 @@ vy_point_lookup(struct vy_lsm *lsm, struct vy_tx *tx,
 	if (rc != 0 || vy_history_is_terminal(&history))
 		goto done;
 
-	rc = vy_point_lookup_scan_cache(lsm, rv, key, &history);
+	bool is_prepared_ok = tx != NULL ? vy_tx_is_prepared_ok(tx) : false;
+	rc = vy_point_lookup_scan_cache(lsm, rv, is_prepared_ok, key, &history);
 	if (rc != 0 || vy_history_is_terminal(&history))
 		goto done;
 
 restart:
-	rc = vy_point_lookup_scan_mems(lsm, rv, key, &mem_history);
+	rc = vy_point_lookup_scan_mems(lsm, tx, rv, is_prepared_ok,
+				       key, &mem_history);
 	if (rc != 0 || vy_history_is_terminal(&mem_history))
 		goto done;
 
@@ -271,7 +291,8 @@ vy_point_lookup(struct vy_lsm *lsm, struct vy_tx *tx,
 		 * matching the search key.
 		 */
 		vy_history_cleanup(&mem_history);
-		rc = vy_point_lookup_scan_mems(lsm, rv, key, &mem_history);
+		rc = vy_point_lookup_scan_mems(lsm, tx, rv, is_prepared_ok,
+					       key, &mem_history);
 		if (rc != 0)
 			goto done;
 		if (vy_history_is_terminal(&mem_history))
@@ -306,11 +327,13 @@ vy_point_lookup_mem(struct vy_lsm *lsm, const struct vy_read_view **rv,
 	struct vy_history history;
 	vy_history_create(&history, &lsm->env->history_node_pool);
 
-	rc = vy_point_lookup_scan_cache(lsm, rv, key, &history);
+	rc = vy_point_lookup_scan_cache(lsm, rv, /*is_prepared_ok=*/true,
+					key, &history);
 	if (rc != 0 || vy_history_is_terminal(&history))
 		goto done;
 
-	rc = vy_point_lookup_scan_mems(lsm, rv, key, &history);
+	rc = vy_point_lookup_scan_mems(lsm, /*tx=*/NULL, rv,
+				       /*is_prepared_ok=*/true, key, &history);
 	if (rc != 0 || vy_history_is_terminal(&history))
 		goto done;
 
diff --git a/src/box/vy_read_iterator.c b/src/box/vy_read_iterator.c
index ca57d9ae53e68bdb21b38cb51e1312107a96a998..7b362caf6553a6a4a237017d54d02cc4309e6c26 100644
--- a/src/box/vy_read_iterator.c
+++ b/src/box/vy_read_iterator.c
@@ -355,7 +355,8 @@ vy_read_iterator_scan_cache(struct vy_read_iterator *itr,
 
 static NODISCARD int
 vy_read_iterator_scan_mem(struct vy_read_iterator *itr, uint32_t mem_src,
-			  struct vy_entry *next, bool *stop)
+			  struct vy_entry *next, bool *stop,
+			  int64_t *min_skipped_plsn)
 {
 	int rc;
 	struct vy_read_src *src = &itr->src[mem_src];
@@ -375,8 +376,8 @@ vy_read_iterator_scan_mem(struct vy_read_iterator *itr, uint32_t mem_src,
 	}
 	if (rc < 0)
 		return -1;
-
 	vy_read_iterator_evaluate_src(itr, src, next, stop);
+	*min_skipped_plsn = MIN(*min_skipped_plsn, src_itr->min_skipped_plsn);
 	return 0;
 }
 
@@ -514,12 +515,22 @@ vy_read_iterator_advance(struct vy_read_iterator *itr)
 	if (stop)
 		goto done;
 
-	for (uint32_t i = itr->mem_src; i < itr->disk_src; i++) {
-		if (vy_read_iterator_scan_mem(itr, i, &next, &stop) != 0)
+	int64_t min_skipped_plsn = INT64_MAX;
+	for (uint32_t i = itr->mem_src; i < itr->disk_src && !stop; i++) {
+		if (vy_read_iterator_scan_mem(itr, i, &next, &stop,
+					      &min_skipped_plsn) != 0)
 			return -1;
-		if (stop)
-			goto done;
 	}
+	if (itr->tx != NULL && min_skipped_plsn != INT64_MAX) {
+		if (vy_tx_send_to_read_view(itr->tx, min_skipped_plsn) != 0)
+			return -1;
+		if (itr->tx->state == VINYL_TX_ABORT) {
+			diag_set(ClientError, ER_TRANSACTION_CONFLICT);
+			return -1;
+		}
+	}
+	if (stop)
+		goto done;
 rescan_disk:
 	/* The following code may yield as it needs to access disk. */
 	vy_read_iterator_pin_slices(itr);
@@ -597,6 +608,7 @@ vy_read_iterator_advance(struct vy_read_iterator *itr)
 	return 0;
 }
 
+/** Add the transaction source to the read iterator. */
 static void
 vy_read_iterator_add_tx(struct vy_read_iterator *itr)
 {
@@ -609,19 +621,21 @@ vy_read_iterator_add_tx(struct vy_read_iterator *itr)
 			     iterator_type, itr->key);
 }
 
+/** Add the cache source to the read iterator. */
 static void
-vy_read_iterator_add_cache(struct vy_read_iterator *itr)
+vy_read_iterator_add_cache(struct vy_read_iterator *itr, bool is_prepared_ok)
 {
 	enum iterator_type iterator_type = (itr->iterator_type != ITER_REQ ?
 					    itr->iterator_type : ITER_LE);
 	struct vy_read_src *sub_src = vy_read_iterator_add_src(itr);
 	vy_cache_iterator_open(&sub_src->cache_iterator, &itr->lsm->cache,
 			       iterator_type, itr->key, itr->read_view,
-			       /*is_prepared_ok=*/true);
+			       is_prepared_ok);
 }
 
+/** Add the memory level source to the read iterator. */
 static void
-vy_read_iterator_add_mem(struct vy_read_iterator *itr)
+vy_read_iterator_add_mem(struct vy_read_iterator *itr, bool is_prepared_ok)
 {
 	enum iterator_type iterator_type = (itr->iterator_type != ITER_REQ ?
 					    itr->iterator_type : ITER_LE);
@@ -633,7 +647,7 @@ vy_read_iterator_add_mem(struct vy_read_iterator *itr)
 	sub_src = vy_read_iterator_add_src(itr);
 	vy_mem_iterator_open(&sub_src->mem_iterator, &lsm->stat.memory.iterator,
 			     lsm->mem, iterator_type, itr->key, itr->read_view,
-			     /*is_prepared_ok=*/true);
+			     is_prepared_ok);
 	/* Add sealed in-memory indexes. */
 	struct vy_mem *mem;
 	rlist_foreach_entry(mem, &lsm->sealed, in_sealed) {
@@ -641,10 +655,11 @@ vy_read_iterator_add_mem(struct vy_read_iterator *itr)
 		vy_mem_iterator_open(&sub_src->mem_iterator,
 				     &lsm->stat.memory.iterator,
 				     mem, iterator_type, itr->key,
-				     itr->read_view, /*is_prepared_ok=*/true);
+				     itr->read_view, is_prepared_ok);
 	}
 }
 
+/** Add the disk level source to the read iterator. */
 static void
 vy_read_iterator_add_disk(struct vy_read_iterator *itr)
 {
@@ -769,16 +784,18 @@ vy_read_iterator_restore(struct vy_read_iterator *itr)
 						    itr->last : itr->key);
 	itr->range_version = itr->curr_range->version;
 
+	bool is_prepared_ok = true;
 	if (itr->tx != NULL) {
+		is_prepared_ok = vy_tx_is_prepared_ok(itr->tx);
 		itr->txw_src = itr->src_count;
 		vy_read_iterator_add_tx(itr);
 	}
 
 	itr->cache_src = itr->src_count;
-	vy_read_iterator_add_cache(itr);
+	vy_read_iterator_add_cache(itr, is_prepared_ok);
 
 	itr->mem_src = itr->src_count;
-	vy_read_iterator_add_mem(itr);
+	vy_read_iterator_add_mem(itr, is_prepared_ok);
 
 	itr->disk_src = itr->src_count;
 	vy_read_iterator_add_disk(itr);
diff --git a/src/box/vy_tx.c b/src/box/vy_tx.c
index 10fb115993b7e406f0f458fc9b7065c81e2fa0a8..7f2af76888d40031c4b286f84a6cc48fad485eed 100644
--- a/src/box/vy_tx.c
+++ b/src/box/vy_tx.c
@@ -158,42 +158,55 @@ vy_tx_manager_mem_used(struct vy_tx_manager *xm)
 }
 
 struct vy_read_view *
-vy_tx_manager_read_view(struct vy_tx_manager *xm)
+vy_tx_manager_read_view(struct vy_tx_manager *xm, int64_t plsn)
 {
+	assert(plsn >= MAX_LSN);
+	/* Look up the last read view with lsn less than the given one. */
 	struct vy_read_view *rv;
+	rlist_foreach_entry_reverse(rv, &xm->read_views, in_read_views) {
+		if (plsn > rv->vlsn)
+			break;
+	}
+	bool rv_exists = !rlist_entry_is_head(rv, &xm->read_views,
+					      in_read_views);
+	/* Look up the last prepared tx with lsn less than the given one. */
+	struct vy_tx *tx;
+	rlist_foreach_entry_reverse(tx, &xm->prepared, in_prepared) {
+		if (plsn > MAX_LSN + tx->psn)
+			break;
+	}
+	bool tx_exists = !rlist_entry_is_head(tx, &xm->prepared, in_prepared);
 	/*
 	 * Check if the last read view can be reused. Reference
 	 * and return it if it's the case.
 	 */
-	struct vy_tx *last_prepared_tx = rlist_empty(&xm->prepared) ? NULL :
-		rlist_last_entry(&xm->prepared, struct vy_tx, in_prepared);
-	if (!rlist_empty(&xm->read_views)) {
-		rv = rlist_last_entry(&xm->read_views, struct vy_read_view,
-				      in_read_views);
-		/** Reuse an existing read view */
-		if ((last_prepared_tx == NULL && rv->vlsn == xm->lsn) ||
-		    (last_prepared_tx != NULL &&
-		     rv->vlsn == MAX_LSN + last_prepared_tx->psn)) {
-
+	if (rv_exists) {
+		if ((!tx_exists && rv->vlsn == xm->lsn) ||
+		    (tx_exists && rv->vlsn == MAX_LSN + tx->psn)) {
 			rv->refs++;
-			return  rv;
+			return rv;
 		}
 	}
+	/*
+	 * Allocate a new read view and insert it into the read view list
+	 * preserving the order.
+	 */
+	struct vy_read_view *prev_rv = rv;
 	rv = mempool_alloc(&xm->read_view_mempool);
 	if (rv == NULL) {
 		diag_set(OutOfMemory, sizeof(*rv),
 			 "mempool", "read view");
 		return NULL;
 	}
-	if (last_prepared_tx != NULL) {
-		rv->vlsn = MAX_LSN + last_prepared_tx->psn;
-		last_prepared_tx->read_view = rv;
+	if (tx_exists) {
+		rv->vlsn = MAX_LSN + tx->psn;
+		tx->read_view = rv;
 		rv->refs = 2;
 	} else {
 		rv->vlsn = xm->lsn;
 		rv->refs = 1;
 	}
-	rlist_add_tail_entry(&xm->read_views, rv, in_read_views);
+	rlist_add_entry(&prev_rv->in_read_views, rv, in_read_views);
 	return rv;
 }
 
@@ -328,6 +341,7 @@ vy_tx_create(struct vy_tx_manager *xm, struct vy_tx *tx)
 	tx->write_set_version = 0;
 	tx->write_size = 0;
 	tx->xm = xm;
+	tx->isolation = TXN_ISOLATION_READ_CONFIRMED;
 	tx->state = VINYL_TX_READY;
 	tx->is_applier_session = false;
 	tx->read_view = (struct vy_read_view *)xm->p_global_read_view;
@@ -379,12 +393,32 @@ vy_tx_is_in_read_view(struct vy_tx *tx)
 	return tx->read_view->vlsn != INT64_MAX;
 }
 
+int
+vy_tx_send_to_read_view(struct vy_tx *tx, int64_t plsn)
+{
+	assert(plsn >= MAX_LSN);
+	assert(tx->state == VINYL_TX_READY);
+	if (tx->read_view->vlsn < plsn)
+		return 0;
+	if (!vy_tx_is_ro(tx)) {
+		vy_tx_abort(tx);
+		return 0;
+	}
+	struct vy_tx_manager *xm = tx->xm;
+	struct vy_read_view *rv = vy_tx_manager_read_view(xm, plsn);
+	if (rv == NULL)
+		return -1;
+	vy_tx_manager_destroy_read_view(xm, tx->read_view);
+	tx->read_view = rv;
+	return 0;
+}
+
 /**
  * Send to read view all transactions that are reading key @v
  * modified by transaction @tx and abort all transactions that are modifying it.
  */
 static int
-vy_tx_send_to_read_view(struct vy_tx *tx, struct txv *v)
+vy_tx_send_readers_to_read_view(struct vy_tx *tx, struct txv *v)
 {
 	struct vy_tx_conflict_iterator it;
 	vy_tx_conflict_iterator_init(&it, &v->lsm->read_set, v->entry);
@@ -396,17 +430,8 @@ vy_tx_send_to_read_view(struct vy_tx *tx, struct txv *v)
 		/* Abort only active TXs */
 		if (abort->state != VINYL_TX_READY)
 			continue;
-		/* already in (earlier) read view */
-		if (vy_tx_is_in_read_view(abort))
-			continue;
-		if (!vy_tx_is_ro(abort)) {
-			vy_tx_abort(abort);
-			continue;
-		}
-		struct vy_read_view *rv = vy_tx_manager_read_view(tx->xm);
-		if (rv == NULL)
+		if (vy_tx_send_to_read_view(abort, INT64_MAX) != 0)
 			return -1;
-		abort->read_view = rv;
 	}
 	return 0;
 }
@@ -433,8 +458,10 @@ vy_tx_abort_readers(struct vy_tx *tx, struct txv *v)
 }
 
 struct vy_tx *
-vy_tx_begin(struct vy_tx_manager *xm)
+vy_tx_begin(struct vy_tx_manager *xm, enum txn_isolation_level isolation)
 {
+	assert(isolation < txn_isolation_level_MAX &&
+	       isolation != TXN_ISOLATION_DEFAULT);
 	struct vy_tx *tx = mempool_alloc(&xm->tx_mempool);
 	if (unlikely(tx == NULL)) {
 		diag_set(OutOfMemory, sizeof(*tx), "mempool", "struct vy_tx");
@@ -446,6 +473,7 @@ vy_tx_begin(struct vy_tx_manager *xm)
 	if (session != NULL && session->type == SESSION_TYPE_APPLIER)
 		tx->is_applier_session = true;
 
+	tx->isolation = isolation;
 	return tx;
 }
 
@@ -678,7 +706,7 @@ vy_tx_prepare(struct vy_tx *tx)
 	struct write_set_iterator it;
 	write_set_ifirst(&tx->write_set, &it);
 	while ((v = write_set_inext(&it)) != NULL) {
-		if (vy_tx_send_to_read_view(tx, v))
+		if (vy_tx_send_readers_to_read_view(tx, v))
 			return -1;
 	}
 
diff --git a/src/box/vy_tx.h b/src/box/vy_tx.h
index e44bc9cbf6e60390f641f005f1cd834afe626576..83ba75750acca8f6bf5f96114b3fcf396f9b66c5 100644
--- a/src/box/vy_tx.h
+++ b/src/box/vy_tx.h
@@ -42,6 +42,7 @@
 #include "iterator_type.h"
 #include "salad/stailq.h"
 #include "trivia/util.h"
+#include "txn.h"
 #include "vy_entry.h"
 #include "vy_lsm.h"
 #include "vy_stat.h"
@@ -174,6 +175,8 @@ struct vy_tx {
 	 * the write set.
 	 */
 	size_t write_size;
+	/** Transaction isolation level. */
+	enum txn_isolation_level isolation;
 	/** Current state of the transaction.*/
 	enum tx_state state;
 	/** Set if the transaction was started by an applier. */
@@ -210,6 +213,23 @@ vy_tx_read_view(struct vy_tx *tx)
 	return (const struct vy_read_view **)&tx->read_view;
 }
 
+/** Return true if the transaction may see prepared statements. */
+static inline bool
+vy_tx_is_prepared_ok(struct vy_tx *tx)
+{
+	switch (tx->isolation) {
+	case TXN_ISOLATION_READ_COMMITTED:
+		return true;
+	case TXN_ISOLATION_READ_CONFIRMED:
+	case TXN_ISOLATION_LINEARIZABLE:
+		return false;
+	case TXN_ISOLATION_BEST_EFFORT:
+		return !rlist_empty(&tx->in_writers);
+	default:
+		unreachable();
+	}
+}
+
 /** Transaction manager object. */
 struct vy_tx_manager {
 	/**
@@ -291,9 +311,12 @@ vy_tx_manager_delete(struct vy_tx_manager *xm);
 size_t
 vy_tx_manager_mem_used(struct vy_tx_manager *xm);
 
-/** Create or reuse an instance of a read view. */
+/**
+ * Create or reuse an instance of a read view whose vlsn is less than the given
+ * prepared statement LSN. Returns NULL on memory allocation error.
+ */
 struct vy_read_view *
-vy_tx_manager_read_view(struct vy_tx_manager *xm);
+vy_tx_manager_read_view(struct vy_tx_manager *xm, int64_t plsn);
 
 /** Dereference and possibly destroy a read view. */
 void
@@ -330,7 +353,7 @@ vy_tx_destroy(struct vy_tx *tx);
 
 /** Begin a new transaction. */
 struct vy_tx *
-vy_tx_begin(struct vy_tx_manager *xm);
+vy_tx_begin(struct vy_tx_manager *xm, enum txn_isolation_level isolation);
 
 /** Prepare a transaction to be committed. */
 int
@@ -423,6 +446,15 @@ vy_tx_track_point(struct vy_tx *tx, struct vy_lsm *lsm, struct vy_entry entry);
 int
 vy_tx_set(struct vy_tx *tx, struct vy_lsm *lsm, struct tuple *stmt);
 
+/**
+ * Send an active transaction to a read view such that its vlsn is less than
+ * the given prepared statement LSN. Returns 0 on success, -1 on memory
+ * allocation error. The transaction is aborted immediately if it has any
+ * write statements.
+ */
+int
+vy_tx_send_to_read_view(struct vy_tx *tx, int64_t plsn);
+
 /**
  * Iterator over the write set of a transaction.
  */
diff --git a/test/box-luatest/suite.ini b/test/box-luatest/suite.ini
index dbea6eca1c868bbf618e893d8dece5668e5e4927..31a2c4b8325111dbc7f1fe0f5b35f19d13e99f49 100644
--- a/test/box-luatest/suite.ini
+++ b/test/box-luatest/suite.ini
@@ -2,5 +2,5 @@
 core = luatest
 description = Database tests
 is_parallel = True
-release_disabled = gh_6819_iproto_watch_not_implemented_test.lua gh_6930_mvcc_net_box_iso_test.lua
+release_disabled = gh_6819_iproto_watch_not_implemented_test.lua
 long_run = gh_7605_qsort_recovery_test.lua gh_7670_memtx_tx_manager_idx_rand_inconsistency_test.lua
diff --git a/test/box-luatest/gh_6930_mvcc_isolation_levels_test.lua b/test/engine-luatest/gh_6930_mvcc_isolation_levels_test.lua
similarity index 77%
rename from test/box-luatest/gh_6930_mvcc_isolation_levels_test.lua
rename to test/engine-luatest/gh_6930_mvcc_isolation_levels_test.lua
index c2fdd45b8a4b7ae880a2d21f503be891aefa2592..ce09be0a377918e83e3d2e8aa545ba5fec1942c9 100644
--- a/test/box-luatest/gh_6930_mvcc_isolation_levels_test.lua
+++ b/test/engine-luatest/gh_6930_mvcc_isolation_levels_test.lua
@@ -1,22 +1,22 @@
 local server = require('test.luatest_helpers.server')
 local t = require('luatest')
 
-local g = t.group()
+local g = t.group(nil, {{engine = 'memtx'}, {engine = 'vinyl'}})
 
-g.before_all = function()
-    g.server = server:new{
+g.before_all(function(cg)
+    cg.server = server:new{
         alias   = 'default',
         box_cfg = {memtx_use_mvcc_engine = true}
     }
-    g.server:start()
-end
+    cg.server:start()
+end)
 
-g.after_all = function()
-    g.server:drop()
-end
+g.after_all(function(cg)
+    cg.server:drop()
+end)
 
-g.test_mvcc_isolation_level_errors = function()
-    g.server:exec(function()
+g.test_mvcc_isolation_level_errors = function(cg)
+    cg.server:exec(function()
         local t = require('luatest')
         t.assert_error_msg_content_equals(
             "Illegal parameters, txn_isolation must be one of " ..
@@ -49,15 +49,15 @@ g.test_mvcc_isolation_level_errors = function()
     end)
 end
 
-g.before_test('test_mvcc_isolation_level_basics', function()
-    g.server:exec(function()
-        local s = box.schema.space.create('test')
+g.before_test('test_mvcc_isolation_level_basics', function(cg)
+    cg.server:exec(function(engine)
+        local s = box.schema.space.create('test', {engine = engine})
         s:create_index('primary')
-    end)
+    end, {cg.params.engine})
 end)
 
-g.test_mvcc_isolation_level_basics = function()
-    g.server:exec(function()
+g.test_mvcc_isolation_level_basics = function(cg)
+    cg.server:exec(function()
         local t = require('luatest')
         local fiber = require('fiber')
         local s = box.space.test
@@ -168,8 +168,41 @@ g.test_mvcc_isolation_level_basics = function()
     end)
 end
 
-g.after_test('test_mvcc_isolation_level_basics', function()
-    g.server:exec(function()
+g.after_test('test_mvcc_isolation_level_basics', function(cg)
+    cg.server:exec(function()
+        local s = box.space.test
+        if s then
+            s:drop()
+        end
+    end)
+end)
+
+g.before_test('test_mvcc_best_effort_read_view', function(cg)
+    cg.server:exec(function(engine)
+        local s = box.schema.space.create('test', {engine = engine})
+        s:create_index('primary')
+    end, {cg.params.engine})
+end)
+
+g.test_mvcc_best_effort_read_view = function(cg)
+    cg.server:exec(function()
+        local t = require('luatest')
+        local fiber = require('fiber')
+        local s = box.space.test
+        local ch = fiber.channel(1)
+        fiber.create(function() s:replace{1} end)
+        fiber.create(function() s:replace{2} ch:put(true) end)
+        box.begin({txn_isolation = 'best-effort'})
+        t.assert_equals(s:select(2), {})
+        t.assert(ch:get(5))
+        t.assert_equals(s:select(1), {{1}})
+        box.commit()
+    end)
+end
+
+g.after_test('test_mvcc_best_effort_read_view', function(cg)
+    cg.server:exec(function()
+        box.error.injection.set('ERRINJ_WAL_DELAY', false)
         local s = box.space.test
         if s then
             s:drop()
diff --git a/test/box-luatest/gh_6930_mvcc_net_box_iso_test.lua b/test/engine-luatest/gh_6930_mvcc_net_box_iso_test.lua
similarity index 80%
rename from test/box-luatest/gh_6930_mvcc_net_box_iso_test.lua
rename to test/engine-luatest/gh_6930_mvcc_net_box_iso_test.lua
index d17255d8dbb2d389a243436ecaccd3ab9310974f..a833c150d5ef66f1650456a5605fd7a8bf5ec423 100644
--- a/test/box-luatest/gh_6930_mvcc_net_box_iso_test.lua
+++ b/test/engine-luatest/gh_6930_mvcc_net_box_iso_test.lua
@@ -1,32 +1,35 @@
+local misc = require('test.luatest_helpers.misc')
 local server = require('test.luatest_helpers.server')
 local t = require('luatest')
 
-local g = t.group()
+local g = t.group(nil, {{engine = 'memtx'}, {engine = 'vinyl'}})
 
-g.before_all = function()
-    g.server = server:new{
+g.before_all(function(cg)
+    cg.server = server:new{
         alias   = 'default',
         box_cfg = {memtx_use_mvcc_engine = true}
     }
-    g.server:start()
-end
+    cg.server:start()
+end)
 
-g.after_all = function()
-    g.server:drop()
-end
+g.after_all(function(cg)
+    cg.server:drop()
+end)
 
-g.before_test('test_mvcc_netbox_isolation_level_basics', function()
-    g.server:exec(function()
-        local s = box.schema.space.create('test')
+g.before_test('test_mvcc_netbox_isolation_level_basics', function(cg)
+    cg.server:exec(function(engine)
+        local s = box.schema.space.create('test', {engine = engine})
         s:create_index('primary')
         box.schema.user.grant('guest', 'read,write', 'space', 'test')
-    end)
+    end, {cg.params.engine})
 end)
 
-g.test_mvcc_netbox_isolation_level_basics = function()
+g.test_mvcc_netbox_isolation_level_basics = function(cg)
+    misc.skip_if_not_debug()
+
     local t = require('luatest')
 
-    g.server:exec(function()
+    cg.server:exec(function()
         local s = box.space.test
         box.error.injection.set('ERRINJ_WAL_DELAY', true)
         local fiber = require('fiber')
@@ -38,7 +41,7 @@ g.test_mvcc_netbox_isolation_level_basics = function()
     end)
 
     local netbox = require('net.box')
-    local conn = netbox.connect(g.server.net_box_uri)
+    local conn = netbox.connect(cg.server.net_box_uri)
 
     t.assert_equals(conn.space.test:select(), {})
     local strm = conn:new_stream()
@@ -59,14 +62,14 @@ g.test_mvcc_netbox_isolation_level_basics = function()
     end
 
     for _,level in pairs(expect0) do
-        g.server:exec(function(cfg_level)
+        cg.server:exec(function(cfg_level)
             box.cfg{txn_isolation = cfg_level}
         end, {level})
         strm:begin()
         t.assert_equals(strm.space.test:select(), {})
         strm:commit()
     end
-    g.server:exec(function()
+    cg.server:exec(function()
         box.cfg{txn_isolation = 'best-effort'}
     end)
 
@@ -81,7 +84,7 @@ g.test_mvcc_netbox_isolation_level_basics = function()
     end
 
     for _,level in pairs(expect1) do
-        g.server:exec(function(cfg_level)
+        cg.server:exec(function(cfg_level)
             box.cfg{txn_isolation = cfg_level}
         end, {level})
         strm:begin()
@@ -91,7 +94,7 @@ g.test_mvcc_netbox_isolation_level_basics = function()
         -- which is always run as read-confirmed
         t.assert_equals(strm.space.test:select(), {})
     end
-    g.server:exec(function()
+    cg.server:exec(function()
         box.cfg{txn_isolation = 'best-effort'}
     end)
 
@@ -109,20 +112,20 @@ g.test_mvcc_netbox_isolation_level_basics = function()
     strm:begin{txn_isolation = 'read-committed'}
     t.assert_equals(strm.space.test:select{1}, {{1}})
     strm.space.test:replace{2}
-    g.server:exec(function()
+    cg.server:exec(function()
         box.error.injection.set('ERRINJ_WAL_DELAY', false)
     end)
     strm:commit()
 
     t.assert_equals(strm.space.test:select{}, {{1}, {2}})
 
-    g.server:exec(function()
+    cg.server:exec(function()
         rawget(_G, 'f'):join()
     end)
 end
 
-g.after_test('test_mvcc_netbox_isolation_level_basics', function()
-    g.server:exec(function()
+g.after_test('test_mvcc_netbox_isolation_level_basics', function(cg)
+    cg.server:exec(function()
         box.error.injection.set('ERRINJ_WAL_DELAY', false)
         local s = box.space.test
         if s then
diff --git a/test/replication/gh-5167-qsync-rollback-snap.result b/test/replication/gh-5167-qsync-rollback-snap.result
index 13166720f69779f8efba892917ff3a32ee241fa2..85ef58612e2e306fab51a1e9a03b53d6aefad40a 100644
--- a/test/replication/gh-5167-qsync-rollback-snap.result
+++ b/test/replication/gh-5167-qsync-rollback-snap.result
@@ -71,7 +71,20 @@ test_run:switch('replica')
 fiber = require('fiber')
  | ---
  | ...
-test_run:wait_cond(function() return box.space.sync:count() == 1 end)
+test_run:cmd("setopt delimiter ';'")
+ | ---
+ | - true
+ | ...
+test_run:wait_cond(function()
+    box.begin({txn_isolation = 'read-committed'})
+    local ret = box.space.sync:count()
+    box.commit()
+    return ret == 1
+end);
+ | ---
+ | - true
+ | ...
+test_run:cmd("setopt delimiter ''");
  | ---
  | - true
  | ...
diff --git a/test/replication/gh-5167-qsync-rollback-snap.test.lua b/test/replication/gh-5167-qsync-rollback-snap.test.lua
index 1a2a31b7c749ade6c3f9b034f42605654e6377cd..5e8950eb91d5483ccb838c7cbbd3fe371c17252d 100644
--- a/test/replication/gh-5167-qsync-rollback-snap.test.lua
+++ b/test/replication/gh-5167-qsync-rollback-snap.test.lua
@@ -29,7 +29,14 @@ end)
 
 test_run:switch('replica')
 fiber = require('fiber')
-test_run:wait_cond(function() return box.space.sync:count() == 1 end)
+test_run:cmd("setopt delimiter ';'")
+test_run:wait_cond(function()
+    box.begin({txn_isolation = 'read-committed'})
+    local ret = box.space.sync:count()
+    box.commit()
+    return ret == 1
+end);
+test_run:cmd("setopt delimiter ''");
 -- Snapshot will stuck in WAL thread on rotation before starting wait on the
 -- limbo.
 box.error.injection.set("ERRINJ_WAL_DELAY", true)
diff --git a/test/replication/qsync_snapshots.result b/test/replication/qsync_snapshots.result
index ca418b168d88468530ce527523d7b04567d7c09a..742a56fce04ac1732bbcd273f466f39c28dba6e3 100644
--- a/test/replication/qsync_snapshots.result
+++ b/test/replication/qsync_snapshots.result
@@ -213,7 +213,20 @@ fiber = require('fiber')
 box.cfg{replication_synchro_timeout=1000}
  | ---
  | ...
-test_run:wait_cond(function() return box.space.sync:count() == 1 end)
+test_run:cmd("setopt delimiter ';'")
+ | ---
+ | - true
+ | ...
+test_run:wait_cond(function()
+    box.begin({txn_isolation = 'read-committed'})
+    local ret = box.space.sync:count()
+    box.commit()
+    return ret == 1
+end);
+ | ---
+ | - true
+ | ...
+test_run:cmd("setopt delimiter ''");
  | ---
  | - true
  | ...
diff --git a/test/replication/qsync_snapshots.test.lua b/test/replication/qsync_snapshots.test.lua
index 82c2e3f7c5a46410ca046350c4684549a6867584..5208c26d4ee82d707e08f35aab626508a6470629 100644
--- a/test/replication/qsync_snapshots.test.lua
+++ b/test/replication/qsync_snapshots.test.lua
@@ -100,7 +100,14 @@ end)
 test_run:switch('replica')
 fiber = require('fiber')
 box.cfg{replication_synchro_timeout=1000}
-test_run:wait_cond(function() return box.space.sync:count() == 1 end)
+test_run:cmd("setopt delimiter ';'")
+test_run:wait_cond(function()
+    box.begin({txn_isolation = 'read-committed'})
+    local ret = box.space.sync:count()
+    box.commit()
+    return ret == 1
+end);
+test_run:cmd("setopt delimiter ''");
 ok, err = nil
 f = fiber.create(function() ok, err = pcall(box.snapshot) end)
 
diff --git a/test/sql/errinj.result b/test/sql/errinj.result
index f19203bc629eba2ba5402d2580ca16f2a4b93f8a..a57a9008c7ccdc26234c8ba4dabd791af004d393 100644
--- a/test/sql/errinj.result
+++ b/test/sql/errinj.result
@@ -137,6 +137,12 @@ box.execute('drop table test')
 -- policy, SQL responses could be corrupted, when DDL/DML is mixed
 -- with DQL. Same as gh-3255.
 --
+txn_isolation_default = box.cfg.txn_isolation
+---
+...
+box.cfg{txn_isolation = 'read-committed'}
+---
+...
 box.execute('CREATE TABLE test (id integer primary key)')
 ---
 - row_count: 1
@@ -171,6 +177,9 @@ box.execute('DROP TABLE test')
 box.schema.user.revoke('guest', 'read,write,execute', 'universe')
 ---
 ...
+box.cfg{txn_isolation = txn_isolation_default}
+---
+...
 ----
 ---- gh-3273: Move SQL TRIGGERs into server.
 ----
diff --git a/test/sql/errinj.test.lua b/test/sql/errinj.test.lua
index 72e96a925504266fb9aeabb51d409ce7f624946a..5436e3b3a12eff9ad79bc6141a87b04c1f33b1a6 100644
--- a/test/sql/errinj.test.lua
+++ b/test/sql/errinj.test.lua
@@ -48,6 +48,9 @@ box.execute('drop table test')
 -- policy, SQL responses could be corrupted, when DDL/DML is mixed
 -- with DQL. Same as gh-3255.
 --
+txn_isolation_default = box.cfg.txn_isolation
+box.cfg{txn_isolation = 'read-committed'}
+
 box.execute('CREATE TABLE test (id integer primary key)')
 cn = remote.connect(box.cfg.listen)
 
@@ -60,6 +63,7 @@ errinj.set("ERRINJ_IPROTO_TX_DELAY", false)
 
 box.execute('DROP TABLE test')
 box.schema.user.revoke('guest', 'read,write,execute', 'universe')
+box.cfg{txn_isolation = txn_isolation_default}
 
 ----
 ---- gh-3273: Move SQL TRIGGERs into server.
diff --git a/test/vinyl/errinj.result b/test/vinyl/errinj.result
index a13006939be5cf65d5d02b43c125c59f3e51bd68..18d10b077bd0d3b513ea39fb42750345c978fd0f 100644
--- a/test/vinyl/errinj.result
+++ b/test/vinyl/errinj.result
@@ -766,6 +766,9 @@ wait_replace = true
 _ = fiber.create(function() s:replace{1, 1} wait_replace = false end)
 ---
 ...
+box.begin({txn_isolation = 'read-committed'})
+---
+...
 gen,param,state = s:pairs({1}, {iterator = 'GE'})
 ---
 ...
@@ -790,6 +793,9 @@ value
 ---
 - [2, 0]
 ...
+box.commit()
+---
+...
 s:drop()
 ---
 ...
diff --git a/test/vinyl/errinj.test.lua b/test/vinyl/errinj.test.lua
index 4538d794b848648a21ec44209d85d7bf9d591508..d698b4408481dc58f57958fe37845a711099321a 100644
--- a/test/vinyl/errinj.test.lua
+++ b/test/vinyl/errinj.test.lua
@@ -274,6 +274,7 @@ s:select{0}
 errinj.set("ERRINJ_WAL_DELAY", true)
 wait_replace = true
 _ = fiber.create(function() s:replace{1, 1} wait_replace = false end)
+box.begin({txn_isolation = 'read-committed'})
 gen,param,state = s:pairs({1}, {iterator = 'GE'})
 state, value = gen(param, state)
 value
@@ -281,6 +282,7 @@ 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/errinj_tx.result b/test/vinyl/errinj_tx.result
index 7d59d15668d89915361b0e69c467d7f7310349ce..101193a478801660e769c72a3951bd03ada008d1 100644
--- a/test/vinyl/errinj_tx.result
+++ b/test/vinyl/errinj_tx.result
@@ -13,6 +13,12 @@ create_iterator = require('utils').create_iterator
 errinj = box.error.injection
 ---
 ...
+txn_isolation_default = box.cfg.txn_isolation
+---
+...
+box.cfg{txn_isolation = 'read-committed'}
+---
+...
 --
 -- gh-1681: vinyl: crash in vy_rollback on ER_WAL_WRITE
 --
@@ -460,6 +466,9 @@ _ = fiber.create(function() pcall(s.replace, s, {1}) ch:put(true) end)
 ---
 ...
 -- Read the tuple from another transaction.
+box.begin({txn_isolation = 'read-committed'})
+---
+...
 itr = create_iterator(s)
 ---
 ...
@@ -519,6 +528,9 @@ c:commit()
 itr = nil
 ---
 ...
+box.rollback()
+---
+...
 s:drop()
 ---
 ...
@@ -532,3 +544,6 @@ box.stat.vinyl().tx.read_views -- 0
 ---
 - 0
 ...
+box.cfg{txn_isolation = txn_isolation_default}
+---
+...
diff --git a/test/vinyl/errinj_tx.test.lua b/test/vinyl/errinj_tx.test.lua
index c434d92ff34f219b20c495595fa36f55a3a2c026..24547b7e6be26d4e7656c9a0e800157e93944a2f 100644
--- a/test/vinyl/errinj_tx.test.lua
+++ b/test/vinyl/errinj_tx.test.lua
@@ -4,6 +4,9 @@ txn_proxy = require('txn_proxy')
 create_iterator = require('utils').create_iterator
 errinj = box.error.injection
 
+txn_isolation_default = box.cfg.txn_isolation
+box.cfg{txn_isolation = 'read-committed'}
+
 --
 -- gh-1681: vinyl: crash in vy_rollback on ER_WAL_WRITE
 --
@@ -208,6 +211,7 @@ errinj.set('ERRINJ_WAL_DELAY', true)
 _ = fiber.create(function() pcall(s.replace, s, {1}) ch:put(true) end)
 
 -- Read the tuple from another transaction.
+box.begin({txn_isolation = 'read-committed'})
 itr = create_iterator(s)
 itr.next()
 
@@ -229,9 +233,12 @@ c('s:replace{1}')
 c:commit()
 
 itr = nil
+box.rollback()
 s:drop()
 
 -- Collect all iterators to make sure no read views are left behind,
 -- as they might disrupt the following test run.
 collectgarbage()
 box.stat.vinyl().tx.read_views -- 0
+
+box.cfg{txn_isolation = txn_isolation_default}
diff --git a/test/vinyl/gh-3395-read-prepared-uncommitted.result b/test/vinyl/gh-3395-read-prepared-uncommitted.result
index 7dd22a1a9b1c0047fbacc9360186780c0c853b8e..17ef990abae031b21f182dd24d7a3c8561e17373 100644
--- a/test/vinyl/gh-3395-read-prepared-uncommitted.result
+++ b/test/vinyl/gh-3395-read-prepared-uncommitted.result
@@ -41,17 +41,29 @@ box.snapshot()
  | - ok
  | ...
 
-c = fiber.channel(1)
+c1 = fiber.channel(1)
+ | ---
+ | ...
+c2 = fiber.channel(1)
  | ---
  | ...
 
 function do_write() s:replace{1, 2} end
  | ---
  | ...
-function init_read() end
+
+test_run:cmd("setopt delimiter ';'")
  | ---
+ | - true
  | ...
-function do_read() local ret = sk:select{2} c:put(ret) end
+function do_read()
+    c1:get()
+    c2:put(true)
+    box.begin({txn_isolation = 'read-committed'})
+    local ret = sk:select{2}
+    box.commit()
+    c2:put(ret)
+end;
  | ---
  | ...
 
@@ -68,10 +80,6 @@ function do_read() local ret = sk:select{2} c:put(ret) end
 -- mem doesn't change and the statement is returned in the result set
 -- (i.e. dirty read takes place).
 --
-test_run:cmd("setopt delimiter ';'");
- | ---
- | - true
- | ...
 -- is_tx_faster_than_wal determines whether wal thread has time
 -- to finish its routine or not. In the first case we add extra
 -- time gap to make sure that  WAL thread finished work and
@@ -79,10 +87,11 @@ test_run:cmd("setopt delimiter ';'");
 --
 function read_prepared_with_delay(is_tx_faster_than_wal)
     errinj.set("ERRINJ_WAL_DELAY", true)
-    fiber.create(do_write, s)
-    init_read()
+    fiber.create(do_write)
+    fiber.create(do_read)
     errinj.set("ERRINJ_VY_READ_PAGE_DELAY", true)
-    fiber.create(do_read, sk, c)
+    c1:put(true)
+    c2:get()
     errinj.set("ERRINJ_WAL_WRITE", true)
     if is_tx_faster_than_wal then
         errinj.set("ERRINJ_RELAY_FASTER_THAN_TX", true)
@@ -90,7 +99,7 @@ function read_prepared_with_delay(is_tx_faster_than_wal)
     errinj.set("ERRINJ_WAL_DELAY", false)
     fiber.sleep(0.1)
     errinj.set("ERRINJ_VY_READ_PAGE_DELAY", false)
-    local res = c:get()
+    local res = c2:get()
     errinj.set("ERRINJ_WAL_WRITE", false)
     if is_tx_faster_than_wal then
         errinj.set("ERRINJ_RELAY_FASTER_THAN_TX", false)
@@ -188,12 +197,27 @@ state = nil
 function do_write() s:replace{3, 20} end
  | ---
  | ...
-function init_read() gen, param, state = sk:pairs({20}, {iterator = box.index.EQ}) gen(param, state) end
+
+test_run:cmd("setopt delimiter ';'")
  | ---
+ | - true
  | ...
-function do_read() local _, ret = gen(param, state) c:put(ret) end
+function do_read()
+    box.begin({txn_isolation = 'read-committed'})
+    gen, param, state = sk:pairs({20}, {iterator = box.index.EQ})
+    c1:get()
+    c2:put(true)
+    gen(param, state)
+    local _, ret = gen(param, state)
+    box.commit()
+    c2:put(ret)
+end;
  | ---
  | ...
+test_run:cmd("setopt delimiter ''");
+ | ---
+ | - true
+ | ...
 
 read_prepared_with_delay(false)
  | ---
@@ -215,7 +239,7 @@ fiber.sleep(0.1)
 --
 gen(param, state)
  | ---
- | - error: The read view is aborted
+ | - error: The transaction the cursor belongs to has ended
  | ...
 
 fiber.sleep(0.1)
diff --git a/test/vinyl/gh-3395-read-prepared-uncommitted.test.lua b/test/vinyl/gh-3395-read-prepared-uncommitted.test.lua
index 70136ddff062c9f800838bd4e09547624bdc44d9..afa70ad2f27da21a677d3e247197729936a698d5 100644
--- a/test/vinyl/gh-3395-read-prepared-uncommitted.test.lua
+++ b/test/vinyl/gh-3395-read-prepared-uncommitted.test.lua
@@ -19,11 +19,20 @@ s:replace{3, 2}
 s:replace{2, 2}
 box.snapshot()
 
-c = fiber.channel(1)
+c1 = fiber.channel(1)
+c2 = fiber.channel(1)
 
 function do_write() s:replace{1, 2} end
-function init_read() end
-function do_read() local ret = sk:select{2} c:put(ret) end
+
+test_run:cmd("setopt delimiter ';'")
+function do_read()
+    c1:get()
+    c2:put(true)
+    box.begin({txn_isolation = 'read-committed'})
+    local ret = sk:select{2}
+    box.commit()
+    c2:put(ret)
+end;
 
 -- Since we have tuples stored on disk, read procedure may
 -- yield, opening window for WAL thread to commit or rollback
@@ -38,7 +47,6 @@ function do_read() local ret = sk:select{2} c:put(ret) end
 -- mem doesn't change and the statement is returned in the result set
 -- (i.e. dirty read takes place).
 --
-test_run:cmd("setopt delimiter ';'");
 -- is_tx_faster_than_wal determines whether wal thread has time
 -- to finish its routine or not. In the first case we add extra
 -- time gap to make sure that  WAL thread finished work and
@@ -46,10 +54,11 @@ test_run:cmd("setopt delimiter ';'");
 --
 function read_prepared_with_delay(is_tx_faster_than_wal)
     errinj.set("ERRINJ_WAL_DELAY", true)
-    fiber.create(do_write, s)
-    init_read()
+    fiber.create(do_write)
+    fiber.create(do_read)
     errinj.set("ERRINJ_VY_READ_PAGE_DELAY", true)
-    fiber.create(do_read, sk, c)
+    c1:put(true)
+    c2:get()
     errinj.set("ERRINJ_WAL_WRITE", true)
     if is_tx_faster_than_wal then
         errinj.set("ERRINJ_RELAY_FASTER_THAN_TX", true)
@@ -57,7 +66,7 @@ function read_prepared_with_delay(is_tx_faster_than_wal)
     errinj.set("ERRINJ_WAL_DELAY", false)
     fiber.sleep(0.1)
     errinj.set("ERRINJ_VY_READ_PAGE_DELAY", false)
-    local res = c:get()
+    local res = c2:get()
     errinj.set("ERRINJ_WAL_WRITE", false)
     if is_tx_faster_than_wal then
         errinj.set("ERRINJ_RELAY_FASTER_THAN_TX", false)
@@ -104,8 +113,19 @@ gen = nil
 param = nil
 state = nil
 function do_write() s:replace{3, 20} end
-function init_read() gen, param, state = sk:pairs({20}, {iterator = box.index.EQ}) gen(param, state) end
-function do_read() local _, ret = gen(param, state) c:put(ret) end
+
+test_run:cmd("setopt delimiter ';'")
+function do_read()
+    box.begin({txn_isolation = 'read-committed'})
+    gen, param, state = sk:pairs({20}, {iterator = box.index.EQ})
+    c1:get()
+    c2:put(true)
+    gen(param, state)
+    local _, ret = gen(param, state)
+    box.commit()
+    c2:put(ret)
+end;
+test_run:cmd("setopt delimiter ''");
 
 read_prepared_with_delay(false)
 -- All the same but test second scenario (WAL thread is not finished
diff --git a/test/vinyl/upsert.result b/test/vinyl/upsert.result
index f4c7ffbf0e75c886b9f112a79c161aed99dc37ca..2db448c56c906dd2d284221dfd67b7233263083a 100644
--- a/test/vinyl/upsert.result
+++ b/test/vinyl/upsert.result
@@ -824,7 +824,11 @@ test_run:cmd("setopt delimiter ';'")
 ---
 - true
 ...
-_ = fiber.create(function() ch:put(s:select()) end)
+_ = fiber.create(function()
+    box.begin({txn_isolation = 'read-committed'})
+    ch:put(s:select())
+    box.commit()
+end)
 s:upsert({10, 10}, {{'+', 2, 10}})
 test_run:cmd("setopt delimiter ''");
 ---
diff --git a/test/vinyl/upsert.test.lua b/test/vinyl/upsert.test.lua
index 02eeba8dfa19fa2f909105f5fc94b5364103de87..4d0e35920f26c29d2fffcc986463243fd32ae883 100644
--- a/test/vinyl/upsert.test.lua
+++ b/test/vinyl/upsert.test.lua
@@ -339,7 +339,11 @@ box.snapshot()
 s:get(10) -- add [10, 10] to the cache
 ch = fiber.channel(1)
 test_run:cmd("setopt delimiter ';'")
-_ = fiber.create(function() ch:put(s:select()) end)
+_ = fiber.create(function()
+    box.begin({txn_isolation = 'read-committed'})
+    ch:put(s:select())
+    box.commit()
+end)
 s:upsert({10, 10}, {{'+', 2, 10}})
 test_run:cmd("setopt delimiter ''");
 ch:get() -- should see the UPSERT and return [10, 20]