summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sync/api/attachments/attachment_store.cc8
-rw-r--r--sync/api/attachments/attachment_store.h14
-rw-r--r--sync/internal_api/attachments/attachment_store_test_template.h159
-rw-r--r--sync/internal_api/attachments/in_memory_attachment_store.cc70
-rw-r--r--sync/internal_api/attachments/on_disk_attachment_store.cc154
-rw-r--r--sync/internal_api/attachments/proto/attachment_store.proto9
-rw-r--r--sync/internal_api/public/attachments/in_memory_attachment_store.h12
-rw-r--r--sync/internal_api/public/attachments/on_disk_attachment_store.h12
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);