[Tarantool-patches] [PATCH v2 2/2] memtx: increase the memory quota if needed to truncate or delete

Ilya Kosarev i.kosarev at tarantool.org
Tue Jan 14 00:31:23 MSK 2020


Trying to perform space:truncate() and space:delete() while reaching
memtx_memory limit we could experience slab allocator failure. This
behavior seems to be quite surprising for users. Now we are increasing
memtx quota if needed for truncation or deletion. After performing it
quota is being set back to the previous value if possible, while it
should be so for almost any case, since we are meant to free some space
during deletion or truncation.

Closes #3807
---
 src/box/blackhole.c      |  1 +
 src/box/box.cc           | 36 +++++++++++++++++++++++++++++++++++-
 src/box/engine.c         | 11 +++++++++++
 src/box/engine.h         |  9 +++++++++
 src/box/memtx_engine.c   | 20 ++++++++++++++++++++
 src/box/memtx_engine.h   |  4 ++++
 src/box/service_engine.c |  1 +
 src/box/sysview.c        |  1 +
 src/box/vinyl.c          |  1 +
 9 files changed, 83 insertions(+), 1 deletion(-)

diff --git a/src/box/blackhole.c b/src/box/blackhole.c
index 69f1deba1..af587f434 100644
--- a/src/box/blackhole.c
+++ b/src/box/blackhole.c
@@ -194,6 +194,7 @@ static const struct engine_vtab blackhole_engine_vtab = {
 	/* .commit_checkpoint = */ generic_engine_commit_checkpoint,
 	/* .abort_checkpoint = */ generic_engine_abort_checkpoint,
 	/* .collect_garbage = */ generic_engine_collect_garbage,
+	/* .guarantee_memory = */ generic_engine_guarantee_memory,
 	/* .backup = */ generic_engine_backup,
 	/* .memory_stat = */ generic_engine_memory_stat,
 	/* .reset_stat = */ generic_engine_reset_stat,
diff --git a/src/box/box.cc b/src/box/box.cc
index 1b2b27d61..18c09ce1b 100644
--- a/src/box/box.cc
+++ b/src/box/box.cc
@@ -1250,7 +1250,27 @@ box_delete(uint32_t space_id, uint32_t index_id, const char *key,
 	request.index_id = index_id;
 	request.key = key;
 	request.key_end = key_end;
-	return box_process1(&request, result);
+
+	struct space *space = space_cache_find(space_id);
+	if (space == NULL)
+		return -1;
+	size_t total;
+	bool extended = false;
+	space->engine->vtab->guarantee_memory(space->engine,
+					      MEMTX_SLAB_SIZE,
+					      &total, &extended);
+
+	int rc = box_process1(&request, result);
+
+	if (extended) {
+		struct memtx_engine *memtx =
+			(struct memtx_engine *)space->engine;
+		size_t new_total = quota_set(&memtx->quota, total);
+		if (new_total > total)
+			quota_set(&memtx->quota, quota_used(&memtx->quota));
+	}
+
+	return rc;
 }
 
 int
@@ -1321,9 +1341,23 @@ space_truncate(struct space *space)
 	ops_buf_end = mp_encode_uint(ops_buf_end, 1);
 	assert(ops_buf_end < buf + buf_size);
 
+	size_t total;
+	bool extended = false;
+	space->engine->vtab->guarantee_memory(space->engine,
+					      MEMTX_SLAB_SIZE,
+					      &total, &extended);
+
 	if (box_upsert(BOX_TRUNCATE_ID, 0, tuple_buf, tuple_buf_end,
 		       ops_buf, ops_buf_end, 0, NULL) != 0)
 		diag_raise();
+
+	if (extended) {
+		struct memtx_engine *memtx =
+			(struct memtx_engine *)space->engine;
+		size_t new_total = quota_set(&memtx->quota, total);
+		if (new_total > total)
+			quota_set(&memtx->quota, quota_used(&memtx->quota));
+	}
 }
 
 int
diff --git a/src/box/engine.c b/src/box/engine.c
index 8dc0df1d0..f393a2629 100644
--- a/src/box/engine.c
+++ b/src/box/engine.c
@@ -412,6 +412,17 @@ generic_engine_memory_stat(struct engine *engine,
 	(void)stat;
 }
 
+void
+generic_engine_guarantee_memory(struct engine *engine,
+				size_t request, size_t *old_total,
+				bool *extended)
+{
+	(void)engine;
+	(void)request;
+	*old_total = 0;
+	*extended = false;
+}
+
 void
 generic_engine_reset_stat(struct engine *engine)
 {
diff --git a/src/box/engine.h b/src/box/engine.h
index 07d7fac9b..d1e3e998f 100644
--- a/src/box/engine.h
+++ b/src/box/engine.h
@@ -185,6 +185,14 @@ struct engine_vtab {
 	 */
 	void (*collect_garbage)(struct engine *engine,
 				const struct vclock *vclock);
+	/**
+	 * Performing space:truncate() or space:delete() while reaching
+	 * memory limit might lead to slab allocator failure. To avoid
+	 * it, we temporally increase memory quota using this function.
+	 */
+        void (*guarantee_memory)(struct engine *engine,
+                                  size_t request, size_t *old_total,
+                                  bool *extended);
 	/**
 	 * Backup callback. It is supposed to call @cb for each file
 	 * that needs to be backed up in order to restore from the
@@ -404,6 +412,7 @@ void generic_engine_collect_garbage(struct engine *, const struct vclock *);
 int generic_engine_backup(struct engine *, const struct vclock *,
 			  engine_backup_cb, void *);
 void generic_engine_memory_stat(struct engine *, struct engine_memory_stat *);
+void generic_engine_guarantee_memory(struct engine *engine, size_t request, size_t *old_total, bool *extended);
 void generic_engine_reset_stat(struct engine *);
 int generic_engine_check_space_def(struct space_def *);
 
diff --git a/src/box/memtx_engine.c b/src/box/memtx_engine.c
index 23ccc4703..6c80b5919 100644
--- a/src/box/memtx_engine.c
+++ b/src/box/memtx_engine.c
@@ -927,6 +927,7 @@ static const struct engine_vtab memtx_engine_vtab = {
 	/* .commit_checkpoint = */ memtx_engine_commit_checkpoint,
 	/* .abort_checkpoint = */ memtx_engine_abort_checkpoint,
 	/* .collect_garbage = */ memtx_engine_collect_garbage,
+	/* .guarantee_memory = */ memtx_engine_guarantee_memory,
 	/* .backup = */ memtx_engine_backup,
 	/* .memory_stat = */ memtx_engine_memory_stat,
 	/* .reset_stat = */ generic_engine_reset_stat,
@@ -1090,6 +1091,25 @@ memtx_engine_set_memory(struct memtx_engine *memtx, size_t size)
 	return 0;
 }
 
+void
+memtx_engine_guarantee_memory(struct engine *engine,
+			      size_t request, size_t *old_total,
+			      bool *extended)
+{
+	struct quota *memtx_quota =
+		&((struct memtx_engine *)engine)->quota;
+	size_t total, used;
+	quota_get_total_and_used(memtx_quota, &total, &used);
+	*old_total = total;
+	if (total - used < request) {
+		quota_set(memtx_quota,
+			  total + request - (total - used));
+		*extended = true;
+		return;
+	}
+	*extended = false;
+}
+
 void
 memtx_engine_set_max_tuple_size(struct memtx_engine *memtx, size_t max_size)
 {
diff --git a/src/box/memtx_engine.h b/src/box/memtx_engine.h
index f562c66df..b8489fffe 100644
--- a/src/box/memtx_engine.h
+++ b/src/box/memtx_engine.h
@@ -213,6 +213,10 @@ memtx_engine_set_snap_io_rate_limit(struct memtx_engine *memtx, double limit);
 int
 memtx_engine_set_memory(struct memtx_engine *memtx, size_t size);
 
+void
+memtx_engine_guarantee_memory(struct engine *engine, size_t request,
+			      size_t *old_total, bool *extended);
+
 void
 memtx_engine_set_max_tuple_size(struct memtx_engine *memtx, size_t max_size);
 
diff --git a/src/box/service_engine.c b/src/box/service_engine.c
index 5a33a735a..cfbd4ee60 100644
--- a/src/box/service_engine.c
+++ b/src/box/service_engine.c
@@ -112,6 +112,7 @@ static const struct engine_vtab service_engine_vtab = {
 	/* .commit_checkpoint = */ generic_engine_commit_checkpoint,
 	/* .abort_checkpoint = */ generic_engine_abort_checkpoint,
 	/* .collect_garbage = */ generic_engine_collect_garbage,
+	/* .guarantee_memory = */ generic_engine_guarantee_memory,
 	/* .backup = */ generic_engine_backup,
 	/* .memory_stat = */ generic_engine_memory_stat,
 	/* .reset_stat = */ generic_engine_reset_stat,
diff --git a/src/box/sysview.c b/src/box/sysview.c
index 00c320b6f..bfc7bd1dd 100644
--- a/src/box/sysview.c
+++ b/src/box/sysview.c
@@ -584,6 +584,7 @@ static const struct engine_vtab sysview_engine_vtab = {
 	/* .commit_checkpoint = */ generic_engine_commit_checkpoint,
 	/* .abort_checkpoint = */ generic_engine_abort_checkpoint,
 	/* .collect_garbage = */ generic_engine_collect_garbage,
+	/* .guarantee_memory = */ generic_engine_guarantee_memory,
 	/* .backup = */ generic_engine_backup,
 	/* .memory_stat = */ generic_engine_memory_stat,
 	/* .reset_stat = */ generic_engine_reset_stat,
diff --git a/src/box/vinyl.c b/src/box/vinyl.c
index 5f169f09b..9343423c5 100644
--- a/src/box/vinyl.c
+++ b/src/box/vinyl.c
@@ -4489,6 +4489,7 @@ static const struct engine_vtab vinyl_engine_vtab = {
 	/* .commit_checkpoint = */ vinyl_engine_commit_checkpoint,
 	/* .abort_checkpoint = */ vinyl_engine_abort_checkpoint,
 	/* .collect_garbage = */ vinyl_engine_collect_garbage,
+	/* .guarantee_memory = */ generic_engine_guarantee_memory,
 	/* .backup = */ vinyl_engine_backup,
 	/* .memory_stat = */ vinyl_engine_memory_stat,
 	/* .reset_stat = */ vinyl_engine_reset_stat,
-- 
2.17.1



More information about the Tarantool-patches mailing list