[Tarantool-patches] [PATCH 3/4] core: introduce tiny tuples

Ilya Kosarev i.kosarev at tarantool.org
Tue Jan 19 02:50:32 MSK 2021


Tuple bsize was stored in 4 byte uint32_t field. Tuple data_offset was
stored in 2 byte uint16_t field. Now the tuple contains flexible array
at the end (0 or 4 bytes), allowing to put it in the same way.
On the other hand, if it is possible, bsize and data_offset use 1 byte
each. Such tuples are called tiny tuples. They only require 6 bytes
instead of 10.
As long as struct tuple is reassembled to achieve it and is now of
variable size, inherited struct vy_stmt is also reworked now. It's
members (except struct tuple itself) are not directly listed anymore.
They were not used directly before and now they are not any more
available to avoid accidental mistakes. lsn, type and flags of
struct vy_stmt have to be accessed only using getters and setters now.

Part of #5385
---
 src/box/memtx_engine.c          |  13 ++-
 src/box/tuple.c                 |  14 ++-
 src/box/tuple.h                 | 184 ++++++++++++++++++++++++++++----
 src/box/vy_stmt.c               |  19 ++--
 src/box/vy_stmt.h               |  38 +++++--
 test/box/errinj.result          |  18 ++--
 test/box/upsert_errinj.result   |   2 +-
 test/vinyl/cache.result         |   6 +-
 test/vinyl/quota.result         |  10 +-
 test/vinyl/quota_timeout.result |   8 +-
 test/vinyl/stat.result          | 104 +++++++++---------
 11 files changed, 300 insertions(+), 116 deletions(-)

diff --git a/src/box/memtx_engine.c b/src/box/memtx_engine.c
index c37b35586..adb90e1c8 100644
--- a/src/box/memtx_engine.c
+++ b/src/box/memtx_engine.c
@@ -1219,6 +1219,8 @@ memtx_tuple_new(struct tuple_format *format, const char *data, const char *end)
 	struct tuple *tuple = NULL;
 	struct region *region = &fiber()->gc;
 	size_t region_svp = region_used(region);
+	size_t tuple_len = end - data;
+	bool is_tiny = (tuple_len <= UINT8_MAX);
 	struct field_map_builder builder;
 	if (tuple_field_map_create(format, data, true, &builder) != 0)
 		goto end;
@@ -1228,7 +1230,12 @@ memtx_tuple_new(struct tuple_format *format, const char *data, const char *end)
 	 * tuple base, not from memtx_tuple, because the struct
 	 * tuple is not the first field of the memtx_tuple.
 	 */
-	uint32_t data_offset = sizeof(struct tuple) + field_map_size;
+	is_tiny = (is_tiny && (sizeof(struct tuple) +
+			       field_map_size <= MAX_TINY_DATA_OFFSET));
+	uint32_t extra_size = field_map_size +
+			      !is_tiny * sizeof(struct tuple_extra);
+	uint32_t data_offset = sizeof(struct tuple) + extra_size;
+	assert(!is_tiny || data_offset <= MAX_TINY_DATA_OFFSET);
 	if (data_offset > INT16_MAX) {
 		/** tuple data_offset can't be more than 15 bits */
 		diag_set(ClientError, ER_TUPLE_METADATA_IS_TOO_BIG,
@@ -1236,8 +1243,7 @@ memtx_tuple_new(struct tuple_format *format, const char *data, const char *end)
 		goto end;
 	}
 
-	size_t tuple_len = end - data;
-	size_t total = sizeof(struct memtx_tuple) + field_map_size + tuple_len;
+	size_t total = sizeof(struct memtx_tuple) + tuple_len + extra_size;
 
 	ERROR_INJECT(ERRINJ_TUPLE_ALLOC, {
 		diag_set(OutOfMemory, total, "slab allocator", "memtx_tuple");
@@ -1263,6 +1269,7 @@ memtx_tuple_new(struct tuple_format *format, const char *data, const char *end)
 	tuple = &memtx_tuple->base;
 	tuple->refs = 0;
 	memtx_tuple->version = memtx->snapshot_version;
+	tuple_set_tiny_bit(tuple, is_tiny);
 	tuple_set_bsize(tuple, tuple_len);
 	tuple->format_id = tuple_format_id(format);
 	tuple_format_ref(format);
diff --git a/src/box/tuple.c b/src/box/tuple.c
index c73ad4566..db95d5872 100644
--- a/src/box/tuple.c
+++ b/src/box/tuple.c
@@ -79,11 +79,17 @@ runtime_tuple_new(struct tuple_format *format, const char *data, const char *end
 	struct tuple *tuple = NULL;
 	struct region *region = &fiber()->gc;
 	size_t region_svp = region_used(region);
+	size_t data_len = end - data;
+	bool is_tiny = (data_len <= UINT8_MAX);
 	struct field_map_builder builder;
 	if (tuple_field_map_create(format, data, true, &builder) != 0)
 		goto end;
 	uint32_t field_map_size = field_map_build_size(&builder);
-	uint32_t data_offset = sizeof(struct tuple) + field_map_size;
+	is_tiny = (is_tiny && (sizeof(struct tuple) +
+			       field_map_size <= MAX_TINY_DATA_OFFSET));
+	uint32_t data_offset = sizeof(struct tuple) + field_map_size +
+			       !is_tiny * sizeof(uint32_t);
+	assert(!is_tiny || data_offset <= MAX_TINY_DATA_OFFSET);
 	if (data_offset > INT16_MAX) {
 		/** tuple data_offset can't be more than 15 bits */
 		diag_set(ClientError, ER_TUPLE_METADATA_IS_TOO_BIG,
@@ -91,9 +97,8 @@ runtime_tuple_new(struct tuple_format *format, const char *data, const char *end
 		goto end;
 	}
 
-	size_t data_len = end - data;
-	size_t total = sizeof(struct tuple) + field_map_size + data_len;
-	tuple = (struct tuple *) smalloc(&runtime_alloc, total);
+	size_t total = data_offset + data_len;
+	tuple = (struct tuple *)smalloc(&runtime_alloc, total);
 	if (tuple == NULL) {
 		diag_set(OutOfMemory, (unsigned) total,
 			 "malloc", "tuple");
@@ -101,6 +106,7 @@ runtime_tuple_new(struct tuple_format *format, const char *data, const char *end
 	}
 
 	tuple->refs = 0;
+	tuple_set_tiny_bit(tuple, is_tiny);
 	tuple_set_bsize(tuple, data_len);
 	tuple->format_id = tuple_format_id(format);
 	tuple_format_ref(format);
diff --git a/src/box/tuple.h b/src/box/tuple.h
index aac054787..9eac52f01 100644
--- a/src/box/tuple.h
+++ b/src/box/tuple.h
@@ -296,6 +296,64 @@ box_tuple_validate(box_tuple_t *tuple, box_tuple_format_t *format);
 
 /** \endcond public */
 
+#define MAX_TINY_DATA_OFFSET 63
+
+/** Part of struct tuple. */
+struct tiny_tuple_props {
+	/**
+	 * Tuple is tiny in case it's bsize fits in 1 byte,
+	 * it's data_offset fits in 6 bits and field map
+	 * offsets fit into 1 byte each.
+	 */
+	bool is_tiny : 1;
+	/**
+	 * The tuple (if it's found in index for example) could be invisible
+	 * for current transactions. The flag means that the tuple must
+	 * be clarified by the transaction engine.
+	 */
+	bool is_dirty : 1;
+	/**
+	 * Offset to the MessagePack from the beginning of the
+	 * tuple. 6 bits in case tuple is tiny.
+	 */
+	uint8_t data_offset : 6;
+	/**
+	 * Length of the MessagePack data in raw part of the
+	 * tuple. 8 bits in case tuple is tiny.
+	 */
+	uint8_t bsize;
+};
+
+/** Part of struct tuple. */
+struct tuple_props {
+	/**
+	 * Tuple is tiny in case it's bsize fits in 1 byte,
+	 * it's data_offset fits in 6 bits and field map
+	 * offsets fit into 1 byte each.
+	 */
+	bool is_tiny : 1;
+	/**
+	 * Offset to the MessagePack from the beginning of the
+	 * tuple. 15 bits in case tuple is not tiny.
+	 */
+	uint16_t data_offset: 15;
+};
+
+/** Part of struct tuple. */
+struct PACKED tuple_extra {
+	/**
+	 * The tuple (if it's found in index for example) could be invisible
+	 * for current transactions. The flag means that the tuple must
+	 * be clarified by the transaction engine.
+	 */
+	bool is_dirty : 1;
+	/**
+	 * Length of the MessagePack data in raw part of the
+	 * tuple. 31 bits in case tuple is not tiny.
+	 */
+	uint32_t bsize : 31;
+};
+
 /**
  * An atom of Tarantool storage. Represents MsgPack Array.
  * Tuple has the following structure:
@@ -323,20 +381,40 @@ struct PACKED tuple
 	/** Format identifier. */
 	uint16_t format_id;
 	/**
-	 * Length of the MessagePack data in raw part of the
-	 * tuple.
-	 */
-	uint32_t bsize;
-	/**
-	 * Offset to the MessagePack from the begin of the tuple.
+	 * Both structs in the following union contain is_tiny bit
+	 * as the first field. It is guaranteed it will be the same
+	 * for both of them in any case. Tuple is tiny in case it's
+	 * bsize fits in 1 byte, it's data_offset fits in 6 bits and
+	 * field map offsets fit into 1 byte each. In case it is tiny
+	 * we will obtain data_offset, bsize and is_dirty flag from the
+	 * struct tiny_tuple_props. Otherwise we will obtain data_offset
+	 * from struct tuple_props, while bsize and is_dirty flag will be
+	 * stored in struct tuple_extra (4 bytes at the end of the tuple).
 	 */
-	uint16_t data_offset : 15;
+	union {
+		/**
+		 * In case the tuple is tiny this struct is used to obtain
+		 * data_offset (offset to the MessagePack from the beginning
+		 * of the tuple), bsize (the length of the MessagePack data
+		 * in the raw part of the tuple) and is_dirty flag (true if
+		 * the tuple must be clarified by transaction engine).
+		 */
+		struct tiny_tuple_props tiny_props;
+		/**
+		 * In case the tuple is not tiny this struct is used to obtain
+		 * data_offset (offset to the MessagePack from the beginning of
+		 * the tuple).
+		 */
+		struct tuple_props props;
+	};
 	/**
-	 * The tuple (if it's found in index for example) could be invisible
-	 * for current transactions. The flag means that the tuple must
-	 * be clarified by transaction engine.
+	 * In case the tuple is not tiny this struct contains is_dirty
+	 * flag (true if the tuple must be clarified by transaction
+	 * engine) and bsize (the length of the MessagePack data
+	 * in the raw part of the tuple).
+	 * If the tuple is tiny, this fields is 0 bytes.
 	 */
-	bool is_dirty : 1;
+	struct tuple_extra extra[];
 	/**
 	 * Engine specific fields and offsets array concatenated
 	 * with MessagePack fields array.
@@ -344,47 +422,117 @@ struct PACKED tuple
 	 */
 };
 
+/**
+ * According to C standard sections 6.5.2.3-5 it is guaranteed
+ * if a union contains several structures that share a common
+ * initial sequence of members (bool is_tiny in this case), it is
+ * permitted to inspect the common initial part of any of them
+ * anywhere that a declaration of the complete type of the union
+ * is visible. Two structures share a common initial sequence if
+ * corresponding members have compatible types (and, for
+ * bit-fields, the same widths) for a sequence of one or more
+ * initial members. Thus we are guaranteed that is_tiny bit is
+ * same for both struct tiny_tuple_props and struct tuple_props
+ * and we can simply read or write any of them.
+ */
+static inline void
+tuple_set_tiny_bit(struct tuple *tuple, bool is_tiny)
+{
+	assert(tuple != NULL);
+	tuple->props.is_tiny = is_tiny;
+}
+
+static inline bool
+tuple_is_tiny(struct tuple *tuple)
+{
+	assert(tuple != NULL);
+	return tuple->props.is_tiny;
+}
+
+static inline void
+tiny_tuple_set_dirty_bit(struct tuple *tuple, bool is_dirty)
+{
+	tuple->tiny_props.is_dirty = is_dirty;
+}
+
+static inline void
+basic_tuple_set_dirty_bit(struct tuple *tuple, bool is_dirty)
+{
+	tuple->extra->is_dirty = is_dirty;
+}
+
 static inline void
 tuple_set_dirty_bit(struct tuple *tuple, bool is_dirty)
 {
 	assert(tuple != NULL);
-	tuple->is_dirty = is_dirty;
+	tuple->props.is_tiny ? tiny_tuple_set_dirty_bit(tuple, is_dirty) :
+			       basic_tuple_set_dirty_bit(tuple, is_dirty);
 }
 
 static inline bool
 tuple_is_dirty(struct tuple *tuple)
 {
 	assert(tuple != NULL);
-	return tuple->is_dirty;
+	return tuple->props.is_tiny ? tuple->tiny_props.is_dirty :
+				      tuple->extra->is_dirty;
+}
+
+static inline void
+tiny_tuple_set_bsize(struct tuple *tuple, uint32_t bsize)
+{
+	assert(bsize <= UINT8_MAX); /* bsize has to fit in UINT8_MAX */
+	tuple->tiny_props.bsize = bsize;
+}
+
+static inline void
+basic_tuple_set_bsize(struct tuple *tuple, uint32_t bsize)
+{
+	assert(bsize <= INT32_MAX); /* bsize has to fit in INT32_MAX */
+	tuple->extra->bsize = bsize;
 }
 
 static inline void
 tuple_set_bsize(struct tuple *tuple, uint32_t bsize)
 {
 	assert(tuple != NULL);
-	assert(bsize <= UINT32_MAX); /* bsize is UINT32_MAX */
-	tuple->bsize = bsize;
+	tuple->props.is_tiny ? tiny_tuple_set_bsize(tuple, bsize) :
+			       basic_tuple_set_bsize(tuple, bsize);
 }
 
 static inline uint32_t
 tuple_bsize(struct tuple *tuple)
 {
 	assert(tuple != NULL);
-	return tuple->bsize;
+	return tuple->props.is_tiny ? tuple->tiny_props.bsize :
+				      tuple->extra->bsize;
+}
+
+static inline void
+tiny_tuple_set_data_offset(struct tuple *tuple, uint8_t data_offset)
+{
+	tuple->tiny_props.data_offset = data_offset;
+}
+
+static inline void
+basic_tuple_set_data_offset(struct tuple *tuple, uint16_t data_offset)
+{
+	tuple->props.data_offset = data_offset;
 }
 
 static inline void
 tuple_set_data_offset(struct tuple *tuple, uint16_t data_offset)
 {
 	assert(tuple != NULL);
-	tuple->data_offset = data_offset;
+	tuple->props.is_tiny ? tiny_tuple_set_data_offset(tuple, data_offset) :
+			       basic_tuple_set_data_offset(tuple, data_offset);
 }
 
 static inline uint16_t
 tuple_data_offset(struct tuple *tuple)
 {
 	assert(tuple != NULL);
-	return tuple->data_offset;
+	return tuple->props.is_tiny ? tuple->tiny_props.data_offset :
+				      tuple->props.data_offset;
 }
 
 /** Size of the tuple including size of struct tuple. */
diff --git a/src/box/vy_stmt.c b/src/box/vy_stmt.c
index accafc654..97bc1e408 100644
--- a/src/box/vy_stmt.c
+++ b/src/box/vy_stmt.c
@@ -158,7 +158,8 @@ vy_stmt_format_new(struct vy_stmt_env *env, struct key_def *const *keys,
 static struct tuple *
 vy_stmt_alloc(struct tuple_format *format, uint32_t data_offset, uint32_t bsize)
 {
-	assert(data_offset >= sizeof(struct vy_stmt) + format->field_map_size);
+	assert(data_offset >= size_of_struct_vy_stmt() +
+			      format->field_map_size);
 
 	if (data_offset > INT16_MAX) {
 		/** tuple data_offset can't be more than 15 bits */
@@ -196,6 +197,7 @@ vy_stmt_alloc(struct tuple_format *format, uint32_t data_offset, uint32_t bsize)
 	tuple->format_id = tuple_format_id(format);
 	if (cord_is_main())
 		tuple_format_ref(format);
+	tuple_set_tiny_bit(tuple, false);
 	tuple_set_bsize(tuple, bsize);
 	tuple_set_data_offset(tuple, data_offset);
 	tuple_set_dirty_bit(tuple, false);
@@ -280,11 +282,12 @@ vy_key_new(struct tuple_format *format, const char *key, uint32_t part_count)
 	/* Allocate stmt */
 	uint32_t key_size = key_end - key;
 	uint32_t bsize = mp_sizeof_array(part_count) + key_size;
-	struct tuple *stmt = vy_stmt_alloc(format, sizeof(struct vy_stmt), bsize);
+	struct tuple *stmt = vy_stmt_alloc(format, size_of_struct_vy_stmt(),
+						   bsize);
 	if (stmt == NULL)
 		return NULL;
 	/* Copy MsgPack data */
-	char *raw = (char *) stmt + sizeof(struct vy_stmt);
+	char *raw = (char *)stmt + size_of_struct_vy_stmt();
 	char *data = mp_encode_array(raw, part_count);
 	memcpy(data, key, key_size);
 	assert(data + key_size == raw + bsize);
@@ -351,7 +354,7 @@ vy_stmt_new_with_ops(struct tuple_format *format, const char *tuple_begin,
 	 */
 	size_t mpsize = (tuple_end - tuple_begin);
 	size_t bsize = mpsize + ops_size;
-	stmt = vy_stmt_alloc(format, sizeof(struct vy_stmt) +
+	stmt = vy_stmt_alloc(format, size_of_struct_vy_stmt() +
 			     field_map_size, bsize);
 	if (stmt == NULL)
 		goto end;
@@ -422,10 +425,10 @@ vy_stmt_replace_from_upsert(struct tuple *upsert)
 	if (replace == NULL)
 		return NULL;
 	/* Copy both data and field_map. */
-	char *dst = (char *)replace + sizeof(struct vy_stmt);
-	char *src = (char *)upsert + sizeof(struct vy_stmt);
+	char *dst = (char *)replace + size_of_struct_vy_stmt();
+	char *src = (char *)upsert + size_of_struct_vy_stmt();
 	memcpy(dst, src, tuple_data_offset(upsert) +
-			 bsize - sizeof(struct vy_stmt));
+			 bsize - size_of_struct_vy_stmt());
 	vy_stmt_set_type(replace, IPROTO_REPLACE);
 	vy_stmt_set_lsn(replace, vy_stmt_lsn(upsert));
 	return replace;
@@ -502,7 +505,7 @@ vy_stmt_new_surrogate_delete_raw(struct tuple_format *format,
 	assert(pos <= data + src_size);
 	uint32_t bsize = pos - data;
 	uint32_t field_map_size = field_map_build_size(&builder);
-	stmt = vy_stmt_alloc(format, sizeof(struct vy_stmt) + field_map_size,
+	stmt = vy_stmt_alloc(format, size_of_struct_vy_stmt() + field_map_size,
 			     bsize);
 	if (stmt == NULL)
 		goto out;
diff --git a/src/box/vy_stmt.h b/src/box/vy_stmt.h
index 69f46d67c..50b196fc3 100644
--- a/src/box/vy_stmt.h
+++ b/src/box/vy_stmt.h
@@ -171,9 +171,15 @@ enum {
  */
 struct vy_stmt {
 	struct tuple base;
-	int64_t lsn;
-	uint8_t  type; /* IPROTO_INSERT/REPLACE/UPSERT/DELETE */
-	uint8_t flags;
+	/**
+	 * Following fields are stored without direct naming
+	 * due to the fact that struct tuple has variable size.
+	 * It also allows us to store them in compact way.
+	 * Use getters and setters to access them.
+	 * int64_t lsn;
+	 * uint8_t type; IPROTO_INSERT/REPLACE/UPSERT/DELETE
+	 * uint8_t flags;
+	 */
 	/**
 	 * Offsets array concatenated with MessagePack fields
 	 * array.
@@ -181,46 +187,60 @@ struct vy_stmt {
 	 */
 };
 
+static inline size_t
+size_of_basic_tuple(void)
+{
+	return sizeof(struct tuple) + sizeof(struct tuple_extra);
+}
+
+static inline size_t
+size_of_struct_vy_stmt(void)
+{
+	return size_of_basic_tuple() + sizeof(int64_t) + 2 * sizeof(uint8_t);
+}
+
 /** Get LSN of the vinyl statement. */
 static inline int64_t
 vy_stmt_lsn(struct tuple *stmt)
 {
-	return ((struct vy_stmt *) stmt)->lsn;
+	return load_u64((void *)stmt + size_of_basic_tuple());
 }
 
 /** Set LSN of the vinyl statement. */
 static inline void
 vy_stmt_set_lsn(struct tuple *stmt, int64_t lsn)
 {
-	((struct vy_stmt *) stmt)->lsn = lsn;
+	store_u64((void *)stmt + size_of_basic_tuple(), lsn);
 }
 
 /** Get type of the vinyl statement. */
 static inline enum iproto_type
 vy_stmt_type(struct tuple *stmt)
 {
-	return (enum iproto_type)((struct vy_stmt *) stmt)->type;
+	return load_u8((void *)stmt + size_of_basic_tuple() + sizeof(int64_t));
 }
 
 /** Set type of the vinyl statement. */
 static inline void
 vy_stmt_set_type(struct tuple *stmt, enum iproto_type type)
 {
-	((struct vy_stmt *) stmt)->type = type;
+	store_u8((void *)stmt + size_of_basic_tuple() + sizeof(int64_t), type);
 }
 
 /** Get flags of the vinyl statement. */
 static inline uint8_t
 vy_stmt_flags(struct tuple *stmt)
 {
-	return ((struct vy_stmt *)stmt)->flags;
+	return load_u8((void *)stmt + size_of_basic_tuple() + sizeof(int64_t) +
+							      sizeof(uint8_t));
 }
 
 /** Set flags of the vinyl statement. */
 static inline void
 vy_stmt_set_flags(struct tuple *stmt, uint8_t flags)
 {
-	((struct vy_stmt *)stmt)->flags = flags;
+	store_u8((void *)stmt + size_of_basic_tuple() +
+				sizeof(int64_t) + sizeof(uint8_t), flags);
 }
 
 /**
diff --git a/test/box/errinj.result b/test/box/errinj.result
index b8c2476c3..0b1990e03 100644
--- a/test/box/errinj.result
+++ b/test/box/errinj.result
@@ -455,7 +455,7 @@ errinj.set("ERRINJ_TUPLE_ALLOC", true)
 ...
 s:auto_increment{}
 ---
-- error: Failed to allocate 16 bytes in slab allocator for memtx_tuple
+- error: Failed to allocate 12 bytes in slab allocator for memtx_tuple
 ...
 s:select{}
 ---
@@ -463,7 +463,7 @@ s:select{}
 ...
 s:auto_increment{}
 ---
-- error: Failed to allocate 16 bytes in slab allocator for memtx_tuple
+- error: Failed to allocate 12 bytes in slab allocator for memtx_tuple
 ...
 s:select{}
 ---
@@ -471,7 +471,7 @@ s:select{}
 ...
 s:auto_increment{}
 ---
-- error: Failed to allocate 16 bytes in slab allocator for memtx_tuple
+- error: Failed to allocate 12 bytes in slab allocator for memtx_tuple
 ...
 s:select{}
 ---
@@ -485,7 +485,7 @@ box.begin()
     s:insert{1}
 box.commit();
 ---
-- error: Failed to allocate 16 bytes in slab allocator for memtx_tuple
+- error: Failed to allocate 12 bytes in slab allocator for memtx_tuple
 ...
 box.rollback();
 ---
@@ -499,7 +499,7 @@ box.begin()
     s:insert{2}
 box.commit();
 ---
-- error: Failed to allocate 16 bytes in slab allocator for memtx_tuple
+- error: Failed to allocate 12 bytes in slab allocator for memtx_tuple
 ...
 s:select{};
 ---
@@ -513,7 +513,7 @@ box.begin()
     s:insert{2}
 box.commit();
 ---
-- error: Failed to allocate 16 bytes in slab allocator for memtx_tuple
+- error: Failed to allocate 12 bytes in slab allocator for memtx_tuple
 ...
 s:select{};
 ---
@@ -532,7 +532,7 @@ box.begin()
     s:insert{2}
 box.commit();
 ---
-- error: Failed to allocate 16 bytes in slab allocator for memtx_tuple
+- error: Failed to allocate 12 bytes in slab allocator for memtx_tuple
 ...
 errinj.set("ERRINJ_TUPLE_ALLOC", false);
 ---
@@ -794,7 +794,7 @@ errinj.set("ERRINJ_TUPLE_ALLOC", true)
 ...
 s:replace{1, "test"}
 ---
-- error: Failed to allocate 21 bytes in slab allocator for memtx_tuple
+- error: Failed to allocate 17 bytes in slab allocator for memtx_tuple
 ...
 s:bsize()
 ---
@@ -806,7 +806,7 @@ utils.space_bsize(s)
 ...
 s:update({1}, {{'=', 3, '!'}})
 ---
-- error: Failed to allocate 20 bytes in slab allocator for memtx_tuple
+- error: Failed to allocate 16 bytes in slab allocator for memtx_tuple
 ...
 s:bsize()
 ---
diff --git a/test/box/upsert_errinj.result b/test/box/upsert_errinj.result
index ed52e2855..3aca98191 100644
--- a/test/box/upsert_errinj.result
+++ b/test/box/upsert_errinj.result
@@ -19,7 +19,7 @@ errinj.set("ERRINJ_TUPLE_ALLOC", true)
 ...
 s:upsert({111, '111', 222, '222'}, {{'!', 5, '!'}})
 ---
-- error: Failed to allocate 26 bytes in slab allocator for memtx_tuple
+- error: Failed to allocate 22 bytes in slab allocator for memtx_tuple
 ...
 errinj.set("ERRINJ_TUPLE_ALLOC", false)
 ---
diff --git a/test/vinyl/cache.result b/test/vinyl/cache.result
index 49d2bcc7a..4c9e0a96d 100644
--- a/test/vinyl/cache.result
+++ b/test/vinyl/cache.result
@@ -1033,14 +1033,14 @@ for i = 1, 100 do s:get{i} end
 ...
 box.stat.vinyl().memory.tuple_cache
 ---
-- 108500
+- 107300
 ...
 box.cfg{vinyl_cache = 50 * 1000}
 ---
 ...
 box.stat.vinyl().memory.tuple_cache
 ---
-- 49910
+- 49358
 ...
 box.cfg{vinyl_cache = 0}
 ---
@@ -1116,7 +1116,7 @@ s.index.i2:count()
 ...
 box.stat.vinyl().memory.tuple_cache -- should be about 200 KB
 ---
-- 219200
+- 218000
 ...
 s:drop()
 ---
diff --git a/test/vinyl/quota.result b/test/vinyl/quota.result
index 70e81453d..0d0cded1a 100644
--- a/test/vinyl/quota.result
+++ b/test/vinyl/quota.result
@@ -36,7 +36,7 @@ space:insert({1, 1})
 ...
 box.stat.vinyl().memory.level0
 ---
-- 98344
+- 98336
 ...
 space:insert({1, 1})
 ---
@@ -44,7 +44,7 @@ space:insert({1, 1})
 ...
 box.stat.vinyl().memory.level0
 ---
-- 98344
+- 98336
 ...
 space:update({1}, {{'!', 1, 100}}) -- try to modify the primary key
 ---
@@ -52,7 +52,7 @@ space:update({1}, {{'!', 1, 100}}) -- try to modify the primary key
 ...
 box.stat.vinyl().memory.level0
 ---
-- 98344
+- 98336
 ...
 space:insert({2, 2})
 ---
@@ -68,7 +68,7 @@ space:insert({4, 4})
 ...
 box.stat.vinyl().memory.level0
 ---
-- 98463
+- 98417
 ...
 box.snapshot()
 ---
@@ -94,7 +94,7 @@ _ = space:replace{1, 1, string.rep('a', 1024 * 1024 * 5)}
 ...
 box.stat.vinyl().memory.level0
 ---
-- 5292080
+- 5292064
 ...
 space:drop()
 ---
diff --git a/test/vinyl/quota_timeout.result b/test/vinyl/quota_timeout.result
index 31ca23670..0690823cf 100644
--- a/test/vinyl/quota_timeout.result
+++ b/test/vinyl/quota_timeout.result
@@ -49,7 +49,7 @@ s:count()
 ...
 box.stat.vinyl().memory.level0
 ---
-- 748248
+- 748232
 ...
 -- Since the following operation requires more memory than configured
 -- and dump is disabled, it should fail with ER_VY_QUOTA_TIMEOUT.
@@ -63,7 +63,7 @@ s:count()
 ...
 box.stat.vinyl().memory.level0
 ---
-- 748248
+- 748232
 ...
 --
 -- Check that increasing box.cfg.vinyl_memory wakes up fibers
@@ -135,7 +135,7 @@ test_run:cmd("push filter '[0-9.]+ sec' to '<sec> sec'")
 ...
 test_run:grep_log('test', 'waited for .* quota for too long.*')
 ---
-- 'waited for 1048615 bytes of vinyl memory quota for too long: <sec> sec'
+- 'waited for 1048603 bytes of vinyl memory quota for too long: <sec> sec'
 ...
 test_run:cmd("clear filter")
 ---
@@ -167,7 +167,7 @@ pad = string.rep('x', box.cfg.vinyl_memory)
 ...
 _ = s:auto_increment{pad}
 ---
-- error: Failed to allocate 1572903 bytes in lsregion for vinyl transaction
+- error: Failed to allocate 1572891 bytes in lsregion for vinyl transaction
 ...
 s:drop()
 ---
diff --git a/test/vinyl/stat.result b/test/vinyl/stat.result
index a895528b9..18b186633 100644
--- a/test/vinyl/stat.result
+++ b/test/vinyl/stat.result
@@ -301,7 +301,7 @@ stat_diff(istat(), st)
 ---
 - put:
     rows: 25
-    bytes: 26525
+    bytes: 26225
   rows: 25
   run_avg: 1
   run_count: 1
@@ -318,7 +318,7 @@ stat_diff(istat(), st)
     dump:
       input:
         rows: 25
-        bytes: 26525
+        bytes: 26225
       count: 1
       output:
         bytes: 26049
@@ -350,7 +350,7 @@ stat_diff(istat(), st)
 ---
 - put:
     rows: 50
-    bytes: 53050
+    bytes: 52450
   bytes: 26042
   disk:
     last_level:
@@ -364,7 +364,7 @@ stat_diff(istat(), st)
     dump:
       input:
         rows: 50
-        bytes: 53050
+        bytes: 52450
       count: 1
       output:
         bytes: 52091
@@ -402,11 +402,11 @@ stat_diff(istat(), st)
 - cache:
     index_size: 49152
     rows: 1
-    bytes: 1061
+    bytes: 1049
     lookup: 1
     put:
       rows: 1
-      bytes: 1061
+      bytes: 1049
   lookup: 1
   disk:
     iterator:
@@ -418,13 +418,13 @@ stat_diff(istat(), st)
       lookup: 1
       get:
         rows: 1
-        bytes: 1061
+        bytes: 1049
   memory:
     iterator:
       lookup: 1
   get:
     rows: 1
-    bytes: 1061
+    bytes: 1049
 ...
 -- point lookup from cache
 st = istat()
@@ -440,14 +440,14 @@ stat_diff(istat(), st)
     lookup: 1
     put:
       rows: 1
-      bytes: 1061
+      bytes: 1049
     get:
       rows: 1
-      bytes: 1061
+      bytes: 1049
   lookup: 1
   get:
     rows: 1
-    bytes: 1061
+    bytes: 1049
 ...
 -- put in memory + cache invalidate
 st = istat()
@@ -461,18 +461,18 @@ stat_diff(istat(), st)
 - cache:
     invalidate:
       rows: 1
-      bytes: 1061
+      bytes: 1049
     rows: -1
-    bytes: -1061
+    bytes: -1049
   rows: 1
   memory:
     index_size: 49152
-    bytes: 1061
+    bytes: 1049
     rows: 1
   put:
     rows: 1
-    bytes: 1061
-  bytes: 1061
+    bytes: 1049
+  bytes: 1049
 ...
 -- point lookup from memory
 st = istat()
@@ -485,22 +485,22 @@ s:get(1) ~= nil
 stat_diff(istat(), st)
 ---
 - cache:
-    bytes: 1061
+    bytes: 1049
     lookup: 1
     rows: 1
     put:
       rows: 1
-      bytes: 1061
+      bytes: 1049
   memory:
     iterator:
       lookup: 1
       get:
         rows: 1
-        bytes: 1061
+        bytes: 1049
   lookup: 1
   get:
     rows: 1
-    bytes: 1061
+    bytes: 1049
 ...
 -- put in txw + point lookup from txw
 st = istat()
@@ -520,16 +520,16 @@ stat_diff(istat(), st)
 ---
 - txw:
     rows: 1
-    bytes: 1061
+    bytes: 1049
     iterator:
       lookup: 1
       get:
         rows: 1
-        bytes: 1061
+        bytes: 1049
   lookup: 1
   get:
     rows: 1
-    bytes: 1061
+    bytes: 1049
 ...
 box.rollback()
 ---
@@ -586,15 +586,15 @@ for i = 1, 100 do s:get(i) end
 ...
 stat_diff(istat(), st, 'cache')
 ---
-- rows: 14
-  bytes: 14854
+- rows: 15
+  bytes: 15735
   evict:
-    rows: 86
-    bytes: 91246
+    rows: 85
+    bytes: 89165
   lookup: 100
   put:
     rows: 100
-    bytes: 106100
+    bytes: 104900
 ...
 -- range split
 for i = 1, 100 do put(i) end
@@ -649,15 +649,15 @@ st = istat()
 stat_diff(istat(), st)
 ---
 - cache:
-    rows: 13
-    bytes: 13793
+    rows: 14
+    bytes: 14686
     evict:
-      rows: 37
-      bytes: 39257
+      rows: 36
+      bytes: 37764
     lookup: 1
     put:
       rows: 51
-      bytes: 54111
+      bytes: 53499
   disk:
     iterator:
       read:
@@ -668,23 +668,23 @@ stat_diff(istat(), st)
       lookup: 2
       get:
         rows: 100
-        bytes: 106100
+        bytes: 104900
   txw:
     iterator:
       lookup: 1
       get:
         rows: 50
-        bytes: 53050
+        bytes: 52450
   memory:
     iterator:
       lookup: 1
       get:
         rows: 100
-        bytes: 106100
+        bytes: 104900
   lookup: 1
   get:
     rows: 100
-    bytes: 106100
+    bytes: 104900
 ...
 box.rollback()
 ---
@@ -717,17 +717,17 @@ stat_diff(istat(), st)
     lookup: 1
     put:
       rows: 5
-      bytes: 5305
+      bytes: 5245
     get:
       rows: 5
-      bytes: 5305
+      bytes: 5245
   txw:
     iterator:
       lookup: 1
   lookup: 1
   get:
     rows: 5
-    bytes: 5305
+    bytes: 5245
 ...
 box.rollback()
 ---
@@ -761,7 +761,7 @@ put(1)
 ...
 stat_diff(gstat(), st, 'memory.level0')
 ---
-- 1064
+- 1049
 ...
 -- use cache
 st = gstat()
@@ -772,7 +772,7 @@ _ = s:get(1)
 ...
 stat_diff(gstat(), st, 'memory.tuple_cache')
 ---
-- 1109
+- 1097
 ...
 s:delete(1)
 ---
@@ -1011,7 +1011,7 @@ istat()
       rows: 0
       bytes: 0
     index_size: 49152
-    rows: 13
+    rows: 14
     evict:
       rows: 0
       bytes: 0
@@ -1019,7 +1019,7 @@ istat()
       rows: 0
       bytes: 0
     lookup: 0
-    bytes: 13793
+    bytes: 14686
     get:
       rows: 0
       bytes: 0
@@ -1088,7 +1088,7 @@ istat()
   upsert:
     squashed: 0
     applied: 0
-  bytes: 317731
+  bytes: 315259
   put:
     rows: 0
     bytes: 0
@@ -1105,7 +1105,7 @@ istat()
         rows: 0
         bytes: 0
   memory:
-    bytes: 213431
+    bytes: 210959
     index_size: 49152
     rows: 206
     iterator:
@@ -1128,9 +1128,9 @@ gstat()
     gap_locks: 0
     read_views: 0
   memory:
-    tuple_cache: 14417
+    tuple_cache: 15358
     tx: 0
-    level0: 263210
+    level0: 260118
     page_index: 1250
     bloom_filter: 140
   disk:
@@ -1173,7 +1173,7 @@ box.snapshot()
 ...
 stat_diff(gstat(), st, 'scheduler')
 ---
-- dump_input: 104200
+- dump_input: 103000
   dump_output: 103592
   tasks_completed: 2
   dump_count: 1
@@ -1190,7 +1190,7 @@ box.snapshot()
 ...
 stat_diff(gstat(), st, 'scheduler')
 ---
-- dump_input: 10420
+- dump_input: 10300
   dump_output: 10411
   tasks_completed: 2
   dump_count: 1
@@ -1272,7 +1272,7 @@ st2 = i2:stat()
 ...
 s:bsize()
 ---
-- 53300
+- 52700
 ...
 i1:len(), i2:len()
 ---
@@ -1397,7 +1397,7 @@ st2 = i2:stat()
 ...
 s:bsize()
 ---
-- 107199
+- 105999
 ...
 i1:len(), i2:len()
 ---
-- 
2.17.1



More information about the Tarantool-patches mailing list