Commit 7f0cff2a authored by Min Qin's avatar Min Qin Committed by Commit Bot

Allow proto database to filter returned entries on key

This CL allows proto db to take a filter to filter out returned entries
Test is also included.
This will facilitate the in-progress download database work.

BUG=842245

Change-Id: I63426527743811ab31560e74a8dc45f922333a00
Reviewed-on: https://chromium-review.googlesource.com/1067149
Commit-Queue: Min Qin <qinmin@chromium.org>
Reviewed-by: default avatarDavid Trainor <dtrainor@chromium.org>
Reviewed-by: default avatarTommy Nyquist <nyquist@chromium.org>
Cr-Commit-Position: refs/heads/master@{#561590}
parent 86ed62be
......@@ -100,6 +100,11 @@ bool LevelDB::Save(const base::StringPairs& entries_to_save,
}
bool LevelDB::Load(std::vector<std::string>* entries) {
return LoadWithFilter(KeyFilter(), entries);
}
bool LevelDB::LoadWithFilter(const KeyFilter& filter,
std::vector<std::string>* entries) {
DFAKE_SCOPED_LOCK(thread_checker_);
if (!db_)
return false;
......@@ -107,6 +112,11 @@ bool LevelDB::Load(std::vector<std::string>* entries) {
leveldb::ReadOptions options;
std::unique_ptr<leveldb::Iterator> db_iterator(db_->NewIterator(options));
for (db_iterator->SeekToFirst(); db_iterator->Valid(); db_iterator->Next()) {
if (!filter.is_null()) {
leveldb::Slice key_slice = db_iterator->key();
if (!filter.Run(std::string(key_slice.data(), key_slice.size())))
continue;
}
leveldb::Slice value_slice = db_iterator->value();
std::string entry(value_slice.data(), value_slice.size());
entries->push_back(entry);
......
......@@ -40,6 +40,8 @@ class LevelDB {
explicit LevelDB(const char* client_name);
virtual ~LevelDB();
using KeyFilter = base::RepeatingCallback<bool(const std::string& key)>;
// Initializes a leveldb with the given options. If |database_dir| is
// empty, this opens an in-memory db.
virtual bool Init(const base::FilePath& database_dir,
......@@ -48,6 +50,8 @@ class LevelDB {
virtual bool Save(const base::StringPairs& pairs_to_save,
const std::vector<std::string>& keys_to_remove);
virtual bool Load(std::vector<std::string>* entries);
virtual bool LoadWithFilter(const KeyFilter& filter,
std::vector<std::string>* entries);
virtual bool LoadKeys(std::vector<std::string>* keys);
virtual bool Get(const std::string& key, bool* found, std::string* entry);
// Close (if currently open) and then destroy (i.e. delete) the database
......
......@@ -11,6 +11,7 @@
#include <vector>
#include "base/callback.h"
#include "components/leveldb_proto/leveldb_database.h"
#include "third_party/leveldatabase/env_chromium.h"
namespace base {
......@@ -59,6 +60,12 @@ class ProtoDatabase {
// when complete.
virtual void LoadEntries(LoadCallback callback) = 0;
// Asynchronously loads entries that satisfies the |filter| from the database
// and invokes |callback| when complete. The filter will be called on
// ProtoDatabase's taskrunner.
virtual void LoadEntriesWithFilter(const LevelDB::KeyFilter& filter,
LoadCallback callback) = 0;
// Asynchronously loads all keys from the database and invokes |callback| with
// those keys when complete.
virtual void LoadKeys(LoadKeysCallback callback) = 0;
......
......@@ -51,6 +51,9 @@ class ProtoDatabaseImpl : public ProtoDatabase<T> {
std::unique_ptr<KeyVector> keys_to_remove,
typename ProtoDatabase<T>::UpdateCallback callback) override;
void LoadEntries(typename ProtoDatabase<T>::LoadCallback callback) override;
void LoadEntriesWithFilter(
const LevelDB::KeyFilter& key_filter,
typename ProtoDatabase<T>::LoadCallback callback) override;
void LoadKeys(typename ProtoDatabase<T>::LoadKeysCallback callback) override;
void GetEntry(const std::string& key,
typename ProtoDatabase<T>::GetCallback callback) override;
......@@ -152,6 +155,7 @@ void UpdateEntriesFromTaskRunner(
template <typename T>
void LoadEntriesFromTaskRunner(LevelDB* database,
const LevelDB::KeyFilter& filter,
std::vector<T>* entries,
bool* success) {
DCHECK(success);
......@@ -160,7 +164,7 @@ void LoadEntriesFromTaskRunner(LevelDB* database,
entries->clear();
std::vector<std::string> loaded_entries;
*success = database->Load(&loaded_entries);
*success = database->LoadWithFilter(filter, &loaded_entries);
for (const auto& serialized_entry : loaded_entries) {
T entry;
......@@ -286,6 +290,13 @@ void ProtoDatabaseImpl<T>::UpdateEntries(
template <typename T>
void ProtoDatabaseImpl<T>::LoadEntries(
typename ProtoDatabase<T>::LoadCallback callback) {
LoadEntriesWithFilter(LevelDB::KeyFilter(), std::move(callback));
}
template <typename T>
void ProtoDatabaseImpl<T>::LoadEntriesWithFilter(
const LevelDB::KeyFilter& key_filter,
typename ProtoDatabase<T>::LoadCallback callback) {
DCHECK(thread_checker_.CalledOnValidThread());
bool* success = new bool(false);
......@@ -296,7 +307,7 @@ void ProtoDatabaseImpl<T>::LoadEntries(
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(LoadEntriesFromTaskRunner<T>, base::Unretained(db_.get()),
entries_ptr, success),
key_filter, entries_ptr, success),
base::BindOnce(RunLoadCallback<T>, std::move(callback),
base::Owned(success), std::move(entries)));
}
......
......@@ -53,6 +53,8 @@ class MockDB : public LevelDB {
const leveldb_env::Options& options));
MOCK_METHOD2(Save, bool(const KeyValueVector&, const KeyVector&));
MOCK_METHOD1(Load, bool(std::vector<std::string>*));
MOCK_METHOD2(LoadWithFilter,
bool(const KeyFilter&, std::vector<std::string>*));
MOCK_METHOD3(Get, bool(const std::string&, bool*, std::string*));
MOCK_METHOD0(Destroy, bool());
......@@ -116,6 +118,10 @@ Matcher<const Options&> OptionsEq(const Options& expected) {
return MakeMatcher(new OptionsEqMatcher(expected));
}
bool ZeroFilter(const std::string& key) {
return key == "0";
}
} // namespace
EntryMap GetSmallModel() {
......@@ -245,7 +251,7 @@ TEST_F(ProtoDatabaseImplTest, TestDBDestroyFailure) {
}
ACTION_P(AppendLoadEntries, model) {
std::vector<std::string>* output = arg0;
std::vector<std::string>* output = arg1;
for (const auto& pair : model)
output->push_back(pair.second.SerializeAsString());
......@@ -274,7 +280,8 @@ TEST_F(ProtoDatabaseImplTest, TestDBLoadSuccess) {
base::WrapUnique(mock_db), path, CreateSimpleOptions(),
base::Bind(&MockDatabaseCaller::InitCallback, base::Unretained(&caller)));
EXPECT_CALL(*mock_db, Load(_)).WillOnce(AppendLoadEntries(model));
EXPECT_CALL(*mock_db, LoadWithFilter(_, _))
.WillOnce(AppendLoadEntries(model));
EXPECT_CALL(caller, LoadCallback1(true, _))
.WillOnce(VerifyLoadEntries(testing::ByRef(model)));
db_->LoadEntries(
......@@ -295,7 +302,7 @@ TEST_F(ProtoDatabaseImplTest, TestDBLoadFailure) {
base::WrapUnique(mock_db), path, CreateSimpleOptions(),
base::Bind(&MockDatabaseCaller::InitCallback, base::Unretained(&caller)));
EXPECT_CALL(*mock_db, Load(_)).WillOnce(Return(false));
EXPECT_CALL(*mock_db, LoadWithFilter(_, _)).WillOnce(Return(false));
EXPECT_CALL(caller, LoadCallback1(false, _));
db_->LoadEntries(
base::Bind(&MockDatabaseCaller::LoadCallback, base::Unretained(&caller)));
......@@ -696,6 +703,34 @@ TEST_F(ProtoDatabaseImplLevelDBTest, TestDBCloseAndReopen) {
TestLevelDBSaveAndLoad(true);
}
TEST_F(ProtoDatabaseImplLevelDBTest, TestDBLoadWithFilter) {
ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
EntryMap model = GetSmallModel();
KeyValueVector save_entries;
std::vector<std::string> load_entries;
KeyVector remove_keys;
for (const auto& pair : model) {
save_entries.push_back(
std::make_pair(pair.second.id(), pair.second.SerializeAsString()));
}
std::unique_ptr<LevelDB> db(new LevelDB(kTestLevelDBClientName));
EXPECT_TRUE(db->Init(temp_dir.GetPath(), CreateSimpleOptions()));
EXPECT_TRUE(db->Save(save_entries, remove_keys));
EXPECT_TRUE(
db->LoadWithFilter(base::BindRepeating(&ZeroFilter), &load_entries));
EXPECT_EQ(load_entries.size(), 1u);
TestProto entry;
ASSERT_TRUE(entry.ParseFromString(load_entries[0]));
EXPECT_EQ(entry.SerializeAsString(), model["0"].SerializeAsString());
}
TEST_F(ProtoDatabaseImplLevelDBTest, TestDBInitFail) {
ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
......
......@@ -40,6 +40,9 @@ class FakeDB : public ProtoDatabase<T> {
std::unique_ptr<std::vector<std::string>> keys_to_remove,
typename ProtoDatabase<T>::UpdateCallback callback) override;
void LoadEntries(typename ProtoDatabase<T>::LoadCallback callback) override;
void LoadEntriesWithFilter(
const LevelDB::KeyFilter& key_filter,
typename ProtoDatabase<T>::LoadCallback callback) override;
void LoadKeys(typename ProtoDatabase<T>::LoadKeysCallback callback) override;
void GetEntry(const std::string& key,
typename ProtoDatabase<T>::GetCallback callback) override;
......@@ -118,9 +121,18 @@ void FakeDB<T>::UpdateEntries(
template <typename T>
void FakeDB<T>::LoadEntries(typename ProtoDatabase<T>::LoadCallback callback) {
LoadEntriesWithFilter(LevelDB::KeyFilter(), std::move(callback));
}
template <typename T>
void FakeDB<T>::LoadEntriesWithFilter(
const LevelDB::KeyFilter& key_filter,
typename ProtoDatabase<T>::LoadCallback callback) {
std::unique_ptr<std::vector<T>> entries(new std::vector<T>());
for (const auto& pair : *db_)
entries->push_back(pair.second);
for (const auto& pair : *db_) {
if (key_filter.is_null() || key_filter.Run(pair.first))
entries->push_back(pair.second);
}
load_callback_ =
base::BindOnce(RunLoadCallback, std::move(callback), std::move(entries));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment