blob: b03a650732a4906d04780cbc9340e131ab2d2221 [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.
#ifndef COMPONENTS_LEVELDB_PROTO_SHARED_PROTO_DATABASE_CLIENT_H_
#define COMPONENTS_LEVELDB_PROTO_SHARED_PROTO_DATABASE_CLIENT_H_
#include "base/sequence_checker.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "components/leveldb_proto/leveldb_database.h"
#include "components/leveldb_proto/proto/shared_db_metadata.pb.h"
#include "components/leveldb_proto/proto_leveldb_wrapper.h"
#include "components/leveldb_proto/unique_proto_database.h"
namespace leveldb_proto {
class SharedProtoDatabase;
using ClientCorruptCallback = base::OnceCallback<void(bool)>;
using SharedClientInitCallback =
base::OnceCallback<void(Enums::InitStatus,
SharedDBMetadataProto::MigrationStatus)>;
std::string StripPrefix(const std::string& key, const std::string& prefix);
std::unique_ptr<std::vector<std::string>> PrefixStrings(
std::unique_ptr<std::vector<std::string>> strings,
const std::string& prefix);
bool KeyFilterStripPrefix(const LevelDB::KeyFilter& key_filter,
const std::string& prefix,
const std::string& key);
std::unique_ptr<std::vector<std::string>> PrefixStrings(
std::unique_ptr<std::vector<std::string>> strings,
const std::string& prefix);
void GetSharedDatabaseInitStatusAsync(
const std::string& client_db_id,
const scoped_refptr<SharedProtoDatabase>& db,
Callbacks::InitStatusCallback callback);
void UpdateClientMetadataAsync(
const scoped_refptr<SharedProtoDatabase>& db,
const std::string& client_db_id,
SharedDBMetadataProto::MigrationStatus migration_status,
ClientCorruptCallback callback);
// Destroys all the data from obsolete clients, for the given |db_wrapper|
// instance. |callback| is called once all the obsolete clients data are
// removed, with failure status if one or more of the update fails.
void DestroyObsoleteSharedProtoDatabaseClients(
std::unique_ptr<ProtoLevelDBWrapper> db_wrapper,
Callbacks::UpdateCallback callback);
// Sets list of client names that are obsolete and will be cleared by next call
// to DestroyObsoleteSharedProtoDatabaseClients(). |list| is list of c strings
// with a nullptr to mark the end of list.
void SetObsoleteClientListForTesting(const char* const* list);
// An implementation of ProtoDatabase<T> that uses a shared LevelDB and task
// runner.
// Should be created, destroyed, and used on the same sequenced task runner.
template <typename T>
class SharedProtoDatabaseClient : public ProtoDatabase<T> {
public:
virtual ~SharedProtoDatabaseClient();
void Init(const std::string& client_uma_name,
Callbacks::InitStatusCallback callback) override;
void Init(const char* client_uma_name,
const base::FilePath& database_dir,
const leveldb_env::Options& options,
Callbacks::InitCallback callback) override;
void InitWithDatabase(LevelDB* database,
const base::FilePath& database_dir,
const leveldb_env::Options& options,
Callbacks::InitStatusCallback callback) override;
// Overrides for prepending namespace and type prefix to all operations on the
// shared database.
void UpdateEntries(std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector>
entries_to_save,
std::unique_ptr<std::vector<std::string>> keys_to_remove,
Callbacks::UpdateCallback callback) override;
void UpdateEntriesWithRemoveFilter(
std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector>
entries_to_save,
const LevelDB::KeyFilter& delete_key_filter,
Callbacks::UpdateCallback callback) override;
void UpdateEntriesWithRemoveFilter(
std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector>
entries_to_save,
const LevelDB::KeyFilter& delete_key_filter,
const std::string& target_prefix,
Callbacks::UpdateCallback callback) override;
void LoadEntries(
typename Callbacks::Internal<T>::LoadCallback callback) override;
void LoadEntriesWithFilter(
const LevelDB::KeyFilter& filter,
typename Callbacks::Internal<T>::LoadCallback callback) override;
void LoadEntriesWithFilter(
const LevelDB::KeyFilter& key_filter,
const leveldb::ReadOptions& options,
const std::string& target_prefix,
typename Callbacks::Internal<T>::LoadCallback callback) override;
void LoadKeys(Callbacks::LoadKeysCallback callback) override;
void LoadKeys(const std::string& target_prefix,
Callbacks::LoadKeysCallback callback) override;
void LoadKeysAndEntries(
typename Callbacks::Internal<T>::LoadKeysAndEntriesCallback callback)
override;
void LoadKeysAndEntriesWithFilter(
const LevelDB::KeyFilter& filter,
typename Callbacks::Internal<T>::LoadKeysAndEntriesCallback callback)
override;
void LoadKeysAndEntriesWithFilter(
const LevelDB::KeyFilter& filter,
const leveldb::ReadOptions& options,
const std::string& target_prefix,
typename Callbacks::Internal<T>::LoadKeysAndEntriesCallback callback)
override;
void LoadKeysAndEntriesInRange(
const std::string& start,
const std::string& end,
typename Callbacks::Internal<T>::LoadKeysAndEntriesCallback callback)
override;
void GetEntry(const std::string& key,
typename Callbacks::Internal<T>::GetCallback callback) override;
void Destroy(Callbacks::DestroyCallback callback) override;
typename Callbacks::InitCallback GetInitCallback() const;
const std::string& client_db_id() const { return prefix_; }
void set_migration_status(
SharedDBMetadataProto::MigrationStatus migration_status) {
migration_status_ = migration_status;
}
void UpdateClientInitMetadata(SharedDBMetadataProto::MigrationStatus);
SharedDBMetadataProto::MigrationStatus migration_status() const {
return migration_status_;
}
private:
friend class SharedProtoDatabase;
friend class SharedProtoDatabaseTest;
friend class SharedProtoDatabaseClientTest;
// Hide this so clients can only be created by the SharedProtoDatabase.
SharedProtoDatabaseClient(
std::unique_ptr<ProtoLevelDBWrapper> db_wrapper,
const std::string& client_namespace,
const std::string& type_prefix,
const scoped_refptr<SharedProtoDatabase>& parent_db);
static void StripPrefixLoadKeysCallback(
Callbacks::LoadKeysCallback callback,
const std::string& prefix,
bool success,
std::unique_ptr<leveldb_proto::KeyVector> keys);
static void StripPrefixLoadKeysAndEntriesCallback(
typename Callbacks::Internal<T>::LoadKeysAndEntriesCallback callback,
const std::string& prefix,
bool success,
std::unique_ptr<std::map<std::string, T>> keys_entries);
static std::unique_ptr<typename Util::Internal<T>::KeyEntryVector>
PrefixKeyEntryVector(
std::unique_ptr<typename Util::Internal<T>::KeyEntryVector> kev,
const std::string& prefix);
SEQUENCE_CHECKER(sequence_checker_);
// |is_corrupt_| should be set by the SharedProtoDatabase that creates this
// when a client is created that doesn't know about a previous shared
// database corruption.
bool is_corrupt_ = false;
SharedDBMetadataProto::MigrationStatus migration_status_ =
SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED;
std::string prefix_;
std::string client_name_;
scoped_refptr<SharedProtoDatabase> parent_db_;
std::unique_ptr<UniqueProtoDatabase<T>> unique_db_;
base::WeakPtrFactory<SharedProtoDatabaseClient<T>> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(SharedProtoDatabaseClient);
};
template <typename T>
SharedProtoDatabaseClient<T>::SharedProtoDatabaseClient(
std::unique_ptr<ProtoLevelDBWrapper> db_wrapper,
const std::string& client_namespace,
const std::string& type_prefix,
const scoped_refptr<SharedProtoDatabase>& parent_db)
: prefix_(base::JoinString({client_namespace, type_prefix, std::string()},
"_")),
parent_db_(parent_db),
unique_db_(
std::make_unique<UniqueProtoDatabase<T>>(std::move(db_wrapper))),
weak_ptr_factory_(this) {
DETACH_FROM_SEQUENCE(sequence_checker_);
}
template <typename T>
SharedProtoDatabaseClient<T>::~SharedProtoDatabaseClient() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
template <typename T>
void SharedProtoDatabaseClient<T>::Init(
const std::string& client_uma_name,
Callbacks::InitStatusCallback callback) {
unique_db_->SetMetricsId(client_uma_name);
GetSharedDatabaseInitStatusAsync(prefix_, parent_db_, std::move(callback));
}
template <typename T>
void SharedProtoDatabaseClient<T>::Init(const char* client_uma_name,
const base::FilePath& database_dir,
const leveldb_env::Options& options,
Callbacks::InitCallback callback) {
NOTREACHED();
}
template <typename T>
void SharedProtoDatabaseClient<T>::InitWithDatabase(
LevelDB* database,
const base::FilePath& database_dir,
const leveldb_env::Options& options,
Callbacks::InitStatusCallback callback) {
NOTREACHED();
}
template <typename T>
void SharedProtoDatabaseClient<T>::UpdateEntries(
std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector> entries_to_save,
std::unique_ptr<std::vector<std::string>> keys_to_remove,
Callbacks::UpdateCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
unique_db_->UpdateEntries(
PrefixKeyEntryVector(std::move(entries_to_save), prefix_),
PrefixStrings(std::move(keys_to_remove), prefix_), std::move(callback));
}
template <typename T>
void SharedProtoDatabaseClient<T>::UpdateEntriesWithRemoveFilter(
std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector> entries_to_save,
const LevelDB::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));
}
template <typename T>
void SharedProtoDatabaseClient<T>::UpdateEntriesWithRemoveFilter(
std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector> entries_to_save,
const LevelDB::KeyFilter& delete_key_filter,
const std::string& target_prefix,
Callbacks::UpdateCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
unique_db_->UpdateEntriesWithRemoveFilter(
PrefixKeyEntryVector(std::move(entries_to_save), prefix_),
base::BindRepeating(&KeyFilterStripPrefix, delete_key_filter, prefix_),
prefix_ + target_prefix, std::move(callback));
}
template <typename T>
void SharedProtoDatabaseClient<T>::LoadEntries(
typename Callbacks::Internal<T>::LoadCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
LoadEntriesWithFilter(LevelDB::KeyFilter(), std::move(callback));
}
template <typename T>
void SharedProtoDatabaseClient<T>::LoadEntriesWithFilter(
const LevelDB::KeyFilter& filter,
typename Callbacks::Internal<T>::LoadCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
LoadEntriesWithFilter(filter, leveldb::ReadOptions(), std::string(),
std::move(callback));
}
template <typename T>
void SharedProtoDatabaseClient<T>::LoadEntriesWithFilter(
const LevelDB::KeyFilter& filter,
const leveldb::ReadOptions& options,
const std::string& target_prefix,
typename Callbacks::Internal<T>::LoadCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
unique_db_->LoadEntriesWithFilter(
base::BindRepeating(&KeyFilterStripPrefix, filter, prefix_), options,
prefix_ + target_prefix, std::move(callback));
}
template <typename T>
void SharedProtoDatabaseClient<T>::LoadKeys(
Callbacks::LoadKeysCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
LoadKeys(std::string(), std::move(callback));
}
template <typename T>
void SharedProtoDatabaseClient<T>::LoadKeys(
const std::string& target_prefix,
Callbacks::LoadKeysCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
unique_db_->LoadKeys(
prefix_ + target_prefix,
base::BindOnce(&SharedProtoDatabaseClient<T>::StripPrefixLoadKeysCallback,
std::move(callback), prefix_));
}
template <typename T>
void SharedProtoDatabaseClient<T>::LoadKeysAndEntries(
typename Callbacks::Internal<T>::LoadKeysAndEntriesCallback callback) {
LoadKeysAndEntriesWithFilter(LevelDB::KeyFilter(), std::move(callback));
}
template <typename T>
void SharedProtoDatabaseClient<T>::LoadKeysAndEntriesWithFilter(
const LevelDB::KeyFilter& filter,
typename Callbacks::Internal<T>::LoadKeysAndEntriesCallback callback) {
LoadKeysAndEntriesWithFilter(filter, leveldb::ReadOptions(), std::string(),
std::move(callback));
}
template <typename T>
void SharedProtoDatabaseClient<T>::LoadKeysAndEntriesWithFilter(
const LevelDB::KeyFilter& filter,
const leveldb::ReadOptions& options,
const std::string& target_prefix,
typename Callbacks::Internal<T>::LoadKeysAndEntriesCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
unique_db_->LoadKeysAndEntriesWithFilter(
filter, options, prefix_ + target_prefix,
base::BindOnce(
&SharedProtoDatabaseClient<T>::StripPrefixLoadKeysAndEntriesCallback,
std::move(callback), prefix_));
}
template <typename T>
void SharedProtoDatabaseClient<T>::LoadKeysAndEntriesInRange(
const std::string& start,
const std::string& end,
typename Callbacks::Internal<T>::LoadKeysAndEntriesCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
unique_db_->LoadKeysAndEntriesInRange(
prefix_ + start, prefix_ + end,
base::BindOnce(
&SharedProtoDatabaseClient<T>::StripPrefixLoadKeysAndEntriesCallback,
std::move(callback), prefix_));
}
template <typename T>
void SharedProtoDatabaseClient<T>::GetEntry(
const std::string& key,
typename Callbacks::Internal<T>::GetCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
unique_db_->GetEntry(prefix_ + key, std::move(callback));
}
template <typename T>
void SharedProtoDatabaseClient<T>::Destroy(
Callbacks::DestroyCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
UpdateEntriesWithRemoveFilter(
std::make_unique<typename Util::Internal<T>::KeyEntryVector>(),
base::BindRepeating([](const std::string& key) { return true; }),
base::BindOnce([](Callbacks::DestroyCallback callback,
bool success) { std::move(callback).Run(success); },
std::move(callback)));
}
template <typename T>
void SharedProtoDatabaseClient<T>::UpdateClientInitMetadata(
SharedDBMetadataProto::MigrationStatus migration_status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
migration_status_ = migration_status;
// Tell the SharedProtoDatabase that we've seen the corruption state so it's
// safe to update its records for this client.
UpdateClientMetadataAsync(parent_db_, prefix_, migration_status_,
base::BindOnce([](bool success) {
// TODO(thildebr): Should we do anything special
// here? If the shared DB can't update the
// client's corruption counter to match its own,
// then the client will think it's corrupt on the
// next Init as well.
}));
}
// static
template <typename T>
void SharedProtoDatabaseClient<T>::StripPrefixLoadKeysCallback(
Callbacks::LoadKeysCallback callback,
const std::string& prefix,
bool success,
std::unique_ptr<leveldb_proto::KeyVector> keys) {
auto stripped_keys = std::make_unique<leveldb_proto::KeyVector>();
for (auto& key : *keys)
stripped_keys->emplace_back(StripPrefix(key, prefix));
std::move(callback).Run(success, std::move(stripped_keys));
}
// static
template <typename T>
void SharedProtoDatabaseClient<T>::StripPrefixLoadKeysAndEntriesCallback(
typename Callbacks::Internal<T>::LoadKeysAndEntriesCallback callback,
const std::string& prefix,
bool success,
std::unique_ptr<std::map<std::string, T>> keys_entries) {
auto stripped_keys_map = std::make_unique<std::map<std::string, T>>();
for (auto& key_entry : *keys_entries) {
stripped_keys_map->insert(
std::make_pair(StripPrefix(key_entry.first, prefix), key_entry.second));
}
std::move(callback).Run(success, std::move(stripped_keys_map));
}
// static
template <typename T>
std::unique_ptr<typename Util::Internal<T>::KeyEntryVector>
SharedProtoDatabaseClient<T>::PrefixKeyEntryVector(
std::unique_ptr<typename Util::Internal<T>::KeyEntryVector> kev,
const std::string& prefix) {
for (auto& key_entry_pair : *kev) {
key_entry_pair.first = base::StrCat({prefix, key_entry_pair.first});
}
return kev;
}
} // namespace leveldb_proto
#endif // COMPONENTS_LEVELDB_PROTO_SHARED_PROTO_DATABASE_CLIENT_H_