diff --git a/src/box/vy_quota.c b/src/box/vy_quota.c
index e6d223486f9a69a2ef037b2c602a42c3b6bfafd6..64ce56c02739a0beafb28667ea67f8f5bd38b874 100644
--- a/src/box/vy_quota.c
+++ b/src/box/vy_quota.c
@@ -31,6 +31,7 @@
 #include "vy_quota.h"
 
 #include <assert.h>
+#include <stdbool.h>
 #include <stddef.h>
 #include <tarantool_ev.h>
 
@@ -40,13 +41,55 @@
 #include "trivia/util.h"
 
 /**
- * Returns true if the quota limit is exceeded and so consumers
- * have to wait.
+ * Return true if the requested amount of memory may be consumed
+ * right now, false if consumers have to wait.
  */
 static inline bool
-vy_quota_is_exceeded(struct vy_quota *q)
+vy_quota_may_use(struct vy_quota *q, size_t size)
 {
-	return q->used > q->limit;
+	if (q->used + size > q->limit)
+		return false;
+	return true;
+}
+
+/**
+ * Consume the given amount of memory without checking the limit.
+ */
+static inline void
+vy_quota_do_use(struct vy_quota *q, size_t size)
+{
+	q->used += size;
+}
+
+/**
+ * Return the given amount of memory without waking blocked fibers.
+ * This function is an exact opposite of vy_quota_do_use().
+ */
+static inline void
+vy_quota_do_unuse(struct vy_quota *q, size_t size)
+{
+	assert(q->used >= size);
+	q->used -= size;
+}
+
+/**
+ * Invoke the registered callback in case memory usage exceeds
+ * the configured limit.
+ */
+static inline void
+vy_quota_check_limit(struct vy_quota *q)
+{
+	if (q->used > q->limit)
+		q->quota_exceeded_cb(q);
+}
+
+/**
+ * Wake up the first consumer in the line waiting for quota.
+ */
+static void
+vy_quota_signal(struct vy_quota *q)
+{
+	fiber_cond_signal(&q->cond);
 }
 
 void
@@ -70,55 +113,63 @@ void
 vy_quota_set_limit(struct vy_quota *q, size_t limit)
 {
 	q->limit = limit;
-	if (q->used >= limit)
-		q->quota_exceeded_cb(q);
-	fiber_cond_signal(&q->cond);
+	vy_quota_check_limit(q);
+	vy_quota_signal(q);
 }
 
 void
 vy_quota_force_use(struct vy_quota *q, size_t size)
 {
-	q->used += size;
-	if (q->used >= q->limit)
-		q->quota_exceeded_cb(q);
+	vy_quota_do_use(q, size);
+	vy_quota_check_limit(q);
 }
 
 void
 vy_quota_release(struct vy_quota *q, size_t size)
 {
-	assert(q->used >= size);
-	q->used -= size;
-	fiber_cond_signal(&q->cond);
+	vy_quota_do_unuse(q, size);
+	vy_quota_signal(q);
 }
 
 int
 vy_quota_use(struct vy_quota *q, size_t size, double timeout)
 {
-	q->used += size;
-	if (vy_quota_is_exceeded(q)) {
-		/* Wait for quota. */
-		double start_time = ev_monotonic_now(loop());
-		double deadline = start_time + timeout;
+	if (vy_quota_may_use(q, size)) {
+		vy_quota_do_use(q, size);
+		return 0;
+	}
 
-		do {
-			q->quota_exceeded_cb(q);
-			q->used -= size;
-			if (fiber_cond_wait_deadline(&q->cond, deadline) != 0)
-				return -1; /* timed out */
-			q->used += 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);
-		}
+	/* Wait for quota. */
+	double wait_start = ev_monotonic_now(loop());
+	double deadline = wait_start + timeout;
+
+	do {
 		/*
-		 * Wake up the next fiber in the line waiting
-		 * for quota.
+		 * If the requested amount of memory cannot be
+		 * consumed due to the configured limit, notify
+		 * the caller before going to sleep so that it
+		 * can start memory reclaim immediately.
 		 */
-		fiber_cond_signal(&q->cond);
+		if (q->used + size > q->limit)
+			q->quota_exceeded_cb(q);
+		if (fiber_cond_wait_deadline(&q->cond, deadline) != 0)
+			return -1; /* timed out */
+	} while (!vy_quota_may_use(q, size));
+
+	double wait_time = ev_monotonic_now(loop()) - wait_start;
+	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);
 	}
+
+	vy_quota_do_use(q, size);
+	/*
+	 * Blocked consumers are awaken one by one to preserve
+	 * the order they were put to sleep. It's a responsibility
+	 * of a consumer that managed to acquire the requested
+	 * amount of quota to wake up the next one in the line.
+	 */
+	vy_quota_signal(q);
 	return 0;
 }
 
@@ -126,11 +177,11 @@ void
 vy_quota_adjust(struct vy_quota *q, size_t reserved, size_t used)
 {
 	if (reserved > used) {
-		size_t excess = reserved - used;
-		assert(q->used >= excess);
-		q->used -= excess;
-		fiber_cond_signal(&q->cond);
+		vy_quota_do_unuse(q, reserved - used);
+		vy_quota_signal(q);
+	}
+	if (reserved < used) {
+		vy_quota_do_use(q, used - reserved);
+		vy_quota_check_limit(q);
 	}
-	if (reserved < used)
-		vy_quota_force_use(q, used - reserved);
 }