[PATCH v2 02/11] vinyl: factor load regulator out of quota

Vladimir Davydov vdavydov.dev at gmail.com
Fri Sep 28 20:40:00 MSK 2018


Turned out that throttling isn't going to be as simple as maintaining
the write rate below the estimated dump bandwidth, because we also need
to take into account whether compaction keeps up with dumps. Tracking
compaction progress isn't a trivial task and mixing it in a module
responsible for resource limiting, which vy_quota is, doesn't seem to be
a good idea. Let's factor out the related code into a separate module
and call it vy_regulator. Currently, the new module only keeps track of
the write rate and the dump bandwidth and sets the memory watermark
accordingly, but soon we will extend it to configure throttling as well.

Since write rate and dump bandwidth are now a part of the regulator
subsystem, this patch renames 'quota' entry of box.stat.vinyl() to
'regulator'. It also removes 'quota.usage' and 'quota.limit' altogether,
because memory usage is reported under 'memory.level0' while the limit
can be read from box.cfg.vinyl_memory, and renames 'use_rate' to
'write_rate', because the latter seems to be a more appropriate name.

Needed for #1862
---
 src/box/CMakeLists.txt             |   1 +
 src/box/vinyl.c                    |  48 ++++++---
 src/box/vy_quota.c                 | 133 +-----------------------
 src/box/vy_quota.h                 |  59 +----------
 src/box/vy_regulator.c             | 206 +++++++++++++++++++++++++++++++++++++
 src/box/vy_regulator.h             | 146 ++++++++++++++++++++++++++
 test/vinyl/errinj.result           |   4 +-
 test/vinyl/errinj.test.lua         |   4 +-
 test/vinyl/info.result             |  14 +--
 test/vinyl/info.test.lua           |   8 +-
 test/vinyl/quota.result            |  26 ++---
 test/vinyl/quota.test.lua          |  26 ++---
 test/vinyl/quota_timeout.result    |   6 +-
 test/vinyl/quota_timeout.test.lua  |   6 +-
 test/vinyl/recovery_quota.result   |   6 +-
 test/vinyl/recovery_quota.test.lua |   6 +-
 16 files changed, 438 insertions(+), 261 deletions(-)
 create mode 100644 src/box/vy_regulator.c
 create mode 100644 src/box/vy_regulator.h

diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index cab6a227..67750898 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -86,6 +86,7 @@ add_library(box STATIC
     vy_history.c
     vy_read_set.c
     vy_scheduler.c
+    vy_regulator.c
     vy_quota.c
     request.c
     space.c
diff --git a/src/box/vinyl.c b/src/box/vinyl.c
index 67b9c589..1c0192cb 100644
--- a/src/box/vinyl.c
+++ b/src/box/vinyl.c
@@ -43,6 +43,7 @@
 #include "vy_point_lookup.h"
 #include "vy_quota.h"
 #include "vy_scheduler.h"
+#include "vy_regulator.h"
 #include "vy_stat.h"
 
 #include <stdbool.h>
@@ -115,6 +116,8 @@ struct vy_env {
 	struct vy_mem_env mem_env;
 	/** Scheduler */
 	struct vy_scheduler scheduler;
+	/** Load regulator. */
+	struct vy_regulator regulator;
 	/** Local recovery context. */
 	struct vy_recovery *recovery;
 	/** Local recovery vclock. */
@@ -249,17 +252,15 @@ static struct trigger on_replace_vinyl_deferred_delete;
 /** {{{ Introspection */
 
 static void
-vy_info_append_quota(struct vy_env *env, struct info_handler *h)
+vy_info_append_regulator(struct vy_env *env, struct info_handler *h)
 {
-	struct vy_quota *q = &env->quota;
+	struct vy_regulator *r = &env->regulator;
 
-	info_table_begin(h, "quota");
-	info_append_int(h, "used", q->used);
-	info_append_int(h, "limit", q->limit);
-	info_append_int(h, "watermark", q->watermark);
-	info_append_int(h, "use_rate", q->use_rate);
-	info_append_int(h, "dump_bandwidth", q->dump_bw);
-	info_table_end(h); /* quota */
+	info_table_begin(h, "regulator");
+	info_append_int(h, "watermark", r->watermark);
+	info_append_int(h, "write_rate", r->write_rate);
+	info_append_int(h, "dump_bandwidth", r->dump_bw);
+	info_table_end(h); /* regulator */
 }
 
 static void
@@ -328,10 +329,10 @@ vinyl_engine_stat(struct vinyl_engine *vinyl, struct info_handler *h)
 	struct vy_env *env = vinyl->env;
 
 	info_begin(h);
-	vy_info_append_quota(env, h);
 	vy_info_append_tx(env, h);
 	vy_info_append_memory(env, h);
 	vy_info_append_disk(env, h);
+	vy_info_append_regulator(env, h);
 	info_end(h);
 }
 
@@ -2366,6 +2367,7 @@ vinyl_engine_prepare(struct engine *engine, struct txn *txn)
 	assert(mem_used_after >= mem_used_before);
 	vy_quota_adjust(&env->quota, tx->write_size,
 			mem_used_after - mem_used_before);
+	vy_regulator_check_watermark(&env->regulator);
 	return rc;
 }
 
@@ -2390,6 +2392,7 @@ vinyl_engine_commit(struct engine *engine, struct txn *txn)
 	assert(mem_used_after >= mem_used_before);
 	/* We can't abort the transaction at this point, use force. */
 	vy_quota_force_use(&env->quota, mem_used_after - mem_used_before);
+	vy_regulator_check_watermark(&env->regulator);
 
 	txn->engine_tx = NULL;
 	if (!txn->is_autocommit)
@@ -2440,6 +2443,13 @@ static void
 vy_env_quota_exceeded_cb(struct vy_quota *quota)
 {
 	struct vy_env *env = container_of(quota, struct vy_env, quota);
+	vy_regulator_no_memory(&env->regulator);
+}
+
+static void
+vy_env_trigger_dump_cb(struct vy_regulator *regulator)
+{
+	struct vy_env *env = container_of(regulator, struct vy_env, regulator);
 
 	if (lsregion_used(&env->mem_env.allocator) == 0) {
 		/*
@@ -2468,7 +2478,7 @@ vy_env_dump_complete_cb(struct vy_scheduler *scheduler,
 	assert(mem_used_after <= mem_used_before);
 	size_t mem_dumped = mem_used_before - mem_used_after;
 	vy_quota_release(quota, mem_dumped);
-	vy_quota_update_dump_bandwidth(quota, mem_dumped, dump_duration);
+	vy_regulator_dump_complete(&env->regulator, mem_dumped, dump_duration);
 	say_info("dumped %zu bytes in %.1f sec", mem_dumped, dump_duration);
 }
 
@@ -2520,8 +2530,9 @@ vy_env_new(const char *path, size_t memory,
 			      vy_squash_schedule, e) != 0)
 		goto error_lsm_env;
 
-	if (vy_quota_create(&e->quota, vy_env_quota_exceeded_cb) != 0)
-		goto error_quota;
+	vy_quota_create(&e->quota, vy_env_quota_exceeded_cb);
+	vy_regulator_create(&e->regulator, &e->quota,
+			    vy_env_trigger_dump_cb);
 
 	struct slab_cache *slab_cache = cord_slab_cache();
 	mempool_create(&e->iterator_pool, slab_cache,
@@ -2530,8 +2541,7 @@ vy_env_new(const char *path, size_t memory,
 	vy_run_env_create(&e->run_env);
 	vy_log_init(e->path);
 	return e;
-error_quota:
-	vy_lsm_env_destroy(&e->lsm_env);
+
 error_lsm_env:
 	vy_mem_env_destroy(&e->mem_env);
 	vy_scheduler_destroy(&e->scheduler);
@@ -2548,6 +2558,7 @@ error_path:
 static void
 vy_env_delete(struct vy_env *e)
 {
+	vy_regulator_destroy(&e->regulator);
 	vy_scheduler_destroy(&e->scheduler);
 	vy_squash_queue_delete(e->squash_queue);
 	tx_manager_delete(e->xm);
@@ -2574,6 +2585,7 @@ vy_env_complete_recovery(struct vy_env *env)
 {
 	vy_scheduler_start(&env->scheduler);
 	vy_quota_set_limit(&env->quota, env->memory);
+	vy_regulator_start(&env->regulator);
 }
 
 struct vinyl_engine *
@@ -2651,7 +2663,7 @@ vinyl_engine_set_snap_io_rate_limit(struct vinyl_engine *vinyl, double limit)
 {
 	int64_t limit_in_bytes = limit * 1024 * 1024;
 	vinyl->env->run_env.snap_io_rate_limit = limit_in_bytes;
-	vy_quota_reset_dump_bandwidth(&vinyl->env->quota, limit_in_bytes);
+	vy_regulator_reset_dump_bw(&vinyl->env->regulator, limit_in_bytes);
 }
 
 /** }}} Environment */
@@ -3194,6 +3206,7 @@ vinyl_space_apply_initial_join_row(struct space *space, struct request *request)
 	assert(mem_used_after >= mem_used_before);
 	size_t used = mem_used_after - mem_used_before;
 	vy_quota_adjust(&env->quota, reserved, used);
+	vy_regulator_check_watermark(&env->regulator);
 	return rc;
 }
 
@@ -3515,6 +3528,7 @@ vy_squash_process(struct vy_squash *squash)
 		vy_mem_commit_stmt(mem, region_stmt);
 		vy_quota_force_use(&env->quota,
 				   mem_used_after - mem_used_before);
+		vy_regulator_check_watermark(&env->regulator);
 	}
 	return rc;
 }
@@ -3989,6 +4003,7 @@ vy_build_insert_tuple(struct vy_env *env, struct vy_lsm *lsm,
 	size_t mem_used_after = lsregion_used(&env->mem_env.allocator);
 	assert(mem_used_after >= mem_used_before);
 	vy_quota_force_use(&env->quota, mem_used_after - mem_used_before);
+	vy_regulator_check_watermark(&env->regulator);
 	vy_quota_wait(&env->quota);
 	return rc;
 }
@@ -4419,6 +4434,7 @@ vy_deferred_delete_on_replace(struct trigger *trigger, void *event)
 	size_t mem_used_after = lsregion_used(&env->mem_env.allocator);
 	assert(mem_used_after >= mem_used_before);
 	vy_quota_force_use(&env->quota, mem_used_after - mem_used_before);
+	vy_regulator_check_watermark(&env->regulator);
 
 	tuple_unref(delete);
 	if (rc != 0)
diff --git a/src/box/vy_quota.c b/src/box/vy_quota.c
index 51f0ba71..e6d22348 100644
--- a/src/box/vy_quota.c
+++ b/src/box/vy_quota.c
@@ -32,42 +32,13 @@
 
 #include <assert.h>
 #include <stddef.h>
-#include <stdint.h>
-#include <math.h>
 #include <tarantool_ev.h>
 
-#include "diag.h"
 #include "fiber.h"
 #include "fiber_cond.h"
 #include "say.h"
-#include "histogram.h"
 #include "trivia/util.h"
 
-enum {
-	/**
-	 * Time interval between successive updates of
-	 * quota watermark and use rate, in seconds.
-	 */
-	VY_QUOTA_UPDATE_INTERVAL = 1,
-	/**
-	 * Period of time over which the quota use rate
-	 * is averaged, in seconds.
-	 */
-	VY_QUOTA_RATE_AVG_PERIOD = 5,
-};
-
-/*
- * Until we dump anything, assume bandwidth to be 10 MB/s,
- * which should be fine for initial guess.
- */
-static const size_t VY_DEFAULT_DUMP_BANDWIDTH = 10 * 1024 * 1024;
-
-/**
- * Histogram percentile used for estimating dump bandwidth.
- * For details see the comment to vy_quota::dump_bw_hist.
- */
-enum { VY_DUMP_BANDWIDTH_PCT = 10 };
-
 /**
  * Returns true if the quota limit is exceeded and so consumers
  * have to wait.
@@ -78,84 +49,19 @@ vy_quota_is_exceeded(struct vy_quota *q)
 	return q->used > q->limit;
 }
 
-static void
-vy_quota_timer_cb(ev_loop *loop, ev_timer *timer, int events)
-{
-	(void)loop;
-	(void)events;
-
-	struct vy_quota *q = timer->data;
-
-	/*
-	 * Update the quota use rate with the new measurement.
-	 */
-	const double weight = 1 - exp(-VY_QUOTA_UPDATE_INTERVAL /
-				      (double)VY_QUOTA_RATE_AVG_PERIOD);
-	q->use_rate = (1 - weight) * q->use_rate +
-		weight * q->use_curr / VY_QUOTA_UPDATE_INTERVAL;
-	q->use_curr = 0;
-
-	/*
-	 * Due to log structured nature of the lsregion allocator,
-	 * which is used for allocating statements, we cannot free
-	 * memory in chunks, only all at once. Therefore we should
-	 * configure the watermark so that by the time we hit the
-	 * limit, all memory have been dumped, i.e.
-	 *
-	 *   limit - watermark      watermark
-	 *   ----------------- = --------------
-	 *        use_rate       dump_bandwidth
-	 */
-	q->watermark = ((double)q->limit * q->dump_bw /
-			(q->dump_bw + q->use_rate + 1));
-	if (q->used >= q->watermark)
-		q->quota_exceeded_cb(q);
-}
-
-int
+void
 vy_quota_create(struct vy_quota *q, vy_quota_exceeded_f quota_exceeded_cb)
 {
-	enum { KB = 1024, MB = KB * KB };
-	static int64_t dump_bandwidth_buckets[] = {
-		100 * KB, 200 * KB, 300 * KB, 400 * KB, 500 * KB, 600 * KB,
-		700 * KB, 800 * KB, 900 * KB,   1 * MB,   2 * MB,   3 * MB,
-		  4 * MB,   5 * MB,   6 * MB,   7 * MB,   8 * MB,   9 * MB,
-		 10 * MB,  15 * MB,  20 * MB,  25 * MB,  30 * MB,  35 * MB,
-		 40 * MB,  45 * MB,  50 * MB,  55 * MB,  60 * MB,  65 * MB,
-		 70 * MB,  75 * MB,  80 * MB,  85 * MB,  90 * MB,  95 * MB,
-		100 * MB, 200 * MB, 300 * MB, 400 * MB, 500 * MB, 600 * MB,
-		700 * MB, 800 * MB, 900 * MB,
-	};
-
-	q->dump_bw_hist = histogram_new(dump_bandwidth_buckets,
-					lengthof(dump_bandwidth_buckets));
-	if (q->dump_bw_hist == NULL) {
-		diag_set(OutOfMemory, 0, "histogram_new",
-			 "dump bandwidth histogram");
-		return -1;
-	}
-
 	q->limit = SIZE_MAX;
-	q->watermark = SIZE_MAX;
 	q->used = 0;
-	q->use_curr = 0;
-	q->use_rate = 0;
 	q->too_long_threshold = TIMEOUT_INFINITY;
-	q->dump_bw = VY_DEFAULT_DUMP_BANDWIDTH;
 	q->quota_exceeded_cb = quota_exceeded_cb;
 	fiber_cond_create(&q->cond);
-	ev_timer_init(&q->timer, vy_quota_timer_cb, 0,
-		      VY_QUOTA_UPDATE_INTERVAL);
-	q->timer.data = q;
-	ev_timer_start(loop(), &q->timer);
-	return 0;
 }
 
 void
 vy_quota_destroy(struct vy_quota *q)
 {
-	ev_timer_stop(loop(), &q->timer);
-	histogram_delete(q->dump_bw_hist);
 	fiber_cond_broadcast(&q->cond);
 	fiber_cond_destroy(&q->cond);
 }
@@ -163,41 +69,17 @@ vy_quota_destroy(struct vy_quota *q)
 void
 vy_quota_set_limit(struct vy_quota *q, size_t limit)
 {
-	q->limit = q->watermark = limit;
+	q->limit = limit;
 	if (q->used >= limit)
 		q->quota_exceeded_cb(q);
 	fiber_cond_signal(&q->cond);
 }
 
 void
-vy_quota_update_dump_bandwidth(struct vy_quota *q, size_t size,
-			       double duration)
-{
-	if (duration > 0) {
-		histogram_collect(q->dump_bw_hist, size / duration);
-		/*
-		 * To avoid unpredictably long stalls, we need to
-		 * know the worst (smallest) dump bandwidth so use
-		 * a lower-bound percentile estimate.
-		 */
-		q->dump_bw = histogram_percentile_lower(q->dump_bw_hist,
-							VY_DUMP_BANDWIDTH_PCT);
-	}
-}
-
-void
-vy_quota_reset_dump_bandwidth(struct vy_quota *q, size_t max)
-{
-	histogram_reset(q->dump_bw_hist);
-	q->dump_bw = MIN(VY_DEFAULT_DUMP_BANDWIDTH, max);
-}
-
-void
 vy_quota_force_use(struct vy_quota *q, size_t size)
 {
 	q->used += size;
-	q->use_curr += size;
-	if (q->used >= q->watermark)
+	if (q->used >= q->limit)
 		q->quota_exceeded_cb(q);
 }
 
@@ -213,7 +95,6 @@ int
 vy_quota_use(struct vy_quota *q, size_t size, double timeout)
 {
 	q->used += size;
-	q->use_curr += size;
 	if (vy_quota_is_exceeded(q)) {
 		/* Wait for quota. */
 		double start_time = ev_monotonic_now(loop());
@@ -222,11 +103,9 @@ vy_quota_use(struct vy_quota *q, size_t size, double timeout)
 		do {
 			q->quota_exceeded_cb(q);
 			q->used -= size;
-			q->use_curr -= size;
 			if (fiber_cond_wait_deadline(&q->cond, deadline) != 0)
 				return -1; /* timed out */
 			q->used += size;
-			q->use_curr += size;
 		} while (vy_quota_is_exceeded(q));
 
 		double wait_time = ev_monotonic_now(loop()) - start_time;
@@ -240,8 +119,6 @@ vy_quota_use(struct vy_quota *q, size_t size, double timeout)
 		 */
 		fiber_cond_signal(&q->cond);
 	}
-	if (q->used >= q->watermark)
-		q->quota_exceeded_cb(q);
 	return 0;
 }
 
@@ -252,10 +129,6 @@ vy_quota_adjust(struct vy_quota *q, size_t reserved, size_t used)
 		size_t excess = reserved - used;
 		assert(q->used >= excess);
 		q->used -= excess;
-		if (q->use_curr >= excess)
-			q->use_curr -= excess;
-		else /* was reset by timeout */
-			q->use_curr = 0;
 		fiber_cond_signal(&q->cond);
 	}
 	if (reserved < used)
diff --git a/src/box/vy_quota.h b/src/box/vy_quota.h
index 9ce53fc4..59fe075f 100644
--- a/src/box/vy_quota.h
+++ b/src/box/vy_quota.h
@@ -40,7 +40,6 @@ extern "C" {
 #endif /* defined(__cplusplus) */
 
 struct vy_quota;
-struct histogram;
 
 typedef void
 (*vy_quota_exceeded_f)(struct vy_quota *quota);
@@ -55,12 +54,6 @@ struct vy_quota {
 	 * throttled until memory is reclaimed.
 	 */
 	size_t limit;
-	/**
-	 * Memory watermark. Exceeding it does not result in
-	 * throttling new transactions, but it does trigger
-	 * background memory reclaim.
-	 */
-	size_t watermark;
 	/** Current memory consumption. */
 	size_t used;
 	/**
@@ -74,40 +67,13 @@ struct vy_quota {
 	 */
 	struct fiber_cond cond;
 	/**
-	 * Called when quota is consumed if used >= watermark.
+	 * Called if the limit is hit when quota is consumed.
 	 * It is supposed to trigger memory reclaim.
 	 */
 	vy_quota_exceeded_f quota_exceeded_cb;
-	/** Timer for updating quota watermark. */
-	ev_timer timer;
-	/**
-	 * Amount of quota used since the last
-	 * invocation of the quota timer callback.
-	 */
-	size_t use_curr;
-	/**
-	 * Quota use rate, in bytes per second.
-	 * Calculated as exponentially weighted
-	 * moving average of use_curr.
-	 */
-	size_t use_rate;
-	/** Current dump bandwidth estimate. */
-	size_t dump_bw;
-	/**
-	 * Dump bandwidth is needed for calculating the quota watermark.
-	 * The higher the bandwidth, the later we can start dumping w/o
-	 * suffering from transaction throttling. So we want to be very
-	 * conservative about estimating the bandwidth.
-	 *
-	 * To make sure we don't overestimate it, we maintain a
-	 * histogram of all observed measurements and assume the
-	 * bandwidth to be equal to the 10th percentile, i.e. the
-	 * best result among 10% worst measurements.
-	 */
-	struct histogram *dump_bw_hist;
 };
 
-int
+void
 vy_quota_create(struct vy_quota *q, vy_quota_exceeded_f quota_exceeded_cb);
 
 void
@@ -120,27 +86,6 @@ vy_quota_destroy(struct vy_quota *q);
 void
 vy_quota_set_limit(struct vy_quota *q, size_t limit);
 
-/** Return dump bandwidth. */
-size_t
-vy_quota_dump_bandwidth(struct vy_quota *q);
-
-/**
- * Update dump bandwidth.
- *
- * @size: size of dumped memory.
- * @duration: how long memory dump took.
- */
-void
-vy_quota_update_dump_bandwidth(struct vy_quota *q, size_t size,
-			       double duration);
-
-/**
- * Reset dump bandwidth histogram and update initial estimate.
- * Called when box.cfg.snap_io_rate_limit is updated.
- */
-void
-vy_quota_reset_dump_bandwidth(struct vy_quota *q, size_t max);
-
 /**
  * Consume @size bytes of memory. In contrast to vy_quota_use()
  * this function does not throttle the caller.
diff --git a/src/box/vy_regulator.c b/src/box/vy_regulator.c
new file mode 100644
index 00000000..3bd4fee4
--- /dev/null
+++ b/src/box/vy_regulator.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2010-2018, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "vy_regulator.h"
+
+#include <math.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <tarantool_ev.h>
+
+#include "fiber.h"
+#include "histogram.h"
+#include "say.h"
+#include "trivia/util.h"
+
+#include "vy_quota.h"
+
+/**
+ * Regulator timer period, in seconds.
+ */
+static const double VY_REGULATOR_TIMER_PERIOD = 1;
+
+/**
+ * Time window over which the write rate is averaged,
+ * in seconds.
+ */
+static const double VY_WRITE_RATE_AVG_WIN = 5;
+
+/**
+ * Histogram percentile used for estimating dump bandwidth.
+ * For details see the comment to vy_regulator::dump_bw_hist.
+ */
+static const int VY_DUMP_BW_PCT = 10;
+
+/*
+ * Until we dump anything, assume bandwidth to be 10 MB/s,
+ * which should be fine for initial guess.
+ */
+static const size_t VY_DUMP_BW_DEFAULT = 10 * 1024 * 1024;
+
+static void
+vy_regulator_update_write_rate(struct vy_regulator *regulator)
+{
+	size_t used_curr = regulator->quota->used;
+	size_t used_last = regulator->used_last;
+
+	/*
+	 * Memory can be dumped between two subsequent timer
+	 * callback invocations, in which case memory usage
+	 * will decrease. Ignore such observations - it's not
+	 * a big deal, because dump is a rare event.
+	 */
+	if (used_curr < used_last)
+		return;
+
+	size_t rate_avg = regulator->write_rate;
+	size_t rate_curr = (used_curr - used_last) / VY_REGULATOR_TIMER_PERIOD;
+
+	double weight = 1 - exp(-VY_REGULATOR_TIMER_PERIOD /
+				VY_WRITE_RATE_AVG_WIN);
+	rate_avg = (1 - weight) * rate_avg + weight * rate_curr;
+
+	regulator->write_rate = rate_avg;
+	regulator->used_last = used_curr;
+}
+
+static void
+vy_regulator_update_watermark(struct vy_regulator *regulator)
+{
+	struct vy_quota *quota = regulator->quota;
+
+	/*
+	 * Due to log structured nature of the lsregion allocator,
+	 * which is used for allocating statements, we cannot free
+	 * memory in chunks, only all at once. Therefore we should
+	 * configure the watermark so that by the time we hit the
+	 * limit, all memory have been dumped, i.e.
+	 *
+	 *   limit - watermark      watermark
+	 *   ----------------- = --------------
+	 *       write_rate      dump_bandwidth
+	 */
+	regulator->watermark = (double)quota->limit * regulator->dump_bw /
+			(regulator->dump_bw + regulator->write_rate + 1);
+}
+
+static void
+vy_regulator_timer_cb(ev_loop *loop, ev_timer *timer, int events)
+{
+	(void)loop;
+	(void)events;
+
+	struct vy_regulator *regulator = timer->data;
+
+	vy_regulator_update_write_rate(regulator);
+	vy_regulator_update_watermark(regulator);
+	vy_regulator_check_watermark(regulator);
+}
+
+void
+vy_regulator_create(struct vy_regulator *regulator, struct vy_quota *quota,
+		    vy_trigger_dump_f trigger_dump_cb)
+{
+	enum { KB = 1024, MB = KB * KB };
+	static int64_t dump_bw_buckets[] = {
+		100 * KB, 200 * KB, 300 * KB, 400 * KB, 500 * KB, 600 * KB,
+		700 * KB, 800 * KB, 900 * KB,   1 * MB,   2 * MB,   3 * MB,
+		  4 * MB,   5 * MB,   6 * MB,   7 * MB,   8 * MB,   9 * MB,
+		 10 * MB,  15 * MB,  20 * MB,  25 * MB,  30 * MB,  35 * MB,
+		 40 * MB,  45 * MB,  50 * MB,  55 * MB,  60 * MB,  65 * MB,
+		 70 * MB,  75 * MB,  80 * MB,  85 * MB,  90 * MB,  95 * MB,
+		100 * MB, 200 * MB, 300 * MB, 400 * MB, 500 * MB, 600 * MB,
+		700 * MB, 800 * MB, 900 * MB,
+	};
+	regulator->dump_bw_hist = histogram_new(dump_bw_buckets,
+						lengthof(dump_bw_buckets));
+	if (regulator->dump_bw_hist == NULL)
+		panic("failed to allocate dump bandwidth histogram");
+
+	regulator->quota = quota;
+	regulator->trigger_dump_cb = trigger_dump_cb;
+	ev_timer_init(&regulator->timer, vy_regulator_timer_cb, 0,
+		      VY_REGULATOR_TIMER_PERIOD);
+	regulator->timer.data = regulator;
+	regulator->watermark = SIZE_MAX;
+	regulator->write_rate = 0;
+	regulator->dump_bw = VY_DUMP_BW_DEFAULT;
+}
+
+void
+vy_regulator_start(struct vy_regulator *regulator)
+{
+	ev_timer_start(loop(), &regulator->timer);
+}
+
+void
+vy_regulator_destroy(struct vy_regulator *regulator)
+{
+	ev_timer_stop(loop(), &regulator->timer);
+	histogram_delete(regulator->dump_bw_hist);
+}
+
+void
+vy_regulator_no_memory(struct vy_regulator *regulator)
+{
+	regulator->trigger_dump_cb(regulator);
+}
+
+void
+vy_regulator_check_watermark(struct vy_regulator *regulator)
+{
+	if (regulator->quota->used >= regulator->watermark)
+		regulator->trigger_dump_cb(regulator);
+}
+
+void
+vy_regulator_dump_complete(struct vy_regulator *regulator,
+			   size_t mem_dumped, double dump_duration)
+{
+	if (dump_duration > 0) {
+		histogram_collect(regulator->dump_bw_hist,
+				  mem_dumped / dump_duration);
+		/*
+		 * To avoid unpredictably long stalls caused by
+		 * mispredicting dump time duration, we need to
+		 * know the worst (smallest) dump bandwidth so
+		 * use a lower-bound percentile estimate.
+		 */
+		regulator->dump_bw = histogram_percentile_lower(
+			regulator->dump_bw_hist, VY_DUMP_BW_PCT);
+	}
+}
+
+void
+vy_regulator_reset_dump_bw(struct vy_regulator *regulator, size_t max)
+{
+	histogram_reset(regulator->dump_bw_hist);
+	regulator->dump_bw = MIN(VY_DUMP_BW_DEFAULT, max);
+}
diff --git a/src/box/vy_regulator.h b/src/box/vy_regulator.h
new file mode 100644
index 00000000..9336adc9
--- /dev/null
+++ b/src/box/vy_regulator.h
@@ -0,0 +1,146 @@
+#ifndef INCLUDES_TARANTOOL_BOX_VY_REGULATOR_H
+#define INCLUDES_TARANTOOL_BOX_VY_REGULATOR_H
+/*
+ * Copyright 2010-2018, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stddef.h>
+#include <tarantool_ev.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+struct histogram;
+struct vy_quota;
+struct vy_regulator;
+
+typedef void
+(*vy_trigger_dump_f)(struct vy_regulator *regulator);
+
+/**
+ * The regulator is supposed to keep track of vinyl memory usage
+ * and dump/compaction progress and adjust transaction write rate
+ * accordingly.
+ */
+struct vy_regulator {
+	/**
+	 * Pointer to a quota object that is used to control
+	 * memory usage.
+	 */
+	struct vy_quota *quota;
+	/**
+	 * Called when the regulator detects that memory usage
+	 * exceeds the computed watermark. Supposed to trigger
+	 * memory dump.
+	 */
+	vy_trigger_dump_f trigger_dump_cb;
+	/**
+	 * Periodic timer that updates the memory watermark
+	 * basing on accumulated statistics.
+	 */
+	ev_timer timer;
+	/**
+	 * Memory watermark. Exceeding it does not result in
+	 * throttling new transactions, but it does trigger
+	 * background memory reclaim.
+	 */
+	size_t watermark;
+	/**
+	 * Average rate at which transactions are writing to
+	 * the database, in bytes per second.
+	 */
+	size_t write_rate;
+	/**
+	 * Amount of memory that was used when the timer was
+	 * executed last time. Needed to update @write_rate.
+	 */
+	size_t used_last;
+	/**
+	 * Current dump bandwidth estimate, in bytes per second.
+	 * See @dump_bw_hist for more details.
+	 */
+	size_t dump_bw;
+	/**
+	 * Dump bandwidth is needed for calculating the watermark.
+	 * The higher the bandwidth, the later we can start dumping
+	 * w/o suffering from transaction throttling. So we want to
+	 * be very conservative about estimating the bandwidth.
+	 *
+	 * To make sure we don't overestimate it, we maintain a
+	 * histogram of all observed measurements and assume the
+	 * bandwidth to be equal to the 10th percentile, i.e. the
+	 * best result among 10% worst measurements.
+	 */
+	struct histogram *dump_bw_hist;
+};
+
+void
+vy_regulator_create(struct vy_regulator *regulator, struct vy_quota *quota,
+		    vy_trigger_dump_f trigger_dump_cb);
+
+void
+vy_regulator_start(struct vy_regulator *regulator);
+
+void
+vy_regulator_destroy(struct vy_regulator *regulator);
+
+/**
+ * Check if memory usage is above the watermark and trigger
+ * memory dump if so.
+ */
+void
+vy_regulator_check_watermark(struct vy_regulator *regulator);
+
+/**
+ * Called when the memory limit is hit by a quota consumer.
+ */
+void
+vy_regulator_no_memory(struct vy_regulator *regulator);
+
+/**
+ * Notify the regulator about memory dump completion.
+ */
+void
+vy_regulator_dump_complete(struct vy_regulator *regulator,
+			   size_t mem_dumped, double dump_duration);
+
+/**
+ * Reset dump bandwidth histogram and update initial estimate.
+ * Called when box.cfg.snap_io_rate_limit is updated.
+ */
+void
+vy_regulator_reset_dump_bw(struct vy_regulator *regulator, size_t max);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif /* defined(__cplusplus) */
+
+#endif /* INCLUDES_TARANTOOL_BOX_VY_REGULATOR_H */
diff --git a/test/vinyl/errinj.result b/test/vinyl/errinj.result
index 8badb47a..b4dc5b69 100644
--- a/test/vinyl/errinj.result
+++ b/test/vinyl/errinj.result
@@ -1359,7 +1359,7 @@ pad = string.rep('x', 100 * 1024)
 _ = fiber.create(function() for i = 1, 11 do box.space.test:replace{i, pad} end end)
 ---
 ...
-repeat fiber.sleep(0.001) q = box.info.vinyl().quota until q.limit - q.used < pad:len()
+repeat fiber.sleep(0.001) until box.cfg.vinyl_memory - box.stat.vinyl().memory.level0 < pad:len()
 ---
 ...
 test_run:cmd("restart server low_quota with args='1048576'")
@@ -1376,7 +1376,7 @@ pad = string.rep('x', 100 * 1024)
 _ = fiber.create(function() for i = 1, 11 do box.space.test:replace{i, pad} end end)
 ---
 ...
-repeat fiber.sleep(0.001) q = box.info.vinyl().quota until q.limit - q.used < pad:len()
+repeat fiber.sleep(0.001) until box.cfg.vinyl_memory - box.stat.vinyl().memory.level0 < pad:len()
 ---
 ...
 test_run:cmd('switch default')
diff --git a/test/vinyl/errinj.test.lua b/test/vinyl/errinj.test.lua
index 5a47ecc4..e82b6aee 100644
--- a/test/vinyl/errinj.test.lua
+++ b/test/vinyl/errinj.test.lua
@@ -529,13 +529,13 @@ box.error.injection.set('ERRINJ_VY_RUN_WRITE_STMT_TIMEOUT', 0.01)
 fiber = require('fiber')
 pad = string.rep('x', 100 * 1024)
 _ = fiber.create(function() for i = 1, 11 do box.space.test:replace{i, pad} end end)
-repeat fiber.sleep(0.001) q = box.info.vinyl().quota until q.limit - q.used < pad:len()
+repeat fiber.sleep(0.001) until box.cfg.vinyl_memory - box.stat.vinyl().memory.level0 < pad:len()
 test_run:cmd("restart server low_quota with args='1048576'")
 box.error.injection.set('ERRINJ_VY_LOG_FLUSH_DELAY', true)
 fiber = require('fiber')
 pad = string.rep('x', 100 * 1024)
 _ = fiber.create(function() for i = 1, 11 do box.space.test:replace{i, pad} end end)
-repeat fiber.sleep(0.001) q = box.info.vinyl().quota until q.limit - q.used < pad:len()
+repeat fiber.sleep(0.001) until box.cfg.vinyl_memory - box.stat.vinyl().memory.level0 < pad:len()
 test_run:cmd('switch default')
 test_run:cmd("stop server low_quota")
 test_run:cmd("cleanup server low_quota")
diff --git a/test/vinyl/info.result b/test/vinyl/info.result
index 21945b9d..a47c9a1b 100644
--- a/test/vinyl/info.result
+++ b/test/vinyl/info.result
@@ -95,13 +95,11 @@ end;
 ...
 -- Return global statistics.
 --
--- Note, quota watermark checking is beyond the scope of this
--- test so we just filter out related statistics.
+-- Note, checking correctness of the load regulator logic is beyond
+-- the scope of this test so we just filter out related statistics.
 function gstat()
     local st = box.stat.vinyl()
-    st.quota.use_rate = nil
-    st.quota.dump_bandwidth = nil
-    st.quota.watermark = nil
+    st.regulator = nil
     return st
 end;
 ---
@@ -231,9 +229,6 @@ gstat()
       out: 0
     data: 0
     index: 0
-  quota:
-    limit: 134217728
-    used: 0
   memory:
     tuple_cache: 0
     tx: 0
@@ -1075,9 +1070,6 @@ gstat()
       out: 0
     data: 104300
     index: 1190
-  quota:
-    limit: 134217728
-    used: 262583
   memory:
     tuple_cache: 14313
     tx: 0
diff --git a/test/vinyl/info.test.lua b/test/vinyl/info.test.lua
index 5912320c..e5794a23 100644
--- a/test/vinyl/info.test.lua
+++ b/test/vinyl/info.test.lua
@@ -77,13 +77,11 @@ end;
 
 -- Return global statistics.
 --
--- Note, quota watermark checking is beyond the scope of this
--- test so we just filter out related statistics.
+-- Note, checking correctness of the load regulator logic is beyond
+-- the scope of this test so we just filter out related statistics.
 function gstat()
     local st = box.stat.vinyl()
-    st.quota.use_rate = nil
-    st.quota.dump_bandwidth = nil
-    st.quota.watermark = nil
+    st.regulator = nil
     return st
 end;
 
diff --git a/test/vinyl/quota.result b/test/vinyl/quota.result
index 48042185..1a0842f9 100644
--- a/test/vinyl/quota.result
+++ b/test/vinyl/quota.result
@@ -12,7 +12,7 @@ test_run:cmd('restart server default')
 --
 -- gh-1863 add BPS tree extents to memory quota
 --
-box.stat.vinyl().quota.used
+box.stat.vinyl().memory.level0
 ---
 - 0
 ...
@@ -29,7 +29,7 @@ space:insert({1, 1})
 ---
 - [1, 1]
 ...
-box.stat.vinyl().quota.used
+box.stat.vinyl().memory.level0
 ---
 - 98343
 ...
@@ -37,7 +37,7 @@ space:insert({1, 1})
 ---
 - error: Duplicate key exists in unique index 'pk' in space 'test'
 ...
-box.stat.vinyl().quota.used
+box.stat.vinyl().memory.level0
 ---
 - 98343
 ...
@@ -45,7 +45,7 @@ space:update({1}, {{'!', 1, 100}}) -- try to modify the primary key
 ---
 - error: Attempt to modify a tuple field which is part of index 'pk' in space 'test'
 ...
-box.stat.vinyl().quota.used
+box.stat.vinyl().memory.level0
 ---
 - 98343
 ...
@@ -61,7 +61,7 @@ space:insert({4, 4})
 ---
 - [4, 4]
 ...
-box.stat.vinyl().quota.used
+box.stat.vinyl().memory.level0
 ---
 - 98460
 ...
@@ -69,7 +69,7 @@ box.snapshot()
 ---
 - ok
 ...
-box.stat.vinyl().quota.used
+box.stat.vinyl().memory.level0
 ---
 - 0
 ...
@@ -80,14 +80,14 @@ space:select{}
   - [3, 3]
   - [4, 4]
 ...
-box.stat.vinyl().quota.used
+box.stat.vinyl().memory.level0
 ---
 - 0
 ...
 _ = space:replace{1, 1, string.rep('a', 1024 * 1024 * 5)}
 ---
 ...
-box.stat.vinyl().quota.used
+box.stat.vinyl().memory.level0
 ---
 - 5341267
 ...
@@ -121,14 +121,14 @@ count = 20
 pad = string.rep('x', 100 * 1024)
 ---
 ...
-box.stat.vinyl().quota.limit
+box.cfg.vinyl_memory
 ---
 - 1048576
 ...
 for i = 1, count do s:replace{i, pad} end -- triggers dump
 ---
 ...
-box.stat.vinyl().quota.used < count * pad:len()
+box.stat.vinyl().memory.level0 < count * pad:len()
 ---
 - true
 ...
@@ -139,14 +139,14 @@ box.snapshot()
 box.cfg{vinyl_memory = 8 * 1024 * 1024}
 ---
 ...
-box.stat.vinyl().quota.limit
+box.cfg.vinyl_memory
 ---
 - 8388608
 ...
 for i = 1, count do s:replace{i, pad} end -- does not trigger dump
 ---
 ...
-box.stat.vinyl().quota.used > count * pad:len()
+box.stat.vinyl().memory.level0 > count * pad:len()
 ---
 - true
 ...
@@ -155,7 +155,7 @@ box.cfg{vinyl_memory = 4 * 1024 * 1024} -- error: decreasing vinyl_memory is not
 - error: 'Incorrect value for option ''vinyl_memory'': cannot decrease memory size
     at runtime'
 ...
-box.stat.vinyl().quota.limit
+box.cfg.vinyl_memory
 ---
 - 8388608
 ...
diff --git a/test/vinyl/quota.test.lua b/test/vinyl/quota.test.lua
index e67e5430..a2793a01 100644
--- a/test/vinyl/quota.test.lua
+++ b/test/vinyl/quota.test.lua
@@ -12,7 +12,7 @@ test_run:cmd('restart server default')
 -- gh-1863 add BPS tree extents to memory quota
 --
 
-box.stat.vinyl().quota.used
+box.stat.vinyl().memory.level0
 
 space = box.schema.space.create('test', { engine = 'vinyl' })
 pk = space:create_index('pk')
@@ -20,33 +20,33 @@ sec = space:create_index('sec', { parts = {2, 'unsigned'} })
 
 space:insert({1, 1})
 
-box.stat.vinyl().quota.used
+box.stat.vinyl().memory.level0
 
 space:insert({1, 1})
 
-box.stat.vinyl().quota.used
+box.stat.vinyl().memory.level0
 
 space:update({1}, {{'!', 1, 100}}) -- try to modify the primary key
 
-box.stat.vinyl().quota.used
+box.stat.vinyl().memory.level0
 
 space:insert({2, 2})
 space:insert({3, 3})
 space:insert({4, 4})
 
-box.stat.vinyl().quota.used
+box.stat.vinyl().memory.level0
 
 box.snapshot()
 
-box.stat.vinyl().quota.used
+box.stat.vinyl().memory.level0
 
 space:select{}
 
-box.stat.vinyl().quota.used
+box.stat.vinyl().memory.level0
 
 _ = space:replace{1, 1, string.rep('a', 1024 * 1024 * 5)}
 
-box.stat.vinyl().quota.used
+box.stat.vinyl().memory.level0
 
 space:drop()
 
@@ -63,21 +63,21 @@ _ = s:create_index('pk')
 count = 20
 pad = string.rep('x', 100 * 1024)
 
-box.stat.vinyl().quota.limit
+box.cfg.vinyl_memory
 
 for i = 1, count do s:replace{i, pad} end -- triggers dump
-box.stat.vinyl().quota.used < count * pad:len()
+box.stat.vinyl().memory.level0 < count * pad:len()
 
 box.snapshot()
 
 box.cfg{vinyl_memory = 8 * 1024 * 1024}
-box.stat.vinyl().quota.limit
+box.cfg.vinyl_memory
 
 for i = 1, count do s:replace{i, pad} end -- does not trigger dump
-box.stat.vinyl().quota.used > count * pad:len()
+box.stat.vinyl().memory.level0 > count * pad:len()
 
 box.cfg{vinyl_memory = 4 * 1024 * 1024} -- error: decreasing vinyl_memory is not allowed
-box.stat.vinyl().quota.limit
+box.cfg.vinyl_memory
 
 test_run:cmd('switch default')
 test_run:cmd("stop server test")
diff --git a/test/vinyl/quota_timeout.result b/test/vinyl/quota_timeout.result
index fd8b0196..990d0a4c 100644
--- a/test/vinyl/quota_timeout.result
+++ b/test/vinyl/quota_timeout.result
@@ -47,7 +47,7 @@ s:count()
 ---
 - 1
 ...
-box.stat.vinyl().quota.used
+box.stat.vinyl().memory.level0
 ---
 - 748241
 ...
@@ -61,7 +61,7 @@ s:count()
 ---
 - 1
 ...
-box.stat.vinyl().quota.used
+box.stat.vinyl().memory.level0
 ---
 - 748241
 ...
@@ -148,7 +148,7 @@ box.snapshot()
 -- The following operation should fail instantly irrespective
 -- of the value of 'vinyl_timeout' (gh-3291).
 --
-box.stat.vinyl().quota.used == 0
+box.stat.vinyl().memory.level0 == 0
 ---
 - true
 ...
diff --git a/test/vinyl/quota_timeout.test.lua b/test/vinyl/quota_timeout.test.lua
index 41a864bb..6266d38b 100644
--- a/test/vinyl/quota_timeout.test.lua
+++ b/test/vinyl/quota_timeout.test.lua
@@ -21,13 +21,13 @@ _ = s:create_index('pk')
 pad = string.rep('x', 2 * box.cfg.vinyl_memory / 3)
 _ = s:auto_increment{pad}
 s:count()
-box.stat.vinyl().quota.used
+box.stat.vinyl().memory.level0
 
 -- Since the following operation requires more memory than configured
 -- and dump is disabled, it should fail with ER_VY_QUOTA_TIMEOUT.
 _ = s:auto_increment{pad}
 s:count()
-box.stat.vinyl().quota.used
+box.stat.vinyl().memory.level0
 
 --
 -- Check that increasing box.cfg.vinyl_memory wakes up fibers
@@ -72,7 +72,7 @@ box.snapshot()
 -- The following operation should fail instantly irrespective
 -- of the value of 'vinyl_timeout' (gh-3291).
 --
-box.stat.vinyl().quota.used == 0
+box.stat.vinyl().memory.level0 == 0
 box.cfg{vinyl_timeout = 9000}
 pad = string.rep('x', box.cfg.vinyl_memory)
 _ = s:auto_increment{pad}
diff --git a/test/vinyl/recovery_quota.result b/test/vinyl/recovery_quota.result
index 323b7967..151f280b 100644
--- a/test/vinyl/recovery_quota.result
+++ b/test/vinyl/recovery_quota.result
@@ -60,7 +60,7 @@ test_run:cmd('restart server test with args="2097152"')
 stat = box.stat.vinyl()
 ---
 ...
-stat.quota.used <= stat.quota.limit or {stat.quota.used, stat.quota.limit}
+stat.memory.level0 <= box.cfg.vinyl_memory or {stat.memory.level0, box.cfg.vinyl_memory}
 ---
 - true
 ...
@@ -109,7 +109,7 @@ for i = 1, box.cfg.vinyl_memory / pad_size do box.space.test:replace{i, pad} end
 ---
 - error: Timed out waiting for Vinyl memory quota
 ...
-box.stat.vinyl().quota.used > 1024 * 1024
+box.stat.vinyl().memory.level0 > 1024 * 1024
 ---
 - true
 ...
@@ -125,7 +125,7 @@ while box.space.test.index.pk:stat().disk.dump.count == 0 do fiber.sleep(0.001)
 stat = box.stat.vinyl()
 ---
 ...
-stat.quota.used <= stat.quota.limit or {stat.quota.used, stat.quota.limit}
+stat.memory.level0 <= box.cfg.vinyl_memory or {stat.memory.level0, box.cfg.vinyl_memory}
 ---
 - true
 ...
diff --git a/test/vinyl/recovery_quota.test.lua b/test/vinyl/recovery_quota.test.lua
index 44e35ba6..1e347667 100644
--- a/test/vinyl/recovery_quota.test.lua
+++ b/test/vinyl/recovery_quota.test.lua
@@ -27,7 +27,7 @@ _ = var:insert{'dump', stat.disk.dump.out.rows}
 test_run:cmd('restart server test with args="2097152"')
 -- Check that we do not exceed quota.
 stat = box.stat.vinyl()
-stat.quota.used <= stat.quota.limit or {stat.quota.used, stat.quota.limit}
+stat.memory.level0 <= box.cfg.vinyl_memory or {stat.memory.level0, box.cfg.vinyl_memory}
 -- Check that we did not replay statements dumped before restart.
 stat = box.space.test.index.pk:stat()
 var = box.space.var
@@ -43,14 +43,14 @@ box.cfg{vinyl_timeout=0.001}
 pad_size = 1000
 pad = string.rep('x', pad_size)
 for i = 1, box.cfg.vinyl_memory / pad_size do box.space.test:replace{i, pad} end
-box.stat.vinyl().quota.used > 1024 * 1024
+box.stat.vinyl().memory.level0 > 1024 * 1024
 -- Check that tarantool can recover with a smaller memory limit.
 test_run:cmd('restart server test with args="1048576"')
 fiber = require 'fiber'
 -- All memory above the limit must be dumped after recovery.
 while box.space.test.index.pk:stat().disk.dump.count == 0 do fiber.sleep(0.001) end
 stat = box.stat.vinyl()
-stat.quota.used <= stat.quota.limit or {stat.quota.used, stat.quota.limit}
+stat.memory.level0 <= box.cfg.vinyl_memory or {stat.memory.level0, box.cfg.vinyl_memory}
 _ = test_run:cmd('switch default')
 
 test_run:cmd('stop server test')
-- 
2.11.0




More information about the Tarantool-patches mailing list