// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab #include "include/encoding.h" #include "KeyValueDBMemory.h" #include #include #include "include/memory.h" #include using namespace std; /** * Iterate over the whole key space of the in-memory store * * @note Removing keys from the store while iterating over the store key-space * may result in unspecified behavior. * If one wants to safely iterate over the store while updating the * store, one should instead use a snapshot iterator, which provides * strong read-consistency. */ class WholeSpaceMemIterator : public KeyValueDB::WholeSpaceIteratorImpl { protected: KeyValueDBMemory *db; bool ready; map, bufferlist>::iterator it; public: explicit WholeSpaceMemIterator(KeyValueDBMemory *db) : db(db), ready(false) { } ~WholeSpaceMemIterator() override { } int seek_to_first() override { if (db->db.empty()) { it = db->db.end(); ready = false; return 0; } it = db->db.begin(); ready = true; return 0; } int seek_to_first(const string &prefix) override { it = db->db.lower_bound(make_pair(prefix, "")); if (db->db.empty() || (it == db->db.end())) { it = db->db.end(); ready = false; return 0; } ready = true; return 0; } int seek_to_last() override { it = db->db.end(); if (db->db.empty()) { ready = false; return 0; } --it; assert(it != db->db.end()); ready = true; return 0; } int seek_to_last(const string &prefix) override { string tmp(prefix); tmp.append(1, (char) 0); it = db->db.upper_bound(make_pair(tmp,"")); if (db->db.empty() || (it == db->db.end())) { seek_to_last(); } else { ready = true; prev(); } return 0; } int lower_bound(const string &prefix, const string &to) override { it = db->db.lower_bound(make_pair(prefix,to)); if ((db->db.empty()) || (it == db->db.end())) { it = db->db.end(); ready = false; return 0; } assert(it != db->db.end()); ready = true; return 0; } int upper_bound(const string &prefix, const string &after) override { it = db->db.upper_bound(make_pair(prefix,after)); if ((db->db.empty()) || (it == db->db.end())) { it = db->db.end(); ready = false; return 0; } assert(it != db->db.end()); ready = true; return 0; } bool valid() override { return ready && (it != db->db.end()); } bool begin() { return ready && (it == db->db.begin()); } int prev() override { if (!begin() && ready) --it; else it = db->db.end(); return 0; } int next() override { if (valid()) ++it; return 0; } string key() override { if (valid()) return (*it).first.second; else return ""; } pair raw_key() override { if (valid()) return (*it).first; else return make_pair("", ""); } bool raw_key_is_prefixed(const string &prefix) override { return prefix == (*it).first.first; } bufferlist value() override { if (valid()) return (*it).second; else return bufferlist(); } int status() override { return 0; } }; int KeyValueDBMemory::get(const string &prefix, const std::set &key, map *out) { if (!exists_prefix(prefix)) return 0; for (std::set::const_iterator i = key.begin(); i != key.end(); ++i) { pair k(prefix, *i); if (db.count(k)) (*out)[*i] = db[k]; } return 0; } int KeyValueDBMemory::get_keys(const string &prefix, const std::set &key, std::set *out) { if (!exists_prefix(prefix)) return 0; for (std::set::const_iterator i = key.begin(); i != key.end(); ++i) { if (db.count(make_pair(prefix, *i))) out->insert(*i); } return 0; } int KeyValueDBMemory::set(const string &prefix, const string &key, const bufferlist &bl) { db[make_pair(prefix,key)] = bl; return 0; } int KeyValueDBMemory::rmkey(const string &prefix, const string &key) { db.erase(make_pair(prefix,key)); return 0; } int KeyValueDBMemory::rmkeys_by_prefix(const string &prefix) { map,bufferlist>::iterator i; i = db.lower_bound(make_pair(prefix, "")); if (i == db.end()) return 0; while (i != db.end()) { std::pair key = (*i).first; if (key.first != prefix) break; ++i; rmkey(key.first, key.second); } return 0; } int KeyValueDBMemory::rm_range_keys(const string &prefix, const string &start, const string &end) { map,bufferlist>::iterator i; i = db.lower_bound(make_pair(prefix, start)); if (i == db.end()) return 0; while (i != db.end()) { std::pair key = (*i).first; if (key.first != prefix) break; if (key.second >= end) break; ++i; rmkey(key.first, key.second); } return 0; } KeyValueDB::WholeSpaceIterator KeyValueDBMemory::_get_iterator() { return ceph::shared_ptr( new WholeSpaceMemIterator(this) ); } class WholeSpaceSnapshotMemIterator : public WholeSpaceMemIterator { public: /** * @note * We perform a copy of the db map, which is populated by bufferlists. * * These are designed as shallow containers, thus there is a chance that * changing the underlying memory pages will lead to the iterator seeing * erroneous states. * * Although we haven't verified this yet, there is this chance, so we should * keep it in mind. */ explicit WholeSpaceSnapshotMemIterator(KeyValueDBMemory *db) : WholeSpaceMemIterator(db) { } ~WholeSpaceSnapshotMemIterator() override { delete db; } };