Rocksdb: wrong iterator after DeleteRange when using custom comparator

Created on 15 Mar 2018  路  3Comments  路  Source: facebook/rocksdb

when using custom comparator (https://github.com/facebook/rocksdb/wiki/Basic-Operations#user-content-comparators) ,
iterator->SeekToFirst() is wrong after DeleteRange.

class MyComparator : public rocksdb::Comparator {
 public:
  // Three-way comparison function:
  // if a < b: negative result
  // if a > b: positive result
  // else: zero result
  int Compare(const rocksdb::Slice& a, const rocksdb::Slice& b) const override {
    int a_int = 0, b_int = 0;
    std::stringstream s;
    s << a.data();
    s >> a_int;
    s.str("");
    s << b.data();
    s >> b_int;
    s.clear();

    if (a_int < b_int) return -1;
    if (a_int > b_int) return 1;
    return 0;
  }

  // Ignore the following methods for now:
  const char* Name() const override { return "MyComparator"; }
  void FindShortestSeparator(std::string*, const rocksdb::Slice&) const override { }
  void FindShortSuccessor(std::string*) const override { }
};

int main() {
  MyComparator cmp;
  rocksdb::DB* db;
  rocksdb::Options options;
  options.create_if_missing = true;
  options.comparator = &cmp;
  rocksdb::Status status = rocksdb::DB::Open(options, "/tmp/testdb", &db);

  rocksdb::WriteOptions write_options;
  db->Put(write_options, "1", "v1");
  db->Put(write_options, "10", "v10");
  db->Put(write_options, "2", "v2");
  db->Put(write_options, "3", "v3");

  std::cout << "before delete:" << std::endl;
  rocksdb::Iterator* it = db->NewIterator(rocksdb::ReadOptions());
  for (it->SeekToFirst(); it->Valid(); it->Next()) {
    std::cout << it->key().ToString() << ": " << it->value().ToString() << std::endl;
  }
  assert(it->status().ok());
  delete it;

  db->DeleteRange(write_options, db->DefaultColumnFamily(), "1", "3");

  std::cout << "after delete:" << std::endl;
  it = db->NewIterator(rocksdb::ReadOptions());
  for (it->SeekToFirst(); it->Valid(); it->Next()) {
    std::cout << it->key().ToString() << ": " << it->value().ToString() << std::endl;
  }
  assert(it->status().ok());
  delete it;

  return 0;
}

the result is:

before delete:
1: v1
2: v2
3: v3
10: v10
after delete:

it should be:

before delete:
1: v1
2: v2
3: v3
10: v10
after delete:
3: v3
10: v10

Most helpful comment

You can鈥檛 convert a.data() to int this way. A slice data() method needs to be used together with size(), since data() does not return a null-terminated string version of a slice.

Using

int a_int = std::stoi(a.ToString()); 
int b_int = std::stoi(b.ToString());

should work fine.

For better performance use fixed size keys (sizeof(int)) converted to BigEndian. This allows to compare numbers using default comparator.

All 3 comments

You can鈥檛 convert a.data() to int this way. A slice data() method needs to be used together with size(), since data() does not return a null-terminated string version of a slice.

Using

int a_int = std::stoi(a.ToString()); 
int b_int = std::stoi(b.ToString());

should work fine.

For better performance use fixed size keys (sizeof(int)) converted to BigEndian. This allows to compare numbers using default comparator.

Thanks @Devernua. Did you verify that it fixes the expected results?

@ajkr, yes

Was this page helpful?
0 / 5 - 0 ratings