[PATCH] vinyl: warn when transaction waits for quota for too long

Vladimir Davydov vdavydov.dev at gmail.com
Wed Feb 14 16:37:36 MSK 2018


If a vinyl transaction stalls waiting for quota for more than
box.cfg.too_long_threshold seconds, emit a warning to the log:

  W> waited for 699089 bytes of vinyl memory quota for too long: 0.504 sec

This will help us understand whether our users experience lags
due to absence of throttling in vinyl (see #1862).

Closes #3096
---
https://github.com/tarantool/tarantool/tree/gh-3096-vy-quota-stall-logging

 src/box/vinyl.c                   |  1 +
 src/box/vy_quota.h                | 15 +++++++++++-
 test/vinyl/quota_timeout.result   | 49 ++++++++++++++++++++++++++++++++++++---
 test/vinyl/quota_timeout.test.lua | 25 ++++++++++++++++++--
 4 files changed, 84 insertions(+), 6 deletions(-)

diff --git a/src/box/vinyl.c b/src/box/vinyl.c
index 94d0d038..03ad2bf4 100644
--- a/src/box/vinyl.c
+++ b/src/box/vinyl.c
@@ -2754,6 +2754,7 @@ void
 vinyl_engine_set_too_long_threshold(struct vinyl_engine *vinyl,
 				    double too_long_threshold)
 {
+	vinyl->env->quota.too_long_threshold = too_long_threshold;
 	vinyl->env->index_env.too_long_threshold = too_long_threshold;
 }
 
diff --git a/src/box/vy_quota.h b/src/box/vy_quota.h
index 8fd390e5..89f88bdc 100644
--- a/src/box/vy_quota.h
+++ b/src/box/vy_quota.h
@@ -37,6 +37,7 @@
 
 #include "fiber.h"
 #include "fiber_cond.h"
+#include "say.h"
 
 #if defined(__cplusplus)
 extern "C" {
@@ -66,6 +67,11 @@ struct vy_quota {
 	/** Current memory consumption. */
 	size_t used;
 	/**
+	 * If vy_quota_use() takes longer than the given
+	 * value, warn about it in the log.
+	 */
+	double too_long_threshold;
+	/**
 	 * Condition variable used for throttling consumers when
 	 * there is no quota left.
 	 */
@@ -83,6 +89,7 @@ vy_quota_create(struct vy_quota *q, vy_quota_exceeded_f quota_exceeded_cb)
 	q->limit = SIZE_MAX;
 	q->watermark = SIZE_MAX;
 	q->used = 0;
+	q->too_long_threshold = TIMEOUT_INFINITY;
 	q->quota_exceeded_cb = quota_exceeded_cb;
 	fiber_cond_create(&q->cond);
 }
@@ -148,12 +155,18 @@ vy_quota_release(struct vy_quota *q, size_t size)
 static inline int
 vy_quota_use(struct vy_quota *q, size_t size, double timeout)
 {
-	double deadline = ev_monotonic_now(loop()) + timeout;
+	double start_time = ev_monotonic_now(loop());
+	double deadline = start_time + timeout;
 	while (q->used + size > q->limit && timeout > 0) {
 		q->quota_exceeded_cb(q);
 		if (fiber_cond_wait_deadline(&q->cond, deadline) != 0)
 			break; /* timed out */
 	}
+	double wait_time = ev_monotonic_now(loop()) - start_time;
+	if (wait_time > q->too_long_threshold) {
+		say_warn("waited for %zu bytes of vinyl memory quota "
+			 "for too long: %.3f sec", size, wait_time);
+	}
 	if (q->used + size > q->limit)
 		return -1;
 	q->used += size;
diff --git a/test/vinyl/quota_timeout.result b/test/vinyl/quota_timeout.result
index 66cd6f88..1e66d392 100644
--- a/test/vinyl/quota_timeout.result
+++ b/test/vinyl/quota_timeout.result
@@ -65,9 +65,6 @@ box.info.vinyl().quota.used
 ---
 - 748241
 ...
-s:drop()
----
-...
 box.error.injection.set('ERRINJ_VY_RUN_WRITE', false)
 ---
 - ok
@@ -76,8 +73,54 @@ fiber.sleep(0.01) -- wait for scheduler to unthrottle
 ---
 ...
 --
+-- Check that there's a warning in the log if a transaction
+-- waits for quota for more than too_long_threshold seconds.
+--
+box.error.injection.set('ERRINJ_VY_RUN_WRITE_TIMEOUT', 0.01)
+---
+- ok
+...
+box.cfg{vinyl_timeout=60}
+---
+...
+box.cfg{too_long_threshold=0.01}
+---
+...
+_ = s:auto_increment{pad}
+---
+...
+_ = s:auto_increment{pad}
+---
+...
+test_run:cmd("push filter '[0-9.]+ sec' to '<sec> sec'")
+---
+- true
+...
+test_run:grep_log('test', 'waited for .* quota for too long.*')
+---
+- 'waited for 699089 bytes of vinyl memory quota for too long: <sec> sec'
+...
+test_run:cmd("clear filter")
+---
+- true
+...
+box.error.injection.set('ERRINJ_VY_RUN_WRITE_TIMEOUT', 0)
+---
+- ok
+...
+s:drop()
+---
+...
+box.snapshot()
+---
+- ok
+...
+--
 -- Check that exceeding quota triggers dump of all spaces.
 --
+box.cfg{vinyl_timeout=0.01}
+---
+...
 s1 = box.schema.space.create('test1', {engine = 'vinyl'})
 ---
 ...
diff --git a/test/vinyl/quota_timeout.test.lua b/test/vinyl/quota_timeout.test.lua
index 227ee80c..ed5ba79d 100644
--- a/test/vinyl/quota_timeout.test.lua
+++ b/test/vinyl/quota_timeout.test.lua
@@ -29,14 +29,35 @@ _ = s:auto_increment{pad}
 s:count()
 box.info.vinyl().quota.used
 
-s:drop()
-
 box.error.injection.set('ERRINJ_VY_RUN_WRITE', false)
 fiber.sleep(0.01) -- wait for scheduler to unthrottle
 
 --
+-- Check that there's a warning in the log if a transaction
+-- waits for quota for more than too_long_threshold seconds.
+--
+box.error.injection.set('ERRINJ_VY_RUN_WRITE_TIMEOUT', 0.01)
+
+box.cfg{vinyl_timeout=60}
+box.cfg{too_long_threshold=0.01}
+
+_ = s:auto_increment{pad}
+_ = s:auto_increment{pad}
+
+test_run:cmd("push filter '[0-9.]+ sec' to '<sec> sec'")
+test_run:grep_log('test', 'waited for .* quota for too long.*')
+test_run:cmd("clear filter")
+
+box.error.injection.set('ERRINJ_VY_RUN_WRITE_TIMEOUT', 0)
+
+s:drop()
+box.snapshot()
+
+--
 -- Check that exceeding quota triggers dump of all spaces.
 --
+box.cfg{vinyl_timeout=0.01}
+
 s1 = box.schema.space.create('test1', {engine = 'vinyl'})
 _ = s1:create_index('pk')
 s2 = box.schema.space.create('test2', {engine = 'vinyl'})
-- 
2.11.0




More information about the Tarantool-patches mailing list