From dade56acffb3df816cf5a2da4cd0c7b696dd9105 Mon Sep 17 00:00:00 2001
From: Aleksandr Lyapunov <alyapunov@tarantool.org>
Date: Mon, 26 Jul 2021 16:10:43 +0300
Subject: [PATCH] txm: track deleted stories

In order to preserve repeated reads transactional manager tracks
read of each transactions. Generally reads can be of two types -
those that have read a tuple or that have found nothing. The first
are stored in tuple story, the second - in special gap and hole
structures.

The problem was that reads that found a dirty tuple that was
invisible to this transaction (the story says that it is deleted)
was not stored neither in story nor in gap/holes.

This patch fixes that.

Part of #6206
---
 src/box/memtx_tx.c       | 14 ++++++---
 test/box/tx_man.result   | 61 ++++++++++++++++++++++++++++++++++++++++
 test/box/tx_man.test.lua | 20 +++++++++++++
 3 files changed, 91 insertions(+), 4 deletions(-)

diff --git a/src/box/memtx_tx.c b/src/box/memtx_tx.c
index 8c4bc242f7..669640d344 100644
--- a/src/box/memtx_tx.c
+++ b/src/box/memtx_tx.c
@@ -1264,8 +1264,10 @@ memtx_tx_handle_gap_write(struct txn *txn, struct space *space,
 			struct key_def *def = index->def->key_def;
 			hint_t oh = def->key_hint(item->key, item->part_count, def);
 			hint_t kh = def->tuple_hint(tuple, def);
-			int cmp = def->tuple_compare_with_key(tuple, kh, item->key,
-							   def->part_count, oh, def);
+			int cmp = def->tuple_compare_with_key(tuple, kh,
+							      item->key,
+							      item->part_count,
+							      oh, def);
 			int dir = iterator_direction(item->type);
 			if (cmp == 0 && (item->type == ITER_EQ ||
 					 item->type == ITER_REQ ||
@@ -1785,6 +1787,10 @@ memtx_tx_history_commit_stmt(struct txn_stmt *stmt)
 	return res;
 }
 
+static int
+memtx_tx_track_read_story(struct txn *txn, struct space *space,
+			  struct memtx_story *story);
+
 struct tuple *
 memtx_tx_tuple_clarify_slow(struct txn *txn, struct space *space,
 			    struct tuple *tuple, struct index *index,
@@ -1810,8 +1816,8 @@ memtx_tx_tuple_clarify_slow(struct txn *txn, struct space *space,
 		if (story == NULL)
 			break;
 	}
-	if (!own_change && result != NULL)
-		memtx_tx_track_read(txn, space, result);
+	if (!own_change && story != NULL)
+		memtx_tx_track_read_story(txn, space, story);
 	if (mk_index != 0) {
 		assert(false); /* TODO: multiindex */
 		panic("multikey indexes are not supported int TX manager");
diff --git a/test/box/tx_man.result b/test/box/tx_man.result
index 49c53f0971..e6a83ada27 100644
--- a/test/box/tx_man.result
+++ b/test/box/tx_man.result
@@ -3098,6 +3098,67 @@ s:drop()
  | ---
  | ...
 
+--https://github.com/tarantool/tarantool/issues/6206
+spc = box.schema.space.create('test')
+ | ---
+ | ...
+_ = spc:create_index('test', {type='tree'})
+ | ---
+ | ...
+
+tx1:begin()
+ | ---
+ | - 
+ | ...
+tx1('spc:select{}')
+ | ---
+ | - - []
+ | ...
+spc:replace{1, 1}
+ | ---
+ | - [1, 1]
+ | ...
+tx1('spc:select{}')
+ | ---
+ | - - []
+ | ...
+tx1:commit()
+ | ---
+ | - 
+ | ...
+
+spc:delete{1}
+ | ---
+ | - [1, 1]
+ | ...
+tx2 = txn_proxy.new()
+ | ---
+ | ...
+tx2:begin()
+ | ---
+ | - 
+ | ...
+tx2('spc:select{}')
+ | ---
+ | - - []
+ | ...
+spc:replace{1, 1}
+ | ---
+ | - [1, 1]
+ | ...
+tx2('spc:select{}')
+ | ---
+ | - - []
+ | ...
+tx2:commit()
+ | ---
+ | - 
+ | ...
+
+spc:drop()
+ | ---
+ | ...
+
 test_run:cmd("switch default")
  | ---
  | - true
diff --git a/test/box/tx_man.test.lua b/test/box/tx_man.test.lua
index 5c9765bd2b..9e1827884b 100644
--- a/test/box/tx_man.test.lua
+++ b/test/box/tx_man.test.lua
@@ -984,6 +984,26 @@ s:replace{0, 1, 0}
 
 s:drop()
 
+--https://github.com/tarantool/tarantool/issues/6206
+spc = box.schema.space.create('test')
+_ = spc:create_index('test', {type='tree'})
+
+tx1:begin()
+tx1('spc:select{}')
+spc:replace{1, 1}
+tx1('spc:select{}')
+tx1:commit()
+
+spc:delete{1}
+tx2 = txn_proxy.new()
+tx2:begin()
+tx2('spc:select{}')
+spc:replace{1, 1}
+tx2('spc:select{}')
+tx2:commit()
+
+spc:drop()
+
 test_run:cmd("switch default")
 test_run:cmd("stop server tx_man")
 test_run:cmd("cleanup server tx_man")
-- 
GitLab