diff --git a/changelogs/unreleased/gh-7113-rev-tree-iters-gap-tracking.md b/changelogs/unreleased/gh-7113-rev-tree-iters-gap-tracking.md
new file mode 100644
index 0000000000000000000000000000000000000000..94d36e3b73d7d24045af04b6549f6074fe8d54d1
--- /dev/null
+++ b/changelogs/unreleased/gh-7113-rev-tree-iters-gap-tracking.md
@@ -0,0 +1,5 @@
+## bugfix/core
+
+* Fixed reversed iterators gap tracking: instead of tracking gaps for
+  successors of keys, gaps for tuples shifted by one to the left of
+  the successor were tracked (gh-7113).
diff --git a/src/box/memtx_tree.cc b/src/box/memtx_tree.cc
index 572ffa51b86fd76418a2205f9e1ff46b7fb08d38..15647017b836fdaf034ca4e27fce445bbc4309f1 100644
--- a/src/box/memtx_tree.cc
+++ b/src/box/memtx_tree.cc
@@ -670,6 +670,13 @@ tree_iterator_start_raw(struct iterator *iterator, struct tuple **ret)
 		}
 	}
 
+	/*
+	 * `it->tree_iterator` could potentially be positioned on successor of
+	 * key: we need to track gap based on it.
+	 */
+	struct memtx_tree_data<USE_HINT> *res =
+		memtx_tree_iterator_get_elem(tree, &it->tree_iterator);
+	struct tuple *successor = res == NULL ? NULL : res->tuple;
 	if (iterator_type_is_reverse(type)) {
 		/*
 		 * Because of limitations of tree search API we use use
@@ -683,57 +690,39 @@ tree_iterator_start_raw(struct iterator *iterator, struct tuple **ret)
 		 * last position in the tree, that's what we need.
 		 */
 		memtx_tree_iterator_prev(tree, &it->tree_iterator);
+		res = memtx_tree_iterator_get_elem(tree, &it->tree_iterator);
 	}
-
-	if (!equals && (type == ITER_EQ || type == ITER_REQ)) {
-		/*
-		 * Found nothing, iteration will be stopped now. That is the
-		 * last chance to record that the transaction have read the key.
-		 */
-		if (key_is_full) {
-			memtx_tx_track_point(txn, space, idx, it->key_data.key);
-			return 0;
-		}
-		/* it->tree_iterator is positioned on successor of a key! */
-		struct memtx_tree_data<USE_HINT> *res =
-			memtx_tree_iterator_get_elem(tree, &it->tree_iterator);
-		struct tuple *successor = res == NULL ? NULL : res->tuple;
-		memtx_tx_track_gap(txn, space, idx, successor, type,
-				   it->key_data.key, it->key_data.part_count);
-		return 0;
-	}
-
-	struct memtx_tree_data<USE_HINT> *res =
-		memtx_tree_iterator_get_elem(tree, &it->tree_iterator);
-	uint32_t mk_index = 0;
-	if (res != NULL) {
-		*ret = res->tuple;
+	/*
+	 * Equality iterators requires exact key match: if the result does not
+	 * equal to the key, iteration ends.
+	 */
+	bool eq_match = equals || (type != ITER_EQ && type != ITER_REQ);
+	if (res != NULL && eq_match) {
 		tree_iterator_set_current(it, res);
 		tree_iterator_set_next_method(it);
 		bool is_multikey = iterator->index->def->key_def->is_multikey;
-		mk_index = is_multikey ? (uint32_t)res->hint : 0;
+		uint32_t mk_index = is_multikey ? (uint32_t)res->hint : 0;
+		/*
+		 * We need to clarify the result tuple before story garbage
+		 * collection, otherwise it could get cleaned there.
+		 */
+		*ret = memtx_tx_tuple_clarify(txn, space, res->tuple, idx,
+					      mk_index);
 	}
-
-	if ((!key_is_full || (type != ITER_EQ && type != ITER_REQ)) &&
-	    memtx_tx_manager_use_mvcc_engine) {
-		/* it->tree_iterator is positioned on successor of a key! */
-		struct tuple *successor = res == NULL ? NULL : res->tuple;
-
+	if (key_is_full && !eq_match)
+		memtx_tx_track_point(txn, space, idx, it->key_data.key);
+	if (!key_is_full ||
+	    ((type == ITER_ALL || type == ITER_GE || type == ITER_LE) &&
+	     !equals) || (type == ITER_GT || type == ITER_LT))
 /********MVCC TRANSACTION MANAGER STORY GARBAGE COLLECTION BOUND START*********/
-		memtx_tx_track_gap(in_txn(), space, idx, successor, type,
+		memtx_tx_track_gap(txn, space, idx, successor, type,
 				   it->key_data.key, it->key_data.part_count);
 /*********MVCC TRANSACTION MANAGER STORY GARBAGE COLLECTION BOUND END**********/
-	}
-
-	if (!res)
+	if (res == NULL || !eq_match)
 		return 0;
-
-	*ret = memtx_tx_tuple_clarify(txn, space, *ret, idx, mk_index);
-	if (*ret == NULL) {
+	if (*ret == NULL)
 		return iterator->next_raw(iterator, ret);
-	} else {
-		tree_iterator_set_current_tuple(it, *ret);
-	}
+	tree_iterator_set_current_tuple(it, *ret);
 	return 0;
 }
 
diff --git a/test/box-luatest/gh_7113_rev_tree_iters_gap_tracking_test.lua b/test/box-luatest/gh_7113_rev_tree_iters_gap_tracking_test.lua
new file mode 100644
index 0000000000000000000000000000000000000000..e8ca8f421046ca1e499646fd58a4e37e1761bb3f
--- /dev/null
+++ b/test/box-luatest/gh_7113_rev_tree_iters_gap_tracking_test.lua
@@ -0,0 +1,136 @@
+local server = require('test.luatest_helpers.server')
+local t = require('luatest')
+
+local g = t.group()
+
+g.before_all(function()
+    g.server = server:new{
+        alias   = 'dflt',
+        box_cfg = {memtx_use_mvcc_engine = true}
+    }
+    g.server:start()
+end)
+
+g.after_all(function()
+    g.server:drop()
+end)
+
+g.before_each(function()
+    g.server:exec(function()
+        local s = box.schema.space.create('s')
+        s:create_index('pk', {parts = {{1, 'unsigned'},
+                                       {2, 'unsigned'}}})
+        s:insert{0, 0}
+        s:insert{1, 0}
+    end)
+end)
+
+g.after_each(function()
+    g.server:eval('box.space.s:drop()')
+end)
+
+g['test_reverse_iter_gap_tracking'] = function()
+    g.server:exec(function()
+        local t = require('luatest')
+        local txn_proxy = require('test.box.lua.txn_proxy')
+
+        local tx = txn_proxy:new()
+
+        local conflict_err = 'Transaction has been aborted by conflict'
+
+        tx:begin()
+        tx('box.space.s:select({1, 0}, {iterator = "LT"})')
+        box.space.s:insert{0, 1}
+        tx('box.space.s:insert{2, 0}')
+        t.assert_equals(tx:commit(), {{error = conflict_err}})
+        box.space.s:delete{0, 1}
+
+        tx:begin()
+        tx('box.space.s:select({1, 0}, {iterator = "LE"})')
+        box.space.s:insert{0, 1}
+        tx('box.space.s:insert{2, 0}')
+        t.assert_equals(tx:commit(), {{error = conflict_err}})
+        box.space.s:delete{0, 1}
+
+        tx:begin()
+        tx('box.space.s:select({1}, {iterator = "LT"})')
+        box.space.s:insert{0, 1}
+        tx('box.space.s:insert{2, 0}')
+        t.assert_equals(tx:commit(), {{error = conflict_err}})
+        box.space.s:delete{0, 1}
+
+        tx:begin()
+        tx('box.space.s:select({1}, {iterator = "LE"})')
+        box.space.s:insert{0, 1}
+        tx('box.space.s:insert{2, 0}')
+        t.assert_equals(tx:commit(), {{error = conflict_err}})
+        box.space.s:delete{0, 1}
+
+        tx:begin()
+        tx('box.space.s:select({0}, {iterator = "REQ"})')
+        box.space.s:insert{0, 1}
+        tx('box.space.s:insert{2, 0}')
+        t.assert_equals(tx:commit(), {{error = conflict_err}})
+        box.space.s:delete{0, 1}
+
+        tx:begin()
+        tx('box.space.s:select({1}, {iterator = "REQ"})')
+        box.space.s:insert{1, 1}
+        tx('box.space.s:insert{2, 0}')
+        t.assert_equals(tx:commit(), {{error = conflict_err}})
+        box.space.s:delete{1, 1}
+
+        tx:begin()
+    end)
+end
+
+g['test_reverse_iter_clarify_before_gap_tracking'] = function()
+    g.server:exec(function()
+        local t = require('luatest')
+        local txn_proxy = require('test.box.lua.txn_proxy')
+
+        local tx = txn_proxy:new()
+
+        --[[
+            The following tests are a safety net for catching the buggy case
+            when tuple clarification could be done after gap tracking
+            (gh-7073).
+        --]]
+        box.internal.memtx_tx_gc(128)
+
+        tx:begin()
+        box.space.s:delete{0, 0}
+        t.assert_equals(tx("box.space.s:select({1, 0}, {iterator = 'LT'})"),
+                        {{}})
+        tx:commit()
+        box.space.s:insert{0, 0}
+
+        tx:begin()
+        box.space.s:delete{0, 0}
+        t.assert_equals(tx("box.space.s:select({0, 0}, {iterator = 'LE'})"),
+                        {{}})
+        tx:commit()
+        box.space.s:insert{0, 0}
+
+        tx:begin()
+        box.space.s:delete{0, 0}
+        t.assert_equals(tx("box.space.s:select({1}, {iterator = 'LT'})"),
+                        {{}})
+        tx:commit()
+        box.space.s:insert{0, 0}
+
+        tx:begin()
+        box.space.s:delete{0, 0}
+        t.assert_equals(tx("box.space.s:select({0}, {iterator = 'LE'})"),
+                        {{}})
+        tx:commit()
+        box.space.s:insert{0, 0}
+
+        tx:begin()
+        box.space.s:delete{0, 0}
+        t.assert_equals(tx("box.space.s:select({0}, {iterator = 'REQ'})"),
+                        {{}})
+        tx:commit()
+        box.space.s:insert{0, 0}
+    end)
+end