diff options
8 files changed, 349 insertions, 89 deletions
diff --git a/sync/api/attachments/attachment_store.cc b/sync/api/attachments/attachment_store.cc index b945c9f..7637835 100644 --- a/sync/api/attachments/attachment_store.cc +++ b/sync/api/attachments/attachment_store.cc @@ -117,6 +117,9 @@ AttachmentStoreForSync::AttachmentStoreForSync( sync_component_(sync_component) { } +AttachmentStoreForSync::~AttachmentStoreForSync() { +} + void AttachmentStoreForSync::SetSyncReference(const AttachmentIdList& ids) { frontend()->SetReference(sync_component_, ids); } @@ -126,4 +129,9 @@ void AttachmentStoreForSync::DropSyncReference(const AttachmentIdList& ids) { base::Bind(&NoOpDropCallback)); } +void AttachmentStoreForSync::ReadSyncMetadata( + const ReadMetadataCallback& callback) { + frontend()->ReadAllMetadata(sync_component_, callback); +} + } // namespace syncer diff --git a/sync/api/attachments/attachment_store.h b/sync/api/attachments/attachment_store.h index cc83619..b29a092 100644 --- a/sync/api/attachments/attachment_store.h +++ b/sync/api/attachments/attachment_store.h @@ -113,7 +113,8 @@ class SYNC_EXPORT AttachmentStore { void ReadMetadata(const AttachmentIdList& ids, const ReadMetadataCallback& callback); - // Asynchronously reads metadata for all attachments in the store. + // Asynchronously reads metadata for all attachments with |component_| + // reference in the store. // // |callback| will be invoked when finished. If any of the metadata entries // could not be read, |callback|'s Result will be UNSPECIFIED_ERROR. @@ -166,14 +167,23 @@ class SYNC_EXPORT AttachmentStore { // AttachmentService writes attachment on behalf of model type after download // and takes reference on attachment for the duration of upload. // Model type implementation shouldn't use this interface. -class AttachmentStoreForSync : public AttachmentStore { +class SYNC_EXPORT_PRIVATE AttachmentStoreForSync : public AttachmentStore { public: + ~AttachmentStoreForSync(); + // Asynchronously adds reference from sync to attachments. void SetSyncReference(const AttachmentIdList& ids); // Asynchronously drops sync reference from attachments. void DropSyncReference(const AttachmentIdList& ids); + // Asynchronously reads metadata for all attachments with |sync_component_| + // reference in the store. + // + // |callback| will be invoked when finished. If any of the metadata entries + // could not be read, |callback|'s Result will be UNSPECIFIED_ERROR. + void ReadSyncMetadata(const ReadMetadataCallback& callback); + private: friend class AttachmentStore; AttachmentStoreForSync(const scoped_refptr<AttachmentStoreFrontend>& frontend, diff --git a/sync/internal_api/attachments/attachment_store_test_template.h b/sync/internal_api/attachments/attachment_store_test_template.h index 80208a3..e8c4419 100644 --- a/sync/internal_api/attachments/attachment_store_test_template.h +++ b/sync/internal_api/attachments/attachment_store_test_template.h @@ -48,6 +48,7 @@ class AttachmentStoreTest : public testing::Test { AttachmentStoreFactory attachment_store_factory; base::MessageLoop message_loop; scoped_ptr<AttachmentStore> store; + scoped_ptr<AttachmentStoreForSync> store_for_sync; AttachmentStore::Result result; scoped_ptr<AttachmentMap> attachments; scoped_ptr<AttachmentIdList> failed_attachment_ids; @@ -65,6 +66,7 @@ class AttachmentStoreTest : public testing::Test { void SetUp() override { store = attachment_store_factory.CreateAttachmentStore(); + store_for_sync = store->CreateAttachmentStoreForSync(); Clear(); read_callback = base::Bind(&AttachmentStoreTest::CopyResultAttachments, @@ -351,7 +353,8 @@ TYPED_TEST_P(AttachmentStoreTest, ReadMetadata) { EXPECT_EQ(attachment2.GetId(), iter->GetId()); } -// Verify getting metadata for all attachments. +// Verify that ReadAllMetadata/ReadSyncMetadata returns metadata for correct +// set of attachments. TYPED_TEST_P(AttachmentStoreTest, ReadAllMetadata) { // Try to read all metadata from an empty store. this->store->ReadAllMetadata(this->read_metadata_callback); @@ -359,37 +362,148 @@ TYPED_TEST_P(AttachmentStoreTest, ReadAllMetadata) { EXPECT_EQ(AttachmentStore::SUCCESS, this->result); EXPECT_EQ(0U, this->attachment_metadata->size()); - // Create and write two attachments. - Attachment attachment1 = Attachment::Create(this->some_data1); - Attachment attachment2 = Attachment::Create(this->some_data2); + // Create and write attachments with different set of references. + Attachment attachment_mt = Attachment::Create(this->some_data1); + Attachment attachment_sync = Attachment::Create(this->some_data1); + Attachment attachment_both = Attachment::Create(this->some_data1); - AttachmentList some_attachments; - some_attachments.push_back(attachment1); - some_attachments.push_back(attachment2); - this->store->Write(some_attachments, this->write_callback); + AttachmentList attachments; + attachments.push_back(attachment_mt); + attachments.push_back(attachment_sync); + attachments.push_back(attachment_both); + this->store->Write(attachments, this->write_callback); this->ClearAndPumpLoop(); EXPECT_EQ(AttachmentStore::SUCCESS, this->result); - // Read all metadata again. + AttachmentIdList ids; + ids.push_back(attachment_sync.GetId()); + ids.push_back(attachment_both.GetId()); + this->store_for_sync->SetSyncReference(ids); + + ids.clear(); + ids.push_back(attachment_sync.GetId()); + this->store->Drop(ids, this->drop_callback); + this->ClearAndPumpLoop(); + + // Calling ReadMetadata for above three attachments should return all of them. + ids.clear(); + ids.push_back(attachment_sync.GetId()); + ids.push_back(attachment_sync.GetId()); + ids.push_back(attachment_both.GetId()); + this->store->ReadMetadata(ids, this->read_metadata_callback); + this->ClearAndPumpLoop(); + EXPECT_EQ(AttachmentStore::SUCCESS, this->result); + EXPECT_EQ(3U, this->attachment_metadata->size()); + + // Call to ReadAllMetadata() should only return attachments with model type + // reference. this->store->ReadAllMetadata(this->read_metadata_callback); this->ClearAndPumpLoop(); EXPECT_EQ(AttachmentStore::SUCCESS, this->result); EXPECT_EQ(2U, this->attachment_metadata->size()); // Verify that we get all attachments back (the order is undefined). - AttachmentIdSet ids; - ids.insert(attachment1.GetId()); - ids.insert(attachment2.GetId()); + AttachmentIdSet id_set; + id_set.insert(attachment_mt.GetId()); + id_set.insert(attachment_both.GetId()); + EXPECT_THAT(id_set, + testing::Contains((*this->attachment_metadata)[0].GetId())); + EXPECT_THAT(id_set, + testing::Contains((*this->attachment_metadata)[1].GetId())); + + // Call to ReadSyncMetadata() should only return attachments with sync + // reference. + this->store_for_sync->ReadSyncMetadata(this->read_metadata_callback); + this->ClearAndPumpLoop(); + EXPECT_EQ(AttachmentStore::SUCCESS, this->result); + EXPECT_EQ(2U, this->attachment_metadata->size()); - AttachmentMetadataList::const_iterator iter = - this->attachment_metadata->begin(); - const AttachmentMetadataList::const_iterator end = - this->attachment_metadata->end(); - for (; iter != end; ++iter) { - EXPECT_THAT(ids, testing::Contains(iter->GetId())); - ids.erase(iter->GetId()); - } - EXPECT_TRUE(ids.empty()); + id_set.clear(); + id_set.insert(attachment_sync.GetId()); + id_set.insert(attachment_both.GetId()); + EXPECT_THAT(id_set, + testing::Contains((*this->attachment_metadata)[0].GetId())); + EXPECT_THAT(id_set, + testing::Contains((*this->attachment_metadata)[1].GetId())); +} + +// Verify that setting/droping references gets reflected in ReadAllMetadata and +// that attachment is only deleted after last reference is droped. +TYPED_TEST_P(AttachmentStoreTest, SetSyncReference_DropSyncReference) { + Attachment attachment = Attachment::Create(this->some_data1); + AttachmentList attachments; + attachments.push_back(attachment); + AttachmentIdList ids; + ids.push_back(attachment.GetId()); + + // When writing attachment to store only model type reference should be set. + this->store->Write(attachments, this->write_callback); + + this->store->ReadAllMetadata(this->read_metadata_callback); + this->ClearAndPumpLoop(); + EXPECT_EQ(AttachmentStore::SUCCESS, this->result); + EXPECT_EQ(1U, this->attachment_metadata->size()); + EXPECT_EQ(attachment.GetId(), this->attachment_metadata->begin()->GetId()); + + this->store_for_sync->ReadSyncMetadata(this->read_metadata_callback); + this->ClearAndPumpLoop(); + EXPECT_EQ(AttachmentStore::SUCCESS, this->result); + EXPECT_EQ(0U, this->attachment_metadata->size()); + + // After call to SetSyncReference() ReadSyncMetadata should start returning + // attachment. + this->store_for_sync->SetSyncReference(ids); + + this->store_for_sync->ReadSyncMetadata(this->read_metadata_callback); + this->ClearAndPumpLoop(); + EXPECT_EQ(AttachmentStore::SUCCESS, this->result); + EXPECT_EQ(1U, this->attachment_metadata->size()); + + // Call SetSyncReference() to verify it is idempotent. + this->store_for_sync->SetSyncReference(ids); + this->ClearAndPumpLoop(); + + // Droping attachment should remove model type reference, but there is still + // sync reference. + this->store->Drop(ids, this->drop_callback); + this->ClearAndPumpLoop(); + EXPECT_EQ(AttachmentStore::SUCCESS, this->result); + + this->store->ReadAllMetadata(this->read_metadata_callback); + this->ClearAndPumpLoop(); + EXPECT_EQ(AttachmentStore::SUCCESS, this->result); + EXPECT_EQ(0U, this->attachment_metadata->size()); + + this->store_for_sync->ReadSyncMetadata(this->read_metadata_callback); + this->ClearAndPumpLoop(); + EXPECT_EQ(AttachmentStore::SUCCESS, this->result); + EXPECT_EQ(1U, this->attachment_metadata->size()); + + // ReadMetadata should still return this attachment even though it does not + // have model type reference. + this->store->ReadMetadata(ids, this->read_metadata_callback); + this->ClearAndPumpLoop(); + EXPECT_EQ(AttachmentStore::SUCCESS, this->result); + EXPECT_EQ(1U, this->attachment_metadata->size()); + + // Call Drop() again to ensure it doesn't fail. + this->store->Drop(ids, this->drop_callback); + this->ClearAndPumpLoop(); + EXPECT_EQ(AttachmentStore::SUCCESS, this->result); + + // After droping sync reference attachment should be deleted from store. + // ReadMetadata should return UNSPECIFIED_ERROR. + this->store_for_sync->DropSyncReference(ids); + + this->store_for_sync->ReadSyncMetadata(this->read_metadata_callback); + this->ClearAndPumpLoop(); + EXPECT_EQ(AttachmentStore::SUCCESS, this->result); + EXPECT_EQ(0U, this->attachment_metadata->size()); + + this->store->ReadMetadata(ids, this->read_metadata_callback); + this->ClearAndPumpLoop(); + EXPECT_EQ(AttachmentStore::UNSPECIFIED_ERROR, this->result); + EXPECT_EQ(0U, this->attachment_metadata->size()); } REGISTER_TYPED_TEST_CASE_P(AttachmentStoreTest, @@ -400,7 +514,8 @@ REGISTER_TYPED_TEST_CASE_P(AttachmentStoreTest, Drop_DropTwoButOnlyOneExists, Drop_DoesNotExist, ReadMetadata, - ReadAllMetadata); + ReadAllMetadata, + SetSyncReference_DropSyncReference); } // namespace syncer diff --git a/sync/internal_api/attachments/in_memory_attachment_store.cc b/sync/internal_api/attachments/in_memory_attachment_store.cc index b0fb846..f69f102 100644 --- a/sync/internal_api/attachments/in_memory_attachment_store.cc +++ b/sync/internal_api/attachments/in_memory_attachment_store.cc @@ -43,16 +43,13 @@ void InMemoryAttachmentStore::Read( const AttachmentStore::ReadCallback& callback) { DCHECK(CalledOnValidThread()); AttachmentStore::Result result_code = AttachmentStore::SUCCESS; - AttachmentIdList::const_iterator id_iter = ids.begin(); - AttachmentIdList::const_iterator id_end = ids.end(); scoped_ptr<AttachmentMap> result_map(new AttachmentMap); scoped_ptr<AttachmentIdList> unavailable_attachments(new AttachmentIdList); - for (; id_iter != id_end; ++id_iter) { - const AttachmentId& id = *id_iter; - syncer::AttachmentMap::iterator attachment_iter = - attachments_.find(*id_iter); + + for (const auto& id : ids) { + AttachmentEntryMap::iterator attachment_iter = attachments_.find(id); if (attachment_iter != attachments_.end()) { - const Attachment& attachment = attachment_iter->second; + const Attachment& attachment = attachment_iter->second.attachment; result_map->insert(std::make_pair(id, attachment)); } else { unavailable_attachments->push_back(id); @@ -70,10 +67,9 @@ void InMemoryAttachmentStore::Write( const AttachmentList& attachments, const AttachmentStore::WriteCallback& callback) { DCHECK(CalledOnValidThread()); - AttachmentList::const_iterator iter = attachments.begin(); - AttachmentList::const_iterator end = attachments.end(); - for (; iter != end; ++iter) { - attachments_.insert(std::make_pair(iter->GetId(), *iter)); + for (const auto& attachment : attachments) { + attachments_.insert(std::make_pair(attachment.GetId(), + AttachmentEntry(attachment, component))); } PostCallback(base::Bind(callback, AttachmentStore::SUCCESS)); } @@ -81,7 +77,12 @@ void InMemoryAttachmentStore::Write( void InMemoryAttachmentStore::SetReference(AttachmentStore::Component component, const AttachmentIdList& ids) { DCHECK(CalledOnValidThread()); - DCHECK_EQ(AttachmentStore::SYNC, component); + for (const auto& id : ids) { + AttachmentEntryMap::iterator attachments_iter = attachments_.find(id); + if (attachments_iter != attachments_.end()) { + attachments_iter->second.components.insert(component); + } + } } void InMemoryAttachmentStore::DropReference( @@ -90,18 +91,13 @@ void InMemoryAttachmentStore::DropReference( const AttachmentStore::DropCallback& callback) { DCHECK(CalledOnValidThread()); AttachmentStore::Result result = AttachmentStore::SUCCESS; - if (component == AttachmentStore::SYNC) { - // TODO(pavely): There is no reference handling implementation yet. All - // calls to AddReferrer are ignored. Calls to Drop coming from sync should - // be ignored too. - PostCallback(base::Bind(callback, AttachmentStore::SUCCESS)); - return; - } - AttachmentIdList::const_iterator ids_iter = ids.begin(); - AttachmentIdList::const_iterator ids_end = ids.end(); - for (; ids_iter != ids_end; ++ids_iter) { - AttachmentMap::iterator attachments_iter = attachments_.find(*ids_iter); - if (attachments_iter != attachments_.end()) { + for (const auto& id : ids) { + AttachmentEntryMap::iterator attachments_iter = attachments_.find(id); + if (attachments_iter == attachments_.end()) { + continue; + } + attachments_iter->second.components.erase(component); + if (attachments_iter->second.components.empty()) { attachments_.erase(attachments_iter); } } @@ -115,13 +111,13 @@ void InMemoryAttachmentStore::ReadMetadata( AttachmentStore::Result result_code = AttachmentStore::SUCCESS; scoped_ptr<AttachmentMetadataList> metadata_list( new AttachmentMetadataList()); - AttachmentIdList::const_iterator ids_iter = ids.begin(); - AttachmentIdList::const_iterator ids_end = ids.end(); - for (; ids_iter != ids_end; ++ids_iter) { - AttachmentMap::iterator attachments_iter = attachments_.find(*ids_iter); + for (const auto& id : ids) { + // TODO(pavely): ReadMetadata should only return attachments with component + // reference similarly to ReadAllMetadata behavior. + AttachmentEntryMap::iterator attachments_iter = attachments_.find(id); if (attachments_iter != attachments_.end()) { - AppendMetadata(metadata_list.get(), attachments_iter->second); + AppendMetadata(metadata_list.get(), attachments_iter->second.attachment); } else { result_code = AttachmentStore::UNSPECIFIED_ERROR; } @@ -137,11 +133,23 @@ void InMemoryAttachmentStore::ReadAllMetadata( scoped_ptr<AttachmentMetadataList> metadata_list( new AttachmentMetadataList()); - for (AttachmentMap::const_iterator iter = attachments_.begin(); + for (AttachmentEntryMap::const_iterator iter = attachments_.begin(); iter != attachments_.end(); ++iter) { - AppendMetadata(metadata_list.get(), iter->second); + if (iter->second.components.count(component) > 0) { + AppendMetadata(metadata_list.get(), iter->second.attachment); + } } PostCallback(base::Bind(callback, result_code, base::Passed(&metadata_list))); } +InMemoryAttachmentStore::AttachmentEntry::AttachmentEntry( + const Attachment& attachment, + AttachmentStore::Component initial_reference_component) + : attachment(attachment) { + components.insert(initial_reference_component); +} + +InMemoryAttachmentStore::AttachmentEntry::~AttachmentEntry() { +} + } // namespace syncer diff --git a/sync/internal_api/attachments/on_disk_attachment_store.cc b/sync/internal_api/attachments/on_disk_attachment_store.cc index a2128a5..00b4019 100644 --- a/sync/internal_api/attachments/on_disk_attachment_store.cc +++ b/sync/internal_api/attachments/on_disk_attachment_store.cc @@ -37,6 +37,20 @@ const int32 kCurrentSchemaVersion = 1; const base::FilePath::CharType kLeveldbDirectory[] = FILE_PATH_LITERAL("leveldb"); +// Converts syncer::AttachmentStore::Component values into +// attachment_store_pb::RecordMetadata::Component. +attachment_store_pb::RecordMetadata::Component ComponentToProto( + syncer::AttachmentStore::Component component) { + switch (component) { + case AttachmentStore::MODEL_TYPE: + return attachment_store_pb::RecordMetadata::MODEL_TYPE; + case AttachmentStore::SYNC: + return attachment_store_pb::RecordMetadata::SYNC; + } + NOTREACHED(); + return attachment_store_pb::RecordMetadata::UNKNOWN; +} + leveldb::WriteOptions MakeWriteOptions() { leveldb::WriteOptions write_options; write_options.sync = true; @@ -80,6 +94,44 @@ leveldb::Status WriteStoreMetadata( return db->Put(MakeWriteOptions(), kDatabaseMetadataKey, data_str); } +// Adds reference to component into RecordMetadata::component set. +// Returns true if record_metadata was modified and needs to be written to disk. +bool SetReferenceInRecordMetadata( + attachment_store_pb::RecordMetadata* record_metadata, + attachment_store_pb::RecordMetadata::Component proto_component) { + DCHECK(record_metadata); + for (const int component : record_metadata->component()) { + if (component == proto_component) + return false; + } + record_metadata->add_component(proto_component); + return true; +} + +// Drops reference to component from RecordMetadata::component set. +// Returns true if record_metadata was modified and needs to be written to disk. +bool DropReferenceInRecordMetadata( + attachment_store_pb::RecordMetadata* record_metadata, + attachment_store_pb::RecordMetadata::Component proto_component) { + DCHECK(record_metadata); + bool component_removed = false; + ::google::protobuf::RepeatedField<int>* mutable_components = + record_metadata->mutable_component(); + for (int i = 0; i < mutable_components->size();) { + if (mutable_components->Get(i) == proto_component) { + if (i < mutable_components->size() - 1) { + // Don't swap last element with itself. + mutable_components->SwapElements(i, mutable_components->size() - 1); + } + mutable_components->RemoveLast(); + component_removed = true; + } else { + ++i; + } + } + return component_removed; +} + } // namespace OnDiskAttachmentStore::OnDiskAttachmentStore( @@ -147,7 +199,7 @@ void OnDiskAttachmentStore::Write( AttachmentList::const_iterator iter = attachments.begin(); const AttachmentList::const_iterator end = attachments.end(); for (; iter != end; ++iter) { - if (!WriteSingleAttachment(*iter)) + if (!WriteSingleAttachment(*iter, component)) result_code = AttachmentStore::UNSPECIFIED_ERROR; } } @@ -157,7 +209,17 @@ void OnDiskAttachmentStore::Write( void OnDiskAttachmentStore::SetReference(AttachmentStore::Component component, const AttachmentIdList& ids) { DCHECK(CalledOnValidThread()); - DCHECK_EQ(AttachmentStore::SYNC, component); + if (!db_) + return; + attachment_store_pb::RecordMetadata::Component proto_component = + ComponentToProto(component); + for (const auto& id : ids) { + attachment_store_pb::RecordMetadata record_metadata; + if (!ReadSingleRecordMetadata(id, &record_metadata)) + continue; + if (SetReferenceInRecordMetadata(&record_metadata, proto_component)) + WriteSingleRecordMetadata(id, record_metadata); + } } void OnDiskAttachmentStore::DropReference( @@ -165,31 +227,35 @@ void OnDiskAttachmentStore::DropReference( const AttachmentIdList& ids, const AttachmentStore::DropCallback& callback) { DCHECK(CalledOnValidThread()); - if (component == AttachmentStore::SYNC) { - // TODO(pavely): There is no reference handling implementation yet. All - // calls to AddReferrer are ignored. Calls to Drop coming from sync should - // be ignored too. - PostCallback(base::Bind(callback, AttachmentStore::SUCCESS)); - return; - } AttachmentStore::Result result_code = AttachmentStore::STORE_INITIALIZATION_FAILED; if (db_) { + attachment_store_pb::RecordMetadata::Component proto_component = + ComponentToProto(component); result_code = AttachmentStore::SUCCESS; leveldb::WriteOptions write_options = MakeWriteOptions(); - AttachmentIdList::const_iterator iter = ids.begin(); - const AttachmentIdList::const_iterator end = ids.end(); - for (; iter != end; ++iter) { - leveldb::WriteBatch write_batch; - write_batch.Delete(MakeDataKeyFromAttachmentId(*iter)); - write_batch.Delete(MakeMetadataKeyFromAttachmentId(*iter)); - - leveldb::Status status = db_->Write(write_options, &write_batch); - if (!status.ok()) { - // DB::Delete doesn't check if record exists, it returns ok just like - // AttachmentStore::Drop should. - DVLOG(1) << "DB::Write failed: status=" << status.ToString(); - result_code = AttachmentStore::UNSPECIFIED_ERROR; + for (const auto& id : ids) { + attachment_store_pb::RecordMetadata record_metadata; + if (!ReadSingleRecordMetadata(id, &record_metadata)) + continue; // Record not found. + if (!DropReferenceInRecordMetadata(&record_metadata, proto_component)) + continue; // Component is not in components set. Metadata was not + // updated. + if (record_metadata.component_size() == 0) { + // Last reference dropped. Need to delete attachment. + leveldb::WriteBatch write_batch; + write_batch.Delete(MakeDataKeyFromAttachmentId(id)); + write_batch.Delete(MakeMetadataKeyFromAttachmentId(id)); + + leveldb::Status status = db_->Write(write_options, &write_batch); + if (!status.ok()) { + // DB::Delete doesn't check if record exists, it returns ok just like + // AttachmentStore::Drop should. + DVLOG(1) << "DB::Write failed: status=" << status.ToString(); + result_code = AttachmentStore::UNSPECIFIED_ERROR; + } + } else { + WriteSingleRecordMetadata(id, record_metadata); } } } @@ -206,13 +272,12 @@ void OnDiskAttachmentStore::ReadMetadata( new AttachmentMetadataList()); if (db_) { result_code = AttachmentStore::SUCCESS; - AttachmentIdList::const_iterator iter = ids.begin(); - const AttachmentIdList::const_iterator end = ids.end(); - for (; iter != end; ++iter) { + // TODO(pavely): ReadMetadata should only return attachments with component + // reference similarly to ReadAllMetadata behavior. + for (const auto& id : ids) { attachment_store_pb::RecordMetadata record_metadata; - if (ReadSingleRecordMetadata(*iter, &record_metadata)) { - metadata_list->push_back( - MakeAttachmentMetadata(*iter, record_metadata)); + if (ReadSingleRecordMetadata(id, &record_metadata)) { + metadata_list->push_back(MakeAttachmentMetadata(id, record_metadata)); } else { result_code = AttachmentStore::UNSPECIFIED_ERROR; } @@ -231,6 +296,8 @@ void OnDiskAttachmentStore::ReadAllMetadata( new AttachmentMetadataList()); if (db_) { + attachment_store_pb::RecordMetadata::Component proto_component = + ComponentToProto(component); result_code = AttachmentStore::SUCCESS; scoped_ptr<leveldb::Iterator> db_iterator( db_->NewIterator(MakeNonCachingReadOptions())); @@ -253,7 +320,16 @@ void OnDiskAttachmentStore::ReadAllMetadata( result_code = AttachmentStore::UNSPECIFIED_ERROR; continue; } - metadata_list->push_back(MakeAttachmentMetadata(id, record_metadata)); + // Check if attachment has reference from component. + bool has_reference_from_component = false; + for (const auto& reference_component : record_metadata.component()) { + if (reference_component == proto_component) { + has_reference_from_component = true; + break; + } + } + if (has_reference_from_component) + metadata_list->push_back(MakeAttachmentMetadata(id, record_metadata)); } if (!db_iterator->status().ok()) { @@ -350,7 +426,8 @@ scoped_ptr<Attachment> OnDiskAttachmentStore::ReadSingleAttachment( } bool OnDiskAttachmentStore::WriteSingleAttachment( - const Attachment& attachment) { + const Attachment& attachment, + AttachmentStore::Component component) { const std::string metadata_key = MakeMetadataKeyFromAttachmentId(attachment.GetId()); const std::string data_key = MakeDataKeyFromAttachmentId(attachment.GetId()); @@ -373,6 +450,7 @@ bool OnDiskAttachmentStore::WriteSingleAttachment( attachment_store_pb::RecordMetadata metadata; metadata.set_attachment_size(attachment.GetData()->size()); metadata.set_crc32c(attachment.GetCrc32c()); + SetReferenceInRecordMetadata(&metadata, ComponentToProto(component)); metadata_str = metadata.SerializeAsString(); write_batch.Put(metadata_key, metadata_str); // Write data. @@ -409,6 +487,22 @@ bool OnDiskAttachmentStore::ReadSingleRecordMetadata( return true; } +bool OnDiskAttachmentStore::WriteSingleRecordMetadata( + const AttachmentId& attachment_id, + const attachment_store_pb::RecordMetadata& record_metadata) { + const std::string metadata_key = + MakeMetadataKeyFromAttachmentId(attachment_id); + std::string metadata_str; + metadata_str = record_metadata.SerializeAsString(); + leveldb::Status status = + db_->Put(MakeWriteOptions(), metadata_key, metadata_str); + if (!status.ok()) { + DVLOG(1) << "DB::Put failed: status=" << status.ToString(); + return false; + } + return true; +} + std::string OnDiskAttachmentStore::MakeDataKeyFromAttachmentId( const AttachmentId& attachment_id) { std::string key = kDataPrefix + attachment_id.GetProto().unique_id(); diff --git a/sync/internal_api/attachments/proto/attachment_store.proto b/sync/internal_api/attachments/proto/attachment_store.proto index a05e1c2..7985df9 100644 --- a/sync/internal_api/attachments/proto/attachment_store.proto +++ b/sync/internal_api/attachments/proto/attachment_store.proto @@ -26,4 +26,13 @@ message RecordMetadata { optional int64 attachment_size = 1; // Crc32c of attachment data. optional fixed32 crc32c = 2; + + // Component enum mirrors values of AttachmentStore::Component. + enum Component { + UNKNOWN = 0; + MODEL_TYPE = 1; + SYNC = 2; + } + // Set of components that reference this attachment. + repeated Component component = 3; } diff --git a/sync/internal_api/public/attachments/in_memory_attachment_store.h b/sync/internal_api/public/attachments/in_memory_attachment_store.h index e511474..3e53ab1 100644 --- a/sync/internal_api/public/attachments/in_memory_attachment_store.h +++ b/sync/internal_api/public/attachments/in_memory_attachment_store.h @@ -49,7 +49,17 @@ class SYNC_EXPORT InMemoryAttachmentStore : public AttachmentStoreBackend, const AttachmentStore::ReadMetadataCallback& callback) override; private: - AttachmentMap attachments_; + struct AttachmentEntry { + AttachmentEntry(const Attachment& attachment, + AttachmentStore::Component initial_reference_component); + ~AttachmentEntry(); + + Attachment attachment; + std::set<AttachmentStore::Component> components; + }; + + typedef std::map<AttachmentId, AttachmentEntry> AttachmentEntryMap; + AttachmentEntryMap attachments_; DISALLOW_COPY_AND_ASSIGN(InMemoryAttachmentStore); }; diff --git a/sync/internal_api/public/attachments/on_disk_attachment_store.h b/sync/internal_api/public/attachments/on_disk_attachment_store.h index bde55c8..3432853 100644 --- a/sync/internal_api/public/attachments/on_disk_attachment_store.h +++ b/sync/internal_api/public/attachments/on_disk_attachment_store.h @@ -69,12 +69,18 @@ class SYNC_EXPORT OnDiskAttachmentStore : public AttachmentStoreBackend, scoped_ptr<Attachment> ReadSingleAttachment( const AttachmentId& attachment_id); // Writes single attachment to store. Returns false in case of errors. - bool WriteSingleAttachment(const Attachment& attachment); - // Reads single store_pb::RecordMetadata from levelDB into the provided - // buffer. Returns false in case of an error. + bool WriteSingleAttachment(const Attachment& attachment, + AttachmentStore::Component component); + // Reads single attachment_store_pb::RecordMetadata from levelDB into the + // provided buffer. Returns false in case of an error. bool ReadSingleRecordMetadata( const AttachmentId& attachment_id, attachment_store_pb::RecordMetadata* record_metadata); + // Writes single attachment_store_pb::RecordMetadata to levelDB. Returns false + // in case of an error. + bool WriteSingleRecordMetadata( + const AttachmentId& attachment_id, + const attachment_store_pb::RecordMetadata& record_metadata); static std::string MakeDataKeyFromAttachmentId( const AttachmentId& attachment_id); |