From 5e340b6eeb3c01a334f808b026b94a8c7a98c842 Mon Sep 17 00:00:00 2001
From: Vladimir Davydov <vdavydov@tarantool.org>
Date: Mon, 28 Mar 2022 13:55:11 +0300
Subject: [PATCH] memtx: drop UNCHANGED (get = get_raw) index vtab optimization

We use a special, less efficient index vtab if a space can store
compressed tuples. The problem is it isn't enough to look at a space
definition to figure out if there are compressed tuples in the space:
there may be compressed tuples left from before the alter operation that
disabled compression, since we don't rebuild tuples on alter. To update
an index vtab dynamically, we implement some complicated logic, but
it's buggy (results in a test failure in EE). Fixing it requires some
non-trivial effort, because a vtab may be changed after index creation
(when a space format is updated).

Let's drop this optimization altogether for now and use the same vtab
for both compressed and uncompressed indexes. We might return to this
issue in future, but first we need to run some benchmarks to check if
this optimization is worth the complexity. Possible ways how we could
resurrect this optimization:
 - Call get_raw from get directly (without function pointer), inline
   memtx_prepare_result_tuple, and move is_compressed flag to struct
   tuple for better cache locality.
 - Rebuild all tuples on space alter and use a different vtab for
   compressed indexes.

NO_DOC=bug fix
NO_TEST=enterprise
NO_CHANGELOG=unrelased
---
 src/box/memtx_bitset.cc |  98 +++++++++-----------------
 src/box/memtx_bitset.h  |   9 ---
 src/box/memtx_engine.cc |   4 +-
 src/box/memtx_hash.cc   | 122 +++++++++++----------------------
 src/box/memtx_hash.h    |   9 ---
 src/box/memtx_rtree.cc  |  99 +++++++++------------------
 src/box/memtx_rtree.h   |   9 ---
 src/box/memtx_space.c   |  75 ++------------------
 src/box/memtx_space.h   |  21 ------
 src/box/memtx_tree.cc   | 148 +++++++++++++---------------------------
 src/box/memtx_tree.h    |   9 ---
 src/box/memtx_tx.c      |   7 +-
 src/box/memtx_tx.h      |   4 +-
 13 files changed, 159 insertions(+), 455 deletions(-)

diff --git a/src/box/memtx_bitset.cc b/src/box/memtx_bitset.cc
index cbfb004cf8..e3e7c718f7 100644
--- a/src/box/memtx_bitset.cc
+++ b/src/box/memtx_bitset.cc
@@ -335,7 +335,6 @@ memtx_bitset_index_replace(struct index *base, struct tuple *old_tuple,
 	return 0;
 }
 
-template <bool UNCHANGED>
 static struct iterator *
 memtx_bitset_index_create_iterator(struct index *base, enum iterator_type type,
 				   const char *key, uint32_t part_count)
@@ -357,7 +356,7 @@ memtx_bitset_index_create_iterator(struct index *base, enum iterator_type type,
 	iterator_create(&it->base, base);
 	it->pool = &memtx->iterator_pool;
 	it->base.next_raw = bitset_index_iterator_next_raw;
-	it->base.next = UNCHANGED ? it->base.next_raw : memtx_iterator_next;
+	it->base.next = memtx_iterator_next;
 	it->base.free = bitset_index_iterator_free;
 
 	tt_bitset_iterator_create(&it->bitset_it, realloc);
@@ -484,62 +483,36 @@ memtx_bitset_index_count(struct index *base, enum iterator_type type,
 	return generic_index_count(base, type, key, part_count);
 }
 
-/**
- * Get index vtab by @a UNCHANGED, template version.
- * If UNCHANGED == true iterator->next and index->get
- * functions are the same as it's raw versions.
- */
-template <bool UNCHANGED>
-static const struct index_vtab *
-get_memtx_bitset_index_vtab(void)
-{
-	static const struct index_vtab vtab = {
-		/* .destroy = */ memtx_bitset_index_destroy,
-		/* .commit_create = */ generic_index_commit_create,
-		/* .abort_create = */ generic_index_abort_create,
-		/* .commit_modify = */ generic_index_commit_modify,
-		/* .commit_drop = */ generic_index_commit_drop,
-		/* .update_def = */ generic_index_update_def,
-		/* .depends_on_pk = */ generic_index_depends_on_pk,
-		/* .def_change_requires_rebuild = */
-			memtx_index_def_change_requires_rebuild,
-		/* .size = */ memtx_bitset_index_size,
-		/* .bsize = */ memtx_bitset_index_bsize,
-		/* .min = */ generic_index_min,
-		/* .max = */ generic_index_max,
-		/* .random = */ generic_index_random,
-		/* .count = */ memtx_bitset_index_count,
-		/* .get_raw = */ generic_index_get_raw,
-		/* .get = */ UNCHANGED ? generic_index_get_raw :
-			     generic_index_get,
-		/* .replace = */ memtx_bitset_index_replace,
-		/* .create_iterator = */
-			memtx_bitset_index_create_iterator<UNCHANGED>,
-		/* .create_snapshot_iterator = */
-			generic_index_create_snapshot_iterator,
-		/* .stat = */ generic_index_stat,
-		/* .compact = */ generic_index_compact,
-		/* .reset_stat = */ generic_index_reset_stat,
-		/* .begin_build = */ generic_index_begin_build,
-		/* .reserve = */ generic_index_reserve,
-		/* .build_next = */ generic_index_build_next,
-		/* .end_build = */ generic_index_end_build,
-	};
-	return &vtab;
-}
-
-/**
- * Get index vtab by @a unchanged, argument version.
- */
-static const struct index_vtab *
-get_memtx_bitset_index_vtab(bool unchanged)
-{
-	static const index_vtab *choice[2] = {
-		get_memtx_bitset_index_vtab<false>(),
-		get_memtx_bitset_index_vtab<true>()
-	};
-	return choice[unchanged];
-}
+static const struct index_vtab memtx_bitset_index_vtab = {
+	/* .destroy = */ memtx_bitset_index_destroy,
+	/* .commit_create = */ generic_index_commit_create,
+	/* .abort_create = */ generic_index_abort_create,
+	/* .commit_modify = */ generic_index_commit_modify,
+	/* .commit_drop = */ generic_index_commit_drop,
+	/* .update_def = */ generic_index_update_def,
+	/* .depends_on_pk = */ generic_index_depends_on_pk,
+	/* .def_change_requires_rebuild = */
+		memtx_index_def_change_requires_rebuild,
+	/* .size = */ memtx_bitset_index_size,
+	/* .bsize = */ memtx_bitset_index_bsize,
+	/* .min = */ generic_index_min,
+	/* .max = */ generic_index_max,
+	/* .random = */ generic_index_random,
+	/* .count = */ memtx_bitset_index_count,
+	/* .get_raw = */ generic_index_get_raw,
+	/* .get = */ generic_index_get,
+	/* .replace = */ memtx_bitset_index_replace,
+	/* .create_iterator = */ memtx_bitset_index_create_iterator,
+	/* .create_snapshot_iterator = */
+		generic_index_create_snapshot_iterator,
+	/* .stat = */ generic_index_stat,
+	/* .compact = */ generic_index_compact,
+	/* .reset_stat = */ generic_index_reset_stat,
+	/* .begin_build = */ generic_index_begin_build,
+	/* .reserve = */ generic_index_reserve,
+	/* .build_next = */ generic_index_build_next,
+	/* .end_build = */ generic_index_end_build,
+};
 
 struct index *
 memtx_bitset_index_new(struct memtx_engine *memtx, struct index_def *def)
@@ -554,9 +527,8 @@ memtx_bitset_index_new(struct memtx_engine *memtx, struct index_def *def)
 			 "malloc", "struct memtx_bitset_index");
 		return NULL;
 	}
-	const struct index_vtab *vtab = get_memtx_bitset_index_vtab(true);
 	if (index_create(&index->base, (struct engine *)memtx,
-			 vtab, def) != 0) {
+			 &memtx_bitset_index_vtab, def) != 0) {
 		free(index);
 		return NULL;
 	}
@@ -575,9 +547,3 @@ memtx_bitset_index_new(struct memtx_engine *memtx, struct index_def *def)
 	tt_bitset_index_create(&index->index, realloc);
 	return &index->base;
 }
-
-void
-memtx_bitset_index_set_vtab(struct index *index, bool unchanged)
-{
-	index->vtab = get_memtx_bitset_index_vtab(unchanged);
-}
diff --git a/src/box/memtx_bitset.h b/src/box/memtx_bitset.h
index 80ed052bc2..148d463d81 100644
--- a/src/box/memtx_bitset.h
+++ b/src/box/memtx_bitset.h
@@ -42,15 +42,6 @@ struct memtx_engine;
 struct index *
 memtx_bitset_index_new(struct memtx_engine *memtx, struct index_def *def);
 
-/**
- * Change @a index vtab according to @a unchanged argument.
- * If @a unchanged is false it's mean that tuple in index should
- * be transformed before return it to user, so we need special
- * `index_get` and `iterator_next` functions version.
- */
-void
-memtx_bitset_index_set_vtab(struct index *index, bool unchanged);
-
 #if defined(__cplusplus)
 } /* extern "C" */
 #endif /* defined(__cplusplus) */
diff --git a/src/box/memtx_engine.cc b/src/box/memtx_engine.cc
index 2007514782..d81e937b63 100644
--- a/src/box/memtx_engine.cc
+++ b/src/box/memtx_engine.cc
@@ -535,8 +535,7 @@ memtx_engine_commit(struct engine *engine, struct txn *txn)
 			struct memtx_space *mspace =
 				(struct memtx_space *)stmt->space;
 			size_t *bsize = &mspace->bsize;
-			uint64_t *ctuples = &mspace->compressed_tuples;
-			memtx_tx_history_commit_stmt(stmt, bsize, ctuples);
+			memtx_tx_history_commit_stmt(stmt, bsize);
 		}
 	}
 }
@@ -586,7 +585,6 @@ memtx_engine_rollback_statement(struct engine *engine, struct txn *txn,
 	}
 
 	memtx_space_update_bsize(space, new_tuple, old_tuple);
-	memtx_space_update_compressed_tuples(space, new_tuple, old_tuple);
 	if (old_tuple != NULL)
 		tuple_ref(old_tuple);
 	if (new_tuple != NULL)
diff --git a/src/box/memtx_hash.cc b/src/box/memtx_hash.cc
index 66dcd1945f..79825a0051 100644
--- a/src/box/memtx_hash.cc
+++ b/src/box/memtx_hash.cc
@@ -102,7 +102,6 @@ hash_iterator_free(struct iterator *iterator)
 	mempool_free(it->pool, it);
 }
 
-template <bool UNCHANGED>
 static int
 hash_iterator_ge_raw_base(struct iterator *ptr, struct tuple **ret)
 {
@@ -115,14 +114,11 @@ hash_iterator_ge_raw_base(struct iterator *ptr, struct tuple **ret)
 	return 0;
 }
 
-template <bool UNCHANGED>
 static int
 hash_iterator_gt_raw_base(struct iterator *ptr, struct tuple **ret)
 {
 	assert(ptr->free == hash_iterator_free);
-	ptr->next_raw = hash_iterator_ge_raw_base<UNCHANGED>;
-	if (UNCHANGED)
-		ptr->next = ptr->next_raw;
+	ptr->next_raw = hash_iterator_ge_raw_base;
 	struct hash_iterator *it = (struct hash_iterator *) ptr;
 	struct memtx_hash_index *index = (struct memtx_hash_index *)ptr->index;
 	struct tuple **res = light_index_iterator_get_and_next(&index->hash_table,
@@ -135,7 +131,6 @@ hash_iterator_gt_raw_base(struct iterator *ptr, struct tuple **ret)
 }
 
 #define WRAP_ITERATOR_METHOD(name)						\
-template <bool UNCHANGED>							\
 static int									\
 name(struct iterator *iterator, struct tuple **ret)				\
 {										\
@@ -146,8 +141,8 @@ name(struct iterator *iterator, struct tuple **ret)				\
 	bool is_first = true;							\
 	do {									\
 		int rc = is_first ?						\
-			name##_base<UNCHANGED>(iterator, ret) :			\
-			hash_iterator_ge_raw_base<UNCHANGED>(iterator, ret);	\
+			name##_base(iterator, ret) :				\
+			hash_iterator_ge_raw_base(iterator, ret);		\
 		if (rc != 0 || *ret == NULL)					\
 			return rc;						\
 		is_first = false;						\
@@ -169,15 +164,12 @@ tree_iterator_dummie(MAYBE_UNUSED struct iterator *it, struct tuple **ret)
 	return 0;
 }
 
-template <bool UNCHANGED>
 static int
 hash_iterator_raw_eq(struct iterator *it, struct tuple **ret)
 {
 	it->next_raw = tree_iterator_dummie;
-	if (UNCHANGED)
-		it->next = it->next_raw;
 	/* always returns zero. */
-	hash_iterator_ge_raw_base<UNCHANGED>(it, ret);
+	hash_iterator_ge_raw_base(it, ret);
 	if (*ret == NULL)
 		return 0;
 	struct txn *txn = in_txn();
@@ -413,7 +405,6 @@ memtx_hash_index_replace(struct index *base, struct tuple *old_tuple,
 	return 0;
 }
 
-template <bool UNCHANGED>
 static struct iterator *
 memtx_hash_index_create_iterator(struct index *base, enum iterator_type type,
 				 const char *key, uint32_t part_count)
@@ -440,10 +431,10 @@ memtx_hash_index_create_iterator(struct index *base, enum iterator_type type,
 		if (part_count != 0) {
 			light_index_iterator_key(&index->hash_table, &it->iterator,
 					key_hash(key, base->def->key_def), key);
-			it->base.next_raw = hash_iterator_gt_raw<UNCHANGED>;
+			it->base.next_raw = hash_iterator_gt_raw;
 		} else {
 			light_index_iterator_begin(&index->hash_table, &it->iterator);
-			it->base.next_raw = hash_iterator_ge_raw<UNCHANGED>;
+			it->base.next_raw = hash_iterator_ge_raw;
 		}
 		/* This iterator needs to be supported as a legacy. */
 		memtx_tx_track_full_scan(in_txn(),
@@ -452,7 +443,7 @@ memtx_hash_index_create_iterator(struct index *base, enum iterator_type type,
 		break;
 	case ITER_ALL:
 		light_index_iterator_begin(&index->hash_table, &it->iterator);
-		it->base.next_raw = hash_iterator_ge_raw<UNCHANGED>;
+		it->base.next_raw = hash_iterator_ge_raw;
 		memtx_tx_track_full_scan(in_txn(),
 					 space_by_id(it->base.space_id),
 					 &index->base);
@@ -461,7 +452,7 @@ memtx_hash_index_create_iterator(struct index *base, enum iterator_type type,
 		assert(part_count > 0);
 		light_index_iterator_key(&index->hash_table, &it->iterator,
 				key_hash(key, base->def->key_def), key);
-		it->base.next_raw = hash_iterator_raw_eq<UNCHANGED>;
+		it->base.next_raw = hash_iterator_raw_eq;
 		if (it->iterator.slotpos == light_index_end)
 			memtx_tx_track_point(in_txn(),
 					     space_by_id(it->base.space_id),
@@ -473,7 +464,7 @@ memtx_hash_index_create_iterator(struct index *base, enum iterator_type type,
 		mempool_free(&memtx->iterator_pool, it);
 		return NULL;
 	}
-	it->base.next = UNCHANGED ? it->base.next_raw : memtx_iterator_next;
+	it->base.next = memtx_iterator_next;
 	return (struct iterator *)it;
 }
 
@@ -564,62 +555,36 @@ memtx_hash_index_create_snapshot_iterator(struct index *base)
 	return (struct snapshot_iterator *) it;
 }
 
-/**
- * Get index vtab by @a UNCHANGED, template version.
- * If UNCHANGED == true iterator->next and index->get
- * functions are the same as it's raw versions.
- */
-template <bool UNCHANGED>
-static const struct index_vtab *
-get_memtx_hash_index_vtab(void)
-{
-	static const struct index_vtab vtab = {
-		/* .destroy = */ memtx_hash_index_destroy,
-		/* .commit_create = */ generic_index_commit_create,
-		/* .abort_create = */ generic_index_abort_create,
-		/* .commit_modify = */ generic_index_commit_modify,
-		/* .commit_drop = */ generic_index_commit_drop,
-		/* .update_def = */ memtx_hash_index_update_def,
-		/* .depends_on_pk = */ generic_index_depends_on_pk,
-		/* .def_change_requires_rebuild = */
-			memtx_index_def_change_requires_rebuild,
-		/* .size = */ memtx_hash_index_size,
-		/* .bsize = */ memtx_hash_index_bsize,
-		/* .min = */ generic_index_min,
-		/* .max = */ generic_index_max,
-		/* .random = */ memtx_hash_index_random,
-		/* .count = */ memtx_hash_index_count,
-		/* .get_raw = */ memtx_hash_index_get_raw,
-		/* .get = */ UNCHANGED ? memtx_hash_index_get_raw :
-			memtx_index_get,
-		/* .replace = */ memtx_hash_index_replace,
-		/* .create_iterator = */
-			memtx_hash_index_create_iterator<UNCHANGED>,
-		/* .create_snapshot_iterator = */
-			memtx_hash_index_create_snapshot_iterator,
-		/* .stat = */ generic_index_stat,
-		/* .compact = */ generic_index_compact,
-		/* .reset_stat = */ generic_index_reset_stat,
-		/* .begin_build = */ generic_index_begin_build,
-		/* .reserve = */ generic_index_reserve,
-		/* .build_next = */ generic_index_build_next,
-		/* .end_build = */ generic_index_end_build,
-	};
-	return &vtab;
-}
-
-/**
- * Get index vtab by @a unchanged, argument version.
- */
-static const struct index_vtab *
-get_memtx_hash_index_vtab(bool unchanged)
-{
-	static const index_vtab *choice[2] = {
-		get_memtx_hash_index_vtab<false>(),
-		get_memtx_hash_index_vtab<true>()
-	};
-	return choice[unchanged];
-}
+static const struct index_vtab memtx_hash_index_vtab = {
+	/* .destroy = */ memtx_hash_index_destroy,
+	/* .commit_create = */ generic_index_commit_create,
+	/* .abort_create = */ generic_index_abort_create,
+	/* .commit_modify = */ generic_index_commit_modify,
+	/* .commit_drop = */ generic_index_commit_drop,
+	/* .update_def = */ memtx_hash_index_update_def,
+	/* .depends_on_pk = */ generic_index_depends_on_pk,
+	/* .def_change_requires_rebuild = */
+		memtx_index_def_change_requires_rebuild,
+	/* .size = */ memtx_hash_index_size,
+	/* .bsize = */ memtx_hash_index_bsize,
+	/* .min = */ generic_index_min,
+	/* .max = */ generic_index_max,
+	/* .random = */ memtx_hash_index_random,
+	/* .count = */ memtx_hash_index_count,
+	/* .get_raw = */ memtx_hash_index_get_raw,
+	/* .get = */ memtx_index_get,
+	/* .replace = */ memtx_hash_index_replace,
+	/* .create_iterator = */ memtx_hash_index_create_iterator,
+	/* .create_snapshot_iterator = */
+		memtx_hash_index_create_snapshot_iterator,
+	/* .stat = */ generic_index_stat,
+	/* .compact = */ generic_index_compact,
+	/* .reset_stat = */ generic_index_reset_stat,
+	/* .begin_build = */ generic_index_begin_build,
+	/* .reserve = */ generic_index_reserve,
+	/* .build_next = */ generic_index_build_next,
+	/* .end_build = */ generic_index_end_build,
+};
 
 struct index *
 memtx_hash_index_new(struct memtx_engine *memtx, struct index_def *def)
@@ -631,9 +596,8 @@ memtx_hash_index_new(struct memtx_engine *memtx, struct index_def *def)
 			 "malloc", "struct memtx_hash_index");
 		return NULL;
 	}
-	const struct index_vtab *vtab = get_memtx_hash_index_vtab(true);
 	if (index_create(&index->base, (struct engine *)memtx,
-			 vtab, def) != 0) {
+			 &memtx_hash_index_vtab, def) != 0) {
 		free(index);
 		return NULL;
 	}
@@ -644,10 +608,4 @@ memtx_hash_index_new(struct memtx_engine *memtx, struct index_def *def)
 	return &index->base;
 }
 
-void
-memtx_hash_index_set_vtab(struct index *index, bool unchanged)
-{
-	index->vtab = get_memtx_hash_index_vtab(unchanged);
-}
-
 /* }}} */
diff --git a/src/box/memtx_hash.h b/src/box/memtx_hash.h
index a110ae71ea..2dba3f5c5b 100644
--- a/src/box/memtx_hash.h
+++ b/src/box/memtx_hash.h
@@ -42,15 +42,6 @@ struct memtx_engine;
 struct index *
 memtx_hash_index_new(struct memtx_engine *memtx, struct index_def *def);
 
-/**
- * Change @a index vtab according to @a unchanged argument.
- * If @a unchanged is false it's mean that tuple in index should
- * be transformed before return it to user, so we need special
- * `index_get` and `iterator_next` functions version.
- */
-void
-memtx_hash_index_set_vtab(struct index *index, bool unchanged);
-
 #if defined(__cplusplus)
 } /* extern "C" */
 #endif /* defined(__cplusplus) */
diff --git a/src/box/memtx_rtree.cc b/src/box/memtx_rtree.cc
index 32f69e8f73..69d57376d2 100644
--- a/src/box/memtx_rtree.cc
+++ b/src/box/memtx_rtree.cc
@@ -294,7 +294,6 @@ memtx_rtree_index_reserve(struct index *base, uint32_t size_hint)
 	return memtx_index_extent_reserve(memtx, RESERVE_EXTENTS_BEFORE_REPLACE);
 }
 
-template <bool UNCHANGED>
 static struct iterator *
 memtx_rtree_index_create_iterator(struct index *base,  enum iterator_type type,
 				  const char *key, uint32_t part_count)
@@ -356,8 +355,7 @@ memtx_rtree_index_create_iterator(struct index *base,  enum iterator_type type,
 	iterator_create(&it->base, base);
 	it->pool = &memtx->rtree_iterator_pool;
 	it->base.next_raw = index_rtree_iterator_next_raw;
-	it->base.next = UNCHANGED ? index_rtree_iterator_next_raw :
-		memtx_iterator_next;
+	it->base.next = memtx_iterator_next;
 	it->base.free = index_rtree_iterator_free;
 	rtree_iterator_init(&it->impl);
 	/*
@@ -371,62 +369,36 @@ memtx_rtree_index_create_iterator(struct index *base,  enum iterator_type type,
 	return (struct iterator *)it;
 }
 
-/**
- * Get index vtab by @a UNCHANGED, template version.
- * If UNCHANGED == true iterator->next and index->get
- * functions are the same as it's raw versions.
- */
-template <bool UNCHANGED>
-static const struct index_vtab *
-get_memtx_rtree_index_vtab(void)
-{
-	static const struct index_vtab vtab = {
-		/* .destroy = */ memtx_rtree_index_destroy,
-		/* .commit_create = */ generic_index_commit_create,
-		/* .abort_create = */ generic_index_abort_create,
-		/* .commit_modify = */ generic_index_commit_modify,
-		/* .commit_drop = */ generic_index_commit_drop,
-		/* .update_def = */ generic_index_update_def,
-		/* .depends_on_pk = */ generic_index_depends_on_pk,
-		/* .def_change_requires_rebuild = */
-			memtx_rtree_index_def_change_requires_rebuild,
-		/* .size = */ memtx_rtree_index_size,
-		/* .bsize = */ memtx_rtree_index_bsize,
-		/* .min = */ generic_index_min,
-		/* .max = */ generic_index_max,
-		/* .random = */ generic_index_random,
-		/* .count = */ memtx_rtree_index_count,
-		/* .get_raw = */ memtx_rtree_index_get_raw,
-		/* .get = */ UNCHANGED ? memtx_rtree_index_get_raw :
-			memtx_index_get,
-		/* .replace = */ memtx_rtree_index_replace,
-		/* .create_iterator = */
-			memtx_rtree_index_create_iterator<UNCHANGED>,
-		/* .create_snapshot_iterator = */
-			generic_index_create_snapshot_iterator,
-		/* .stat = */ generic_index_stat,
-		/* .compact = */ generic_index_compact,
-		/* .reset_stat = */ generic_index_reset_stat,
-		/* .begin_build = */ generic_index_begin_build,
-		/* .reserve = */ memtx_rtree_index_reserve,
-		/* .build_next = */ generic_index_build_next,
-		/* .end_build = */ generic_index_end_build,
-	};
-	return &vtab;
-}
-
-/**
- * Get index vtab by @a unchanged, argument version.
- */
-static const struct index_vtab *
-get_memtx_rtree_index_vtab(bool unchanged)
-{
-	static const index_vtab *choice[2] = {
-		get_memtx_rtree_index_vtab<false>(),
-		get_memtx_rtree_index_vtab<true>()
-	};
-	return choice[unchanged];
-}
+static const struct index_vtab memtx_rtree_index_vtab = {
+	/* .destroy = */ memtx_rtree_index_destroy,
+	/* .commit_create = */ generic_index_commit_create,
+	/* .abort_create = */ generic_index_abort_create,
+	/* .commit_modify = */ generic_index_commit_modify,
+	/* .commit_drop = */ generic_index_commit_drop,
+	/* .update_def = */ generic_index_update_def,
+	/* .depends_on_pk = */ generic_index_depends_on_pk,
+	/* .def_change_requires_rebuild = */
+		memtx_rtree_index_def_change_requires_rebuild,
+	/* .size = */ memtx_rtree_index_size,
+	/* .bsize = */ memtx_rtree_index_bsize,
+	/* .min = */ generic_index_min,
+	/* .max = */ generic_index_max,
+	/* .random = */ generic_index_random,
+	/* .count = */ memtx_rtree_index_count,
+	/* .get_raw = */ memtx_rtree_index_get_raw,
+	/* .get = */ memtx_index_get,
+	/* .replace = */ memtx_rtree_index_replace,
+	/* .create_iterator = */ memtx_rtree_index_create_iterator,
+	/* .create_snapshot_iterator = */
+		generic_index_create_snapshot_iterator,
+	/* .stat = */ generic_index_stat,
+	/* .compact = */ generic_index_compact,
+	/* .reset_stat = */ generic_index_reset_stat,
+	/* .begin_build = */ generic_index_begin_build,
+	/* .reserve = */ memtx_rtree_index_reserve,
+	/* .build_next = */ generic_index_build_next,
+	/* .end_build = */ generic_index_end_build,
+};
 
 struct index *
 memtx_rtree_index_new(struct memtx_engine *memtx, struct index_def *def)
@@ -463,9 +435,8 @@ memtx_rtree_index_new(struct memtx_engine *memtx, struct index_def *def)
 			 "malloc", "struct memtx_rtree_index");
 		return NULL;
 	}
-	const struct index_vtab *vtab = get_memtx_rtree_index_vtab(true);
 	if (index_create(&index->base, (struct engine *)memtx,
-			 vtab, def) != 0) {
+			 &memtx_rtree_index_vtab, def) != 0) {
 		free(index);
 		return NULL;
 	}
@@ -476,9 +447,3 @@ memtx_rtree_index_new(struct memtx_engine *memtx, struct index_def *def)
 		   distance_type);
 	return &index->base;
 }
-
-void
-memtx_rtree_index_set_vtab(struct index *index, bool unchanged)
-{
-	index->vtab = get_memtx_rtree_index_vtab(unchanged);
-}
diff --git a/src/box/memtx_rtree.h b/src/box/memtx_rtree.h
index 82d75dc68e..2941b805a0 100644
--- a/src/box/memtx_rtree.h
+++ b/src/box/memtx_rtree.h
@@ -42,15 +42,6 @@ struct memtx_engine;
 struct index *
 memtx_rtree_index_new(struct memtx_engine *memtx, struct index_def *def);
 
-/**
- * Change @a index vtab according to @a unchanged argument.
- * If @a unchanged is false it's mean that tuple in index should
- * be transformed before return it to user, so we need special
- * `index_get` and `iterator_next` functions version.
- */
-void
-memtx_rtree_index_set_vtab(struct index *index, bool unchanged);
-
 #if defined(__cplusplus)
 } /* extern "C" */
 #endif /* defined(__cplusplus) */
diff --git a/src/box/memtx_space.c b/src/box/memtx_space.c
index 38542612ca..c3210d0ce1 100644
--- a/src/box/memtx_space.c
+++ b/src/box/memtx_space.c
@@ -89,22 +89,6 @@ memtx_space_update_bsize(struct space *space, struct tuple *old_tuple,
 	memtx_space->bsize += new_bsize - old_bsize;
 }
 
-void
-memtx_space_update_compressed_tuples(struct space *space,
-				     struct tuple *old_tuple,
-				     struct tuple *new_tuple)
-{
-	struct memtx_space *memtx_space = (struct memtx_space *)space;
-	uint64_t old_compressed_tuples = memtx_space->compressed_tuples;
-	if (old_tuple != NULL && tuple_is_compressed(old_tuple))
-		memtx_space->compressed_tuples--;
-	if (new_tuple != NULL && tuple_is_compressed(new_tuple))
-		memtx_space->compressed_tuples++;
-	if ((old_compressed_tuples != 0 && memtx_space->compressed_tuples == 0) ||
-	    (old_compressed_tuples == 0 && memtx_space->compressed_tuples != 0))
-		memtx_space_update_indexes_vtab(space);
-}
-
 /**
  * A version of space_replace for a space which has
  * no indexes (is not yet fully built).
@@ -150,7 +134,6 @@ memtx_space_replace_build_next(struct space *space, struct tuple *old_tuple,
 	if (index_build_next(space->index[0], new_tuple) != 0)
 		return -1;
 	memtx_space_update_bsize(space, NULL, new_tuple);
-	memtx_space_update_compressed_tuples(space, NULL, new_tuple);
 	tuple_ref(new_tuple);
 	return 0;
 }
@@ -170,7 +153,6 @@ memtx_space_replace_primary_key(struct space *space, struct tuple *old_tuple,
 			  new_tuple, mode, &old_tuple, &successor) != 0)
 		return -1;
 	memtx_space_update_bsize(space, old_tuple, new_tuple);
-	memtx_space_update_compressed_tuples(space, old_tuple, new_tuple);
 	if (new_tuple != NULL)
 		tuple_ref(new_tuple);
 	*result = old_tuple;
@@ -323,7 +305,6 @@ memtx_space_replace_all_keys(struct space *space, struct tuple *old_tuple,
 	}
 
 	memtx_space_update_bsize(space, old_tuple, new_tuple);
-	memtx_space_update_compressed_tuples(space, old_tuple, new_tuple);
 	if (new_tuple != NULL)
 		tuple_ref(new_tuple);
 	*result = old_tuple;
@@ -877,36 +858,6 @@ sequence_data_index_new(struct memtx_engine *memtx, struct index_def *def)
 	return index;
 }
 
-/**
- * Update @a index vtab according to @a space format and count of
- * compressed tuples in space. If there is no compressed tuples
- * in space and current space format without compression we set
- * vtab with `index_get` and `iterator_next` function versions
- * same as it's raw analogs. Otherwise we set vtab with special
- * versions of this functions.
- */
-static void
-memtx_space_update_index_vtab(struct space *space, struct index *index)
-{
-	if (space_is_system(space))
-		return;
-	struct memtx_space *memtx_space = (struct memtx_space *)space;
-	bool unchanged = (!memtx_space->base.format->is_compressed &&
-		memtx_space->compressed_tuples == 0);
-	switch (index->def->type) {
-	case HASH:
-		return memtx_hash_index_set_vtab(index, unchanged);
-	case TREE:
-		return memtx_tree_index_set_vtab(index, unchanged);
-	case RTREE:
-		return memtx_rtree_index_set_vtab(index, unchanged);
-	case BITSET:
-		return memtx_bitset_index_set_vtab(index, unchanged);
-	default:
-		unreachable();
-	}
-}
-
 static struct index *
 memtx_space_create_index(struct space *space, struct index_def *index_def)
 {
@@ -923,27 +874,19 @@ memtx_space_create_index(struct space *space, struct index_def *index_def)
 		return sequence_data_index_new(memtx, index_def);
 	}
 
-	struct index *index = NULL;
 	switch (index_def->type) {
 	case HASH:
-		index = memtx_hash_index_new(memtx, index_def);
-		break;
+		return memtx_hash_index_new(memtx, index_def);
 	case TREE:
-		index = memtx_tree_index_new(memtx, index_def);
-		break;
+		return memtx_tree_index_new(memtx, index_def);
 	case RTREE:
-		index = memtx_rtree_index_new(memtx, index_def);
-		break;
+		return memtx_rtree_index_new(memtx, index_def);
 	case BITSET:
-		index = memtx_bitset_index_new(memtx, index_def);
-		break;
+		return memtx_bitset_index_new(memtx, index_def);
 	default:
 		unreachable();
 		return NULL;
 	}
-	if (index != NULL)
-		memtx_space_update_index_vtab(space, index);
-	return index;
 }
 
 /**
@@ -1101,7 +1044,6 @@ memtx_space_drop_primary_key(struct space *space)
 	 */
 	memtx_space->replace = memtx_space_replace_no_keys;
 	memtx_space->bsize = 0;
-	memtx_space->compressed_tuples = 0;
 }
 
 static void
@@ -1418,7 +1360,6 @@ memtx_space_prepare_alter(struct space *old_space, struct space *new_space)
 
 	new_memtx_space->replace = old_memtx_space->replace;
 	new_memtx_space->bsize = old_memtx_space->bsize;
-	new_memtx_space->compressed_tuples = old_memtx_space->compressed_tuples;
 	return 0;
 }
 
@@ -1486,14 +1427,6 @@ memtx_space_new(struct memtx_engine *memtx,
 
 	memtx_space->bsize = 0;
 	memtx_space->rowid = 0;
-	memtx_space->compressed_tuples = 0;
 	memtx_space->replace = memtx_space_replace_no_keys;
 	return (struct space *)memtx_space;
 }
-
-void
-memtx_space_update_indexes_vtab(struct space *space)
-{
-	for (uint32_t i = 0; i < space->index_count; i++)
-		memtx_space_update_index_vtab(space, space->index[i]);
-}
diff --git a/src/box/memtx_space.h b/src/box/memtx_space.h
index 23e948353d..a13dae7145 100644
--- a/src/box/memtx_space.h
+++ b/src/box/memtx_space.h
@@ -50,8 +50,6 @@ struct memtx_space {
 	 * tuples within one unique primary key.
 	 */
 	uint64_t rowid;
-        /** Count of compressed tuples contained in this space. */
-        uint64_t compressed_tuples;
 	/**
 	 * A pointer to replace function, set to different values
 	 * at different stages of recovery.
@@ -73,18 +71,6 @@ void
 memtx_space_update_bsize(struct space *space, struct tuple *old_tuple,
 			 struct tuple *new_tuple);
 
-/**
- * Undate count of compressed tuples in @a space. If @a old_tuple
- * is compressed wwe decrement count of compressed tuples. If @a
- * new_tuple is compressed we increment count of compressed tuples.
- * If count of compressed tuples is equal to zero we update vtabs
- * of all space indexes.
- */
-void
-memtx_space_update_compressed_tuples(struct space *space,
-                                     struct tuple *old_tuple,
-                                     struct tuple *new_tuple);
-
 int
 memtx_space_replace_no_keys(struct space *, struct tuple *, struct tuple *,
 			    enum dup_replace_mode, struct tuple **);
@@ -102,13 +88,6 @@ struct space *
 memtx_space_new(struct memtx_engine *memtx,
 		struct space_def *def, struct rlist *key_list);
 
-/**
- * Update vtabs of all indexes in @a space according to
- * @a space format and count of compressed tuples.
- */
-void
-memtx_space_update_indexes_vtab(struct space *space);
-
 static inline bool
 memtx_space_is_recovering(struct space *space)
 {
diff --git a/src/box/memtx_tree.cc b/src/box/memtx_tree.cc
index 9831c13877..8fcc16ff52 100644
--- a/src/box/memtx_tree.cc
+++ b/src/box/memtx_tree.cc
@@ -355,16 +355,13 @@ tree_iterator_dummie(struct iterator *iterator, struct tuple **ret)
 	return 0;
 }
 
-template <bool UNCHANGED>
 void
 tree_iterator_set_dummie(struct iterator *iterator)
 {
 	iterator->next_raw = tree_iterator_dummie;
-	if (UNCHANGED)
-		iterator->next = tree_iterator_dummie;
 }
 
-template <bool UNCHANGED, bool USE_HINT>
+template <bool USE_HINT>
 static int
 tree_iterator_next_raw_base(struct iterator *iterator, struct tuple **ret)
 {
@@ -385,7 +382,7 @@ tree_iterator_next_raw_base(struct iterator *iterator, struct tuple **ret)
 	tree_iterator_set_current<USE_HINT>(it, res);
 	*ret = it->current.tuple;
 	if (*ret == NULL)
-		tree_iterator_set_dummie<UNCHANGED>(iterator);
+		tree_iterator_set_dummie(iterator);
 	struct index *idx = iterator->index;
 	struct space *space = space_by_id(iterator->space_id);
 
@@ -400,7 +397,7 @@ tree_iterator_next_raw_base(struct iterator *iterator, struct tuple **ret)
 	return 0;
 }
 
-template <bool UNCHANGED, bool USE_HINT>
+template <bool USE_HINT>
 static int
 tree_iterator_prev_raw_base(struct iterator *iterator, struct tuple **ret)
 {
@@ -422,7 +419,7 @@ tree_iterator_prev_raw_base(struct iterator *iterator, struct tuple **ret)
 	tree_iterator_set_current<USE_HINT>(it, res);
 	*ret = it->current.tuple;
 	if (*ret == NULL)
-		tree_iterator_set_dummie<UNCHANGED>(iterator);
+		tree_iterator_set_dummie(iterator);
 	struct index *idx = iterator->index;
 	struct space *space = space_by_id(iterator->space_id);
 
@@ -438,7 +435,7 @@ tree_iterator_prev_raw_base(struct iterator *iterator, struct tuple **ret)
 	return 0;
 }
 
-template <bool UNCHANGED, bool USE_HINT>
+template <bool USE_HINT>
 static int
 tree_iterator_next_equal_raw_base(struct iterator *iterator, struct tuple **ret)
 {
@@ -466,7 +463,7 @@ tree_iterator_next_equal_raw_base(struct iterator *iterator, struct tuple **ret)
 				   it->key_data.hint,
 				   index->base.def->key_def) != 0) {
 		tree_iterator_set_current<USE_HINT>(it, NULL);
-		tree_iterator_set_dummie<UNCHANGED>(iterator);
+		tree_iterator_set_dummie(iterator);
 		*ret = NULL;
 		/*
 		 * Got end of key. Store gap from the previous tuple to the
@@ -494,7 +491,7 @@ tree_iterator_next_equal_raw_base(struct iterator *iterator, struct tuple **ret)
 	return 0;
 }
 
-template <bool UNCHANGED, bool USE_HINT>
+template <bool USE_HINT>
 static int
 tree_iterator_prev_equal_raw_base(struct iterator *iterator, struct tuple **ret)
 {
@@ -523,7 +520,7 @@ tree_iterator_prev_equal_raw_base(struct iterator *iterator, struct tuple **ret)
 				   it->key_data.hint,
 				   index->base.def->key_def) != 0) {
 		tree_iterator_set_current<USE_HINT>(it, NULL);
-		tree_iterator_set_dummie<UNCHANGED>(iterator);
+		tree_iterator_set_dummie(iterator);
 		*ret = NULL;
 
 /********MVCC TRANSACTION MANAGER STORY GARBAGE COLLECTION BOUND START*********/
@@ -552,7 +549,7 @@ tree_iterator_prev_equal_raw_base(struct iterator *iterator, struct tuple **ret)
 }
 
 #define WRAP_ITERATOR_METHOD(name)						\
-template <bool UNCHANGED, bool USE_HINT>					\
+template <bool USE_HINT>							\
 static int									\
 name(struct iterator *iterator, struct tuple **ret)				\
 {										\
@@ -567,7 +564,7 @@ name(struct iterator *iterator, struct tuple **ret)				\
 	struct space *space = space_by_id(iterator->space_id);			\
 	bool is_rw = txn != NULL;						\
 	do {									\
-		int rc = name##_base<UNCHANGED, USE_HINT>(iterator, ret);	\
+		int rc = name##_base<USE_HINT>(iterator, ret);			\
 		if (rc != 0 || *ret == NULL)					\
 			return rc;						\
 		uint32_t mk_index = 0;						\
@@ -592,43 +589,37 @@ WRAP_ITERATOR_METHOD(tree_iterator_prev_equal_raw);
 
 #undef WRAP_ITERATOR_METHOD
 
-template <bool UNCHANGED, bool USE_HINT>
+template <bool USE_HINT>
 static void
 tree_iterator_set_next_method(struct tree_iterator<USE_HINT> *it)
 {
 	assert(it->current.tuple != NULL);
 	switch (it->type) {
 	case ITER_EQ:
-		it->base.next_raw =
-			tree_iterator_next_equal_raw<UNCHANGED, USE_HINT>;
+		it->base.next_raw = tree_iterator_next_equal_raw<USE_HINT>;
 		break;
 	case ITER_REQ:
-		it->base.next_raw =
-			tree_iterator_prev_equal_raw<UNCHANGED, USE_HINT>;
+		it->base.next_raw = tree_iterator_prev_equal_raw<USE_HINT>;
 		break;
 	case ITER_ALL:
-		it->base.next_raw =
-			tree_iterator_next_raw<UNCHANGED, USE_HINT>;
+		it->base.next_raw = tree_iterator_next_raw<USE_HINT>;
 		break;
 	case ITER_LT:
 	case ITER_LE:
-		it->base.next_raw =
-			tree_iterator_prev_raw<UNCHANGED, USE_HINT>;
+		it->base.next_raw = tree_iterator_prev_raw<USE_HINT>;
 		break;
 	case ITER_GE:
 	case ITER_GT:
-		it->base.next_raw =
-			tree_iterator_next_raw<UNCHANGED, USE_HINT>;
+		it->base.next_raw = tree_iterator_next_raw<USE_HINT>;
 		break;
 	default:
 		/* The type was checked in initIterator */
 		assert(false);
 	}
-	it->base.next = UNCHANGED ?
-			it->base.next_raw : memtx_iterator_next;
+	it->base.next = memtx_iterator_next;
 }
 
-template <bool UNCHANGED, bool USE_HINT>
+template <bool USE_HINT>
 static int
 tree_iterator_start_raw(struct iterator *iterator, struct tuple **ret)
 {
@@ -636,7 +627,7 @@ tree_iterator_start_raw(struct iterator *iterator, struct tuple **ret)
 	struct memtx_tree_index<USE_HINT> *index =
 		(struct memtx_tree_index<USE_HINT> *)iterator->index;
 	struct tree_iterator<USE_HINT> *it = get_tree_iterator<USE_HINT>(iterator);
-	tree_iterator_set_dummie<UNCHANGED>(iterator);
+	tree_iterator_set_dummie(iterator);
 	memtx_tree_t<USE_HINT> *tree = &index->tree;
 	enum iterator_type type = it->type;
 	struct txn *txn = in_txn();
@@ -709,7 +700,7 @@ tree_iterator_start_raw(struct iterator *iterator, struct tuple **ret)
 	if (res != NULL) {
 		*ret = res->tuple;
 		tree_iterator_set_current(it, res);
-		tree_iterator_set_next_method<UNCHANGED>(it);
+		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;
 	}
@@ -1407,7 +1398,7 @@ memtx_tree_func_index_replace(struct index *base, struct tuple *old_tuple,
 	return rc;
 }
 
-template <bool UNCHANGED, bool USE_HINT>
+template <bool USE_HINT>
 static struct iterator *
 memtx_tree_index_create_iterator(struct index *base, enum iterator_type type,
 				 const char *key, uint32_t part_count)
@@ -1442,8 +1433,8 @@ memtx_tree_index_create_iterator(struct index *base, enum iterator_type type,
 	}
 	iterator_create(&it->base, base);
 	it->pool = &memtx->iterator_pool;
-	it->base.next_raw = tree_iterator_start_raw<UNCHANGED, USE_HINT>;
-	it->base.next = UNCHANGED ? it->base.next_raw : memtx_iterator_next;
+	it->base.next_raw = tree_iterator_start_raw<USE_HINT>;
+	it->base.next = memtx_iterator_next;
 	it->base.free = tree_iterator_free<USE_HINT>;
 	it->type = type;
 	it->key_data.key = key;
@@ -1803,12 +1794,10 @@ enum memtx_tree_vtab_type {
 };
 
 /**
- * Get index vtab by @a TYPE, @a UNCHANGED and @a USE_HINT, template
- * version. USE_HINT == false is only allowed for general index type.
- * If UNCHANGED == true iterator->next and index->get functions are
- * the same as it's raw versions.
+ * Get index vtab by @a TYPE and @a USE_HINT, template version.
+ * USE_HINT == false is only allowed for general index type.
  */
-template <memtx_tree_vtab_type TYPE, bool UNCHANGED, bool USE_HINT = true>
+template <memtx_tree_vtab_type TYPE, bool USE_HINT = true>
 static const struct index_vtab *
 get_memtx_tree_index_vtab(void)
 {
@@ -1837,13 +1826,12 @@ get_memtx_tree_index_vtab(void)
 		/* .random = */ memtx_tree_index_random<USE_HINT>,
 		/* .count = */ memtx_tree_index_count<USE_HINT>,
 		/* .get_raw */ memtx_tree_index_get_raw<USE_HINT>,
-		/* .get = */ UNCHANGED ? memtx_tree_index_get_raw<USE_HINT> :
-			memtx_index_get,
+		/* .get = */ memtx_index_get,
 		/* .replace = */ is_mk ? memtx_tree_index_replace_multikey :
 				 is_func ? memtx_tree_func_index_replace :
 				 memtx_tree_index_replace<USE_HINT>,
 		/* .create_iterator = */
-			memtx_tree_index_create_iterator<UNCHANGED, USE_HINT>,
+			memtx_tree_index_create_iterator<USE_HINT>,
 		/* .create_snapshot_iterator = */
 			memtx_tree_index_create_snapshot_iterator<USE_HINT>,
 		/* .stat = */ generic_index_stat,
@@ -1859,35 +1847,6 @@ get_memtx_tree_index_vtab(void)
 	return &vtab;
 }
 
-/**
- * Get index vtab by @a type and @a use_hint, argument version.
- * @a use_hint is ignored for every type except MEMTX_TREE_VTAB_GENERAL.
- */
-static const struct index_vtab *
-get_memtx_tree_index_vtab(memtx_tree_vtab_type type, bool unchanged,
-			  bool use_hint)
-{
-	static const index_vtab *choice[MEMTX_TREE_VTAB_TYPE_COUNT][2][2] = {
-		{{get_memtx_tree_index_vtab<MEMTX_TREE_VTAB_GENERAL, false, false>(),
-		  get_memtx_tree_index_vtab<MEMTX_TREE_VTAB_GENERAL, false, true>()},
-		 {get_memtx_tree_index_vtab<MEMTX_TREE_VTAB_GENERAL, true, false>(),
-		  get_memtx_tree_index_vtab<MEMTX_TREE_VTAB_GENERAL, true, true>()}},
-		{{get_memtx_tree_index_vtab<MEMTX_TREE_VTAB_MULTIKEY, false>(),
-		  get_memtx_tree_index_vtab<MEMTX_TREE_VTAB_MULTIKEY, false>()},
-		 {get_memtx_tree_index_vtab<MEMTX_TREE_VTAB_MULTIKEY, true>(),
-		  get_memtx_tree_index_vtab<MEMTX_TREE_VTAB_MULTIKEY, true>()}},
-		{{get_memtx_tree_index_vtab<MEMTX_TREE_VTAB_FUNC, false>(),
-		  get_memtx_tree_index_vtab<MEMTX_TREE_VTAB_FUNC, false>()},
-		 {get_memtx_tree_index_vtab<MEMTX_TREE_VTAB_FUNC, true>(),
-		  get_memtx_tree_index_vtab<MEMTX_TREE_VTAB_FUNC, true>()}},
-		{{get_memtx_tree_index_vtab<MEMTX_TREE_VTAB_DISABLED, false>(),
-		  get_memtx_tree_index_vtab<MEMTX_TREE_VTAB_DISABLED, false>()},
-		 {get_memtx_tree_index_vtab<MEMTX_TREE_VTAB_DISABLED, true>(),
-		  get_memtx_tree_index_vtab<MEMTX_TREE_VTAB_DISABLED, true>()}}
-	};
-	return choice[type][unchanged][use_hint];
-}
-
 template <bool USE_HINT>
 static struct index *
 memtx_tree_index_new_tpl(struct memtx_engine *memtx, struct index_def *def,
@@ -1917,44 +1876,33 @@ memtx_tree_index_new_tpl(struct memtx_engine *memtx, struct index_def *def,
 	return &index->base;
 }
 
-static void
-memtx_tree_choose_type_and_hint(struct index_def *def,
-				memtx_tree_vtab_type *type,
-				bool *use_hint)
+struct index *
+memtx_tree_index_new(struct memtx_engine *memtx, struct index_def *def)
 {
-	*type = MEMTX_TREE_VTAB_GENERAL;
-	*use_hint = true; /* Force hints for multikey and func indexes. */
+	bool use_hint = false;
+	const struct index_vtab *vtab;
 	if (def->key_def->for_func_index) {
-		if (def->key_def->func_index_func == NULL)
-			*type = MEMTX_TREE_VTAB_DISABLED;
-		else
-			*type = MEMTX_TREE_VTAB_FUNC;
+		if (def->key_def->func_index_func != NULL) {
+			vtab = get_memtx_tree_index_vtab
+				<MEMTX_TREE_VTAB_FUNC>();
+			use_hint = true;
+		} else {
+			vtab = get_memtx_tree_index_vtab
+				<MEMTX_TREE_VTAB_DISABLED>();
+		}
 	} else if (def->key_def->is_multikey) {
-		*type = MEMTX_TREE_VTAB_MULTIKEY;
+		vtab = get_memtx_tree_index_vtab<MEMTX_TREE_VTAB_MULTIKEY>();
+		use_hint = true;
+	} else if (def->opts.hint) {
+		vtab = get_memtx_tree_index_vtab
+			<MEMTX_TREE_VTAB_GENERAL, true>();
+		use_hint = true;
 	} else {
-		*use_hint = def->opts.hint;
+		vtab = get_memtx_tree_index_vtab
+			<MEMTX_TREE_VTAB_GENERAL, false>();
 	}
-}
-
-struct index *
-memtx_tree_index_new(struct memtx_engine *memtx, struct index_def *def)
-{
-	const struct index_vtab *vtab;
-	memtx_tree_vtab_type type;
-	bool use_hint;
-	memtx_tree_choose_type_and_hint(def, &type, &use_hint);
-	vtab = get_memtx_tree_index_vtab(type, true, use_hint);
 	if (use_hint)
 		return memtx_tree_index_new_tpl<true>(memtx, def, vtab);
 	else
 		return memtx_tree_index_new_tpl<false>(memtx, def, vtab);
 }
-
-void
-memtx_tree_index_set_vtab(struct index *index, bool unchanged)
-{
-	memtx_tree_vtab_type type;
-	bool use_hint;
-	memtx_tree_choose_type_and_hint(index->def, &type, &use_hint);
-	index->vtab = get_memtx_tree_index_vtab(type, unchanged, use_hint);
-}
diff --git a/src/box/memtx_tree.h b/src/box/memtx_tree.h
index 5c9c5ab9a0..edeaebab93 100644
--- a/src/box/memtx_tree.h
+++ b/src/box/memtx_tree.h
@@ -42,15 +42,6 @@ struct memtx_engine;
 struct index *
 memtx_tree_index_new(struct memtx_engine *memtx, struct index_def *def);
 
-/**
- * Change @a index vtab according to @a unchanged argument.
- * If @a unchanged is false it's mean that tuple in index should
- * be transformed before return it to user, so we need special
- * `index_get` and `iterator_next` functions version.
- */
-void
-memtx_tree_index_set_vtab(struct index *index, bool unchanged);
-
 #if defined(__cplusplus)
 } /* extern "C" */
 #endif /* defined(__cplusplus) */
diff --git a/src/box/memtx_tx.c b/src/box/memtx_tx.c
index 7dcc016e56..a66853fe5a 100644
--- a/src/box/memtx_tx.c
+++ b/src/box/memtx_tx.c
@@ -1884,20 +1884,15 @@ memtx_tx_history_prepare_stmt(struct txn_stmt *stmt)
 }
 
 void
-memtx_tx_history_commit_stmt(struct txn_stmt *stmt, size_t *bsize,
-			     uint64_t *compressed_tuples)
+memtx_tx_history_commit_stmt(struct txn_stmt *stmt, size_t *bsize)
 {
 	if (stmt->add_story != NULL) {
 		assert(stmt->add_story->add_stmt == stmt);
 		*bsize += tuple_bsize(stmt->add_story->tuple);
-		if (tuple_is_compressed(stmt->add_story->tuple))
-			(*compressed_tuples)++;
 		memtx_tx_story_unlink_added_by(stmt->add_story, stmt);
 	}
 	if (stmt->del_story != NULL) {
 		*bsize -= tuple_bsize(stmt->del_story->tuple);
-		if (tuple_is_compressed(stmt->del_story->tuple))
-			(*compressed_tuples)--;
 		memtx_tx_story_unlink_deleted_by(stmt->del_story, stmt);
 	}
 }
diff --git a/src/box/memtx_tx.h b/src/box/memtx_tx.h
index abd13e4526..36e529b601 100644
--- a/src/box/memtx_tx.h
+++ b/src/box/memtx_tx.h
@@ -278,11 +278,9 @@ memtx_tx_history_prepare_stmt(struct txn_stmt *stmt);
  *
  * @param stmt current statement.
  * @param bsize the space bsize.
- * @param compressed_tuples count of compressed tuples in space.
  */
 void
-memtx_tx_history_commit_stmt(struct txn_stmt *stmt, size_t *bsize,
-			     uint64_t *compressed_tuples);
+memtx_tx_history_commit_stmt(struct txn_stmt *stmt, size_t *bsize);
 
 /** Helper of memtx_tx_tuple_clarify */
 struct tuple *
-- 
GitLab