blob: c95024cc5e0d67eac537d7607b01d9c211cdcb26 [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/sync/model_impl/blocking_model_type_store_impl.h"
#include <utility>
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/optional.h"
#include "components/sync/model/model_error.h"
#include "components/sync/model_impl/model_type_store_backend.h"
#include "components/sync/protocol/entity_metadata.pb.h"
#include "components/sync/protocol/model_type_state.pb.h"
#include "third_party/leveldatabase/src/include/leveldb/env.h"
#include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
namespace syncer {
namespace {
// Key prefix for data/metadata records.
const char kDataPrefix[] = "-dt-";
const char kMetadataPrefix[] = "-md-";
// Key for global metadata record.
const char kGlobalMetadataKey[] = "-GlobalMetadata";
// Formats key prefix for data records of |type|.
std::string FormatDataPrefix(ModelType type) {
return std::string(GetModelTypeRootTag(type)) + kDataPrefix;
}
// Formats key prefix for metadata records of |type|.
std::string FormatMetaPrefix(ModelType type) {
return std::string(GetModelTypeRootTag(type)) + kMetadataPrefix;
}
// Formats key for global metadata record of |type|.
std::string FormatGlobalMetadataKey(ModelType type) {
return std::string(GetModelTypeRootTag(type)) + kGlobalMetadataKey;
}
class LevelDbMetadataChangeList : public MetadataChangeList {
public:
LevelDbMetadataChangeList(ModelType type,
leveldb::WriteBatch* leveldb_write_batch)
: leveldb_write_batch_(leveldb_write_batch),
metadata_prefix_(FormatMetaPrefix(type)),
global_metadata_key_(FormatGlobalMetadataKey(type)) {
DCHECK(leveldb_write_batch_);
}
// MetadataChangeList implementation.
void UpdateModelTypeState(
const sync_pb::ModelTypeState& model_type_state) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
leveldb_write_batch_->Put(global_metadata_key_,
model_type_state.SerializeAsString());
}
void ClearModelTypeState() override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
leveldb_write_batch_->Delete(global_metadata_key_);
}
void UpdateMetadata(const std::string& storage_key,
const sync_pb::EntityMetadata& metadata) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
leveldb_write_batch_->Put(FormatMetadataKey(storage_key),
metadata.SerializeAsString());
}
void ClearMetadata(const std::string& storage_key) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
leveldb_write_batch_->Delete(FormatMetadataKey(storage_key));
}
private:
// Format key for metadata records with given id.
std::string FormatMetadataKey(const std::string& id) const {
return metadata_prefix_ + id;
}
leveldb::WriteBatch* const leveldb_write_batch_;
// Key for this type's metadata records.
const std::string metadata_prefix_;
const std::string global_metadata_key_;
SEQUENCE_CHECKER(sequence_checker_);
};
class LevelDbWriteBatch : public BlockingModelTypeStoreImpl::WriteBatch {
public:
static std::unique_ptr<leveldb::WriteBatch> ToLevelDbWriteBatch(
std::unique_ptr<LevelDbWriteBatch> batch) {
return std::move(batch->leveldb_write_batch_);
}
explicit LevelDbWriteBatch(ModelType type)
: type_(type),
data_prefix_(FormatDataPrefix(type)),
leveldb_write_batch_(std::make_unique<leveldb::WriteBatch>()),
metadata_change_list_(type, leveldb_write_batch_.get()) {}
~LevelDbWriteBatch() override {}
ModelType GetModelType() const { return type_; }
// WriteBatch implementation.
void WriteData(const std::string& id, const std::string& value) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
leveldb_write_batch_->Put(FormatDataKey(id), value);
}
void DeleteData(const std::string& id) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
leveldb_write_batch_->Delete(FormatDataKey(id));
}
MetadataChangeList* GetMetadataChangeList() override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return &metadata_change_list_;
}
private:
// Format key for data records with given id.
std::string FormatDataKey(const std::string& id) const {
return data_prefix_ + id;
}
const ModelType type_;
// Key prefix for data records of this model type.
const std::string data_prefix_;
std::unique_ptr<leveldb::WriteBatch> leveldb_write_batch_;
LevelDbMetadataChangeList metadata_change_list_;
SEQUENCE_CHECKER(sequence_checker_);
};
} // namespace
BlockingModelTypeStoreImpl::BlockingModelTypeStoreImpl(
ModelType type,
scoped_refptr<ModelTypeStoreBackend> backend)
: type_(type),
backend_(std::move(backend)),
data_prefix_(FormatDataPrefix(type)),
metadata_prefix_(FormatMetaPrefix(type)),
global_metadata_key_(FormatGlobalMetadataKey(type)) {
DCHECK(backend_);
}
BlockingModelTypeStoreImpl::~BlockingModelTypeStoreImpl() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
base::Optional<ModelError> BlockingModelTypeStoreImpl::ReadData(
const IdList& id_list,
RecordList* data_records,
IdList* missing_id_list) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(data_records);
DCHECK(missing_id_list);
return backend_->ReadRecordsWithPrefix(data_prefix_, id_list, data_records,
missing_id_list);
}
base::Optional<ModelError> BlockingModelTypeStoreImpl::ReadAllData(
RecordList* data_records) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(data_records);
return backend_->ReadAllRecordsWithPrefix(data_prefix_, data_records);
}
base::Optional<ModelError> BlockingModelTypeStoreImpl::ReadAllMetadata(
MetadataBatch* metadata_batch) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(metadata_batch);
// Read global metadata.
RecordList global_metadata_records;
IdList missing_global_metadata_id;
base::Optional<ModelError> error = backend_->ReadRecordsWithPrefix(
/*prefix=*/std::string(), {global_metadata_key_},
&global_metadata_records, &missing_global_metadata_id);
if (error.has_value()) {
return error;
}
// If global metadata is missing, no need to read further.
if (!missing_global_metadata_id.empty()) {
// Missing global metadata record is not an error; we can just return the
// default instance.
DCHECK_EQ(global_metadata_key_, missing_global_metadata_id[0]);
DCHECK(global_metadata_records.empty());
} else {
sync_pb::ModelTypeState state;
if (!state.ParseFromString(global_metadata_records[0].value)) {
return ModelError(FROM_HERE, "Failed to deserialize model type state.");
}
metadata_batch->SetModelTypeState(state);
}
// Read individual metadata records.
RecordList metadata_records;
error =
backend_->ReadAllRecordsWithPrefix(metadata_prefix_, &metadata_records);
if (error.has_value()) {
return error;
}
for (const Record& r : metadata_records) {
sync_pb::EntityMetadata entity_metadata;
if (!entity_metadata.ParseFromString(r.value)) {
return ModelError(FROM_HERE, "Failed to deserialize entity metadata.");
}
metadata_batch->AddMetadata(r.id, entity_metadata);
}
return base::nullopt;
}
std::unique_ptr<BlockingModelTypeStoreImpl::WriteBatch>
BlockingModelTypeStoreImpl::CreateWriteBatch() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return CreateWriteBatchForType(type_);
}
base::Optional<ModelError> BlockingModelTypeStoreImpl::CommitWriteBatch(
std::unique_ptr<WriteBatch> write_batch) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(write_batch);
std::unique_ptr<LevelDbWriteBatch> write_batch_impl(
static_cast<LevelDbWriteBatch*>(write_batch.release()));
DCHECK_EQ(write_batch_impl->GetModelType(), type_);
UMA_HISTOGRAM_ENUMERATION("Sync.ModelTypeStoreCommitCount",
ModelTypeToHistogramInt(type_),
static_cast<int>(MODEL_TYPE_COUNT));
return backend_->WriteModifications(
LevelDbWriteBatch::ToLevelDbWriteBatch(std::move(write_batch_impl)));
}
base::Optional<ModelError>
BlockingModelTypeStoreImpl::DeleteAllDataAndMetadata() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return backend_->DeleteDataAndMetadataForPrefix(GetModelTypeRootTag(type_));
}
// static
std::unique_ptr<BlockingModelTypeStoreImpl::WriteBatch>
BlockingModelTypeStoreImpl::CreateWriteBatchForType(ModelType type) {
return std::make_unique<LevelDbWriteBatch>(type);
}
} // namespace syncer