initial code repo
[stor4nfv.git] / src / ceph / src / test / ObjectMap / test_keyvaluedb_iterators.cc
diff --git a/src/ceph/src/test/ObjectMap/test_keyvaluedb_iterators.cc b/src/ceph/src/test/ObjectMap/test_keyvaluedb_iterators.cc
new file mode 100644 (file)
index 0000000..cd8e28a
--- /dev/null
@@ -0,0 +1,1758 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+* Ceph - scalable distributed file system
+*
+* Copyright (C) 2012 Inktank, Inc.
+*
+* This is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License version 2.1, as published by the Free Software
+* Foundation. See file COPYING.
+*/
+#include "include/memory.h"
+#include <map>
+#include <set>
+#include <deque>
+#include <boost/scoped_ptr.hpp>
+
+#include "test/ObjectMap/KeyValueDBMemory.h"
+#include "kv/KeyValueDB.h"
+#include <sys/types.h>
+#include "global/global_init.h"
+#include "common/ceph_argparse.h"
+#include "gtest/gtest.h"
+
+using namespace std;
+
+string store_path;
+
+class IteratorTest : public ::testing::Test
+{
+public:
+  boost::scoped_ptr<KeyValueDB> db;
+  boost::scoped_ptr<KeyValueDBMemory> mock;
+
+  void SetUp() override {
+    assert(!store_path.empty());
+
+    KeyValueDB *db_ptr = KeyValueDB::create(g_ceph_context, "leveldb", store_path);
+    assert(!db_ptr->create_and_open(std::cerr));
+    db.reset(db_ptr);
+    mock.reset(new KeyValueDBMemory());
+  }
+
+  void TearDown() override { }
+
+  ::testing::AssertionResult validate_db_clear(KeyValueDB *store) {
+    KeyValueDB::WholeSpaceIterator it = store->get_iterator();
+    it->seek_to_first();
+    while (it->valid()) {
+      pair<string,string> k = it->raw_key();
+      if (mock->db.count(k)) {
+       return ::testing::AssertionFailure()
+               << __func__
+               << " mock store count " << mock->db.count(k)
+               << " key(" << k.first << "," << k.second << ")";
+      }
+      it->next();
+    }
+    return ::testing::AssertionSuccess();
+  }
+
+  ::testing::AssertionResult validate_db_match() {
+    KeyValueDB::WholeSpaceIterator it = db->get_iterator();
+    it->seek_to_first();
+    while (it->valid()) {
+      pair<string, string> k = it->raw_key();
+      if (!mock->db.count(k)) {
+       return ::testing::AssertionFailure()
+               << __func__
+               << " mock db.count() " << mock->db.count(k)
+               << " key(" << k.first << "," << k.second << ")";
+      }
+
+      bufferlist it_bl = it->value();
+      bufferlist mock_bl = mock->db[k];
+
+      string it_val = _bl_to_str(it_bl);
+      string mock_val = _bl_to_str(mock_bl);
+
+      if (it_val != mock_val) {
+       return ::testing::AssertionFailure()
+               << __func__
+               << " key(" << k.first << "," << k.second << ")"
+               << " mismatch db value(" << it_val << ")"
+               << " mock value(" << mock_val << ")";
+      }
+      it->next();
+    }
+    return ::testing::AssertionSuccess();
+  }
+
+  ::testing::AssertionResult validate_iterator(
+                               KeyValueDB::WholeSpaceIterator it,
+                               string expected_prefix,
+                               string expected_key,
+                               string expected_value) {
+    if (!it->valid()) {
+      return ::testing::AssertionFailure()
+             << __func__
+             << " iterator not valid";
+    }
+    
+    if (!it->raw_key_is_prefixed(expected_prefix)) {
+      return ::testing::AssertionFailure()
+             << __func__
+             << " expected raw_key_is_prefixed() == TRUE"
+             << " got FALSE";
+    }
+    
+    if (it->raw_key_is_prefixed("??__SomeUnexpectedValue__??")) {
+      return ::testing::AssertionFailure()
+             << __func__
+             << " expected raw_key_is_prefixed() == FALSE"
+             << " got TRUE";
+    }
+    pair<string,string> key = it->raw_key();
+
+    if (expected_prefix != key.first) {
+      return ::testing::AssertionFailure()
+             << __func__
+             << " expected prefix '" << expected_prefix << "'"
+             << " got prefix '" << key.first << "'";
+    }
+
+    if (expected_key != it->key()) {
+      return ::testing::AssertionFailure()
+             << __func__
+             << " expected key '" << expected_key << "'"
+             << " got key '" << it->key() << "'";
+    }
+
+    if (it->key() != key.second) {
+      return ::testing::AssertionFailure()
+             << __func__
+             << " key '" << it->key() << "'"
+             << " does not match"
+             << " pair key '" << key.second << "'";
+    }
+
+    if (_bl_to_str(it->value()) != expected_value) {
+      return ::testing::AssertionFailure()
+       << __func__
+       << " key '(" << key.first << "," << key.second << ")''"
+       << " expected value '" << expected_value << "'"
+       << " got value '" << _bl_to_str(it->value()) << "'";
+    }
+
+    return ::testing::AssertionSuccess();
+  }
+
+  /**
+   * Checks if each key in the queue can be forward sequentially read from
+   * the iterator iter. All keys must be present and be prefixed with prefix,
+   * otherwise the validation will fail.
+   *
+   * Assumes that each key value must be based on the key name and generated
+   * by _gen_val().
+   */
+  void validate_prefix(KeyValueDB::WholeSpaceIterator iter,
+      string &prefix, deque<string> &keys) {
+
+    while (!keys.empty()) {
+      ASSERT_TRUE(iter->valid());
+      string expected_key = keys.front();
+      keys.pop_front();
+      string expected_value = _gen_val_str(expected_key);
+
+      ASSERT_TRUE(validate_iterator(iter, prefix,
+                 expected_key, expected_value));
+
+      iter->next();
+    }
+  }
+  /**
+   * Checks if each key in the queue can be backward sequentially read from
+   * the iterator iter. All keys must be present and be prefixed with prefix,
+   * otherwise the validation will fail.
+   *
+   * Assumes that each key value must be based on the key name and generated
+   * by _gen_val().
+   */
+  void validate_prefix_backwards(KeyValueDB::WholeSpaceIterator iter,
+      string &prefix, deque<string> &keys) {
+
+    while (!keys.empty()) {
+      ASSERT_TRUE(iter->valid());
+      string expected_key = keys.front();
+      keys.pop_front();
+      string expected_value = _gen_val_str(expected_key);
+
+      ASSERT_TRUE(validate_iterator(iter, prefix,
+                 expected_key, expected_value));
+
+      iter->prev();
+    }
+  }
+
+  void clear(KeyValueDB *store) {
+    KeyValueDB::WholeSpaceIterator it = store->get_iterator();
+    it->seek_to_first();
+    KeyValueDB::Transaction t = store->get_transaction();
+    while (it->valid()) {
+      pair<string,string> k = it->raw_key();
+      t->rmkey(k.first, k.second);
+      it->next();
+    }
+    store->submit_transaction_sync(t);
+  }
+
+  string _bl_to_str(bufferlist val) {
+    string str(val.c_str(), val.length());
+    return str;
+  }
+
+  string _gen_val_str(string key) {
+    ostringstream ss;
+    ss << "##value##" << key << "##";
+    return ss.str();
+ }
+
+  bufferlist _gen_val(string key) {
+    bufferlist bl;
+    bl.append(_gen_val_str(key));
+    return bl;
+  }
+
+  void print_iterator(KeyValueDB::WholeSpaceIterator iter) {
+    if (!iter->valid()) {
+      std::cerr << __func__ << " iterator is not valid; stop." << std::endl;
+      return;
+    }
+
+    int i = 0;
+    while (iter->valid()) {
+      pair<string,string> k = iter->raw_key();
+      std::cerr << __func__
+               << " pos " << (++i)
+               << " key (" << k.first << "," << k.second << ")"
+               << " value(" << _bl_to_str(iter->value()) << ")" << std::endl;
+      iter->next();
+    }
+  }
+
+  void print_db(KeyValueDB *store) {
+    KeyValueDB::WholeSpaceIterator it = store->get_iterator();
+    it->seek_to_first();
+    print_iterator(it);
+  }
+};
+
+// ------- Remove Keys / Remove Keys By Prefix -------
+class RmKeysTest : public IteratorTest
+{
+public:
+  string prefix1;
+  string prefix2;
+  string prefix3;
+
+  void init(KeyValueDB *db) {
+    KeyValueDB::Transaction tx = db->get_transaction();
+
+    tx->set(prefix1, "11", _gen_val("11"));
+    tx->set(prefix1, "12", _gen_val("12"));
+    tx->set(prefix1, "13", _gen_val("13"));
+    tx->set(prefix2, "21", _gen_val("21"));
+    tx->set(prefix2, "22", _gen_val("22"));
+    tx->set(prefix2, "23", _gen_val("23"));
+    tx->set(prefix3, "31", _gen_val("31"));
+    tx->set(prefix3, "32", _gen_val("32"));
+    tx->set(prefix3, "33", _gen_val("33"));
+
+    db->submit_transaction_sync(tx);
+  }
+
+  void SetUp() override {
+    IteratorTest::SetUp();
+
+    prefix1 = "_PREFIX_1_";
+    prefix2 = "_PREFIX_2_";
+    prefix3 = "_PREFIX_3_";
+
+    clear(db.get());
+    ASSERT_TRUE(validate_db_clear(db.get()));
+    clear(mock.get());
+    ASSERT_TRUE(validate_db_match());
+
+    init(db.get());
+    init(mock.get());
+
+    ASSERT_TRUE(validate_db_match());
+  }
+
+  void TearDown() override {
+    IteratorTest::TearDown();
+  }
+
+
+  /**
+   * Test the transaction's rmkeys behavior when we remove a given prefix
+   * from the beginning of the key space, or from the end of the key space,
+   * or even simply in the middle.
+   */
+  void RmKeysByPrefix(KeyValueDB *store) {
+    // remove prefix2 ; check if prefix1 remains, and then prefix3
+    KeyValueDB::Transaction tx = store->get_transaction();
+    // remove the prefix in the middle of the key space
+    tx->rmkeys_by_prefix(prefix2);
+    store->submit_transaction_sync(tx);
+
+    deque<string> key_deque;
+    KeyValueDB::WholeSpaceIterator iter = store->get_iterator();
+    iter->seek_to_first();
+
+    // check for prefix1
+    key_deque.push_back("11");
+    key_deque.push_back("12");
+    key_deque.push_back("13");
+    validate_prefix(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // check for prefix3
+    ASSERT_TRUE(iter->valid());
+    key_deque.clear();
+    key_deque.push_back("31");
+    key_deque.push_back("32");
+    key_deque.push_back("33");
+    validate_prefix(iter, prefix3, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+
+    ASSERT_FALSE(iter->valid());
+
+    clear(store);
+    ASSERT_TRUE(validate_db_clear(store));
+    init(store);
+
+    // remove prefix1 ; check if prefix2 and then prefix3 remain
+    tx = store->get_transaction();
+    // remove the prefix at the beginning of the key space
+    tx->rmkeys_by_prefix(prefix1);
+    store->submit_transaction_sync(tx);
+
+    iter = store->get_iterator();
+    iter->seek_to_first();
+
+    // check for prefix2
+    key_deque.clear();
+    key_deque.push_back("21");
+    key_deque.push_back("22");
+    key_deque.push_back("23");
+    validate_prefix(iter, prefix2, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // check for prefix3
+    ASSERT_TRUE(iter->valid());
+    key_deque.clear();
+    key_deque.push_back("31");
+    key_deque.push_back("32");
+    key_deque.push_back("33");
+    validate_prefix(iter, prefix3, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+
+    ASSERT_FALSE(iter->valid());
+
+    clear(store);
+    ASSERT_TRUE(validate_db_clear(store));
+    init(store);
+
+    // remove prefix3 ; check if prefix1 and then prefix2 remain
+    tx = store->get_transaction();
+    // remove the prefix at the end of the key space
+    tx->rmkeys_by_prefix(prefix3);
+    store->submit_transaction_sync(tx);
+
+    iter = store->get_iterator();
+    iter->seek_to_first();
+
+    // check for prefix1
+    key_deque.clear();
+    key_deque.push_back("11");
+    key_deque.push_back("12");
+    key_deque.push_back("13");
+    validate_prefix(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // check for prefix2
+    ASSERT_TRUE(iter->valid());
+    key_deque.clear();
+    key_deque.push_back("21");
+    key_deque.push_back("22");
+    key_deque.push_back("23");
+    validate_prefix(iter, prefix2, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+
+    ASSERT_FALSE(iter->valid());
+  }
+
+  /**
+   * Test how the leveldb's whole-space iterator behaves when we remove
+   * keys from the store while iterating over them.
+   */
+  void RmKeysWhileIteratingSnapshot(KeyValueDB *store,
+                                   KeyValueDB::WholeSpaceIterator iter) {
+
+    SCOPED_TRACE("RmKeysWhileIteratingSnapshot");
+
+    iter->seek_to_first();
+    ASSERT_TRUE(iter->valid());
+
+    KeyValueDB::Transaction t = store->get_transaction();
+    t->rmkey(prefix1, "11");
+    t->rmkey(prefix1, "12");
+    t->rmkey(prefix2, "23");
+    t->rmkey(prefix3, "33");
+    store->submit_transaction_sync(t);
+
+    deque<string> key_deque;
+
+    // check for prefix1
+    key_deque.push_back("11");
+    key_deque.push_back("12");
+    key_deque.push_back("13");
+    validate_prefix(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // check for prefix2
+    key_deque.clear();
+    key_deque.push_back("21");
+    key_deque.push_back("22");
+    key_deque.push_back("23");
+    validate_prefix(iter, prefix2, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+
+    // check for prefix3
+    key_deque.clear();
+    key_deque.push_back("31");
+    key_deque.push_back("32");
+    key_deque.push_back("33");
+    validate_prefix(iter, prefix3, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+
+    iter->next();
+    ASSERT_FALSE(iter->valid());
+
+    // make sure those keys were removed from the store
+    KeyValueDB::WholeSpaceIterator tmp_it = store->get_iterator();
+    tmp_it->seek_to_first();
+    ASSERT_TRUE(tmp_it->valid());
+
+    key_deque.clear();
+    key_deque.push_back("13");
+    validate_prefix(tmp_it, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+
+    ASSERT_TRUE(tmp_it->valid());
+    key_deque.clear();
+    key_deque.push_back("21");
+    key_deque.push_back("22");
+    validate_prefix(tmp_it, prefix2, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+
+    ASSERT_TRUE(tmp_it->valid());
+    key_deque.clear();
+    key_deque.push_back("31");
+    key_deque.push_back("32");
+    validate_prefix(tmp_it, prefix3, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+
+    ASSERT_FALSE(tmp_it->valid());
+  }
+};
+
+TEST_F(RmKeysTest, RmKeysByPrefixLevelDB)
+{
+  SCOPED_TRACE("LevelDB");
+  RmKeysByPrefix(db.get());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(RmKeysTest, RmKeysByPrefixMockDB)
+{
+  SCOPED_TRACE("Mock DB");
+  RmKeysByPrefix(mock.get());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+/**
+ * If you refer to function RmKeysTest::RmKeysWhileIteratingSnapshot(),
+ * you will notice that we seek the iterator to the first key, and then
+ * we go on to remove several keys from the underlying store, including
+ * the first couple keys.
+ *
+ * We would expect that during this test, as soon as we removed the keys
+ * from the store, the iterator would get invalid, or cause some sort of
+ * unexpected mess.
+ *
+ * Instead, the current version of leveldb handles it perfectly, by making
+ * the iterator to use a snapshot instead of the store's real state. This
+ * way, LevelDBStore's whole-space iterator will behave much like its own
+ * whole-space snapshot iterator.
+ *
+ * However, this particular behavior of the iterator hasn't been documented
+ * on leveldb, and we should assume that it can be changed at any point in
+ * time.
+ *
+ * Therefore, we keep this test, being exactly the same as the one for the
+ * whole-space snapshot iterator, as we currently assume they should behave
+ * identically. If this test fails, at some point, and the whole-space
+ * snapshot iterator passes, then it probably means that leveldb changed
+ * how its iterator behaves.
+ */
+TEST_F(RmKeysTest, RmKeysWhileIteratingLevelDB)
+{
+  SCOPED_TRACE("LevelDB -- WholeSpaceIterator");
+  RmKeysWhileIteratingSnapshot(db.get(), db->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(RmKeysTest, RmKeysWhileIteratingMockDB)
+{
+  std::cout << "There is no safe way to test key removal while iterating\n"
+           << "over the mock store without using snapshots" << std::endl;
+}
+
+// ------- Set Keys / Update Values -------
+class SetKeysTest : public IteratorTest
+{
+public:
+  string prefix1;
+  string prefix2;
+
+  void init(KeyValueDB *db) {
+    KeyValueDB::Transaction tx = db->get_transaction();
+
+    tx->set(prefix1, "aaa", _gen_val("aaa"));
+    tx->set(prefix1, "ccc", _gen_val("ccc"));
+    tx->set(prefix1, "eee", _gen_val("eee"));
+    tx->set(prefix2, "vvv", _gen_val("vvv"));
+    tx->set(prefix2, "xxx", _gen_val("xxx"));
+    tx->set(prefix2, "zzz", _gen_val("zzz"));
+
+    db->submit_transaction_sync(tx);
+  }
+
+  void SetUp() override {
+    IteratorTest::SetUp();
+
+    prefix1 = "_PREFIX_1_";
+    prefix2 = "_PREFIX_2_";
+
+    clear(db.get());
+    ASSERT_TRUE(validate_db_clear(db.get()));
+    clear(mock.get());
+    ASSERT_TRUE(validate_db_match());
+
+    init(db.get());
+    init(mock.get());
+
+    ASSERT_TRUE(validate_db_match());
+  }
+
+  void TearDown() override {
+    IteratorTest::TearDown();
+  }
+
+  /**
+   * Make sure that the iterator picks on new keys added if it hasn't yet
+   * iterated away from that position.
+   * 
+   * This should only happen for the whole-space iterator when not using
+   * the snapshot version.
+   *
+   * We don't need to test the validity of all elements, but we do test
+   * inserting while moving from the first element to the last, using next()
+   * to move forward, and then we test the same behavior while iterating
+   * from the last element to the first, using prev() to move backwards.
+   */
+  void SetKeysWhileIterating(KeyValueDB *store,
+                            KeyValueDB::WholeSpaceIterator iter) {
+    iter->seek_to_first();
+    ASSERT_TRUE(iter->valid());
+    ASSERT_TRUE(validate_iterator(iter, prefix1, "aaa",
+               _gen_val_str("aaa")));
+    iter->next();
+    ASSERT_TRUE(iter->valid());
+    ASSERT_TRUE(validate_iterator(iter, prefix1, "ccc",
+               _bl_to_str(_gen_val("ccc"))));
+
+    // insert new key 'ddd' after 'ccc' and before 'eee'
+    KeyValueDB::Transaction tx = store->get_transaction();
+    tx->set(prefix1, "ddd", _gen_val("ddd"));
+    store->submit_transaction_sync(tx);
+
+    iter->next();
+    ASSERT_TRUE(iter->valid());
+    ASSERT_TRUE(validate_iterator(iter, prefix1, "ddd",
+               _gen_val_str("ddd")));
+
+    iter->seek_to_last();
+    ASSERT_TRUE(iter->valid());
+    tx = store->get_transaction();
+    tx->set(prefix2, "yyy", _gen_val("yyy"));
+    store->submit_transaction_sync(tx);
+
+    iter->prev();
+    ASSERT_TRUE(iter->valid());
+    ASSERT_TRUE(validate_iterator(iter, prefix2,
+                                 "yyy", _gen_val_str("yyy")));
+  }
+
+  /**
+   * Make sure that the whole-space snapshot iterator does not pick on new keys
+   * added to the store since we created the iterator, thus guaranteeing
+   * read-consistency.
+   *
+   * We don't need to test the validity of all elements, but we do test
+   * inserting while moving from the first element to the last, using next()
+   * to move forward, and then we test the same behavior while iterating
+   * from the last element to the first, using prev() to move backwards.
+   */
+  void SetKeysWhileIteratingSnapshot(KeyValueDB *store,
+                                    KeyValueDB::WholeSpaceIterator iter) {
+    iter->seek_to_first();
+    ASSERT_TRUE(iter->valid());
+    ASSERT_TRUE(validate_iterator(iter, prefix1, "aaa",
+               _gen_val_str("aaa")));
+    iter->next();
+    ASSERT_TRUE(iter->valid());
+    ASSERT_TRUE(validate_iterator(iter, prefix1, "ccc",
+               _bl_to_str(_gen_val("ccc"))));
+
+    // insert new key 'ddd' after 'ccc' and before 'eee'
+    KeyValueDB::Transaction tx = store->get_transaction();
+    tx->set(prefix1, "ddd", _gen_val("ddd"));
+    store->submit_transaction_sync(tx);
+
+    iter->next();
+    ASSERT_TRUE(iter->valid());
+    ASSERT_TRUE(validate_iterator(iter, prefix1, "eee",
+               _gen_val_str("eee")));
+
+    iter->seek_to_last();
+    ASSERT_TRUE(iter->valid());
+    tx = store->get_transaction();
+    tx->set(prefix2, "yyy", _gen_val("yyy"));
+    store->submit_transaction_sync(tx);
+
+    iter->prev();
+    ASSERT_TRUE(iter->valid());
+    ASSERT_TRUE(validate_iterator(iter, prefix2,
+                                 "xxx", _gen_val_str("xxx")));
+  }
+
+  /**
+   * Make sure that the whole-space iterator is able to read values changed on
+   * the store, even after we moved to the updated position.
+   *
+   * This should only be possible when not using the whole-space snapshot
+   * version of the iterator.
+   */
+  void UpdateValuesWhileIterating(KeyValueDB *store,
+                                 KeyValueDB::WholeSpaceIterator iter) {
+    iter->seek_to_first();
+    ASSERT_TRUE(iter->valid());
+    ASSERT_TRUE(validate_iterator(iter, prefix1,
+                                 "aaa", _gen_val_str("aaa")));
+
+    KeyValueDB::Transaction tx = store->get_transaction();
+    tx->set(prefix1, "aaa", _gen_val("aaa_1"));
+    store->submit_transaction_sync(tx);
+
+    ASSERT_TRUE(validate_iterator(iter, prefix1,
+                                 "aaa", _gen_val_str("aaa_1")));
+
+    iter->seek_to_last();
+    ASSERT_TRUE(iter->valid());
+    ASSERT_TRUE(validate_iterator(iter, prefix2,
+                                 "zzz", _gen_val_str("zzz")));
+
+    tx = store->get_transaction();
+    tx->set(prefix2, "zzz", _gen_val("zzz_1"));
+    store->submit_transaction_sync(tx);
+
+    ASSERT_TRUE(validate_iterator(iter, prefix2,
+                                 "zzz", _gen_val_str("zzz_1")));
+  }
+
+  /**
+   * Make sure that the whole-space iterator is able to read values changed on
+   * the store, even after we moved to the updated position.
+   *
+   * This should only be possible when not using the whole-space snapshot
+   * version of the iterator.
+   */
+  void UpdateValuesWhileIteratingSnapshot(
+                                 KeyValueDB *store,
+                                 KeyValueDB::WholeSpaceIterator iter) {
+    iter->seek_to_first();
+    ASSERT_TRUE(iter->valid());
+    ASSERT_TRUE(validate_iterator(iter, prefix1,
+                                 "aaa", _gen_val_str("aaa")));
+
+    KeyValueDB::Transaction tx = store->get_transaction();
+    tx->set(prefix1, "aaa", _gen_val("aaa_1"));
+    store->submit_transaction_sync(tx);
+
+    ASSERT_TRUE(validate_iterator(iter, prefix1,
+                                 "aaa", _gen_val_str("aaa")));
+
+    iter->seek_to_last();
+    ASSERT_TRUE(iter->valid());
+    ASSERT_TRUE(validate_iterator(iter, prefix2,
+                                 "zzz", _gen_val_str("zzz")));
+
+    tx = store->get_transaction();
+    tx->set(prefix2, "zzz", _gen_val("zzz_1"));
+    store->submit_transaction_sync(tx);
+
+    ASSERT_TRUE(validate_iterator(iter, prefix2,
+                                 "zzz", _gen_val_str("zzz")));
+
+    // check those values were really changed in the store
+    KeyValueDB::WholeSpaceIterator tmp_iter = store->get_iterator();
+    tmp_iter->seek_to_first();
+    ASSERT_TRUE(tmp_iter->valid());
+    ASSERT_TRUE(validate_iterator(tmp_iter, prefix1,
+                                 "aaa", _gen_val_str("aaa_1")));
+    tmp_iter->seek_to_last();
+    ASSERT_TRUE(tmp_iter->valid());
+    ASSERT_TRUE(validate_iterator(tmp_iter, prefix2,
+                                 "zzz", _gen_val_str("zzz_1")));
+  }
+
+
+};
+
+TEST_F(SetKeysTest, DISABLED_SetKeysWhileIteratingLevelDB)
+{
+  SCOPED_TRACE("LevelDB: SetKeysWhileIteratingLevelDB");
+  SetKeysWhileIterating(db.get(), db->get_iterator());
+  ASSERT_TRUE(HasFatalFailure());
+}
+
+TEST_F(SetKeysTest, SetKeysWhileIteratingMockDB)
+{
+  SCOPED_TRACE("Mock DB: SetKeysWhileIteratingMockDB");
+  SetKeysWhileIterating(mock.get(), mock->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(SetKeysTest, DISABLED_UpdateValuesWhileIteratingLevelDB)
+{
+  SCOPED_TRACE("LevelDB: UpdateValuesWhileIteratingLevelDB");
+  UpdateValuesWhileIterating(db.get(), db->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(SetKeysTest, UpdateValuesWhileIteratingMockDB)
+{
+  SCOPED_TRACE("MockDB: UpdateValuesWhileIteratingMockDB");
+  UpdateValuesWhileIterating(mock.get(), mock->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+class BoundsTest : public IteratorTest
+{
+public:
+  string prefix1;
+  string prefix2;
+  string prefix3;
+
+  void init(KeyValueDB *store) {
+    KeyValueDB::Transaction tx = store->get_transaction();
+
+    tx->set(prefix1, "aaa", _gen_val("aaa"));
+    tx->set(prefix1, "ccc", _gen_val("ccc"));
+    tx->set(prefix1, "eee", _gen_val("eee"));
+    tx->set(prefix2, "vvv", _gen_val("vvv"));
+    tx->set(prefix2, "xxx", _gen_val("xxx"));
+    tx->set(prefix2, "zzz", _gen_val("zzz"));
+    tx->set(prefix3, "aaa", _gen_val("aaa"));
+    tx->set(prefix3, "mmm", _gen_val("mmm"));
+    tx->set(prefix3, "yyy", _gen_val("yyy"));
+
+    store->submit_transaction_sync(tx);
+  }
+
+  void SetUp() override {
+    IteratorTest::SetUp();
+
+    prefix1 = "_PREFIX_1_";
+    prefix2 = "_PREFIX_2_";
+    prefix3 = "_PREFIX_4_";
+
+    clear(db.get());
+    ASSERT_TRUE(validate_db_clear(db.get()));
+    clear(mock.get());
+    ASSERT_TRUE(validate_db_match());
+
+    init(db.get());
+    init(mock.get());
+
+    ASSERT_TRUE(validate_db_match());
+  }
+
+  void TearDown() override {
+    IteratorTest::TearDown();
+  }
+
+  void LowerBoundWithEmptyKeyOnWholeSpaceIterator(
+                           KeyValueDB::WholeSpaceIterator iter) {
+    deque<string> key_deque;
+    // see what happens when we have an empty key and try to get to the
+    // first available prefix
+    iter->lower_bound(prefix1, "");
+    ASSERT_TRUE(iter->valid());
+
+    key_deque.push_back("aaa");
+    key_deque.push_back("ccc");
+    key_deque.push_back("eee");
+    validate_prefix(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+    // if we got here without problems, then it is safe to assume the
+    // remaining prefixes are intact.
+
+    // see what happens when we have an empty key and try to get to the
+    // middle of the key-space
+    iter->lower_bound(prefix2, "");
+    ASSERT_TRUE(iter->valid());
+    key_deque.clear();
+
+    key_deque.push_back("vvv");
+    key_deque.push_back("xxx");
+    key_deque.push_back("zzz");
+    validate_prefix(iter, prefix2, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+    // if we got here without problems, then it is safe to assume the
+    // remaining prefixes are intact.
+
+    // see what happens when we have an empty key and try to get to the
+    // last prefix on the key-space
+    iter->lower_bound(prefix3, "");
+    ASSERT_TRUE(iter->valid());
+    key_deque.clear();
+
+    key_deque.push_back("aaa");
+    key_deque.push_back("mmm");
+    key_deque.push_back("yyy");
+    validate_prefix(iter, prefix3, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_FALSE(iter->valid());
+    // we reached the end of the key_space, so the iterator should no longer
+    // be valid
+    
+    // see what happens when we look for an inexistent prefix, that will
+    // compare higher than the existing prefixes, with an empty key
+    // expected: reach the store's end; iterator becomes invalid
+    iter->lower_bound("_PREFIX_9_", "");
+    ASSERT_FALSE(iter->valid());
+
+    // see what happens when we look for an inexistent prefix, that will
+    // compare lower than the existing prefixes, with an empty key
+    // expected: find the first prefix; iterator is valid
+    iter->lower_bound("_PREFIX_0_", "");
+    ASSERT_TRUE(iter->valid());
+    key_deque.clear();
+    key_deque.push_back("aaa");
+    key_deque.push_back("ccc");
+    key_deque.push_back("eee");
+    validate_prefix(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+
+    // see what happens when we look for an empty prefix (that should compare
+    // lower than any existing prefixes)
+    // expected: find the first prefix; iterator is valid
+    iter->lower_bound("", "");
+    ASSERT_TRUE(iter->valid());
+    key_deque.push_back("aaa");
+    key_deque.push_back("ccc");
+    key_deque.push_back("eee");
+    validate_prefix(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+  }
+
+  void LowerBoundWithEmptyPrefixOnWholeSpaceIterator(
+                         KeyValueDB::WholeSpaceIterator iter) {
+    deque<string> key_deque;
+    // check for an empty prefix, with key 'aaa'. Since this key is shared
+    // among two different prefixes, it is relevant to check which will be
+    // found first.
+    // expected: find key (prefix1, aaa); iterator is valid
+    iter->lower_bound("", "aaa");
+    ASSERT_TRUE(iter->valid());
+
+    key_deque.push_back("aaa");
+    key_deque.push_back("ccc");
+    key_deque.push_back("eee");
+    validate_prefix(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+    // since we found prefix1, it is safe to assume that the remaining
+    // prefixes (prefix2 and prefix3) will follow
+
+    // any lower_bound operation with an empty prefix should always put the
+    // iterator in the first key in the key-space, despite what key is
+    // specified. This means that looking for ("","AAAAAAAAAA") should
+    // also position the iterator on (prefix1, aaa).
+    // expected: find key (prefix1, aaa); iterator is valid
+    iter->lower_bound("", "AAAAAAAAAA");
+    ASSERT_TRUE(iter->valid());
+    key_deque.clear();
+    key_deque.push_back("aaa");
+    key_deque.push_back("ccc");
+    key_deque.push_back("eee");
+    validate_prefix(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+
+    // note: this test is a duplicate of the one in the function above. Why?
+    // Well, because it also fits here (being its prefix empty), and one could
+    // very well run solely this test (instead of the whole battery) and would
+    // certainly expect this case to be tested.
+
+    // see what happens when we look for an empty prefix (that should compare
+    // lower than any existing prefixes)
+    // expected: find the first prefix; iterator is valid
+    iter->lower_bound("", "");
+    ASSERT_TRUE(iter->valid());
+    key_deque.push_back("aaa");
+    key_deque.push_back("ccc");
+    key_deque.push_back("eee");
+    validate_prefix(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+  }
+
+  void LowerBoundOnWholeSpaceIterator(
+                         KeyValueDB::WholeSpaceIterator iter) {
+    deque<string> key_deque;
+    // check that we find the first key in the store
+    // expected: find (prefix1, aaa); iterator is valid
+    iter->lower_bound(prefix1, "aaa");
+    ASSERT_TRUE(iter->valid());
+    key_deque.push_back("aaa");
+    validate_prefix(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+
+    // check that we find the last key in the store
+    // expected: find (prefix3, yyy); iterator is valid
+    iter->lower_bound(prefix3, "yyy");
+    ASSERT_TRUE(iter->valid());
+    key_deque.clear();
+    key_deque.push_back("yyy");
+    validate_prefix(iter, prefix3, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_FALSE(iter->valid());
+
+    // check that looking for non-existent prefix '_PREFIX_0_' will
+    // always result in the first value of prefix1 (prefix1,"aaa")
+    // expected: find (prefix1, aaa); iterator is valid
+    iter->lower_bound("_PREFIX_0_", "AAAAA");
+    ASSERT_TRUE(iter->valid());
+    key_deque.clear();
+    key_deque.push_back("aaa");
+    validate_prefix(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+
+    // check that looking for non-existent prefix '_PREFIX_3_' will
+    // always result in the first value of prefix3 (prefix4,"aaa")
+    // expected: find (prefix3, aaa); iterator is valid
+    iter->lower_bound("_PREFIX_3_", "AAAAA");
+    ASSERT_TRUE(iter->valid());
+    key_deque.clear();
+    key_deque.push_back("aaa");
+    validate_prefix(iter, prefix3, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+
+    // check that looking for non-existent prefix '_PREFIX_9_' will
+    // always result in an invalid iterator.
+    // expected: iterator is invalid
+    iter->lower_bound("_PREFIX_9_", "AAAAA");
+    ASSERT_FALSE(iter->valid());
+  }
+
+  void UpperBoundWithEmptyKeyOnWholeSpaceIterator(
+                         KeyValueDB::WholeSpaceIterator iter) {
+    deque<string> key_deque;
+    // check that looking for (prefix1, "") will result in finding
+    // the first key in prefix1 (prefix1, "aaa")
+    // expected: find (prefix1, aaa); iterator is valid
+    iter->upper_bound(prefix1, "");
+    key_deque.push_back("aaa");
+    validate_prefix(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+
+    // check that looking for (prefix2, "") will result in finding
+    // the first key in prefix2 (prefix2, vvv)
+    // expected: find (prefix2, aaa); iterator is valid
+    iter->upper_bound(prefix2, "");
+    key_deque.push_back("vvv");
+    validate_prefix(iter, prefix2, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+
+
+    // check that looking for (prefix3, "") will result in finding
+    // the first key in prefix3 (prefix3, aaa)
+    // expected: find (prefix3, aaa); iterator is valid
+    iter->upper_bound(prefix3, "");
+    key_deque.push_back("aaa");
+    validate_prefix(iter, prefix3, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+
+    // see what happens when we look for an inexistent prefix, that will
+    // compare higher than the existing prefixes, with an empty key
+    // expected: reach the store's end; iterator becomes invalid
+    iter->upper_bound("_PREFIX_9_", "");
+    ASSERT_FALSE(iter->valid());
+
+    // see what happens when we look for an inexistent prefix, that will
+    // compare lower than the existing prefixes, with an empty key
+    // expected: find the first prefix; iterator is valid
+    iter->upper_bound("_PREFIX_0_", "");
+    ASSERT_TRUE(iter->valid());
+    key_deque.clear();
+    key_deque.push_back("aaa");
+    validate_prefix(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+
+    // see what happens when we look for an empty prefix (that should compare
+    // lower than any existing prefixes)
+    // expected: find the first prefix; iterator is valid
+    iter->upper_bound("", "");
+    ASSERT_TRUE(iter->valid());
+    key_deque.push_back("aaa");
+    validate_prefix(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+  }
+
+  void UpperBoundWithEmptyPrefixOnWholeSpaceIterator(
+                         KeyValueDB::WholeSpaceIterator iter) {
+    deque<string> key_deque;
+    // check for an empty prefix, with key 'aaa'. Since this key is shared
+    // among two different prefixes, it is relevant to check which will be
+    // found first.
+    // expected: find key (prefix1, aaa); iterator is valid
+    iter->upper_bound("", "aaa");
+    ASSERT_TRUE(iter->valid());
+    key_deque.push_back("aaa");
+    key_deque.push_back("ccc");
+    key_deque.push_back("eee");
+    validate_prefix(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+
+    // any upper_bound operation with an empty prefix should always put the
+    // iterator in the first key whose prefix compares greater, despite the
+    // key that is specified. This means that looking for ("","AAAAAAAAAA")
+    // should position the iterator on (prefix1, aaa).
+    // expected: find key (prefix1, aaa); iterator is valid
+    iter->upper_bound("", "AAAAAAAAAA");
+    ASSERT_TRUE(iter->valid());
+    key_deque.clear();
+    key_deque.push_back("aaa");
+    validate_prefix(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+
+    // note: this test is a duplicate of the one in the function above. Why?
+    // Well, because it also fits here (being its prefix empty), and one could
+    // very well run solely this test (instead of the whole battery) and would
+    // certainly expect this case to be tested.
+
+    // see what happens when we look for an empty prefix (that should compare
+    // lower than any existing prefixes)
+    // expected: find the first prefix; iterator is valid
+    iter->upper_bound("", "");
+    ASSERT_TRUE(iter->valid());
+    key_deque.push_back("aaa");
+    validate_prefix(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+  }
+
+  void UpperBoundOnWholeSpaceIterator(
+                         KeyValueDB::WholeSpaceIterator iter) {
+    deque<string> key_deque;
+    // check that we find the second key in the store
+    // expected: find (prefix1, ccc); iterator is valid
+    iter->upper_bound(prefix1, "bbb");
+    ASSERT_TRUE(iter->valid());
+    key_deque.push_back("ccc");
+    validate_prefix(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+
+    // check that we find the last key in the store
+    // expected: find (prefix3, yyy); iterator is valid
+    iter->upper_bound(prefix3, "xxx");
+    ASSERT_TRUE(iter->valid());
+    key_deque.clear();
+    key_deque.push_back("yyy");
+    validate_prefix(iter, prefix3, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_FALSE(iter->valid());
+
+    // check that looking for non-existent prefix '_PREFIX_0_' will
+    // always result in the first value of prefix1 (prefix1,"aaa")
+    // expected: find (prefix1, aaa); iterator is valid
+    iter->upper_bound("_PREFIX_0_", "AAAAA");
+    ASSERT_TRUE(iter->valid());
+    key_deque.clear();
+    key_deque.push_back("aaa");
+    validate_prefix(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+
+    // check that looking for non-existent prefix '_PREFIX_3_' will
+    // always result in the first value of prefix3 (prefix3,"aaa")
+    // expected: find (prefix3, aaa); iterator is valid
+    iter->upper_bound("_PREFIX_3_", "AAAAA");
+    ASSERT_TRUE(iter->valid());
+    key_deque.clear();
+    key_deque.push_back("aaa");
+    validate_prefix(iter, prefix3, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+
+    // check that looking for non-existent prefix '_PREFIX_9_' will
+    // always result in an invalid iterator.
+    // expected: iterator is invalid
+    iter->upper_bound("_PREFIX_9_", "AAAAA");
+    ASSERT_FALSE(iter->valid());
+  }
+};
+
+TEST_F(BoundsTest, LowerBoundWithEmptyKeyOnWholeSpaceIteratorLevelDB)
+{
+  SCOPED_TRACE("LevelDB: Lower Bound, Empty Key, Whole-Space Iterator");
+  LowerBoundWithEmptyKeyOnWholeSpaceIterator(db->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(BoundsTest, LowerBoundWithEmptyKeyOnWholeSpaceIteratorMockDB)
+{
+  SCOPED_TRACE("MockDB: Lower Bound, Empty Key, Whole-Space Iterator");
+  LowerBoundWithEmptyKeyOnWholeSpaceIterator(mock->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(BoundsTest, LowerBoundWithEmptyPrefixOnWholeSpaceIteratorLevelDB)
+{
+  SCOPED_TRACE("LevelDB: Lower Bound, Empty Prefix, Whole-Space Iterator");
+  LowerBoundWithEmptyPrefixOnWholeSpaceIterator(db->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(BoundsTest, LowerBoundWithEmptyPrefixOnWholeSpaceIteratorMockDB)
+{
+  SCOPED_TRACE("MockDB: Lower Bound, Empty Prefix, Whole-Space Iterator");
+  LowerBoundWithEmptyPrefixOnWholeSpaceIterator(mock->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(BoundsTest, LowerBoundOnWholeSpaceIteratorLevelDB)
+{
+  SCOPED_TRACE("LevelDB: Lower Bound, Whole-Space Iterator");
+  LowerBoundOnWholeSpaceIterator(db->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(BoundsTest, LowerBoundOnWholeSpaceIteratorMockDB)
+{
+  SCOPED_TRACE("MockDB: Lower Bound, Whole-Space Iterator");
+  LowerBoundOnWholeSpaceIterator(mock->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(BoundsTest, UpperBoundWithEmptyKeyOnWholeSpaceIteratorLevelDB)
+{
+  SCOPED_TRACE("LevelDB: Upper Bound, Empty Key, Whole-Space Iterator");
+  UpperBoundWithEmptyKeyOnWholeSpaceIterator(db->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(BoundsTest, UpperBoundWithEmptyKeyOnWholeSpaceIteratorMockDB)
+{
+  SCOPED_TRACE("MockDB: Upper Bound, Empty Key, Whole-Space Iterator");
+  UpperBoundWithEmptyKeyOnWholeSpaceIterator(mock->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(BoundsTest, UpperBoundWithEmptyPrefixOnWholeSpaceIteratorLevelDB)
+{
+  SCOPED_TRACE("LevelDB: Upper Bound, Empty Prefix, Whole-Space Iterator");
+  UpperBoundWithEmptyPrefixOnWholeSpaceIterator(db->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(BoundsTest, UpperBoundWithEmptyPrefixOnWholeSpaceIteratorMockDB)
+{
+  SCOPED_TRACE("MockDB: Upper Bound, Empty Prefix, Whole-Space Iterator");
+  UpperBoundWithEmptyPrefixOnWholeSpaceIterator(mock->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(BoundsTest, UpperBoundOnWholeSpaceIteratorLevelDB)
+{
+  SCOPED_TRACE("LevelDB: Upper Bound, Whole-Space Iterator");
+  UpperBoundOnWholeSpaceIterator(db->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(BoundsTest, UpperBoundOnWholeSpaceIteratorMockDB)
+{
+  SCOPED_TRACE("MockDB: Upper Bound, Whole-Space Iterator");
+  UpperBoundOnWholeSpaceIterator(mock->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+
+class SeeksTest : public IteratorTest
+{
+public:
+  string prefix0;
+  string prefix1;
+  string prefix2;
+  string prefix3;
+  string prefix4;
+  string prefix5;
+
+  void init(KeyValueDB *store) {
+    KeyValueDB::Transaction tx = store->get_transaction();
+
+    tx->set(prefix1, "aaa", _gen_val("aaa"));
+    tx->set(prefix1, "ccc", _gen_val("ccc"));
+    tx->set(prefix1, "eee", _gen_val("eee"));
+    tx->set(prefix2, "vvv", _gen_val("vvv"));
+    tx->set(prefix2, "xxx", _gen_val("xxx"));
+    tx->set(prefix2, "zzz", _gen_val("zzz"));
+    tx->set(prefix4, "aaa", _gen_val("aaa"));
+    tx->set(prefix4, "mmm", _gen_val("mmm"));
+    tx->set(prefix4, "yyy", _gen_val("yyy"));
+
+    store->submit_transaction_sync(tx);
+  }
+
+  void SetUp() override {
+    IteratorTest::SetUp();
+
+    prefix0 = "_PREFIX_0_";
+    prefix1 = "_PREFIX_1_";
+    prefix2 = "_PREFIX_2_";
+    prefix3 = "_PREFIX_3_";
+    prefix4 = "_PREFIX_4_";
+    prefix5 = "_PREFIX_5_";
+
+    clear(db.get());
+    ASSERT_TRUE(validate_db_clear(db.get()));
+    clear(mock.get());
+    ASSERT_TRUE(validate_db_match());
+
+    init(db.get());
+    init(mock.get());
+
+    ASSERT_TRUE(validate_db_match());
+  }
+
+  void TearDown() override {
+    IteratorTest::TearDown();
+  }
+
+
+  void SeekToFirstOnWholeSpaceIterator(
+                         KeyValueDB::WholeSpaceIterator iter) {
+    iter->seek_to_first();
+    ASSERT_TRUE(iter->valid());
+    deque<string> key_deque;
+    key_deque.push_back("aaa");
+    key_deque.push_back("ccc");
+    key_deque.push_back("eee");
+    validate_prefix(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+  }
+
+  void SeekToFirstWithPrefixOnWholeSpaceIterator(
+                         KeyValueDB::WholeSpaceIterator iter) {
+    deque<string> key_deque;
+
+    // if the prefix is empty, we must end up seeking to the first key.
+    // expected: seek to (prefix1, aaa); iterator is valid
+    iter->seek_to_first("");
+    ASSERT_TRUE(iter->valid());
+    key_deque.push_back("aaa");
+    validate_prefix(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+
+    // try seeking to non-existent prefix that compares lower than the
+    // first available prefix
+    // expected: seek to (prefix1, aaa); iterator is valid
+    iter->seek_to_first(prefix0);
+    ASSERT_TRUE(iter->valid());
+    key_deque.clear();
+    key_deque.push_back("aaa");
+    validate_prefix(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+
+    // try seeking to non-existent prefix
+    // expected: seek to (prefix4, aaa); iterator is valid
+    iter->seek_to_first(prefix3);
+    ASSERT_TRUE(iter->valid());
+    key_deque.clear();
+    key_deque.push_back("aaa");
+    validate_prefix(iter, prefix4, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+
+    // try seeking to non-existent prefix that compares greater than the
+    // last available prefix
+    // expected: iterator is invalid
+    iter->seek_to_first(prefix5);
+    ASSERT_FALSE(iter->valid());
+
+    // try seeking to the first prefix and make sure we end up in its first
+    // position
+    // expected: seek to (prefix1,aaa); iterator is valid
+    iter->seek_to_first(prefix1);
+    ASSERT_TRUE(iter->valid());
+    key_deque.clear();
+    key_deque.push_back("aaa");
+    validate_prefix(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+
+    // try seeking to the second prefix and make sure we end up in its
+    // first position
+    // expected: seek to (prefix2,vvv); iterator is valid
+    iter->seek_to_first(prefix2);
+    ASSERT_TRUE(iter->valid());
+    key_deque.clear();
+    key_deque.push_back("vvv");
+    validate_prefix(iter, prefix2, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+
+    // try seeking to the last prefix and make sure we end up in its
+    // first position
+    // expected: seek to (prefix4,aaa); iterator is valid
+    iter->seek_to_first(prefix4);
+    ASSERT_TRUE(iter->valid());
+    key_deque.clear();
+    key_deque.push_back("aaa");
+    validate_prefix(iter, prefix4, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+  }
+
+  void SeekToLastOnWholeSpaceIterator(
+                         KeyValueDB::WholeSpaceIterator iter) {
+    deque<string> key_deque;
+    iter->seek_to_last();
+    key_deque.push_back("yyy");
+    validate_prefix(iter, prefix4, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_FALSE(iter->valid());
+  }
+
+  void SeekToLastWithPrefixOnWholeSpaceIterator(
+                         KeyValueDB::WholeSpaceIterator iter) {
+    deque<string> key_deque;
+
+    // if the prefix is empty, we must end up seeking to last position
+    // that has an empty prefix, or to the previous position to the first
+    // position whose prefix compares higher than empty.
+    // expected: iterator is invalid (because (prefix1,aaa) is the first
+    //          position that compared higher than an empty prefix)
+    iter->seek_to_last("");
+    ASSERT_FALSE(iter->valid());
+
+    // try seeking to non-existent prefix that compares lower than the
+    // first available prefix
+    // expected: iterator is invalid (because (prefix1,aaa) is the first
+    //          position that compared higher than prefix0)
+    iter->seek_to_last(prefix0);
+    ASSERT_FALSE(iter->valid());
+
+    // try seeking to non-existent prefix
+    // expected: seek to (prefix2, zzz); iterator is valid
+    iter->seek_to_last(prefix3);
+    ASSERT_TRUE(iter->valid());
+    key_deque.clear();
+    key_deque.push_back("zzz");
+    validate_prefix(iter, prefix2, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+
+    // try seeking to non-existent prefix that compares greater than the
+    // last available prefix
+    // expected: iterator is in the last position of the store;
+    //          i.e., (prefix4,yyy)
+    iter->seek_to_last(prefix5);
+    ASSERT_TRUE(iter->valid());
+    key_deque.clear();
+    key_deque.push_back("yyy");
+    validate_prefix(iter, prefix4, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_FALSE(iter->valid());
+
+    // try seeking to the first prefix and make sure we end up in its last
+    // position
+    // expected: seek to (prefix1,eee); iterator is valid
+    iter->seek_to_last(prefix1);
+    ASSERT_TRUE(iter->valid());
+    key_deque.clear();
+    key_deque.push_back("eee");
+    validate_prefix(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+
+    // try seeking to the second prefix and make sure we end up in its
+    // last position
+    // expected: seek to (prefix2,vvv); iterator is valid
+    iter->seek_to_last(prefix2);
+    ASSERT_TRUE(iter->valid());
+    key_deque.clear();
+    key_deque.push_back("zzz");
+    validate_prefix(iter, prefix2, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_TRUE(iter->valid());
+
+    // try seeking to the last prefix and make sure we end up in its
+    // last position
+    // expected: seek to (prefix4,aaa); iterator is valid
+    iter->seek_to_last(prefix4);
+    ASSERT_TRUE(iter->valid());
+    key_deque.clear();
+    key_deque.push_back("yyy");
+    validate_prefix(iter, prefix4, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_FALSE(iter->valid());
+  }
+};
+
+TEST_F(SeeksTest, SeekToFirstOnWholeSpaceIteratorLevelDB) {
+  SCOPED_TRACE("LevelDB: Seek To First, Whole Space Iterator");
+  SeekToFirstOnWholeSpaceIterator(db->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(SeeksTest, SeekToFirstOnWholeSpaceIteratorMockDB) {
+  SCOPED_TRACE("MockDB: Seek To First, Whole Space Iterator");
+  SeekToFirstOnWholeSpaceIterator(mock->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(SeeksTest, SeekToFirstWithPrefixOnWholeSpaceIteratorLevelDB) {
+  SCOPED_TRACE("LevelDB: Seek To First, With Prefix, Whole Space Iterator");
+  SeekToFirstWithPrefixOnWholeSpaceIterator(db->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(SeeksTest, SeekToFirstWithPrefixOnWholeSpaceIteratorMockDB) {
+  SCOPED_TRACE("MockDB: Seek To First, With Prefix, Whole Space Iterator");
+  SeekToFirstWithPrefixOnWholeSpaceIterator(mock->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(SeeksTest, SeekToLastOnWholeSpaceIteratorLevelDB) {
+  SCOPED_TRACE("LevelDB: Seek To Last, Whole Space Iterator");
+  SeekToLastOnWholeSpaceIterator(db->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(SeeksTest, SeekToLastOnWholeSpaceIteratorMockDB) {
+  SCOPED_TRACE("MockDB: Seek To Last, Whole Space Iterator");
+  SeekToLastOnWholeSpaceIterator(mock->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(SeeksTest, SeekToLastWithPrefixOnWholeSpaceIteratorLevelDB) {
+  SCOPED_TRACE("LevelDB: Seek To Last, With Prefix, Whole Space Iterator");
+  SeekToLastWithPrefixOnWholeSpaceIterator(db->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(SeeksTest, SeekToLastWithPrefixOnWholeSpaceIteratorMockDB) {
+  SCOPED_TRACE("MockDB: Seek To Last, With Prefix, Whole Space Iterator");
+  SeekToLastWithPrefixOnWholeSpaceIterator(mock->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+class KeySpaceIteration : public IteratorTest
+{
+public:
+  string prefix1;
+
+  void init(KeyValueDB *store) {
+    KeyValueDB::Transaction tx = store->get_transaction();
+
+    tx->set(prefix1, "aaa", _gen_val("aaa"));
+    tx->set(prefix1, "vvv", _gen_val("vvv"));
+    tx->set(prefix1, "zzz", _gen_val("zzz"));
+
+    store->submit_transaction_sync(tx);
+  }
+
+  void SetUp() override {
+    IteratorTest::SetUp();
+
+    prefix1 = "_PREFIX_1_";
+
+    clear(db.get());
+    ASSERT_TRUE(validate_db_clear(db.get()));
+    clear(mock.get());
+    ASSERT_TRUE(validate_db_match());
+
+    init(db.get());
+    init(mock.get());
+
+    ASSERT_TRUE(validate_db_match());
+  }
+
+  void TearDown() override {
+    IteratorTest::TearDown();
+  }
+
+  void ForwardIteration(KeyValueDB::WholeSpaceIterator iter) {
+    deque<string> key_deque;
+    iter->seek_to_first();
+    key_deque.push_back("aaa");
+    key_deque.push_back("vvv");
+    key_deque.push_back("zzz");
+    validate_prefix(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_FALSE(iter->valid());
+  }
+
+  void BackwardIteration(KeyValueDB::WholeSpaceIterator iter) {
+    deque<string> key_deque;
+    iter->seek_to_last();
+    key_deque.push_back("zzz");
+    key_deque.push_back("vvv");
+    key_deque.push_back("aaa");
+    validate_prefix_backwards(iter, prefix1, key_deque);
+    ASSERT_FALSE(HasFatalFailure());
+    ASSERT_FALSE(iter->valid());
+  }
+};
+
+TEST_F(KeySpaceIteration, ForwardIterationLevelDB)
+{
+  SCOPED_TRACE("LevelDB: Forward Iteration, Whole Space Iterator");
+  ForwardIteration(db->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(KeySpaceIteration, ForwardIterationMockDB) {
+  SCOPED_TRACE("MockDB: Forward Iteration, Whole Space Iterator");
+  ForwardIteration(mock->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(KeySpaceIteration, BackwardIterationLevelDB)
+{
+  SCOPED_TRACE("LevelDB: Backward Iteration, Whole Space Iterator");
+  BackwardIteration(db->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(KeySpaceIteration, BackwardIterationMockDB) {
+  SCOPED_TRACE("MockDB: Backward Iteration, Whole Space Iterator");
+  BackwardIteration(mock->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+class EmptyStore : public IteratorTest
+{
+public:
+  void SetUp() override {
+    IteratorTest::SetUp();
+
+    clear(db.get());
+    ASSERT_TRUE(validate_db_clear(db.get()));
+    clear(mock.get());
+    ASSERT_TRUE(validate_db_match());
+  }
+
+  void SeekToFirst(KeyValueDB::WholeSpaceIterator iter) {
+    // expected: iterator is invalid
+    iter->seek_to_first();
+    ASSERT_FALSE(iter->valid());
+  }
+
+  void SeekToFirstWithPrefix(KeyValueDB::WholeSpaceIterator iter) {
+    // expected: iterator is invalid
+    iter->seek_to_first("prefix");
+    ASSERT_FALSE(iter->valid());
+  }
+
+  void SeekToLast(KeyValueDB::WholeSpaceIterator iter) {
+    // expected: iterator is invalid
+    iter->seek_to_last();
+    ASSERT_FALSE(iter->valid());
+  }
+
+  void SeekToLastWithPrefix(KeyValueDB::WholeSpaceIterator iter) {
+    // expected: iterator is invalid
+    iter->seek_to_last("prefix");
+    ASSERT_FALSE(iter->valid());
+  }
+
+  void LowerBound(KeyValueDB::WholeSpaceIterator iter) {
+    // expected: iterator is invalid
+    iter->lower_bound("prefix", "");
+    ASSERT_FALSE(iter->valid());
+
+    // expected: iterator is invalid
+    iter->lower_bound("", "key");
+    ASSERT_FALSE(iter->valid());
+
+    // expected: iterator is invalid
+    iter->lower_bound("prefix", "key");
+    ASSERT_FALSE(iter->valid());
+  }
+
+  void UpperBound(KeyValueDB::WholeSpaceIterator iter) {
+    // expected: iterator is invalid
+    iter->upper_bound("prefix", "");
+    ASSERT_FALSE(iter->valid());
+
+    // expected: iterator is invalid
+    iter->upper_bound("", "key");
+    ASSERT_FALSE(iter->valid());
+
+    // expected: iterator is invalid
+    iter->upper_bound("prefix", "key");
+    ASSERT_FALSE(iter->valid());
+  }
+};
+
+TEST_F(EmptyStore, SeekToFirstLevelDB)
+{
+  SCOPED_TRACE("LevelDB: Empty Store, Seek To First");
+  SeekToFirst(db->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(EmptyStore, SeekToFirstMockDB)
+{
+  SCOPED_TRACE("MockDB: Empty Store, Seek To First");
+  SeekToFirst(mock->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(EmptyStore, SeekToFirstWithPrefixLevelDB)
+{
+  SCOPED_TRACE("LevelDB: Empty Store, Seek To First With Prefix");
+  SeekToFirstWithPrefix(db->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(EmptyStore, SeekToFirstWithPrefixMockDB)
+{
+  SCOPED_TRACE("MockDB: Empty Store, Seek To First With Prefix");
+  SeekToFirstWithPrefix(mock->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(EmptyStore, SeekToLastLevelDB)
+{
+  SCOPED_TRACE("LevelDB: Empty Store, Seek To Last");
+  SeekToLast(db->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(EmptyStore, SeekToLastMockDB)
+{
+  SCOPED_TRACE("MockDB: Empty Store, Seek To Last");
+  SeekToLast(mock->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(EmptyStore, SeekToLastWithPrefixLevelDB)
+{
+  SCOPED_TRACE("LevelDB: Empty Store, Seek To Last With Prefix");
+  SeekToLastWithPrefix(db->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(EmptyStore, SeekToLastWithPrefixMockDB)
+{
+  SCOPED_TRACE("MockDB: Empty Store, Seek To Last With Prefix");
+  SeekToLastWithPrefix(mock->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(EmptyStore, LowerBoundLevelDB)
+{
+  SCOPED_TRACE("LevelDB: Empty Store, Lower Bound");
+  LowerBound(db->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(EmptyStore, LowerBoundMockDB)
+{
+  SCOPED_TRACE("MockDB: Empty Store, Lower Bound");
+  LowerBound(mock->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(EmptyStore, UpperBoundLevelDB)
+{
+  SCOPED_TRACE("LevelDB: Empty Store, Upper Bound");
+  UpperBound(db->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+TEST_F(EmptyStore, UpperBoundMockDB)
+{
+  SCOPED_TRACE("MockDB: Empty Store, Upper Bound");
+  UpperBound(mock->get_iterator());
+  ASSERT_FALSE(HasFatalFailure());
+}
+
+
+int main(int argc, char *argv[])
+{
+  vector<const char*> args;
+  argv_to_vec(argc, (const char **) argv, args);
+
+  auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_UTILITY, 0);
+  common_init_finish(g_ceph_context);
+  ::testing::InitGoogleTest(&argc, argv);
+
+  if (argc < 2) {
+    std::cerr << "Usage: " << argv[0]
+             << "[ceph_options] [gtest_options] <store_path>" << std::endl;
+    return 1;
+  }
+  store_path = string(argv[1]);
+
+  return RUN_ALL_TESTS();
+}