diff --git a/src/box/memtx_hash.cc b/src/box/memtx_hash.cc
index 78b732c6bfc00836484850344ccbd12f01ff3dfa..4443e3f309b3c8abbfb4921d8d3709ba6ee6b627 100644
--- a/src/box/memtx_hash.cc
+++ b/src/box/memtx_hash.cc
@@ -156,7 +156,7 @@ typedef uint32_t hash_t;
 struct hash_iterator {
 	struct iterator base; /* Must be the first member. */
 	struct light_index_core *hash_table;
-	uint32_t h_pos;
+	struct light_index_iterator hitr;
 };
 
 void
@@ -171,13 +171,8 @@ hash_iterator_ge(struct iterator *ptr)
 {
 	assert(ptr->free == hash_iterator_free);
 	struct hash_iterator *it = (struct hash_iterator *) ptr;
-
-	while (it->h_pos < it->hash_table->table_size) {
-		if (light_index_pos_valid(it->hash_table, it->h_pos))
-			return light_index_get(it->hash_table, it->h_pos++);
-		it->h_pos++;
-	}
-	return NULL;
+	struct tuple **res = light_index_itr_get_and_next(it->hash_table, &it->hitr);
+	return res ? *res : 0;
 }
 
 static struct tuple *
@@ -307,9 +302,8 @@ MemtxHash::replace(struct tuple *old_tuple, struct tuple *new_tuple,
 
 	if (old_tuple) {
 		uint32_t h = tuple_hash(old_tuple, key_def);
-		hash_t slot = light_index_find(hash_table, h, old_tuple);
-		assert(slot != light_index_end);
-		light_index_delete(hash_table, slot);
+		int res = light_index_delete_value(hash_table, h, old_tuple);
+		assert(res == 0);
 	}
 	return old_tuple;
 }
@@ -327,6 +321,8 @@ MemtxHash::allocIterator() const
 
 	it->base.next = hash_iterator_ge;
 	it->base.free = hash_iterator_free;
+	it->hash_table = hash_table;
+	light_index_itr_begin(it->hash_table, &it->hitr);
 	return (struct iterator *) it;
 }
 
@@ -339,19 +335,15 @@ MemtxHash::initIterator(struct iterator *ptr, enum iterator_type type,
 	assert(ptr->free == hash_iterator_free);
 
 	struct hash_iterator *it = (struct hash_iterator *) ptr;
-	it->hash_table = hash_table;
 
 	switch (type) {
 	case ITER_ALL:
-		it->h_pos = 0;
-		while (it->h_pos < it->hash_table->table_size
-		       && !light_index_pos_valid(it->hash_table, it->h_pos))
-			it->h_pos++;
+		light_index_itr_begin(it->hash_table, &it->hitr);
 		it->base.next = hash_iterator_ge;
 		break;
 	case ITER_EQ:
 		assert(part_count > 0);
-		it->h_pos = light_index_find_key(hash_table, key_hash(key, key_def), key);
+		light_index_itr_key(it->hash_table, &it->hitr, key_hash(key, key_def), key);
 		it->base.next = hash_iterator_eq;
 		break;
 	default:
diff --git a/src/lib/salad/light.h b/src/lib/salad/light.h
index ca3c22a01e338cafa0773b2a9a8f61466e19c2a7..d49785dbc709703f57a234f4972b7c39985cba77 100644
--- a/src/lib/salad/light.h
+++ b/src/lib/salad/light.h
@@ -139,7 +139,6 @@ struct LIGHT(core) {
 	 * Start of chain of empty slots
 	 */
 	uint32_t empty_slot;
-	struct LIGHT(record) *empty_record;
 
 	/* additional parameter for data comparison */
 	LIGHT_CMP_ARG_TYPE arg;
@@ -148,6 +147,17 @@ struct LIGHT(core) {
 	struct matras mtable;
 };
 
+/**
+ * Iterator, for iterating all values in hash_table.
+ * It also may be used for restoring one value by key.
+ */
+struct LIGHT(iterator) {
+	/* Current position on table (ID of a current record) */
+	uint32_t slotpos;
+	/* Version of matras memory. 0 for main version, nonzero for MVCC */
+	uint32_t matras_version;
+};
+
 /**
  * Type of functions for memory allocation and deallocation
  */
@@ -156,9 +166,12 @@ typedef void (*LIGHT(extent_free_t))(void *);
 
 /**
  * Special result of light_find that means that nothing was found
+ * Must be equal or greater than possible hash table size
  */
 static const uint32_t LIGHT(end) = 0xFFFFFFFF;
 
+/* Functions declaration */
+
 /**
  * @brief Hash table construction. Fills struct light members.
  * @param ht - pointer to a hash table struct
@@ -226,10 +239,22 @@ LIGHT(replace)(struct LIGHT(core) *ht, uint32_t hash,
  * @brief Delete a record from a hash table by given record ID
  * @param ht - pointer to a hash table struct
  * @param slotpos - ID of an record. See LIGHT(find) for details.
+ * @return 0 if ok, -1 on memory error (only with freezed iterators)
  */
-void
+int
 LIGHT(delete)(struct LIGHT(core) *ht, uint32_t slotpos);
 
+/**
+ * @brief Delete a record from a hash table by that value and its hash.
+ * @param ht - pointer to a hash table struct
+ * @param slotpos - ID of an record. See LIGHT(find) for details.
+ * @return 0 if ok, 1 if not found or -1 on memory error
+ * (only with freezed iterators)
+ */
+int
+LIGHT(delete_value)(struct LIGHT(core) *ht,
+		    uint32_t hash, LIGHT_DATA_TYPE value);
+
 /**
  * @brief Get a value from a desired position
  * @param ht - pointer to a hash table struct
@@ -248,8 +273,65 @@ LIGHT(get)(struct LIGHT(core) *ht, uint32_t slotpos);
 bool
 LIGHT(pos_valid)(struct LIGHT(core) *ht, uint32_t slotpos);
 
+/**
+ * @brief Set iterator to the beginning of hash table
+ * @param ht - pointer to a hash table struct
+ * @param itr - iterator to set
+ */
+void
+LIGHT(itr_begin)(const struct LIGHT(core) *ht, struct LIGHT(iterator) *itr);
+
+/**
+ * @brief Set iterator to position determined by key
+ * @param ht - pointer to a hash table struct
+ * @param itr - iterator to set
+ * @param hash - hash to find
+ * @param data - key to find
+ */
+void
+LIGHT(itr_key)(const struct LIGHT(core) *ht, struct LIGHT(iterator) *itr,
+	       uint32_t hash, LIGHT_KEY_TYPE data);
 
+/**
+ * @brief Get the value that iterator currently points to
+ * @param ht - pointer to a hash table struct
+ * @param itr - iterator to set
+ * @return poiner to the value or NULL if iteration is complete
+ */
+LIGHT_DATA_TYPE *
+LIGHT(itr_get_and_next)(const struct LIGHT(core) *ht,
+			struct LIGHT(iterator) *itr);
 
+/**
+ * @brief Freezes state for given iterator. All following hash table modification
+ * will not apply to that iterator iteration. That iterator should be destroyed
+ * with a light_itr_destroy call after usage.
+ * @param ht - pointer to a hash table struct
+ * @param itr - iterator to freeze
+ * @return - 0 on success, -1 if no more version is available in matras
+ */
+int
+LIGHT(itr_freeze)(struct LIGHT(core) *ht, struct LIGHT(iterator) *itr);
+
+/**
+ * @brief Destroy an iterator that was frozen before. Useless for not frozen
+ * iterators.
+ * @param ht - pointer to a hash table struct
+ * @param itr - iterator to destroy
+ */
+void
+LIGHT(itr_destroy)(struct LIGHT(core) *ht, struct LIGHT(iterator) *itr);
+
+/* Functions definition */
+
+/**
+ * @brief Hash table construction. Fills struct light members.
+ * @param ht - pointer to a hash table struct
+ * @param extent_size - size of allocating memory blocks
+ * @param extent_alloc_func - memory blocks allocation function
+ * @param extent_free_func - memory blocks allocation function
+ * @param arg - optional parameter to save for comparing function
+ */
 inline void
 LIGHT(create)(struct LIGHT(core) *ht, size_t extent_size,
 	      LIGHT(extent_alloc_t) extent_alloc_func,
@@ -261,19 +343,26 @@ LIGHT(create)(struct LIGHT(core) *ht, size_t extent_size,
 	ht->count = 0;
 	ht->table_size = 0;
 	ht->empty_slot = LIGHT(end);
-	ht->empty_record = 0;
 	ht->arg = arg;
 	matras_create(&ht->mtable,
 		      extent_size, sizeof(struct LIGHT(record)),
 		      extent_alloc_func, extent_free_func);
 }
 
+/**
+ * @brief Hash table destruction. Frees all allocated memory
+ * @param ht - pointer to a hash table struct
+ */
 inline void
 LIGHT(destroy)(struct LIGHT(core) *ht)
 {
 	matras_destroy(&ht->mtable);
 }
 
+/**
+ * Find a slot (index in the hash table), where an item with
+ * given hash should be placed.
+ */
 inline uint32_t
 LIGHT(slot)(const struct LIGHT(core) *ht, uint32_t hash)
 {
@@ -286,6 +375,13 @@ LIGHT(slot)(const struct LIGHT(core) *ht, uint32_t hash)
 
 }
 
+/**
+ * @brief Find a record with given hash and value
+ * @param ht - pointer to a hash table struct
+ * @param hash - hash to find
+ * @param data - value to find
+ * @return integer ID of found record or light_end if nothing found
+ */
 inline uint32_t
 LIGHT(find)(const struct LIGHT(core) *ht, uint32_t hash, LIGHT_DATA_TYPE value)
 {
@@ -297,7 +393,8 @@ LIGHT(find)(const struct LIGHT(core) *ht, uint32_t hash, LIGHT_DATA_TYPE value)
 	if (record->next == slot)
 		return LIGHT(end);
 	while (1) {
-		if (record->hash == hash && LIGHT_EQUAL((record->value), (value), (ht->arg)))
+		if (record->hash == hash
+		    && LIGHT_EQUAL((record->value), (value), (ht->arg)))
 			return slot;
 		slot = record->next;
 		if (slot == LIGHT(end))
@@ -309,8 +406,15 @@ LIGHT(find)(const struct LIGHT(core) *ht, uint32_t hash, LIGHT_DATA_TYPE value)
 	return LIGHT(end);
 }
 
+/**
+ * @brief Find a record with given hash and key
+ * @param ht - pointer to a hash table struct
+ * @param hash - hash to find
+ * @param data - key to find
+ * @return integer ID of found record or light_end if nothing found
+ */
 inline uint32_t
-LIGHT(replace)(struct LIGHT(core) *ht, uint32_t hash, LIGHT_DATA_TYPE value, LIGHT_DATA_TYPE *replaced)
+LIGHT(find_key)(const struct LIGHT(core) *ht, uint32_t hash, LIGHT_KEY_TYPE key)
 {
 	if (ht->count == 0)
 		return LIGHT(end);
@@ -320,11 +424,9 @@ LIGHT(replace)(struct LIGHT(core) *ht, uint32_t hash, LIGHT_DATA_TYPE value, LIG
 	if (record->next == slot)
 		return LIGHT(end);
 	while (1) {
-		if (record->hash == hash && LIGHT_EQUAL((record->value), (value), (ht->arg))) {
-			*replaced = record->value;
-			record->value = value;
+		if (record->hash == hash &&
+		    LIGHT_EQUAL_KEY((record->value), (key), (ht->arg)))
 			return slot;
-		}
 		slot = record->next;
 		if (slot == LIGHT(end))
 			return LIGHT(end);
@@ -335,10 +437,17 @@ LIGHT(replace)(struct LIGHT(core) *ht, uint32_t hash, LIGHT_DATA_TYPE value, LIG
 	return LIGHT(end);
 }
 
-
-
+/**
+ * @brief Replace a record with given hash and value
+ * @param ht - pointer to a hash table struct
+ * @param hash - hash to find
+ * @param data - value to find and replace
+ * @param replaced - pointer to a value that was stored in table before replace
+ * @return integer ID of found record or light_end if nothing found
+ */
 inline uint32_t
-LIGHT(find_key)(const struct LIGHT(core) *ht, uint32_t hash, LIGHT_KEY_TYPE key)
+LIGHT(replace)(struct LIGHT(core) *ht, uint32_t hash,
+	       LIGHT_DATA_TYPE value, LIGHT_DATA_TYPE *replaced)
 {
 	if (ht->count == 0)
 		return LIGHT(end);
@@ -348,8 +457,16 @@ LIGHT(find_key)(const struct LIGHT(core) *ht, uint32_t hash, LIGHT_KEY_TYPE key)
 	if (record->next == slot)
 		return LIGHT(end);
 	while (1) {
-		if (record->hash == hash && LIGHT_EQUAL_KEY((record->value), (key), (ht->arg)))
+		if (record->hash == hash
+		    && LIGHT_EQUAL((record->value), (value), (ht->arg))) {
+			record = (struct LIGHT(record) *)
+				matras_touch(&ht->mtable, slot);
+			if (!record)
+				return LIGHT(end);
+			*replaced = record->value;
+			record->value = value;
 			return slot;
+		}
 		slot = record->next;
 		if (slot == LIGHT(end))
 			return LIGHT(end);
@@ -360,81 +477,138 @@ LIGHT(find_key)(const struct LIGHT(core) *ht, uint32_t hash, LIGHT_KEY_TYPE key)
 	return LIGHT(end);
 }
 
+/*
+ * Empty records (that do not store value) are linked into doubly linked list.
+ * Get an slot of the previous record in that list.
+ */
 inline uint32_t
 LIGHT(get_empty_prev)(struct LIGHT(record) *record)
 {
 	return record->hash;
 }
 
+/*
+ * Empty records (that do not store value) are linked into doubly linked list.
+ * Set an slot of the previous record in that list.
+ */
 inline void
 LIGHT(set_empty_prev)(struct LIGHT(record) *record, uint32_t pos)
 {
 	record->hash = pos;
 }
 
+/*
+ * Empty records (that do not store value) are linked into doubly linked list.
+ * Get an slot of the next record in that list.
+ */
 inline uint32_t
 LIGHT(get_empty_next)(struct LIGHT(record) *record)
 {
 	return (uint32_t)(uint64_t)record->value;
 }
 
+/*
+ * Empty records (that do not store value) are linked into doubly linked list.
+ * Get an slot of the next record in that list.
+ */
 inline void
 LIGHT(set_empty_next)(struct LIGHT(record) *record, uint32_t pos)
 {
 	record->value = (LIGHT_DATA_TYPE)(int64_t)pos;
 }
 
-inline void
-LIGHT(enqueue_empty)(struct LIGHT(core) *ht, uint32_t slot, struct LIGHT(record) *record)
+/*
+ * Empty records (that do not store value) are linked into doubly linked list.
+ * Add given record with given slot to that list.
+ * Touches matras of the record
+ */
+inline int
+LIGHT(enqueue_empty)(struct LIGHT(core) *ht, uint32_t slot,
+		     struct LIGHT(record) *record)
 {
 	record->next = slot;
-	if (ht->empty_record)
-		LIGHT(set_empty_prev)(ht->empty_record, slot);
+	if (ht->empty_slot != LIGHT(end)) {
+		struct LIGHT(record) *empty_record = (struct LIGHT(record) *)
+			matras_touch(&ht->mtable, ht->empty_slot);
+		if (!empty_record)
+			return -1;
+		LIGHT(set_empty_prev)(empty_record, slot);
+	}
 	LIGHT(set_empty_prev)(record, LIGHT(end));
 	LIGHT(set_empty_next)(record, ht->empty_slot);
-	ht->empty_record = record;
 	ht->empty_slot = slot;
+	return 0;
 }
 
-inline void
+/*
+ * Empty records (that do not store value) are linked into doubly linked list.
+ * Remove from list first record of that list and return that record
+ * Touches matras of result and all chaning records
+ */
+inline struct LIGHT(record) *
 LIGHT(detach_first_empty)(struct LIGHT(core) *ht)
 {
-	assert(ht->empty_record);
-	ht->empty_slot = LIGHT(get_empty_next)(ht->empty_record);
-	if (ht->empty_slot == LIGHT(end)) {
-		ht->empty_record = 0;
-	} else {
-		ht->empty_record = (struct LIGHT(record) *)
-			matras_get(&ht->mtable, ht->empty_slot);
-		LIGHT(set_empty_prev)(ht->empty_record, LIGHT(end));
+	assert(ht->empty_slot != LIGHT(end));
+	struct LIGHT(record) *empty_record = (struct LIGHT(record) *)
+		matras_touch(&ht->mtable, ht->empty_slot);
+	if (!empty_record)
+		return 0;
+	assert(empty_record->next == ht->empty_slot);
+	uint32_t new_empty_slot = LIGHT(get_empty_next)(empty_record);
+	if (new_empty_slot != LIGHT(end)) {
+		struct LIGHT(record) *new_empty_record = (struct LIGHT(record) *)
+			matras_touch(&ht->mtable, new_empty_slot);
+		if (!new_empty_record)
+			return 0;
+		LIGHT(set_empty_prev)(new_empty_record, LIGHT(end));
 	}
+	ht->empty_slot = new_empty_slot;
+	return empty_record;
 }
 
-inline void
-LIGHT(detach_empty)(struct LIGHT(core) *ht, struct LIGHT(record) *record)
+/*
+ * Empty records (that do not store value) are linked into doubly linked list.
+ * Remove from list the record by given slot and return that record
+ * Touches matras of result and all chaning records
+ */
+inline struct LIGHT(record) *
+LIGHT(detach_empty)(struct LIGHT(core) *ht, uint32_t slot)
 {
+	struct LIGHT(record) *record = (struct LIGHT(record) *)
+		matras_touch(&ht->mtable, slot);
+	if (!record)
+		return 0;
 	uint32_t prev_slot = LIGHT(get_empty_prev)(record);
 	uint32_t next_slot = LIGHT(get_empty_next)(record);
-	if (prev_slot == LIGHT(end)) {
-		ht->empty_slot = next_slot;
-		if (next_slot == LIGHT(end))
-			ht->empty_record = 0;
-		else
-			ht->empty_record = (struct LIGHT(record) *)
-				matras_get(&ht->mtable, next_slot);
-	} else {
-		struct LIGHT(record) *prev_record = (struct LIGHT(record) *)
-			matras_get(&ht->mtable, prev_slot);
+	struct LIGHT(record) *prev_record = 0;
+	if (prev_slot != LIGHT(end)) {
+		prev_record = (struct LIGHT(record) *)
+			matras_touch(&ht->mtable, prev_slot);
+		if (!prev_record)
+			return 0;
+	}
+	struct LIGHT(record) *next_record = 0;
+	if (next_slot != LIGHT(end)) {
+		next_record = (struct LIGHT(record) *)
+			matras_touch(&ht->mtable, next_slot);
+		if (!next_record)
+			return 0;
+	}
+	if (prev_slot != LIGHT(end)) {
 		LIGHT(set_empty_next)(prev_record, next_slot);
+	} else {
+		ht->empty_slot = next_slot;
 	}
 	if (next_slot != LIGHT(end)) {
-		struct LIGHT(record) *next_record = (struct LIGHT(record) *)
-			matras_get(&ht->mtable, next_slot);
 		LIGHT(set_empty_prev)(next_record, prev_slot);
 	}
+	return record;
 }
 
-inline bool
+/*
+ * Allocate memory and initialize empty list to get ready for first insertion
+ */
+inline int
 LIGHT(prepare_first_insert)(struct LIGHT(core) *ht)
 {
 	assert(ht->count == 0);
@@ -445,12 +619,11 @@ LIGHT(prepare_first_insert)(struct LIGHT(core) *ht)
 	struct LIGHT(record) *record = (struct LIGHT(record) *)
 		matras_alloc_range(&ht->mtable, &slot, ht->GROW_INCREMENT);
 	if (!record)
-		return false;
+		return -1;
 	assert(slot == 0);
 	ht->table_size = ht->GROW_INCREMENT;
 	ht->cover_mask = ht->GROW_INCREMENT - 1;
 	ht->empty_slot = 0;
-	ht->empty_record = record;
 	for (int i = 0; i < ht->GROW_INCREMENT; i++) {
 		record[i].next = i;
 		LIGHT(set_empty_prev)(record + i, i - 1);
@@ -458,19 +631,30 @@ LIGHT(prepare_first_insert)(struct LIGHT(core) *ht)
 	}
 	LIGHT(set_empty_prev)(record, LIGHT(end));
 	LIGHT(set_empty_next)(record + ht->GROW_INCREMENT - 1, LIGHT(end));
-	return true;
+	return 0;
 }
 
-inline bool
+/*
+ * Enlarge hash table to store more values
+ */
+inline int
 LIGHT(grow)(struct LIGHT(core) *ht)
 {
+	assert(ht->empty_slot == LIGHT(end));
 	uint32_t new_slot;
 	struct LIGHT(record) *new_record = (struct LIGHT(record) *)
 		matras_alloc_range(&ht->mtable, &new_slot, ht->GROW_INCREMENT);
 	if (!new_record) /* memory failure */
-		return false;
-	ht->table_size += ht->GROW_INCREMENT;
+		return -1;
+	new_record = (struct LIGHT(record) *)
+		matras_touch(&ht->mtable, new_slot);
+	if (!new_record) { /* memory failure */
+		matras_dealloc_range(&ht->mtable, ht->GROW_INCREMENT);
+		return -1;
+	}
 
+	uint32_t save_cover_mask = ht->cover_mask;
+	ht->table_size += ht->GROW_INCREMENT;
 	if (ht->cover_mask < ht->table_size - 1)
 		ht->cover_mask = (ht->cover_mask << 1) | (uint32_t)1;
 
@@ -479,9 +663,16 @@ LIGHT(grow)(struct LIGHT(core) *ht)
 
 	uint32_t susp_slot = new_slot & split_comm_mask;
 	struct LIGHT(record) *susp_record = (struct LIGHT(record) *)
-		matras_get(&ht->mtable, susp_slot);
+		matras_touch(&ht->mtable, susp_slot);
+	if (!susp_record) {
+		matras_dealloc_range(&ht->mtable, ht->GROW_INCREMENT);
+		ht->cover_mask = save_cover_mask;
+		ht->table_size -= ht->GROW_INCREMENT;
+		return -1;
+	}
 
-	for (int i = 0; i < ht->GROW_INCREMENT; i++, susp_slot++, susp_record++, new_slot++, new_record++) {
+	for (int i = 0; i < ht->GROW_INCREMENT;
+	     i++, susp_slot++, susp_record++, new_slot++, new_record++) {
 		if (susp_record->next == susp_slot) {
 			/* Suspicious slot is empty, nothing to split */
 			LIGHT(enqueue_empty)(ht, new_slot, new_record);
@@ -500,30 +691,34 @@ LIGHT(grow)(struct LIGHT(core) *ht)
 		assert(split_diff_mask == (((uint32_t)1) << shift));
 
 		uint32_t last_empty_slot = new_slot;
-		struct LIGHT(record) *last_empty_record = new_record;
 		uint32_t prev_flag = 0;
 		struct LIGHT(record) *test_record = susp_record;
 		uint32_t test_slot = susp_slot;
 		struct LIGHT(record) *prev_record = 0;
+		uint32_t prev_slot = LIGHT(end);
 		while (1) {
-			uint32_t test_flag = (test_record->hash >> shift) & ((uint32_t)1);
-			if (test_flag  != prev_flag) {
+			uint32_t test_flag = (test_record->hash >> shift)
+					     & ((uint32_t)1);
+			if (test_flag != prev_flag) {
+				if (prev_slot != LIGHT(end))
+					prev_record = (struct LIGHT(record) *)
+					matras_touch(&ht->mtable,
+							     prev_slot);
+					/* TODO: check the result */
 				chain_tail[prev_flag] = prev_record;
 				if (chain_tail[test_flag]) {
 					chain_tail[test_flag]->next = test_slot;
 				} else {
 					*chain_head[test_flag] = *test_record;
 					last_empty_slot = test_slot;
-					last_empty_record = test_record;
-					test_record = chain_head[test_flag];
 					test_slot = chain_head_slot[test_flag];
 				}
 				prev_flag = test_flag;
 			}
+			prev_slot = test_slot;
 			test_slot = test_record->next;
 			if (test_slot == LIGHT(end))
 				break;
-			prev_record = test_record;
 			test_record = (struct LIGHT(record) *)
 				matras_get(&ht->mtable, test_slot);
 		}
@@ -531,40 +726,71 @@ LIGHT(grow)(struct LIGHT(core) *ht)
 		if (chain_tail[prev_flag])
 			chain_tail[prev_flag]->next = LIGHT(end);
 
+		struct LIGHT(record) *last_empty_record =
+			(struct LIGHT(record) *)
+			matras_touch(&ht->mtable, last_empty_slot);
 		LIGHT(enqueue_empty)(ht, last_empty_slot, last_empty_record);
 	}
-	return true;
+	return 0;
 }
 
+/**
+ * @brief Insert a record with given hash and value
+ * @param ht - pointer to a hash table struct
+ * @param hash - hash to insert
+ * @param data - value to insert
+ * @return integer ID of inserted record or light_end if failed
+ */
 inline uint32_t
 LIGHT(insert)(struct LIGHT(core) *ht, uint32_t hash, LIGHT_DATA_TYPE value)
 {
 	if (ht->table_size == 0)
-		if (!LIGHT(prepare_first_insert)(ht))
+		if (LIGHT(prepare_first_insert)(ht))
 			return LIGHT(end);
-	if (!ht->empty_record)
-		if (!LIGHT(grow)(ht))
+	if (ht->empty_slot == LIGHT(end))
+		if (LIGHT(grow)(ht))
 			return LIGHT(end);
+	assert(ht->table_size == ht->mtable.block_counts[0]);
 
 	ht->count++;
 	uint32_t slot = LIGHT(slot)(ht, hash);
 	struct LIGHT(record) *record = (struct LIGHT(record) *)
-		matras_get(&ht->mtable, slot);
+		matras_touch(&ht->mtable, slot);
+	if (!record)
+		return LIGHT(end);
 
 	if (record->next == slot) {
 		/* Inserting to an empty slot */
-		LIGHT(detach_empty)(ht, record);
+		record = LIGHT(detach_empty)(ht, slot);
+		if (!record)
+			return LIGHT(end);
 		record->value = value;
 		record->hash = hash;
 		record->next = LIGHT(end);
 		return slot;
 	}
 
+	uint32_t chain_slot = LIGHT(slot)(ht, record->hash);
+	struct LIGHT(record) *chain = 0;
+	if (chain_slot != slot) {
+		chain = (struct LIGHT(record) *)
+			matras_get(&ht->mtable, chain_slot);
+		while (chain->next != slot) {
+			chain_slot = chain->next;
+			chain = (struct LIGHT(record) *)
+				matras_get(&ht->mtable, chain_slot);
+		}
+		chain = (struct LIGHT(record) *)
+			matras_touch(&ht->mtable, chain_slot);
+		if (!chain)
+			return LIGHT(end);
+	}
+
 	uint32_t empty_slot = ht->empty_slot;
-	struct LIGHT(record) *empty_record = ht->empty_record;
-	LIGHT(detach_first_empty)(ht);
+	struct LIGHT(record) *empty_record = LIGHT(detach_first_empty)(ht);
+	if (!empty_record)
+		return LIGHT(end);
 
-	uint32_t chain_slot = LIGHT(slot)(ht, record->hash);
 	if (chain_slot == slot) {
 		/* add to existing chain */
 		empty_record->value = value;
@@ -574,13 +800,6 @@ LIGHT(insert)(struct LIGHT(core) *ht, uint32_t hash, LIGHT_DATA_TYPE value)
 		return empty_slot;
 	} else {
 		/* create new chain */
-		struct LIGHT(record) *chain = (struct LIGHT(record) *)
-			matras_get(&ht->mtable, chain_slot);
-		while (chain->next != slot) {
-			chain_slot = chain->next;
-			chain = (struct LIGHT(record) *)
-				matras_get(&ht->mtable, chain_slot);
-		}
 		*empty_record = *record;
 		chain->next = empty_slot;
 		record->value = value;
@@ -590,19 +809,33 @@ LIGHT(insert)(struct LIGHT(core) *ht, uint32_t hash, LIGHT_DATA_TYPE value)
 	}
 }
 
-inline void
+/**
+ * @brief Delete a record from a hash table by given record ID
+ * @param ht - pointer to a hash table struct
+ * @param slotpos - ID of an record. See LIGHT(find) for details.
+ * @return 0 if ok, -1 on memory error (only with freezed iterators)
+ */
+inline int
 LIGHT(delete)(struct LIGHT(core) *ht, uint32_t slot)
 {
 	assert(slot < ht->table_size);
 	uint32_t empty_slot;
 	struct LIGHT(record) *empty_record;
 	struct LIGHT(record) *record = (struct LIGHT(record) *)
-		matras_get(&ht->mtable, slot);
+		matras_touch(&ht->mtable, slot);
+	if (!record)
+		return -1;
 	assert(record->next != slot);
+	if (ht->empty_slot != LIGHT(end)) {
+		if (!matras_touch(&ht->mtable, ht->empty_slot))
+			return -1;
+	}
 	if (record->next != LIGHT(end)) {
 		empty_slot = record->next;
 		empty_record = (struct LIGHT(record) *)
-			matras_get(&ht->mtable, empty_slot);
+			matras_touch(&ht->mtable, empty_slot);
+		if (!empty_record)
+			return -1;
 		*record = *empty_record;
 	} else {
 		empty_slot = slot;
@@ -617,55 +850,94 @@ LIGHT(delete)(struct LIGHT(core) *ht, uint32_t slot)
 			while (chain_next_slot != slot) {
 				chain_slot = chain_next_slot;
 				chain = (struct LIGHT(record) *)
-					matras_get(&ht->mtable, chain_next_slot);
+					matras_get(&ht->mtable, chain_slot);
 				chain_next_slot = chain->next;
 				assert(chain_next_slot != LIGHT(end));
 			}
+			chain = (struct LIGHT(record) *)
+				matras_touch(&ht->mtable, chain_slot);
+			if (!chain)
+				return -1;
 			chain->next = LIGHT(end);
 		}
 	}
 	LIGHT(enqueue_empty)(ht, empty_slot, empty_record);
 	ht->count--;
+	return 0;
 }
 
-inline void
+/**
+ * @brief Delete a record from a hash table by that value and its hash.
+ * @param ht - pointer to a hash table struct
+ * @param slotpos - ID of an record. See LIGHT(find) for details.
+ * @return 0 if ok, 1 if not found or -1 on memory error
+ * (only with freezed iterators)
+ */
+inline int
 LIGHT(delete_value)(struct LIGHT(core) *ht, uint32_t hash, LIGHT_DATA_TYPE value)
 {
 	if (ht->count == 0)
-		return;
+		return 1; /* not found */
 	uint32_t slot = LIGHT(slot)(ht, hash);
 	struct LIGHT(record) *record = (struct LIGHT(record) *)
 		matras_get(&ht->mtable, slot);
 	if (record->next == slot)
-		return;
+		return 1; /* not found */
+	uint32_t prev_slot = LIGHT(end);
 	struct LIGHT(record) *prev_record = 0;
 	while (1) {
-		if (record->hash == hash && LIGHT_EQUAL((record->value), (value), (ht->arg)))
+		if (record->hash == hash
+		    && LIGHT_EQUAL((record->value), (value), (ht->arg)))
 			break;
+		prev_slot = slot;
 		slot = record->next;
 		if (slot == LIGHT(end))
-			return;
+			return 1; /* not found */
 		prev_record = record;
 		record = (struct LIGHT(record) *)
 			matras_get(&ht->mtable, slot);
 	}
-	ht->count--;
+	record = (struct LIGHT(record) *)
+		matras_touch(&ht->mtable, slot);
+	if (!record) {
+		return -1; /* mem fail */
+	}
+	if (ht->empty_slot != LIGHT(end)) {
+		if (!matras_touch(&ht->mtable, ht->empty_slot))
+			return -1; /* mem fail */
+	}
 	if (prev_record) {
+		prev_record = (struct LIGHT(record) *)
+			matras_touch(&ht->mtable, prev_slot);
+		if (!prev_record)
+			return -1; /* mem fail */
 		prev_record->next = record->next;
 		LIGHT(enqueue_empty)(ht, slot, record);
-		return;
+		ht->count--;
+		return 0;
 	}
 	if (record->next == LIGHT(end)) {
 		LIGHT(enqueue_empty)(ht, slot, record);
-		return;
+		ht->count--;
+		return 0;
 	}
 	uint32_t next_slot = record->next;
 	struct LIGHT(record) *next_record = (struct LIGHT(record) *)
-		matras_get(&ht->mtable, next_slot);
+		matras_touch(&ht->mtable, next_slot);
+	if (!next_record)
+		return -1; /* mem fail */
 	*record = *next_record;
 	LIGHT(enqueue_empty)(ht, next_slot, next_record);
+	ht->count--;
+	return 0;
 }
 
+/**
+ * @brief Get a value from a desired position
+ * @param ht - pointer to a hash table struct
+ * @param slotpos - ID of an record
+ *  ID must be vaild, check it by light_pos_valid (asserted).
+ */
 inline LIGHT_DATA_TYPE
 LIGHT(get)(struct LIGHT(core) *ht, uint32_t slotpos)
 {
@@ -680,6 +952,7 @@ LIGHT(get)(struct LIGHT(core) *ht, uint32_t slotpos)
  * @brief Determine if posision holds a value
  * @param ht - pointer to a hash table struct
  * @param slotpos - ID of an record
+ *  ID must be in valid range [0, ht->table_size) (asserted).
  */
 inline bool
 LIGHT(pos_valid)(LIGHT(core) *ht, uint32_t slotpos)
@@ -690,7 +963,91 @@ LIGHT(pos_valid)(LIGHT(core) *ht, uint32_t slotpos)
 	return record->next != slotpos;
 }
 
+/**
+ * @brief Set iterator to the beginning of hash table
+ * @param ht - pointer to a hash table struct
+ * @param itr - iterator to set
+ */
+inline void
+LIGHT(itr_begin)(const struct LIGHT(core) *ht, struct LIGHT(iterator) *itr)
+{
+	(void)ht;
+	itr->slotpos = 0;
+	itr->matras_version = 0;
+}
+
+/**
+ * @brief Set iterator to position determined by key
+ * @param ht - pointer to a hash table struct
+ * @param itr - iterator to set
+ * @param hash - hash to find
+ * @param data - key to find
+ */
+inline void
+LIGHT(itr_key)(const struct LIGHT(core) *ht, struct LIGHT(iterator) *itr,
+	       uint32_t hash, LIGHT_KEY_TYPE data)
+{
+	itr->slotpos = LIGHT(find_key)(ht, hash, data);
+	itr->matras_version = 0;
+}
 
+/**
+ * @brief Get the value that iterator currently points to
+ * @param ht - pointer to a hash table struct
+ * @param itr - iterator to set
+ * @return poiner to the value or NULL if iteration is complete
+ */
+inline LIGHT_DATA_TYPE *
+LIGHT(itr_get_and_next)(const struct LIGHT(core) *ht,
+			struct LIGHT(iterator) *itr)
+{
+	while (itr->slotpos < ht->mtable.block_counts[itr->matras_version]) {
+		uint32_t slotpos = itr->slotpos;
+		struct LIGHT(record) *record = (struct LIGHT(record) *)
+			matras_getv(&ht->mtable, slotpos,
+				    itr->matras_version);
+		itr->slotpos++;
+		if (record->next != slotpos)
+			return &record->value;
+	}
+	return 0;
+}
+
+/**
+ * @brief Freezes state for given iterator. All following hash table modification
+ * will not apply to that iterator iteration. That iterator should be destroyed
+ * with a light_itr_destroy call after usage.
+ * @param ht - pointer to a hash table struct
+ * @param itr - iterator to freeze
+ * @return - 0 on success, -1 if no more version is available in matras
+ */
+inline int
+LIGHT(itr_freeze)(struct LIGHT(core) *ht, struct LIGHT(iterator) *itr)
+{
+	itr->matras_version = matras_create_read_view(&ht->mtable);
+	return itr->matras_version > 0 ? 0 : -1;
+}
+
+/**
+ * @brief Destroy an iterator that was frozen before. Useless for not frozen
+ * iterators.
+ * @param ht - pointer to a hash table struct
+ * @param itr - iterator to destroy
+ */
+inline void
+LIGHT(itr_destroy)(struct LIGHT(core) *ht, struct LIGHT(iterator) *itr)
+{
+	if (itr->matras_version) {
+		matras_destroy_read_view(&ht->mtable, itr->matras_version);
+		itr->matras_version = 0;
+	}
+}
+
+/*
+ * Selfcheck of the internal state of hash table. Used only for debugging.
+ * That means that you should not use this function.
+ * If return not zero, something went terribly wrong.
+ */
 inline int
 LIGHT(selfcheck)(const struct LIGHT(core) *ht)
 {
@@ -698,15 +1055,6 @@ LIGHT(selfcheck)(const struct LIGHT(core) *ht)
 	if (ht->table_size != ht->mtable.block_counts[0])
 		res |= 64;
 	uint32_t empty_slot = ht->empty_slot;
-	if (empty_slot == LIGHT(end)) {
-		if (ht->empty_record)
-			res |= 512;
-	} else {
-		struct LIGHT(record) *should_be = (struct LIGHT(record) *)
-			matras_get(&ht->mtable, empty_slot);
-		if (ht->empty_record != should_be)
-			res |= 1024;
-	}
 	uint32_t prev_empty_slot = LIGHT(end);
 	while (empty_slot != LIGHT(end)) {
 		struct LIGHT(record) *empty_record = (struct LIGHT(record) *)
@@ -724,7 +1072,8 @@ LIGHT(selfcheck)(const struct LIGHT(core) *ht)
 		if (record->next == i) {
 			uint32_t empty_slot = ht->empty_slot;
 			while (empty_slot != LIGHT(end) && empty_slot != i) {
-				struct LIGHT(record) *empty_record = (struct LIGHT(record) *)
+				struct LIGHT(record) *empty_record =
+					(struct LIGHT(record) *)
 					matras_get(&ht->mtable, empty_slot);
 				empty_slot = LIGHT(get_empty_next)(empty_record);
 			}
@@ -738,7 +1087,8 @@ LIGHT(selfcheck)(const struct LIGHT(core) *ht)
 			uint32_t chain_slot = slot;
 			uint32_t chain_start_slot = slot;
 			do {
-				struct LIGHT(record) *chain_record = (struct LIGHT(record) *)
+				struct LIGHT(record) *chain_record =
+					(struct LIGHT(record) *)
 					matras_get(&ht->mtable, chain_slot);
 				chain_slot = chain_record->next;
 				if (chain_slot >= ht->table_size) {
@@ -758,12 +1108,14 @@ LIGHT(selfcheck)(const struct LIGHT(core) *ht)
 				res |= 1; /* slot is out of chain */
 		} else {
 			do {
-				struct LIGHT(record) *record = (struct LIGHT(record) *)
+				struct LIGHT(record) *record =
+					(struct LIGHT(record) *)
 					matras_get(&ht->mtable, slot);
 				if (LIGHT(slot)(ht, record->hash) != i)
 					res |= 2; /* wrong value in chain */
 				slot = record->next;
-				if (slot != LIGHT(end) && slot >= ht->table_size) {
+				if (slot != LIGHT(end)
+				    && slot >= ht->table_size) {
 					res |= 32; /* out of bounds (2) */
 					break;
 				}
diff --git a/src/lib/small/matras.c b/src/lib/small/matras.c
index 3bf2e21c2cf3d4cbe7b3438c0fae2237a1026914..9ae7dc0168af6d1230eefcbff4d27f8f577c0c73 100644
--- a/src/lib/small/matras.c
+++ b/src/lib/small/matras.c
@@ -314,7 +314,7 @@ matras_extent_count(const struct matras *m)
 
 /*
  * Create new version of matras memory.
- * Return -1 if all version IDs are occupied.
+ * Return 0 if all version IDs are occupied.
  */
 matras_id_t
 matras_create_read_view(struct matras *m)
@@ -331,7 +331,7 @@ matras_create_read_view(struct matras *m)
 #endif
 	assert(ver_id > 0);
 	if (ver_id >= MATRAS_VERSION_COUNT)
-		return -1;
+		return 0;
 	m->ver_occ_mask |= ((matras_version_tag_t)1) << ver_id;
 	m->roots[ver_id] = m->roots[0];
 	m->block_counts[ver_id] = m->block_counts[0];
diff --git a/src/lib/small/matras.h b/src/lib/small/matras.h
index ae1aaf1c4c099307efe44e291f31f68824ee9b57..66912c4a4c3594f0e866f3946c8e5003193b2d99 100644
--- a/src/lib/small/matras.h
+++ b/src/lib/small/matras.h
@@ -284,7 +284,7 @@ matras_extent_count(const struct matras *m);
 
 /*
  * Create new version of matras memory.
- * Return -1 if all version IDs are occupied.
+ * Return 0 if all version IDs are occupied.
  */
 matras_id_t
 matras_create_read_view(struct matras *m);
diff --git a/test/unit/bps_tree_itr.cc b/test/unit/bps_tree_itr.cc
index 3a464235bdd60baf517e0b37b1deea20cc7216f3..0c7deadf4080807e9666a0809432c30c4bd0ea84 100644
--- a/test/unit/bps_tree_itr.cc
+++ b/test/unit/bps_tree_itr.cc
@@ -2,6 +2,7 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <stdbool.h>
+#include <time.h>
 
 #include "unit.h"
 
@@ -473,6 +474,7 @@ itr_freeze_check()
 int
 main(void)
 {
+	srand(time(0));
 	itr_check();
 	itr_invalidate_check();
 	itr_freeze_check();
diff --git a/test/unit/light.cc b/test/unit/light.cc
index 2dee5dcba3040cb2260857dfaea5e4ac08d5ce19..4de77d92cbb0a912e1bc3471d61cb930c24a380b 100644
--- a/test/unit/light.cc
+++ b/test/unit/light.cc
@@ -4,6 +4,7 @@
 #include <stdbool.h>
 #include <inttypes.h>
 #include <vector>
+#include <time.h>
 
 #include "unit.h"
 
@@ -183,11 +184,145 @@ collision_test()
 	footer();
 }
 
+static void
+itr_test()
+{
+	header();
+
+	struct light_core ht;
+	light_create(&ht, light_extent_size, my_light_alloc, my_light_free, 0);
+	const size_t rounds = 1000;
+	const size_t start_limits = 20;
+
+	const size_t iterator_count = 16;
+	struct light_iterator itrs[iterator_count];
+	for (size_t i = 0; i < iterator_count; i++)
+		light_itr_begin(&ht, itrs + i);
+	size_t cur_iterator = 0;
+	hash_value_t strage_thing = 0;
+
+	for(size_t limits = start_limits; limits <= 2 * rounds; limits *= 10) {
+		for (size_t i = 0; i < rounds; i++) {
+			hash_value_t val = rand() % limits;
+			hash_t h = hash(val);
+			hash_t fnd = light_find(&ht, h, val);
+
+			if (fnd == light_end) {
+				light_insert(&ht, h, val);
+			} else {
+				light_delete(&ht, fnd);
+			}
+
+			hash_value_t *pval = light_itr_get_and_next(&ht, itrs + cur_iterator);
+			if (pval)
+				strage_thing ^= *pval;
+			if (!pval || (rand() % iterator_count) == 0) {
+				if (rand() % iterator_count) {
+					hash_value_t val = rand() % limits;
+					hash_t h = hash(val);
+					light_itr_key(&ht, itrs + cur_iterator, h, val);
+				} else {
+					light_itr_begin(&ht, itrs + cur_iterator);
+				}
+			}
+
+			cur_iterator++;
+			if (cur_iterator >= iterator_count)
+				cur_iterator = 0;
+		}
+	}
+	light_destroy(&ht);
+
+	if (strage_thing >> 20) {
+		printf("impossible!\n"); // prevent strage_thing to be optimized out
+	}
+
+	footer();
+}
+
+static void
+itr_freeze_check()
+{
+	header();
+
+	const int test_rounds_size = 10;
+	const int test_data_size = 1000;
+	hash_value_t comp_buf1[test_data_size];
+	hash_value_t comp_buf2[test_data_size];
+	const int test_data_mod = 2000;
+	srand(0);
+	struct light_core ht;
+
+	for (int i = 0; i < 10; i++) {
+		light_create(&ht, light_extent_size, my_light_alloc, my_light_free, 0);
+		int comp_buf_size1 = 0;
+		int comp_buf_size2 = 0;
+		for (int j = 0; j < test_data_size; j++) {
+			hash_value_t val = rand() % test_data_mod;
+			hash_t h = hash(val);
+			light_insert(&ht, h, val);
+		}
+		struct light_iterator itr;
+		light_itr_begin(&ht, &itr);
+		hash_value_t *e;
+		while ((e = light_itr_get_and_next(&ht, &itr))) {
+			comp_buf1[comp_buf_size1++] = *e;
+		}
+		struct light_iterator itr1;
+		light_itr_begin(&ht, &itr1);
+		light_itr_freeze(&ht, &itr1);
+		struct light_iterator itr2;
+		light_itr_begin(&ht, &itr2);
+		light_itr_freeze(&ht, &itr2);
+		for (int j = 0; j < test_data_size; j++) {
+			hash_value_t val = rand() % test_data_mod;
+			hash_t h = hash(val);
+			light_insert(&ht, h, val);
+		}
+		int tested_count = 0;
+		while ((e = light_itr_get_and_next(&ht, &itr1))) {
+			if (*e != comp_buf1[tested_count]) {
+				fail("version restore failed (1)", "true");
+			}
+			tested_count++;
+			if (tested_count > comp_buf_size1) {
+				fail("version restore failed (2)", "true");
+			}
+		}
+		light_itr_destroy(&ht, &itr1);
+		for (int j = 0; j < test_data_size; j++) {
+			hash_value_t val = rand() % test_data_mod;
+			hash_t h = hash(val);
+			hash_t pos = light_find(&ht, h, val);
+			if (pos != light_end)
+				light_delete(&ht, pos);
+		}
+
+		tested_count = 0;
+		while ((e = light_itr_get_and_next(&ht, &itr2))) {
+			if (*e != comp_buf1[tested_count]) {
+				fail("version restore failed (1)", "true");
+			}
+			tested_count++;
+			if (tested_count > comp_buf_size1) {
+				fail("version restore failed (2)", "true");
+			}
+		}
+
+		light_destroy(&ht);
+	}
+
+	footer();
+}
+
 int
 main(int, const char**)
 {
+	srand(time(0));
 	simple_test();
 	collision_test();
+	itr_test();
+	itr_freeze_check();
 	if (extents_count != 0)
 		fail("memory leak!", "true");
 }
diff --git a/test/unit/light.result b/test/unit/light.result
index b37b0ed568394b2cebd65e2e951c4a5c5c7528f9..43011b2fcef38f96ff326122f11a6c712f74d9ad 100644
--- a/test/unit/light.result
+++ b/test/unit/light.result
@@ -2,4 +2,8 @@
 	*** simple_test: done ***
  	*** collision_test ***
 	*** collision_test: done ***
+ 	*** itr_test ***
+	*** itr_test: done ***
+ 	*** itr_freeze_check ***
+	*** itr_freeze_check: done ***
  
\ No newline at end of file
diff --git a/test/unit/matras.cc b/test/unit/matras.cc
index 6907ae7cbbc1652af5ae86605a0912e8d659d2d0..ee22b3cdcd9156e66db3db42896755f8c493fcaf 100644
--- a/test/unit/matras.cc
+++ b/test/unit/matras.cc
@@ -189,8 +189,8 @@ matras_vers_test()
 					add_ver = rand() % 2 == 0;
 				if (add_ver) {
 					cur_num_or_ver++;
-					int new_ver = matras_create_read_view(&local);
-					assert(new_ver > 0);
+					matras_id_t new_ver = matras_create_read_view(&local);
+					check(new_ver > 0, "create read view failed");
 					use_mask |= (1 << new_ver);
 					comps[new_ver] = comps[0];
 				} else {