blob: 7af767fcb02090085a74fce971c566cf1da000b0 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/data_sharing/migration/internal/migration_state_database_impl.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/task/thread_pool.h"
namespace data_sharing {
namespace {
constexpr char kMigrationStateTableName[] = "migration_state";
constexpr int kCurrentSchemaVersion = 1;
// TODO(crbug.com/301390275): make this consistent with other places where
// amount of groups are limited once numbers are known.
constexpr size_t kMaxNumEntriesInDB = 20000;
bool InitOnDBSequence(
base::FilePath profile_dir,
sql::Database* db,
sqlite_proto::ProtoTableManager* table_manager,
sqlite_proto::KeyValueData<data_sharing_pb::MigrationState>*
migration_state_data) {
CHECK(db);
CHECK(table_manager);
CHECK(migration_state_data);
base::FilePath db_dir = profile_dir.Append(FILE_PATH_LITERAL("DataSharing"));
if (!base::CreateDirectory(db_dir)) {
LOG(ERROR) << "Failed to create or open DB directory: " << db_dir.value();
return false;
}
const base::FilePath db_path =
db_dir.Append(FILE_PATH_LITERAL("MigrationStateDatabase"));
if (!db->Open(db_path)) {
LOG(ERROR) << "Failed to open DB " << db_path << ": "
<< db->GetErrorMessage();
return false;
}
table_manager->InitializeOnDbSequence(
db, std::vector<std::string>{kMigrationStateTableName},
kCurrentSchemaVersion);
migration_state_data->InitializeOnDBSequence();
return true;
}
} // namespace
MigrationStateDatabaseImpl::MigrationStateDatabaseImpl(
const base::FilePath& profile_dir)
: profile_path_(profile_dir),
db_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
db_(std::make_unique<sql::Database>(
sql::Database::Tag("DataSharingMigrationStateStorage"))),
proto_table_manager_(
base::MakeRefCounted<sqlite_proto::ProtoTableManager>(
db_task_runner_)),
migration_state_table_(
std::make_unique<
sqlite_proto::KeyValueTable<data_sharing_pb::MigrationState>>(
kMigrationStateTableName)),
migration_state_data_(
std::make_unique<
sqlite_proto::KeyValueData<data_sharing_pb::MigrationState>>(
proto_table_manager_,
migration_state_table_.get(),
kMaxNumEntriesInDB,
/*flush_delay=*/base::TimeDelta())) {}
MigrationStateDatabaseImpl::~MigrationStateDatabaseImpl() {
// Shutdown `proto_table_manager_` and delete it together with `db_` and
// KeyValueTable on DB sequence, then deletes KeyValueData and runs
// `shutdown_callback_` on the main sequence.
// This ensures that DB objects outlive any other task posted to DB sequence,
// since their deletion is the very last posted task.
db_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
[](std::unique_ptr<sql::Database> db,
scoped_refptr<sqlite_proto::ProtoTableManager> table_manager,
auto migration_state_table) { table_manager->WillShutdown(); },
std::move(db_), std::move(proto_table_manager_),
std::move(migration_state_table_)));
}
void MigrationStateDatabaseImpl::Init(InitCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Unretained() for DB objects is safe here, because `this` destructor ensures
// that these objects outlive any task posted to DB sequence.
db_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&InitOnDBSequence, profile_path_,
base::Unretained(db_.get()),
base::Unretained(proto_table_manager_.get()),
base::Unretained(migration_state_data_.get())),
base::BindOnce(&MigrationStateDatabaseImpl::OnInitialized,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
std::optional<data_sharing_pb::MigrationState>
MigrationStateDatabaseImpl::GetMigrationState(
const ContextId& context_id) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(is_initialized_);
data_sharing_pb::MigrationState state;
if (!migration_state_data_->TryGetData(context_id.value(), &state)) {
return std::nullopt;
}
return state;
}
void MigrationStateDatabaseImpl::UpdateMigrationState(
const ContextId& context_id,
const data_sharing_pb::MigrationState& state) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(is_initialized_);
migration_state_data_->UpdateData(context_id.value(), state);
}
void MigrationStateDatabaseImpl::DeleteMigrationState(
const ContextId& context_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(is_initialized_);
migration_state_data_->DeleteData({context_id.value()});
}
void MigrationStateDatabaseImpl::OnInitialized(InitCallback callback,
bool success) {
is_initialized_ = success;
std::move(callback).Run(success);
}
} // namespace data_sharing