| // 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 |