blob: c83643186ac97b43b0547c611bbd3a77ffef00ae [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/leveldb_proto/internal/proto_leveldb_wrapper.h"
#include <string>
#include "base/sequenced_task_runner.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "components/leveldb_proto/internal/leveldb_database.h"
#include "components/leveldb_proto/internal/proto_leveldb_wrapper_metrics.h"
#include "components/leveldb_proto/public/proto_database.h"
namespace leveldb_proto {
namespace {
Enums::InitStatus InitFromTaskRunner(LevelDB* database,
const base::FilePath& database_dir,
const leveldb_env::Options& options,
bool destroy_on_corruption,
const std::string& client_id) {
// TODO(cjhopman): Histogram for database size.
auto status = database->Init(database_dir, options, destroy_on_corruption);
ProtoLevelDBWrapperMetrics::RecordInit(client_id, status);
if (status.ok())
return Enums::InitStatus::kOK;
if (status.IsCorruption())
return Enums::InitStatus::kCorrupt;
if (status.IsNotSupportedError() || status.IsInvalidArgument())
return Enums::InitStatus::kInvalidOperation;
return Enums::InitStatus::kError;
}
bool DestroyFromTaskRunner(LevelDB* database, const std::string& client_id) {
auto status = database->Destroy();
bool success = status.ok();
ProtoLevelDBWrapperMetrics::RecordDestroy(client_id, success);
return success;
}
void LoadKeysFromTaskRunner(
LevelDB* database,
const std::string& target_prefix,
const std::string& client_id,
Callbacks::LoadKeysCallback callback,
scoped_refptr<base::SequencedTaskRunner> callback_task_runner) {
auto keys = std::make_unique<KeyVector>();
bool success = database->LoadKeys(target_prefix, keys.get());
ProtoLevelDBWrapperMetrics::RecordLoadKeys(client_id, success);
callback_task_runner->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), success, std::move(keys)));
}
void RemoveKeysFromTaskRunner(
LevelDB* database,
const std::string& target_prefix,
const KeyFilter& filter,
const std::string& client_id,
Callbacks::UpdateCallback callback,
scoped_refptr<base::SequencedTaskRunner> callback_task_runner) {
leveldb::Status status;
bool success = database->UpdateWithRemoveFilter(base::StringPairs(), filter,
target_prefix, &status);
ProtoLevelDBWrapperMetrics::RecordUpdate(client_id, success, status);
callback_task_runner->PostTask(FROM_HERE,
base::BindOnce(std::move(callback), success));
}
void RunLoadCallback(Callbacks::LoadCallback callback,
bool* success,
std::unique_ptr<ValueVector> entries) {
std::move(callback).Run(*success, std::move(entries));
}
void RunLoadKeysAndEntriesCallback(
Callbacks::LoadKeysAndEntriesCallback callback,
bool* success,
std::unique_ptr<KeyValueMap> keys_entries) {
std::move(callback).Run(*success, std::move(keys_entries));
}
void RunGetCallback(Callbacks::GetCallback callback,
const bool* success,
const bool* found,
std::unique_ptr<std::string> entry) {
std::move(callback).Run(*success, *found ? std::move(entry) : nullptr);
}
bool UpdateEntriesFromTaskRunner(
LevelDB* database,
std::unique_ptr<KeyValueVector> entries_to_save,
std::unique_ptr<KeyVector> keys_to_remove,
const std::string& client_id) {
leveldb::Status status;
bool success = database->Save(*entries_to_save, *keys_to_remove, &status);
ProtoLevelDBWrapperMetrics::RecordUpdate(client_id, success, status);
return success;
}
bool UpdateEntriesWithRemoveFilterFromTaskRunner(
LevelDB* database,
std::unique_ptr<KeyValueVector> entries_to_save,
const KeyFilter& delete_key_filter,
const std::string& target_prefix,
const std::string& client_id) {
leveldb::Status status;
bool success = database->UpdateWithRemoveFilter(
*entries_to_save, delete_key_filter, target_prefix, &status);
ProtoLevelDBWrapperMetrics::RecordUpdate(client_id, success, status);
return success;
}
void LoadKeysAndEntriesFromTaskRunner(LevelDB* database,
const KeyFilter& while_callback,
const KeyFilter& filter,
const leveldb::ReadOptions& options,
const std::string& target_prefix,
const std::string& client_id,
bool* success,
KeyValueMap* keys_entries) {
DCHECK(success);
DCHECK(keys_entries);
keys_entries->clear();
*success = database->LoadKeysAndEntriesWhile(filter, keys_entries, options,
target_prefix, while_callback);
ProtoLevelDBWrapperMetrics::RecordLoadKeysAndEntries(client_id, success);
}
void LoadEntriesFromTaskRunner(LevelDB* database,
const KeyFilter& filter,
const leveldb::ReadOptions& options,
const std::string& target_prefix,
const std::string& client_id,
bool* success,
ValueVector* entries) {
*success = database->LoadWithFilter(filter, entries, options, target_prefix);
ProtoLevelDBWrapperMetrics::RecordLoadEntries(client_id, success);
}
void GetEntryFromTaskRunner(LevelDB* database,
const std::string& key,
const std::string& client_id,
bool* success,
bool* found,
std::string* entry) {
leveldb::Status status;
*success = database->Get(key, found, entry, &status);
ProtoLevelDBWrapperMetrics::RecordGet(client_id, *success, *found, status);
}
} // namespace
ProtoLevelDBWrapper::ProtoLevelDBWrapper(
const scoped_refptr<base::SequencedTaskRunner>& task_runner)
: task_runner_(task_runner), weak_ptr_factory_(this) {
DETACH_FROM_SEQUENCE(sequence_checker_);
}
ProtoLevelDBWrapper::ProtoLevelDBWrapper(
const scoped_refptr<base::SequencedTaskRunner>& task_runner,
LevelDB* db)
: task_runner_(task_runner), db_(db), weak_ptr_factory_(this) {
DETACH_FROM_SEQUENCE(sequence_checker_);
}
ProtoLevelDBWrapper::~ProtoLevelDBWrapper() = default;
void ProtoLevelDBWrapper::RunInitCallback(Callbacks::InitCallback callback,
const leveldb::Status* status) {
std::move(callback).Run(status->ok());
}
void ProtoLevelDBWrapper::InitWithDatabase(
LevelDB* database,
const base::FilePath& database_dir,
const leveldb_env::Options& options,
bool destroy_on_corruption,
Callbacks::InitStatusCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(database);
db_ = database;
base::PostTaskAndReplyWithResult(
task_runner_.get(), FROM_HERE,
base::BindOnce(InitFromTaskRunner, base::Unretained(db_), database_dir,
options, destroy_on_corruption, metrics_id_),
std::move(callback));
}
void ProtoLevelDBWrapper::UpdateEntries(
std::unique_ptr<KeyValueVector> entries_to_save,
std::unique_ptr<KeyVector> keys_to_remove,
typename Callbacks::UpdateCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::PostTaskAndReplyWithResult(
task_runner_.get(), FROM_HERE,
base::BindOnce(UpdateEntriesFromTaskRunner, base::Unretained(db_),
std::move(entries_to_save), std::move(keys_to_remove),
metrics_id_),
std::move(callback));
}
void ProtoLevelDBWrapper::UpdateEntriesWithRemoveFilter(
std::unique_ptr<KeyValueVector> entries_to_save,
const KeyFilter& delete_key_filter,
Callbacks::UpdateCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
UpdateEntriesWithRemoveFilter(std::move(entries_to_save), delete_key_filter,
std::string(), std::move(callback));
}
void ProtoLevelDBWrapper::UpdateEntriesWithRemoveFilter(
std::unique_ptr<KeyValueVector> entries_to_save,
const KeyFilter& delete_key_filter,
const std::string& target_prefix,
Callbacks::UpdateCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::PostTaskAndReplyWithResult(
task_runner_.get(), FROM_HERE,
base::BindOnce(UpdateEntriesWithRemoveFilterFromTaskRunner,
base::Unretained(db_), std::move(entries_to_save),
delete_key_filter, target_prefix, metrics_id_),
std::move(callback));
}
void ProtoLevelDBWrapper::LoadEntries(Callbacks::LoadCallback callback) {
LoadEntriesWithFilter(KeyFilter(), std::move(callback));
}
void ProtoLevelDBWrapper::LoadEntriesWithFilter(
const KeyFilter& key_filter,
Callbacks::LoadCallback callback) {
LoadEntriesWithFilter(key_filter, leveldb::ReadOptions(), std::string(),
std::move(callback));
}
void ProtoLevelDBWrapper::LoadEntriesWithFilter(
const KeyFilter& key_filter,
const leveldb::ReadOptions& options,
const std::string& target_prefix,
Callbacks::LoadCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
bool* success = new bool(false);
auto entries = std::make_unique<ValueVector>();
// Get this pointer before |entries| is std::move()'d so we can use it below.
auto* entries_ptr = entries.get();
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(LoadEntriesFromTaskRunner, base::Unretained(db_),
key_filter, options, target_prefix, metrics_id_, success,
entries_ptr),
base::BindOnce(RunLoadCallback, std::move(callback), base::Owned(success),
std::move(entries)));
}
void ProtoLevelDBWrapper::LoadKeysAndEntries(
Callbacks::LoadKeysAndEntriesCallback callback) {
LoadKeysAndEntriesWithFilter(KeyFilter(), std::move(callback));
}
void ProtoLevelDBWrapper::LoadKeysAndEntriesWithFilter(
const KeyFilter& key_filter,
Callbacks::LoadKeysAndEntriesCallback callback) {
LoadKeysAndEntriesWithFilter(key_filter, leveldb::ReadOptions(),
std::string(), std::move(callback));
}
void ProtoLevelDBWrapper::LoadKeysAndEntriesWithFilter(
const KeyFilter& key_filter,
const leveldb::ReadOptions& options,
const std::string& target_prefix,
Callbacks::LoadKeysAndEntriesCallback callback) {
LoadKeysAndEntriesWhile(
base::BindRepeating(
[](const std::string& prefix, const std::string& key) {
return base::StartsWith(key, prefix, base::CompareCase::SENSITIVE);
},
target_prefix),
key_filter, options, target_prefix, std::move(callback));
}
void ProtoLevelDBWrapper::LoadKeysAndEntriesInRange(
const std::string& start,
const std::string& end,
Callbacks::LoadKeysAndEntriesCallback callback) {
LoadKeysAndEntriesWhile(
base::BindRepeating(
[](const std::string& range_end, const std::string& key) {
return key.compare(range_end) <= 0;
},
end),
KeyFilter(), leveldb::ReadOptions(), start, std::move(callback));
}
void ProtoLevelDBWrapper::LoadKeysAndEntriesWhile(
const KeyFilter& while_callback,
const KeyFilter& filter,
const leveldb::ReadOptions& options,
const std::string& target_prefix,
Callbacks::LoadKeysAndEntriesCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
bool* success = new bool(false);
auto keys_entries = std::make_unique<KeyValueMap>();
// Get this pointer before |keys_entries| is std::move()'d so we can use it
// below.
auto* keys_entries_ptr = keys_entries.get();
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(LoadKeysAndEntriesFromTaskRunner, base::Unretained(db_),
while_callback, filter, options, target_prefix,
metrics_id_, success, keys_entries_ptr),
base::BindOnce(RunLoadKeysAndEntriesCallback, std::move(callback),
base::Owned(success), std::move(keys_entries)));
}
void ProtoLevelDBWrapper::GetEntry(const std::string& key,
Callbacks::GetCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
bool* success = new bool(false);
bool* found = new bool(false);
auto entry = std::make_unique<std::string>();
// Get this pointer before |entry| is std::move()'d so we can use it below.
auto* entry_ptr = entry.get();
task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(GetEntryFromTaskRunner, base::Unretained(db_), key,
metrics_id_, success, found, entry_ptr),
base::BindOnce(RunGetCallback, std::move(callback), base::Owned(success),
base::Owned(found), std::move(entry)));
}
void ProtoLevelDBWrapper::LoadKeys(
typename Callbacks::LoadKeysCallback callback) {
LoadKeys(std::string(), std::move(callback));
}
void ProtoLevelDBWrapper::LoadKeys(
const std::string& target_prefix,
typename Callbacks::LoadKeysCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
task_runner_->PostTask(
FROM_HERE, base::BindOnce(LoadKeysFromTaskRunner, base::Unretained(db_),
target_prefix, metrics_id_, std::move(callback),
base::SequencedTaskRunnerHandle::Get()));
}
void ProtoLevelDBWrapper::RemoveKeys(const KeyFilter& filter,
const std::string& target_prefix,
Callbacks::UpdateCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(RemoveKeysFromTaskRunner, base::Unretained(db_),
target_prefix, filter, metrics_id_, std::move(callback),
base::SequencedTaskRunnerHandle::Get()));
}
void ProtoLevelDBWrapper::Destroy(Callbacks::DestroyCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(db_);
base::PostTaskAndReplyWithResult(
task_runner_.get(), FROM_HERE,
base::BindOnce(DestroyFromTaskRunner, base::Unretained(db_), metrics_id_),
std::move(callback));
}
void ProtoLevelDBWrapper::SetMetricsId(const std::string& id) {
metrics_id_ = id;
}
bool ProtoLevelDBWrapper::GetApproximateMemoryUse(uint64_t* approx_mem_use) {
if (!db_)
return 0;
return db_->GetApproximateMemoryUse(approx_mem_use);
}
const scoped_refptr<base::SequencedTaskRunner>&
ProtoLevelDBWrapper::task_runner() {
return task_runner_;
}
} // namespace leveldb_proto