[PATCH 05/18] vinyl: wake up fibers waiting for quota one by one

Vladimir Davydov vdavydov.dev at gmail.com
Tue Aug 28 16:19:14 MSK 2018


Discussed verbally. Agreed to wake up the next fiber on vy_quota_use()
only if we waited for quota (otherwise we don't need to do that). Here's
the updated patch:

>From 7c10049ebf9c96adb197a719d4ed7c16f61a4f33 Mon Sep 17 00:00:00 2001
From: Vladimir Davydov <vdavydov.dev at gmail.com>
Date: Tue, 28 Aug 2018 16:15:33 +0300
Subject: [PATCH] vinyl: wake up fibers waiting for quota one by one

Currently, we wake up all fibers whenever we free some memory. This
is inefficient, because it might occur that all available quota gets
consumed by a few fibers while the rest will have to go back to sleep.
This is also kinda unfair, because waking up all fibers breaks the order
in which the fibers were put to sleep. This works now, because we free
memory and wake up fibers infrequently (on dump) and there normally
shouldn't be any fibers waiting for quota (if there were, the latency
would rocket sky high because of absence of any kind of throttling).
However, once throttling is introduced, fibers waiting for quota will
become the norm. So let's wake up fibers one by one: whenever we free
memory we wake up the first fiber in the line, which will wake up the
next fiber on success and so forth.

diff --git a/src/box/vy_quota.c b/src/box/vy_quota.c
index 1e7c754f..b3f073c3 100644
--- a/src/box/vy_quota.c
+++ b/src/box/vy_quota.c
@@ -56,6 +56,16 @@ enum {
 	VY_QUOTA_RATE_AVG_PERIOD = 5,
 };
 
+/**
+ * Returns true if the quota limit is exceeded and so consumers
+ * have to wait.
+ */
+static inline bool
+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)
 {
@@ -152,7 +162,7 @@ vy_quota_set_limit(struct vy_quota *q, size_t limit)
 	q->limit = q->watermark = limit;
 	if (q->used >= limit)
 		q->quota_exceeded_cb(q);
-	fiber_cond_broadcast(&q->cond);
+	fiber_cond_signal(&q->cond);
 }
 
 size_t
@@ -184,28 +194,40 @@ vy_quota_release(struct vy_quota *q, size_t size)
 {
 	assert(q->used >= size);
 	q->used -= size;
-	fiber_cond_broadcast(&q->cond);
+	fiber_cond_signal(&q->cond);
 }
 
 int
 vy_quota_use(struct vy_quota *q, size_t size, double 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;
 	q->use_curr += size;
+	if (vy_quota_is_exceeded(q)) {
+		/* Wait for quota. */
+		double start_time = ev_monotonic_now(loop());
+		double deadline = start_time + 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;
+		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);
+		}
+		/*
+		 * Wake up the next fiber in the line waiting
+		 * for quota.
+		 */
+		fiber_cond_signal(&q->cond);
+	}
 	if (q->used >= q->watermark)
 		q->quota_exceeded_cb(q);
 	return 0;
@@ -222,7 +244,7 @@ vy_quota_adjust(struct vy_quota *q, size_t reserved, size_t used)
 			q->use_curr -= excess;
 		else /* was reset by timeout */
 			q->use_curr = 0;
-		fiber_cond_broadcast(&q->cond);
+		fiber_cond_signal(&q->cond);
 	}
 	if (reserved < used)
 		vy_quota_force_use(q, used - reserved);



More information about the Tarantool-patches mailing list