From 921a07177521cfdea7c59ec84a78b19be7ef44ff Mon Sep 17 00:00:00 2001
From: Vladimir Davydov <vdavydov@tarantool.org>
Date: Tue, 7 Mar 2023 15:58:48 +0300
Subject: [PATCH] memtx: add stubs to keep track of upgraded read view tuples

If a read view is created while space upgrade is in progress, tuples
fetched from the read view may be either upgraded or not. We need to
be able to differentiate those tuples so that we can use the appropriate
tuple format for them. To achieve that this commit adds the following
function stubs:

 - memtx_space_upgrade_track_tuple and memtx_space_upgrade_untrack_tuple
   will be used to maintain a set of all upgraded tuples.
 - memtx_read_view_tuple_needs_upgrade will do a lookup in the set of
   all upgraded tuples to check if a tuple needs upgrade.

The stubs will be implemented in the EE repository.

Note that we have to call memtx_space_upgrade_untrack_tuple from
memtx_engine_rollback_statement. The problem is that the space may be
deleted while a transaction is inprogress, in which case we must not
access space->upgrade in memtx_engine_rollback_statement. Fortunately,
we call memtx_tx_on_space_delete when a memtx space is altered to
rollback memtx transactions. So to handle this situation we set
txn_stmt->engine_savepoint to NULL from memtx_tx_history_remove_stmt
called from memtx_tx_on_space_delete. This makes the rollback function
return early.

Needed for tarantool/tarantool-ee#236

NO_DOC=ee
NO_TEST=ee
NO_CHANGELOG=ee
---
 src/box/index.h               |  7 ++++++
 src/box/memtx_engine.cc       | 21 +++++++++++++---
 src/box/memtx_space.c         |  2 ++
 src/box/memtx_space_upgrade.h | 47 +++++++++++++++++++++++++++++++++++
 src/box/memtx_tx.c            |  1 +
 5 files changed, 74 insertions(+), 4 deletions(-)

diff --git a/src/box/index.h b/src/box/index.h
index 7439da5612..4f97db1a55 100644
--- a/src/box/index.h
+++ b/src/box/index.h
@@ -658,6 +658,12 @@ struct index {
  * Auxiliary struct used for returning tuple data fetched from a read view.
  */
 struct read_view_tuple {
+	/**
+	 * If a read view was created while a space upgrade was running,
+	 * a tuple fetched from the read view may be either upgraded or not.
+	 * This flag is set if the tuple needs to be upgraded.
+	 */
+	bool needs_upgrade;
 	/** Pointer to tuple data. */
 	const char *data;
 	/** Size of tuple data. */
@@ -669,6 +675,7 @@ static inline struct read_view_tuple
 read_view_tuple_none(void)
 {
 	struct read_view_tuple tuple;
+	tuple.needs_upgrade = false;
 	tuple.data = NULL;
 	tuple.size = 0;
 	return tuple;
diff --git a/src/box/memtx_engine.cc b/src/box/memtx_engine.cc
index 9832beb282..44f99f47ac 100644
--- a/src/box/memtx_engine.cc
+++ b/src/box/memtx_engine.cc
@@ -29,7 +29,6 @@
  * SUCH DAMAGE.
  */
 #include "memtx_engine.h"
-#include "memtx_space.h"
 
 #include <small/quota.h>
 #include <small/small.h>
@@ -56,6 +55,7 @@
 #include "read_view.h"
 #include "memtx_tuple_compression.h"
 #include "memtx_space.h"
+#include "memtx_space_upgrade.h"
 
 #include <type_traits>
 
@@ -600,15 +600,22 @@ static void
 memtx_engine_commit(struct engine *engine, struct txn *txn)
 {
 	(void)engine;
-	if (memtx_tx_manager_use_mvcc_engine) {
-		struct txn_stmt *stmt;
-		stailq_foreach_entry(stmt, &txn->stmts, next) {
+	struct txn_stmt *stmt;
+	stailq_foreach_entry(stmt, &txn->stmts, next) {
+		if (memtx_tx_manager_use_mvcc_engine) {
 			assert(stmt->space->engine == engine);
 			struct memtx_space *mspace =
 				(struct memtx_space *)stmt->space;
 			size_t *bsize = &mspace->bsize;
 			memtx_tx_history_commit_stmt(stmt, bsize);
 		}
+		if (stmt->engine_savepoint != NULL) {
+			struct space *space = stmt->space;
+			struct tuple *old_tuple = stmt->rollback_info.old_tuple;
+			if (space->upgrade != NULL && old_tuple != NULL)
+				memtx_space_upgrade_untrack_tuple(
+						space->upgrade, old_tuple);
+		}
 	}
 }
 
@@ -634,6 +641,9 @@ memtx_engine_rollback_statement(struct engine *engine, struct txn *txn,
 	if (stmt->engine_savepoint == NULL)
 		return;
 
+	if (space->upgrade != NULL && new_tuple != NULL)
+		memtx_space_upgrade_untrack_tuple(space->upgrade, new_tuple);
+
 	if (memtx_tx_manager_use_mvcc_engine)
 		return memtx_tx_history_rollback_stmt(stmt);
 
@@ -1706,6 +1716,9 @@ memtx_prepare_read_view_tuple(struct tuple *tuple,
 		*result = read_view_tuple_none();
 		return 0;
 	}
+	result->needs_upgrade = index->space->upgrade == NULL ? false :
+				memtx_read_view_tuple_needs_upgrade(
+					index->space->upgrade, tuple);
 	result->data = tuple_data_range(tuple, &result->size);
 	if (!index->space->rv->disable_decompression) {
 		result->data = memtx_tuple_decompress_raw(
diff --git a/src/box/memtx_space.c b/src/box/memtx_space.c
index aad44096de..435096ac58 100644
--- a/src/box/memtx_space.c
+++ b/src/box/memtx_space.c
@@ -371,6 +371,8 @@ memtx_space_replace_tuple(struct space *space, struct txn_stmt *stmt,
 		tuple_ref(stmt->old_tuple);
 		tuple_unref(orig_old_tuple);
 	}
+	if (space->upgrade != NULL && new_tuple != NULL)
+		memtx_space_upgrade_track_tuple(space->upgrade, new_tuple);
 finish:
 	/*
 	 * Regardless of whether the function ended with success or
diff --git a/src/box/memtx_space_upgrade.h b/src/box/memtx_space_upgrade.h
index 100912b1a3..3129615801 100644
--- a/src/box/memtx_space_upgrade.h
+++ b/src/box/memtx_space_upgrade.h
@@ -11,16 +11,63 @@
 # include "memtx_space_upgrade_impl.h"
 #else /* !defined(ENABLE_SPACE_UPGRADE) */
 
+#include <stdbool.h>
+
+#include "trivia/util.h"
+
 #if defined(__cplusplus)
 extern "C" {
 #endif /* defined(__cplusplus) */
 
 struct space;
+struct space_upgrade;
+struct space_upgrade_read_view;
+struct tuple;
 
 /** Memtx implementation of space_vtab::prepare_upgrade. */
 int
 memtx_space_prepare_upgrade(struct space *old_space, struct space *new_space);
 
+/**
+ * Adds a tuple to the upgraded tuple set.
+ * The tuple must not be in the set.
+ */
+static inline void
+memtx_space_upgrade_track_tuple(struct space_upgrade *upgrade,
+				struct tuple *tuple)
+{
+	(void)upgrade;
+	(void)tuple;
+	unreachable();
+}
+
+/**
+ * Removes a tuple from the upgraded tuple set.
+ * Does nothing if the tuple isn't in the set.
+ */
+static inline void
+memtx_space_upgrade_untrack_tuple(struct space_upgrade *upgrade,
+				  struct tuple *tuple)
+{
+	(void)upgrade;
+	(void)tuple;
+	unreachable();
+}
+
+/**
+ * Returns true if a tuple fetched from a read view needs to be upgraded.
+ * See the comment to read_view_tuple::needs_upgrade.
+ */
+static inline bool
+memtx_read_view_tuple_needs_upgrade(struct space_upgrade_read_view *rv,
+				    struct tuple *tuple)
+{
+	(void)rv;
+	(void)tuple;
+	unreachable();
+	return false;
+}
+
 #if defined(__cplusplus)
 } /* extern "C" */
 #endif /* defined(__cplusplus) */
diff --git a/src/box/memtx_tx.c b/src/box/memtx_tx.c
index ec78ceaa52..2a555faaf4 100644
--- a/src/box/memtx_tx.c
+++ b/src/box/memtx_tx.c
@@ -2382,6 +2382,7 @@ memtx_tx_history_remove_stmt(struct txn_stmt *stmt)
 		memtx_tx_history_remove_added_story(stmt);
 	if (stmt->del_story != NULL)
 		memtx_tx_history_remove_deleted_story(stmt);
+	stmt->engine_savepoint = NULL;
 }
 
 /**
-- 
GitLab