blob: f45386524a4ce08d33fb0b4975fefd9718c3ddc6 [file] [log] [blame]
// Copyright 2019 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_database_selector.h"
#include <memory>
#include <utility>
#include "base/metrics/histogram_macros.h"
#include "components/leveldb_proto/internal/migration_delegate.h"
#include "components/leveldb_proto/internal/shared_proto_database.h"
#include "components/leveldb_proto/internal/shared_proto_database_provider.h"
#include "components/leveldb_proto/internal/unique_proto_database.h"
namespace leveldb_proto {
namespace {
void RunInitCallbackOnTaskRunner(
Callbacks::InitStatusCallback callback,
scoped_refptr<base::SequencedTaskRunner> task_runner,
Enums::InitStatus status) {
task_runner->PostTask(FROM_HERE, base::BindOnce(std::move(callback), status));
}
} // namespace
// static
void ProtoDatabaseSelector::RecordInitState(
ProtoDatabaseSelector::ProtoDatabaseInitState state) {
UMA_HISTOGRAM_ENUMERATION("ProtoDB.SharedDbInitStatus", state);
}
ProtoDatabaseSelector::ProtoDatabaseSelector(
ProtoDbType db_type,
scoped_refptr<base::SequencedTaskRunner> task_runner,
std::unique_ptr<SharedProtoDatabaseProvider> db_provider)
: db_type_(db_type),
task_runner_(task_runner),
db_provider_(std::move(db_provider)),
migration_delegate_(std::make_unique<MigrationDelegate>()) {
DETACH_FROM_SEQUENCE(sequence_checker_);
}
ProtoDatabaseSelector::~ProtoDatabaseSelector() {
if (db_)
task_runner_->DeleteSoon(FROM_HERE, std::move(db_));
}
void ProtoDatabaseSelector::InitWithDatabase(
LevelDB* database,
const base::FilePath& database_dir,
const leveldb_env::Options& options,
scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
Callbacks::InitStatusCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!db_)
db_ = std::make_unique<UniqueProtoDatabase>(task_runner_);
unique_database_dir_ = database_dir;
db_->InitWithDatabase(
database, database_dir, options, false,
base::BindOnce(&RunInitCallbackOnTaskRunner, std::move(callback),
callback_task_runner));
OnInitDone(ProtoDatabaseInitState::kLegacyInitCalled);
}
void ProtoDatabaseSelector::InitUniqueOrShared(
const std::string& client_name,
base::FilePath db_dir,
const leveldb_env::Options& unique_db_options,
bool use_shared_db,
scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
Callbacks::InitStatusCallback callback) {
RecordInitState(ProtoDatabaseInitState::kSharedDbInitAttempted);
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
init_status_ = InitStatus::IN_PROGRESS;
unique_database_dir_ = db_dir;
client_name_ = client_name;
if (unique_database_dir_.empty()) {
DCHECK(!use_shared_db) << "Opening in memory shared db is not supported";
// In case we set up field trials by mistake, ignore the use shared flag and
// return unique db.
use_shared_db = false;
}
auto unique_options = unique_db_options;
// There are two Init methods, one that receives Options for its unique DB and
// another that uses CreateSimpleOptions() to open the unique DB. In case a
// shared DB needs to be used then we don't need to create a new unique DB if
// it doesn't exist. In case a unique DB needs to be used then we don't change
// the create_if_missing parameter, because it may have been set by a client.
if (use_shared_db) {
unique_options.create_if_missing = false;
}
auto unique_db = std::make_unique<UniqueProtoDatabase>(db_dir, unique_options,
task_runner_);
auto* unique_db_ptr = unique_db.get();
unique_db_ptr->Init(
client_name, base::BindOnce(&ProtoDatabaseSelector::OnInitUniqueDB, this,
std::move(unique_db), use_shared_db,
base::BindOnce(&RunInitCallbackOnTaskRunner,
std::move(callback),
callback_task_runner)));
}
void ProtoDatabaseSelector::OnInitUniqueDB(
std::unique_ptr<UniqueProtoDatabase> unique_db,
bool use_shared_db,
Callbacks::InitStatusCallback callback,
Enums::InitStatus status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// If the unique DB is corrupt, just return it early with the corruption
// status to avoid silently migrating a corrupt database and giving no errors
// back.
if (status == Enums::InitStatus::kCorrupt) {
db_ = std::move(unique_db);
std::move(callback).Run(Enums::InitStatus::kCorrupt);
OnInitDone(ProtoDatabaseInitState::kFailureUniqueDbCorrupted);
return;
}
// Clear out the unique_db before sending an unusable DB into InitSharedDB,
// a nullptr indicates opening a unique DB failed.
if (status != Enums::InitStatus::kOK)
unique_db.reset();
// If no SharedProtoDatabaseProvider is set then we use the unique DB (if it
// opened correctly). If in memory db is requested then do not try to migrate
// data from shared db, which was the behavior when only unique db existed.
if (!db_provider_ || unique_database_dir_.empty()) {
db_ = std::move(unique_db);
std::move(callback).Run(status);
OnInitDone(ProtoDatabaseInitState::kFailureNoDatabaseProvider);
return;
}
// Get the current task runner to ensure the callback is run on the same
// callback as the rest, and the WeakPtr checks out on the right sequence.
DCHECK(task_runner_->RunsTasksInCurrentSequence());
db_provider_->GetDBInstance(
base::BindOnce(&ProtoDatabaseSelector::OnInitSharedDB, this,
std::move(unique_db), status, use_shared_db,
std::move(callback)),
task_runner_);
}
void ProtoDatabaseSelector::OnInitSharedDB(
std::unique_ptr<UniqueProtoDatabase> unique_db,
Enums::InitStatus unique_db_status,
bool use_shared_db,
Callbacks::InitStatusCallback callback,
scoped_refptr<SharedProtoDatabase> shared_db) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (shared_db) {
// If we have a reference to the shared database, try to get a client.
shared_db->GetClientAsync(
db_type_, use_shared_db,
base::BindOnce(&ProtoDatabaseSelector::OnGetSharedDBClient, this,
std::move(unique_db), unique_db_status, use_shared_db,
std::move(callback)));
return;
}
// Otherwise, we just call the OnGetSharedDBClient function with a nullptr
// client.
OnGetSharedDBClient(std::move(unique_db), unique_db_status, use_shared_db,
std::move(callback), nullptr, Enums::InitStatus::kError);
}
void ProtoDatabaseSelector::OnGetSharedDBClient(
std::unique_ptr<UniqueProtoDatabase> unique_db,
Enums::InitStatus unique_db_status,
bool use_shared_db,
Callbacks::InitStatusCallback callback,
std::unique_ptr<SharedProtoDatabaseClient> client,
Enums::InitStatus shared_db_status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!unique_db && !client) {
std::move(callback).Run(Enums::InitStatus::kError);
OnInitDone(ProtoDatabaseInitState::kBothUniqueAndSharedFailedOpen);
return;
}
if (!client) {
if (use_shared_db) {
// If there's no shared client and one is requested we return an error,
// because it should be created if missing.
std::move(callback).Run(Enums::InitStatus::kError);
OnInitDone(ProtoDatabaseInitState::kSharedDbClientMissingInitFailed);
return;
} else {
// ProtoLevelDBWrapper::InitWithDatabase() returns kInvalidOperation when
// a database doesn't exist and create_if_missing is false.
if (shared_db_status == Enums::InitStatus::kInvalidOperation) {
// If the shared DB doesn't exist and a unique DB is requested then we
// return the unique DB.
db_ = std::move(unique_db);
std::move(callback).Run(Enums::InitStatus::kOK);
OnInitDone(
ProtoDatabaseInitState::kSharedDbClientMissingUniqueReturned);
return;
} else {
// If the shared DB failed to open and a unique DB is requested then we
// throw an error, as the shared DB may contain unmigrated data.
std::move(callback).Run(Enums::InitStatus::kError);
OnInitDone(ProtoDatabaseInitState::kSharedDbOpenFailed);
return;
}
}
}
if (!unique_db) {
switch (client->migration_status()) {
case SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED:
// ProtoLevelDBWrapper::InitWithDatabase() returns kInvalidOperation
// when a database doesn't exist and create_if_missing is false.
if (unique_db_status == Enums::kInvalidOperation) {
// If the unique DB doesn't exist and the migration status is not
// attempted then we set the status to migrated to shared and return
// the shared DB. We don't check use_shared_db because we should only
// get here when use_shared_db is true, otherwise the unique_db is
// opened using create_if_missing
client->UpdateClientInitMetadata(
SharedDBMetadataProto::MIGRATE_TO_SHARED_SUCCESSFUL);
db_ = std::move(client);
std::move(callback).Run(Enums::InitStatus::kOK);
OnInitDone(ProtoDatabaseInitState::kUniqueDbMissingSharedReturned);
return;
} else {
// If the unique DB failed to open and the migration status is not
// attempted then we return an error, as we don't know if the unique
// DB contains any data.
std::move(callback).Run(Enums::InitStatus::kError);
OnInitDone(ProtoDatabaseInitState::kUniqueDbOpenFailed);
return;
}
break;
case SharedDBMetadataProto::MIGRATE_TO_SHARED_SUCCESSFUL:
case SharedDBMetadataProto::MIGRATE_TO_SHARED_UNIQUE_TO_BE_DELETED:
// If the unique DB failed to open, but the data is located in shared
// then we use the shared DB. We do the same when the unique DB is
// marked for deletion because there's no way to delete it.
// use_shared_db is ignored because we know the data is in shared DB,
// and there's no way to migrate.
db_ = std::move(client);
std::move(callback).Run(Enums::InitStatus::kOK);
OnInitDone(ProtoDatabaseInitState::kMigratedSharedDbOpened);
return;
break;
case SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SUCCESSFUL:
case SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SHARED_TO_BE_DELETED:
// If the unique DB failed to open, and the data is located on it then
// we throw an error. We ignore the deletion flag because we want both
// databases to be open before we delete the shared DB.
std::move(callback).Run(Enums::InitStatus::kError);
OnInitDone(ProtoDatabaseInitState::kUniqueDbOpenFailed);
return;
break;
}
}
// Both databases opened correctly. Migrate data and delete old DB if needed.
if (use_shared_db) {
switch (client->migration_status()) {
case SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED:
case SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SUCCESSFUL: {
// Migrate from unique to shared.
UniqueProtoDatabase* from = unique_db.get();
UniqueProtoDatabase* to = client.get();
RecordInitState(ProtoDatabaseInitState::kMigrateToSharedAttempted);
migration_delegate_->DoMigration(
from, to,
base::BindOnce(&ProtoDatabaseSelector::OnMigrationTransferComplete,
this, std::move(unique_db), std::move(client),
use_shared_db, std::move(callback)));
return;
}
case SharedDBMetadataProto::MIGRATE_TO_SHARED_SUCCESSFUL:
// Unique db was deleted in previous migration, so nothing to do here.
return OnMigrationCleanupComplete(std::move(unique_db),
std::move(client), use_shared_db,
std::move(callback), true);
case SharedDBMetadataProto::MIGRATE_TO_SHARED_UNIQUE_TO_BE_DELETED:
// Migration transfer was completed, so just try deleting the unique db.
return OnMigrationTransferComplete(std::move(unique_db),
std::move(client), use_shared_db,
std::move(callback), true);
case SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SHARED_TO_BE_DELETED:
// Shared db was not deleted in last migration and we want to use shared
// db. So, delete stale data, and attempt migration.
return DeleteOldDataAndMigrate(std::move(unique_db), std::move(client),
use_shared_db, std::move(callback));
}
} else {
switch (client->migration_status()) {
case SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED:
case SharedDBMetadataProto::MIGRATE_TO_SHARED_SUCCESSFUL: {
// Migrate from shared to unique.
UniqueProtoDatabase* from = client.get();
UniqueProtoDatabase* to = unique_db.get();
RecordInitState(ProtoDatabaseInitState::kMigrateToUniqueAttempted);
migration_delegate_->DoMigration(
from, to,
base::BindOnce(&ProtoDatabaseSelector::OnMigrationTransferComplete,
this, std::move(unique_db), std::move(client),
use_shared_db, std::move(callback)));
return;
}
case SharedDBMetadataProto::MIGRATE_TO_SHARED_UNIQUE_TO_BE_DELETED:
// Unique db was not deleted in last migration and we want to use unique
// db. So, delete stale data, and attempt migration.
return DeleteOldDataAndMigrate(std::move(unique_db), std::move(client),
use_shared_db, std::move(callback));
case SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SUCCESSFUL:
// Shared db was deleted in previous migration, so nothing to do here.
return OnMigrationCleanupComplete(std::move(unique_db),
std::move(client), use_shared_db,
std::move(callback), true);
case SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SHARED_TO_BE_DELETED:
// Migration transfer was completed, so just try deleting the shared db.
return OnMigrationTransferComplete(std::move(unique_db),
std::move(client), use_shared_db,
std::move(callback), true);
}
}
}
void ProtoDatabaseSelector::DeleteOldDataAndMigrate(
std::unique_ptr<UniqueProtoDatabase> unique_db,
std::unique_ptr<SharedProtoDatabaseClient> client,
bool use_shared_db,
Callbacks::InitStatusCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
UniqueProtoDatabase* to_remove_old_data =
use_shared_db ? client.get() : unique_db.get();
auto maybe_do_migration =
base::BindOnce(&ProtoDatabaseSelector::MaybeDoMigrationOnDeletingOld,
this, std::move(unique_db), std::move(client),
std::move(callback), use_shared_db);
to_remove_old_data->UpdateEntriesWithRemoveFilter(
std::make_unique<KeyValueVector>(),
base::BindRepeating([](const std::string& key) { return true; }),
std::move(maybe_do_migration));
}
void ProtoDatabaseSelector::MaybeDoMigrationOnDeletingOld(
std::unique_ptr<UniqueProtoDatabase> unique_db,
std::unique_ptr<SharedProtoDatabaseClient> client,
Callbacks::InitStatusCallback callback,
bool use_shared_db,
bool delete_success) {
if (!delete_success) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Old data has not been removed from the database we want to use. We also
// know that previous attempt of migration failed for same reason. Give up
// on this database and use the other.
// This update is not necessary since this was the old value. But update to
// be clear.
client->UpdateClientInitMetadata(
use_shared_db
? SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SHARED_TO_BE_DELETED
: SharedDBMetadataProto::MIGRATE_TO_SHARED_UNIQUE_TO_BE_DELETED);
db_ = use_shared_db ? std::move(unique_db) : std::move(client);
std::move(callback).Run(Enums::InitStatus::kOK);
OnInitDone(ProtoDatabaseInitState::kDeletionOfOldDataFailed);
return;
}
auto* from = use_shared_db ? unique_db.get() : client.get();
auto* to = use_shared_db ? client.get() : unique_db.get();
RecordInitState(use_shared_db
? ProtoDatabaseInitState::kMigrateToSharedAttempted
: ProtoDatabaseInitState::kMigrateToUniqueAttempted);
migration_delegate_->DoMigration(
from, to,
base::BindOnce(&ProtoDatabaseSelector::OnMigrationTransferComplete, this,
std::move(unique_db), std::move(client), use_shared_db,
std::move(callback)));
}
void ProtoDatabaseSelector::OnMigrationTransferComplete(
std::unique_ptr<UniqueProtoDatabase> unique_db,
std::unique_ptr<SharedProtoDatabaseClient> client,
bool use_shared_db,
Callbacks::InitStatusCallback callback,
bool success) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (success) {
// Call Destroy on the DB we no longer want to use.
auto* db_destroy_ptr = use_shared_db ? unique_db.get() : client.get();
db_destroy_ptr->Destroy(
base::BindOnce(&ProtoDatabaseSelector::OnMigrationCleanupComplete, this,
std::move(unique_db), std::move(client), use_shared_db,
std::move(callback)));
return;
}
// Failing to transfer the old data means that the requested database to be
// used could have some bad data. So, mark them to be deleted before use in
// the next runs.
client->UpdateClientInitMetadata(
use_shared_db
? SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SHARED_TO_BE_DELETED
: SharedDBMetadataProto::MIGRATE_TO_SHARED_UNIQUE_TO_BE_DELETED);
db_ = use_shared_db ? std::move(unique_db) : std::move(client);
std::move(callback).Run(Enums::InitStatus::kOK);
OnInitDone(use_shared_db ? ProtoDatabaseInitState::kMigrateToSharedFailed
: ProtoDatabaseInitState::kMigrateToUniqueFailed);
}
void ProtoDatabaseSelector::OnMigrationCleanupComplete(
std::unique_ptr<UniqueProtoDatabase> unique_db,
std::unique_ptr<SharedProtoDatabaseClient> client,
bool use_shared_db,
Callbacks::InitStatusCallback callback,
bool success) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// We still return true in our callback below because we do have a database as
// far as the original caller is concerned. As long as |db_| is assigned, we
// return true.
ProtoDatabaseInitState state;
if (success) {
client->UpdateClientInitMetadata(
use_shared_db ? SharedDBMetadataProto::MIGRATE_TO_SHARED_SUCCESSFUL
: SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SUCCESSFUL);
state = use_shared_db ? ProtoDatabaseInitState::kMigrateToSharedSuccess
: ProtoDatabaseInitState::kMigrateToUniqueSuccess;
} else {
client->UpdateClientInitMetadata(
use_shared_db
? SharedDBMetadataProto::MIGRATE_TO_SHARED_UNIQUE_TO_BE_DELETED
: SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SHARED_TO_BE_DELETED);
state =
use_shared_db
? ProtoDatabaseInitState::kMigrateToUniqueCompleteDeletionFailed
: ProtoDatabaseInitState::kMigrateToSharedCompleteDeletionFailed;
}
// Migration transfer was complete. So, we should use the requested database.
db_ = use_shared_db ? std::move(client) : std::move(unique_db);
std::move(callback).Run(Enums::InitStatus::kOK);
OnInitDone(state);
}
void ProtoDatabaseSelector::AddTransaction(base::OnceClosure task) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
switch (init_status_) {
case InitStatus::IN_PROGRESS:
if (pending_tasks_.size() > 10) {
std::move(pending_tasks_.front()).Run();
pending_tasks_.pop();
}
pending_tasks_.push(std::move(task));
break;
case InitStatus::NOT_STARTED:
NOTREACHED();
FALLTHROUGH;
case InitStatus::DONE:
std::move(task).Run();
break;
}
}
void ProtoDatabaseSelector::UpdateEntries(
std::unique_ptr<KeyValueVector> entries_to_save,
std::unique_ptr<KeyVector> keys_to_remove,
Callbacks::UpdateCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(init_status_, InitStatus::DONE);
if (!db_) {
std::move(callback).Run(false);
return;
}
db_->UpdateEntries(std::move(entries_to_save), std::move(keys_to_remove),
std::move(callback));
}
void ProtoDatabaseSelector::UpdateEntriesWithRemoveFilter(
std::unique_ptr<KeyValueVector> entries_to_save,
const KeyFilter& delete_key_filter,
Callbacks::UpdateCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!db_) {
std::move(callback).Run(false);
return;
}
db_->UpdateEntriesWithRemoveFilter(std::move(entries_to_save),
delete_key_filter, std::move(callback));
}
void ProtoDatabaseSelector::LoadEntries(
typename Callbacks::LoadCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!db_) {
std::move(callback).Run(false, nullptr);
return;
}
db_->LoadEntries(std::move(callback));
}
void ProtoDatabaseSelector::LoadEntriesWithFilter(
const KeyFilter& key_filter,
const leveldb::ReadOptions& options,
const std::string& target_prefix,
typename Callbacks::LoadCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!db_) {
std::move(callback).Run(false, nullptr);
return;
}
db_->LoadEntriesWithFilter(key_filter, options, target_prefix,
std::move(callback));
}
void ProtoDatabaseSelector::LoadKeysAndEntries(
typename Callbacks::LoadKeysAndEntriesCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!db_) {
std::move(callback).Run(false, nullptr);
return;
}
db_->LoadKeysAndEntries(std::move(callback));
}
void ProtoDatabaseSelector::LoadKeysAndEntriesWithFilter(
const KeyFilter& filter,
const leveldb::ReadOptions& options,
const std::string& target_prefix,
typename Callbacks::LoadKeysAndEntriesCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!db_) {
std::move(callback).Run(false, nullptr);
return;
}
db_->LoadKeysAndEntriesWithFilter(filter, options, target_prefix,
std::move(callback));
}
void ProtoDatabaseSelector::LoadKeysAndEntriesInRange(
const std::string& start,
const std::string& end,
typename Callbacks::LoadKeysAndEntriesCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!db_) {
std::move(callback).Run(false, nullptr);
return;
}
db_->LoadKeysAndEntriesInRange(start, end, std::move(callback));
}
void ProtoDatabaseSelector::LoadKeys(Callbacks::LoadKeysCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!db_) {
std::move(callback).Run(false, nullptr);
return;
}
db_->LoadKeys(std::move(callback));
}
void ProtoDatabaseSelector::GetEntry(const std::string& key,
typename Callbacks::GetCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!db_) {
std::move(callback).Run(false, nullptr);
return;
}
db_->GetEntry(key, std::move(callback));
}
void ProtoDatabaseSelector::Destroy(Callbacks::DestroyCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!db_) {
if (!unique_database_dir_.empty()) {
ProtoLevelDBWrapper::Destroy(unique_database_dir_, client_name_,
task_runner_, std::move(callback));
return;
}
std::move(callback).Run(false);
return;
}
db_->Destroy(std::move(callback));
}
void ProtoDatabaseSelector::RemoveKeysForTesting(
const KeyFilter& key_filter,
const std::string& target_prefix,
Callbacks::UpdateCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!db_) {
std::move(callback).Run(false);
return;
}
db_->RemoveKeysForTesting(key_filter, target_prefix, std::move(callback));
}
void ProtoDatabaseSelector::OnInitDone(
ProtoDatabaseSelector::ProtoDatabaseInitState state) {
RecordInitState(state);
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
init_status_ = InitStatus::DONE;
while (!pending_tasks_.empty()) {
task_runner_->PostTask(FROM_HERE, std::move(pending_tasks_.front()));
pending_tasks_.pop();
}
}
} // namespace leveldb_proto