diff --git a/src/box/vinyl.c b/src/box/vinyl.c
index 18e1b23c1c227f677c692f32da7e57fd82d58035..f1c3f126cab8ecd8c8df8e1592b13fd88cad67d8 100644
--- a/src/box/vinyl.c
+++ b/src/box/vinyl.c
@@ -2500,13 +2500,14 @@ vy_index_commit_create(struct vy_env *env, struct vy_index *index)
 		 * the index isn't in the recovery context and we
 		 * need to retry to log it now.
 		 */
-		if (vy_recovery_lookup_index(env->recovery,
-					     index->opts.lsn) != NULL) {
+		if (index->is_committed) {
 			vy_scheduler_add_index(env->scheduler, index);
 			return;
 		}
 	}
 
+	index->is_committed = true;
+
 	assert(index->range_count == 1);
 	struct vy_range *range = vy_range_tree_first(index->tree);
 
diff --git a/src/box/vy_index.c b/src/box/vy_index.c
index 1dc4b465903dd01968bb4647a1e0b134383f73f6..22950e6aa6015d4a5845147b8bb9201d650b5d7e 100644
--- a/src/box/vy_index.c
+++ b/src/box/vy_index.c
@@ -366,7 +366,7 @@ struct vy_index_recovery_cb_arg {
 	struct mh_i64ptr_t *run_hash;
 };
 
-/** Index recovery callback, passed to vy_recovery_iterate_index(). */
+/** Index recovery callback, passed to vy_recovery_load_index(). */
 static int
 vy_index_recovery_cb(const struct vy_log_record *record, void *cb_arg)
 {
@@ -380,6 +380,8 @@ vy_index_recovery_cb(const struct vy_log_record *record, void *cb_arg)
 	struct vy_slice *slice;
 	bool success = false;
 
+	assert(record->type == VY_LOG_CREATE_INDEX || index->is_committed);
+
 	if (record->type == VY_LOG_INSERT_RANGE ||
 	    record->type == VY_LOG_INSERT_SLICE) {
 		if (record->begin != NULL) {
@@ -397,6 +399,7 @@ vy_index_recovery_cb(const struct vy_log_record *record, void *cb_arg)
 	switch (record->type) {
 	case VY_LOG_CREATE_INDEX:
 		assert(record->index_lsn == index->opts.lsn);
+		index->is_committed = true;
 		break;
 	case VY_LOG_DUMP_INDEX:
 		assert(record->index_lsn == index->opts.lsn);
@@ -478,18 +481,6 @@ vy_index_recover(struct vy_index *index, struct vy_recovery *recovery,
 {
 	assert(index->range_count == 0);
 
-	struct vy_index_recovery_info *ri;
-	ri = vy_recovery_lookup_index(recovery, index->opts.lsn);
-	if (ri == NULL) {
-		if (!allow_missing) {
-			diag_set(ClientError, ER_INVALID_VYLOG_FILE,
-				 tt_sprintf("Index %lld not found",
-					    (long long)index->opts.lsn));
-			return -1;
-		}
-		return vy_index_create(index);
-	}
-
 	struct vy_index_recovery_cb_arg arg = { .index = index };
 	arg.run_hash = mh_i64ptr_new();
 	if (arg.run_hash == NULL) {
@@ -497,27 +488,45 @@ vy_index_recover(struct vy_index *index, struct vy_recovery *recovery,
 		return -1;
 	}
 
-	int rc = vy_recovery_iterate_index(ri, false,
-			vy_index_recovery_cb, &arg);
+	int rc = vy_recovery_load_index(recovery, index->opts.lsn,
+					vy_index_recovery_cb, &arg);
 
 	mh_int_t k;
 	mh_foreach(arg.run_hash, k) {
 		struct vy_run *run = mh_i64ptr_node(arg.run_hash, k)->val;
-		if (run->refs == 1) {
+		if (run->refs > 1)
+			vy_index_add_run(index, run);
+		if (run->refs == 1 && rc == 0) {
 			diag_set(ClientError, ER_INVALID_VYLOG_FILE,
 				 tt_sprintf("Unused run %lld in index %lld",
 					    (long long)run->id,
 					    (long long)index->opts.lsn));
 			rc = -1;
-		} else
-			vy_index_add_run(index, run);
+			/*
+			 * Continue the loop to unreference
+			 * all runs in the hash.
+			 */
+		}
 		/* Drop the reference held by the hash. */
 		vy_run_unref(run);
 	}
 	mh_i64ptr_delete(arg.run_hash);
 
-	if (rc != 0)
+	if (rc != 0) {
+		/* Recovery callback failed. */
 		return -1;
+	}
+
+	if (!index->is_committed) {
+		/* Index was not found in the metadata log. */
+		if (!allow_missing) {
+			diag_set(ClientError, ER_INVALID_VYLOG_FILE,
+				 tt_sprintf("Index %lld not found",
+					    (long long)index->opts.lsn));
+			return -1;
+		}
+		return vy_index_create(index);
+	}
 
 	/*
 	 * Account ranges to the index and check that the range tree
diff --git a/src/box/vy_index.h b/src/box/vy_index.h
index 68227a6ee62b6e1abfdf03179979a1ef9748417c..187b73a77ad32c5c6b06f2dee07e5b4b36af2959 100644
--- a/src/box/vy_index.h
+++ b/src/box/vy_index.h
@@ -219,6 +219,11 @@ struct vy_index {
 	 * been dumped yet.
 	 */
 	int64_t dump_lsn;
+	/**
+	 * This flag is set if the index creation was
+	 * committed to the metadata log.
+	 */
+	bool is_committed;
 	/**
 	 * This flag is set if the index was dropped.
 	 * It is also set on local recovery if the index
diff --git a/src/box/vy_log.c b/src/box/vy_log.c
index e2e96a90f063e01e1549cb9f244856ba5732c839..965a83a192e80d34d21f1661511506a4b4e371f4 100644
--- a/src/box/vy_log.c
+++ b/src/box/vy_log.c
@@ -1227,7 +1227,7 @@ vy_log_write(const struct vy_log_record *record)
 }
 
 /** Lookup a vinyl index in vy_recovery::index_hash map. */
-struct vy_index_recovery_info *
+static struct vy_index_recovery_info *
 vy_recovery_lookup_index(struct vy_recovery *recovery, int64_t index_lsn)
 {
 	struct mh_i64ptr_t *h = recovery->index_hash;
@@ -2007,7 +2007,7 @@ vy_recovery_cb_call(vy_recovery_cb cb, void *cb_arg,
 	return cb(record, cb_arg);
 }
 
-int
+static int
 vy_recovery_iterate_index(struct vy_index_recovery_info *index,
 		bool include_deleted, vy_recovery_cb cb, void *cb_arg)
 {
@@ -2152,3 +2152,14 @@ vy_recovery_iterate(struct vy_recovery *recovery, bool include_deleted,
 	}
 	return 0;
 }
+
+int
+vy_recovery_load_index(struct vy_recovery *recovery, int64_t index_lsn,
+		       vy_recovery_cb cb, void *cb_arg)
+{
+	struct vy_index_recovery_info *index;
+	index = vy_recovery_lookup_index(recovery, index_lsn);
+	if (index == NULL)
+		return 0;
+	return vy_recovery_iterate_index(index, false, cb, cb_arg);
+}
diff --git a/src/box/vy_log.h b/src/box/vy_log.h
index 1f529af1b4c8f8a67dcd4d0c3de6aa72db3ae68a..06881fcb1aec6ded59ffbba88b9a140fe59558ec 100644
--- a/src/box/vy_log.h
+++ b/src/box/vy_log.h
@@ -59,7 +59,6 @@ struct vclock;
 struct key_def;
 
 struct vy_recovery;
-struct vy_index_recovery_info;
 
 /** Type of a metadata log record. */
 enum vy_log_record_type {
@@ -354,30 +353,18 @@ vy_recovery_new(int64_t signature, bool only_snapshot);
 void
 vy_recovery_delete(struct vy_recovery *recovery);
 
-/**
- * Lookup a vinyl index in a recovery context.
- *
- * Returns the index handle that can be used for recovering the
- * index structure with the aid of vy_recovery_iterate_index()
- * or NULL if the index is not found.
- */
-struct vy_index_recovery_info *
-vy_recovery_lookup_index(struct vy_recovery *recovery, int64_t index_lsn);
-
 typedef int
 (*vy_recovery_cb)(const struct vy_log_record *record, void *arg);
 
 /**
- * Recover a vinyl index from a recovery context.
+ * Iterate over all objects stored in a recovery context.
+ *
+ * This function invokes callback @cb for each object (index, run, etc)
+ * stored in the given recovery context. The callback is passed a record
+ * used to log the object and optional argument @cb_arg. If the callback
+ * returns a value different from 0, iteration stops and -1 is returned,
+ * otherwise the function returns 0.
  *
- * For each range and run of the index, this function calls @cb passing
- * a log record and an optional @cb_arg to it. A log record type is
- * either VY_LOG_CREATE_INDEX, VY_LOG_CREATE_RUN, VY_LOG_INSERT_RANGE,
- * or VY_LOG_INSERT_SLICE unless the index was dropped. In the latter case,
- * a VY_LOG_DROP_INDEX record is issued in the end.
- * The callback is supposed to rebuild the index structure and open run
- * files. If the callback returns a non-zero value, the function stops
- * iteration over ranges and runs and returns error.
  * To ease the work done by the callback, records corresponding to
  * slices of a range always go right after the range, in the
  * chronological order, while an index's runs go after the index
@@ -386,22 +373,24 @@ typedef int
  * If @include_deleted is set, this function will also iterate over
  * deleted objects, issuing the corresponding "delete" record for each
  * of them.
- *
- * Returns 0 on success, -1 on failure.
  */
 int
-vy_recovery_iterate_index(struct vy_index_recovery_info *index,
-		bool include_deleted, vy_recovery_cb cb, void *cb_arg);
+vy_recovery_iterate(struct vy_recovery *recovery, bool include_deleted,
+		    vy_recovery_cb cb, void *cb_arg);
 
 /**
- * Given a recovery context, iterate over all indexes stored in it.
- * See vy_recovery_iterate_index() for more details.
+ * Load an index from a recovery context.
  *
- * Returns 0 on success, -1 on failure.
+ * Call @cb for each object related to the index. Break the loop and
+ * return -1 if @cb returned a non-zero value, otherwise return 0.
+ * Objects are loaded in the same order as by vy_recovery_iterate().
+ *
+ * Note, this function returns 0 if there's no index with the requested
+ * id in the recovery context. In this case, @cb isn't called at all.
  */
 int
-vy_recovery_iterate(struct vy_recovery *recovery, bool include_deleted,
-		    vy_recovery_cb cb, void *cb_arg);
+vy_recovery_load_index(struct vy_recovery *recovery, int64_t index_lsn,
+		       vy_recovery_cb cb, void *cb_arg);
 
 /**
  * Initialize a log record with default values.