From b7065d33f547038881c36c49b03ea4a9d8a7caec Mon Sep 17 00:00:00 2001
From: Aleksandr Lyapunov <alyapunov@tarantool.org>
Date: Thu, 12 Aug 2021 10:36:35 +0300
Subject: [PATCH] txm: track read instead of direct conflict in clarify

After the previous patch it became possible to link read trackers
to in-progress stories.

This patch use one read tracker instead of bunch of direct
conflicts in tuple_clarify. This is a bit accurate. Is also allows
to avoid unnecessary conflict when a transaction reads its own
change.

Part of #5999
---
 src/box/memtx_tx.c       | 12 ++----
 test/box/tx_man.result   | 81 ++++++++++++++++++++++++++++++++++++++++
 test/box/tx_man.test.lua | 25 +++++++++++++
 3 files changed, 110 insertions(+), 8 deletions(-)

diff --git a/src/box/memtx_tx.c b/src/box/memtx_tx.c
index 08a13b7658..60f910855d 100644
--- a/src/box/memtx_tx.c
+++ b/src/box/memtx_tx.c
@@ -1856,6 +1856,7 @@ memtx_tx_tuple_clarify_slow(struct txn *txn, struct space *space,
 {
 	assert(tuple->is_dirty);
 	struct memtx_story *story = memtx_tx_story_get(tuple);
+	struct memtx_story *last_checked_story = story;
 	bool own_change = false;
 	struct tuple *result = NULL;
 
@@ -1864,17 +1865,12 @@ memtx_tx_tuple_clarify_slow(struct txn *txn, struct space *space,
 					      is_prepared_ok, &own_change)) {
 			break;
 		}
-		/*
-		 * If somebody have added a tuple that we don't see, then
-		 * when he commits we must go to read view or conflicted.
-		 */
-		if (story->add_stmt != NULL && txn != NULL)
-			memtx_tx_cause_conflict(story->add_stmt->txn, txn);
 		story = story->link[index->dense_id].older_story;
 		if (story == NULL)
 			break;
+		last_checked_story = story;
 	}
-	if (!own_change && story != NULL) {
+	if (!own_change) {
 		/*
 		 * If the result tuple exists (is visible) - it is visible in
 		 * every index. But if we found a story of deleted tuple - we
@@ -1883,7 +1879,7 @@ memtx_tx_tuple_clarify_slow(struct txn *txn, struct space *space,
 		 */
 		int shift = index->dense_id & 63;
 		uint64_t mask = result == NULL ? 1ull << shift : UINT64_MAX;
-		memtx_tx_track_read_story(txn, space, story, mask);
+		memtx_tx_track_read_story(txn, space, last_checked_story, mask);
 	}
 	if (mk_index != 0) {
 		assert(false); /* TODO: multiindex */
diff --git a/test/box/tx_man.result b/test/box/tx_man.result
index e3f81d8ce6..109c48bd56 100644
--- a/test/box/tx_man.result
+++ b/test/box/tx_man.result
@@ -3436,6 +3436,87 @@ s:drop()
  | ---
  | ...
 
+-- A pair of test that was created during #5999 investigation.
+s=box.schema.space.create("s", {engine="memtx"})
+ | ---
+ | ...
+ti=s:create_index("ti", {type="tree"})
+ | ---
+ | ...
+tx1:begin()
+ | ---
+ | - 
+ | ...
+tx2:begin()
+ | ---
+ | - 
+ | ...
+tx1('s:replace{1, 1}')
+ | ---
+ | - - [1, 1]
+ | ...
+tx2('s:replace{1, 2}')
+ | ---
+ | - - [1, 2]
+ | ...
+rc1 = tx1('s:select{1}')
+ | ---
+ | ...
+rc2 = tx2('s:select{1}')
+ | ---
+ | ...
+tx1:commit() -- Must not fail
+ | ---
+ | - 
+ | ...
+tx2:commit() -- Must not fail
+ | ---
+ | - 
+ | ...
+s:drop()
+ | ---
+ | ...
+
+s=box.schema.space.create("s", {engine="memtx"})
+ | ---
+ | ...
+ti=s:create_index("ti", {type="tree"})
+ | ---
+ | ...
+tx1:begin()
+ | ---
+ | - 
+ | ...
+tx2:begin()
+ | ---
+ | - 
+ | ...
+tx1('s:replace{1, 1}')
+ | ---
+ | - - [1, 1]
+ | ...
+tx2('s:replace{1, 2}')
+ | ---
+ | - - [1, 2]
+ | ...
+rc1 = tx1('s:select{1}')
+ | ---
+ | ...
+rc2 = tx2('s:select{1}')
+ | ---
+ | ...
+tx2:commit() -- Must not fail
+ | ---
+ | - 
+ | ...
+tx1:commit() -- Must not fail
+ | ---
+ | - 
+ | ...
+s:drop()
+ | ---
+ | ...
+
 test_run:cmd("switch default")
  | ---
  | - true
diff --git a/test/box/tx_man.test.lua b/test/box/tx_man.test.lua
index 4ac4d8af30..972816f2d6 100644
--- a/test/box/tx_man.test.lua
+++ b/test/box/tx_man.test.lua
@@ -1105,6 +1105,31 @@ tx2:commit() -- MUST BE OK
 
 s:drop()
 
+-- A pair of test that was created during #5999 investigation.
+s=box.schema.space.create("s", {engine="memtx"})
+ti=s:create_index("ti", {type="tree"})
+tx1:begin()
+tx2:begin()
+tx1('s:replace{1, 1}')
+tx2('s:replace{1, 2}')
+rc1 = tx1('s:select{1}')
+rc2 = tx2('s:select{1}')
+tx1:commit() -- Must not fail
+tx2:commit() -- Must not fail
+s:drop()
+
+s=box.schema.space.create("s", {engine="memtx"})
+ti=s:create_index("ti", {type="tree"})
+tx1:begin()
+tx2:begin()
+tx1('s:replace{1, 1}')
+tx2('s:replace{1, 2}')
+rc1 = tx1('s:select{1}')
+rc2 = tx2('s:select{1}')
+tx2:commit() -- Must not fail
+tx1:commit() -- Must not fail
+s:drop()
+
 test_run:cmd("switch default")
 test_run:cmd("stop server tx_man")
 test_run:cmd("cleanup server tx_man")
-- 
GitLab