Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(168)

Side by Side Diff: ios/chrome/browser/reading_list/reading_list_store.cc

Issue 2451843002: Add Store+Sync to reading list. (Closed)
Patch Set: reading_list_model_unittests Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ios/chrome/browser/reading_list/reading_list_store.h"
6
7 #include "base/bind.h"
8 #include "base/files/file_path.h"
9 #include "base/logging.h"
10 #include "base/memory/ptr_util.h"
11 #include "components/sync/model/entity_change.h"
12 #include "components/sync/model/metadata_batch.h"
13 #include "components/sync/model/metadata_change_list.h"
14 #include "components/sync/model/model_type_change_processor.h"
15 #include "components/sync/model/mutable_data_batch.h"
16 #include "components/sync/model/simple_metadata_change_list.h"
17 #include "components/sync/protocol/model_type_state.pb.h"
18 #include "ios/chrome/browser/reading_list/proto/reading_list.pb.h"
19 #include "ios/chrome/browser/reading_list/reading_list_model_impl.h"
20 #include "ios/web/public/web_thread.h"
21
22 ReadingListStore::ReadingListStore(std::unique_ptr<ReadingListDB> database,
23 const base::FilePath& database_dir,
24 StoreFactoryFunction create_store_callback)
25 : ModelTypeService(base::Bind(&syncer::ModelTypeChangeProcessor::Create),
26 syncer::READING_LIST),
27 database_loaded_(false),
28 create_store_callback_(create_store_callback),
29 pending_transaction_(0) {}
30
31 ReadingListStore::~ReadingListStore() {
32 DCHECK(pending_transaction_ == 0);
33 }
34
35 void ReadingListStore::SetReadingListModel(ReadingListModelImpl* model) {
36 DCHECK(CalledOnValidThread());
37 model_ = model;
38 create_store_callback_.Run(
39 base::Bind(&ReadingListStore::OnStoreCreated, base::AsWeakPtr(this)));
40 }
41
42 void ReadingListStore::BeginTransaction() {
43 DCHECK(CalledOnValidThread());
44 pending_transaction_++;
45 if (pending_transaction_ == 1) {
46 batch_ = store_->CreateWriteBatch();
47 }
48 }
49
50 void ReadingListStore::CommitTransaction() {
51 DCHECK(CalledOnValidThread());
52 pending_transaction_--;
53 if (pending_transaction_ == 0) {
54 store_->CommitWriteBatch(
55 std::move(batch_),
56 base::Bind(&ReadingListStore::OnDatabaseSave, base::AsWeakPtr(this)));
57 batch_.reset();
58 }
59 }
60
61 void ReadingListStore::SaveEntry(const ReadingListEntry& entry,
62 bool read,
63 bool from_sync) {
64 DCHECK(CalledOnValidThread());
65 BeginTransaction();
66
67 std::unique_ptr<reading_list::ReadingListLocal> pb_entry =
68 entry.AsReadingListLocal(read);
69
70 store_->WriteData(batch_.get(), entry.URL().spec(),
71 pb_entry->SerializeAsString());
72
73 if (!change_processor()->IsTrackingMetadata() || from_sync) {
74 CommitTransaction();
75 return;
76 }
77 std::unique_ptr<sync_pb::ReadingListSpecifics> pb_entry_sync =
78 entry.AsReadingListSpecifics(read);
79
80 std::unique_ptr<syncer::MetadataChangeList> metadata_change_list =
81 CreateMetadataChangeList();
82
83 std::unique_ptr<syncer::EntityData> entity_data(new syncer::EntityData());
84 *entity_data->specifics.mutable_reading_list() = *pb_entry_sync;
85 entity_data->non_unique_name = pb_entry_sync->entry_id();
86
87 change_processor()->Put(entry.URL().spec(), std::move(entity_data),
88 metadata_change_list.get());
89
90 static_cast<syncer::SimpleMetadataChangeList*>(metadata_change_list.get())
91 ->TransferChanges(store_.get(), batch_.get());
92 CommitTransaction();
93 }
94
95 void ReadingListStore::RemoveEntry(const ReadingListEntry& entry,
96 bool from_sync) {
97 DCHECK(CalledOnValidThread());
98 BeginTransaction();
99 store_->DeleteData(batch_.get(), entry.URL().spec());
100 if (!change_processor()->IsTrackingMetadata() || from_sync) {
101 CommitTransaction();
102 return;
103 }
104 std::unique_ptr<syncer::MetadataChangeList> metadata_change_list =
105 CreateMetadataChangeList();
106
107 change_processor()->Delete(entry.URL().spec(), metadata_change_list.get());
108 static_cast<syncer::SimpleMetadataChangeList*>(metadata_change_list.get())
109 ->TransferChanges(store_.get(), batch_.get());
110
111 CommitTransaction();
112 }
113
114 void ReadingListStore::OnDatabaseLoad(
115 syncer::ModelTypeStore::Result result,
116 std::unique_ptr<syncer::ModelTypeStore::RecordList> entries) {
117 DCHECK(CalledOnValidThread());
118 if (result != syncer::ModelTypeStore::Result::SUCCESS) {
119 return;
120 }
121 database_loaded_ = true;
122 auto read = base::MakeUnique<ReadingListEntries>();
123 auto unread = base::MakeUnique<ReadingListEntries>();
124
125 for (const syncer::ModelTypeStore::Record& r : *entries.get()) {
126 // for (const reading_list::ReadingListLocal& pb_entry : *entries) {
127 std::unique_ptr<reading_list::ReadingListLocal> proto =
128 base::MakeUnique<reading_list::ReadingListLocal>();
129 if (!proto->ParseFromString(r.value)) {
130 continue;
131 // TODO(skym, crbug.com/582460): Handle unrecoverable initialization
132 // failure.
133 }
134
135 std::unique_ptr<ReadingListEntry> entry(
136 ReadingListEntry::FromReadingListLocal(*proto));
137 if (!entry) {
138 continue;
139 }
140 if (proto->status() == reading_list::ReadingListLocal::READ) {
141 read->push_back(std::move(*entry));
142 } else {
143 unread->push_back(std::move(*entry));
144 }
145 }
146
147 model_->ModelLoaded(std::move(unread), std::move(read));
148
149 store_->ReadAllMetadata(
150 base::Bind(&ReadingListStore::OnReadAllMetadata, base::AsWeakPtr(this)));
151 }
152
153 void ReadingListStore::OnReadAllMetadata(
154 syncer::ModelTypeStore::Result result,
155 std::unique_ptr<syncer::ModelTypeStore::RecordList> metadata_records,
156 const std::string& global_metadata) {
157 DCHECK(CalledOnValidThread());
158 if (result != syncer::ModelTypeStore::Result::SUCCESS) {
159 // Store has encountered some serious error. We should still be able to
160 // continue as a read only service, since if we got this far we must have
161 // loaded all data out succesfully.
162 return;
163 }
164
165 std::unique_ptr<syncer::MetadataBatch> batch(new syncer::MetadataBatch());
166 sync_pb::ModelTypeState state;
167 if (state.ParseFromString(global_metadata)) {
168 batch->SetModelTypeState(state);
169 } else {
170 // TODO(skym): How bad is this scenario? We may be able to just give an
171 // empty batch to the processor and we'll treat corrupted data type state
172 // as no data type state at all. The question is do we want to add any of
173 // the entity metadata to the batch or completely skip that step? We're
174 // going to have to perform a merge shortly. Does this decision/logic even
175 // belong in this service?
176 change_processor()->OnMetadataLoaded(
177 change_processor()->CreateAndUploadError(
178 FROM_HERE, "Failed to deserialize global metadata."),
179 nullptr);
180 }
181 for (const syncer::ModelTypeStore::Record& r : *metadata_records.get()) {
182 sync_pb::EntityMetadata entity_metadata;
183 if (entity_metadata.ParseFromString(r.value)) {
184 batch->AddMetadata(r.id, entity_metadata);
185 } else {
186 // TODO(skym): This really isn't too bad. We just want to regenerate
187 // metadata for this particular entity. Unfortunately there isn't a
188 // convenient way to tell the processor to do this.
189 LOG(WARNING) << "Failed to deserialize entity metadata.";
190 }
191 }
192 change_processor()->OnMetadataLoaded(syncer::SyncError(), std::move(batch));
193 }
194
195 void ReadingListStore::OnDatabaseSave(syncer::ModelTypeStore::Result result) {
196 return;
197 }
198
199 void ReadingListStore::OnStoreCreated(
200 syncer::ModelTypeStore::Result result,
201 std::unique_ptr<syncer::ModelTypeStore> store) {
202 DCHECK(CalledOnValidThread());
203 store_ = std::move(store);
204 store_->ReadAllData(
205 base::Bind(&ReadingListStore::OnDatabaseLoad, base::AsWeakPtr(this)));
206 return;
207 }
208
209 syncer::ModelTypeService* ReadingListStore::GetModelTypeService() {
210 return this;
211 }
212
213 // Creates an object used to communicate changes in the sync metadata to the
214 // model type store.
215 std::unique_ptr<syncer::MetadataChangeList>
216 ReadingListStore::CreateMetadataChangeList() {
217 return base::MakeUnique<syncer::SimpleMetadataChangeList>();
218 }
219
220 // Perform the initial merge between local and sync data. This should only be
221 // called when a data type is first enabled to start syncing, and there is no
222 // sync metadata. Best effort should be made to match local and sync data. The
223 // keys in the |entity_data_map| will have been created via GetClientTag(...),
224 // and if a local and sync data should match/merge but disagree on tags, the
225 // service should use the sync data's tag. Any local pieces of data that are
226 // not present in sync should immediately be Put(...) to the processor before
227 // returning. The same MetadataChangeList that was passed into this function
228 // can be passed to Put(...) calls. Delete(...) can also be called but should
229 // not be needed for most model types. Durable storage writes, if not able to
230 // combine all change atomically, should save the metadata after the data
231 // changes, so that this merge will be re-driven by sync if is not completely
232 // saved during the current run.
233 syncer::SyncError ReadingListStore::MergeSyncData(
234 std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
235 syncer::EntityDataMap entity_data_map) {
236 DCHECK(CalledOnValidThread());
237 BeginTransaction();
238 // Keep track of the last update of each item.
239 std::map<GURL, int64_t> last_update;
240 std::unique_ptr<ReadingListModel::ScopedReadingListBatchUpdate>
241 model_batch_updates = model_->BeginBatchUpdates();
242
243 // Merge sync to local data.
244 for (const auto& kv : entity_data_map) {
245 const sync_pb::ReadingListSpecifics& specifics =
246 kv.second.value().specifics.reading_list();
247 std::unique_ptr<ReadingListEntry> entry(
248 ReadingListEntry::FromReadingListSpecifics(specifics));
249 DCHECK(entry->URL().spec() == kv.first);
250 DCHECK(specifics.entry_id() == kv.first);
251 last_update[entry->URL()] = entry->UpdateTime();
252 // This method will update the model and the local store.
253 model_->SyncAddEntry(
254 std::move(entry),
255 specifics.status() == sync_pb::ReadingListSpecifics::READ);
256 }
257
258 int unread_count = model_->unread_size();
259 int read_count = model_->read_size();
260 for (int index = 0; index < unread_count + read_count; index++) {
261 bool read = index >= unread_count;
262 const ReadingListEntry& entry =
263 read ? model_->GetReadEntryAtIndex(index - unread_count)
264 : model_->GetUnreadEntryAtIndex(index);
265 if (last_update.count(entry.URL()) &&
266 last_update[entry.URL()] >= entry.UpdateTime()) {
267 // The synced entry is up to date.
268 continue;
269 }
270 std::unique_ptr<sync_pb::ReadingListSpecifics> entry_pb =
271 entry.AsReadingListSpecifics(read);
272
273 auto entity_data = base::MakeUnique<syncer::EntityData>();
274 *(entity_data->specifics.mutable_reading_list()) = *entry_pb;
275 entity_data->non_unique_name = entry_pb->entry_id();
276
277 change_processor()->Put(entry_pb->entry_id(), std::move(entity_data),
278 metadata_change_list.get());
279 }
280 static_cast<syncer::SimpleMetadataChangeList*>(metadata_change_list.get())
281 ->TransferChanges(store_.get(), batch_.get());
282 CommitTransaction();
283 return syncer::SyncError();
284 }
285
286 // Apply changes from the sync server locally.
287 // Please note that |entity_changes| might have fewer entries than
288 // |metadata_change_list| in case when some of the data changes are filtered
289 // out, or even be empty in case when a commit confirmation is processed and
290 // only the metadata needs to persisted.
291 syncer::SyncError ReadingListStore::ApplySyncChanges(
292 std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
293 syncer::EntityChangeList entity_changes) {
294 DCHECK(CalledOnValidThread());
295 std::unique_ptr<ReadingListModel::ScopedReadingListBatchUpdate> batch =
296 model_->BeginBatchUpdates();
297 for (syncer::EntityChange& change : entity_changes) {
298 const sync_pb::ReadingListSpecifics& specifics =
299 change.data().specifics.reading_list();
300 if (change.type() == syncer::EntityChange::ACTION_DELETE) {
301 model_->SyncRemoveEntry(GURL(specifics.url()));
302 continue;
303 }
304
305 std::unique_ptr<ReadingListEntry> entry(
306 ReadingListEntry::FromReadingListSpecifics(specifics));
307 model_->SyncAddEntry(
308 std::move(entry),
309 specifics.status() == sync_pb::ReadingListSpecifics::READ);
310 }
311 static_cast<syncer::SimpleMetadataChangeList*>(metadata_change_list.get())
312 ->TransferChanges(store_.get(), batch_.get());
313 return syncer::SyncError();
314 }
315
316 void ReadingListStore::GetData(StorageKeyList storage_keys,
317 DataCallback callback) {
318 DCHECK(CalledOnValidThread());
319 auto batch = base::MakeUnique<syncer::MutableDataBatch>();
320 for (std::string url_string : storage_keys) {
321 auto add_to_batch_callback =
322 base::Bind(&ReadingListStore::AddEntryToBatch, base::Unretained(this),
323 batch.get());
324 model_->CallbackEntryReadStatusURL(GURL(url_string), add_to_batch_callback);
325 }
326
327 callback.Run(syncer::SyncError(), std::move(batch));
328 }
329
330 void ReadingListStore::GetAllData(DataCallback callback) {
331 DCHECK(CalledOnValidThread());
332 auto batch = base::MakeUnique<syncer::MutableDataBatch>();
333 int unread_count = model_->unread_size();
334 int read_count = model_->read_size();
335 for (int index = 0; index < unread_count + read_count; index++) {
336 bool read = index >= unread_count;
337 const ReadingListEntry& entry =
338 read ? model_->GetReadEntryAtIndex(index - unread_count)
339 : model_->GetUnreadEntryAtIndex(index);
340 AddEntryToBatch(batch.get(), entry, read);
341 }
342
343 callback.Run(syncer::SyncError(), std::move(batch));
344 }
345
346 void ReadingListStore::AddEntryToBatch(syncer::MutableDataBatch* batch,
347 const ReadingListEntry& entry,
348 bool read) {
349 DCHECK(CalledOnValidThread());
350 std::unique_ptr<sync_pb::ReadingListSpecifics> entry_pb =
351 entry.AsReadingListSpecifics(read);
352
353 std::unique_ptr<syncer::EntityData> entity_data(new syncer::EntityData());
354 *(entity_data->specifics.mutable_reading_list()) = *entry_pb;
355 entity_data->non_unique_name = entry_pb->entry_id();
356
357 batch->Put(entry_pb->entry_id(), std::move(entity_data));
358 }
359
360 // Get or generate a client tag for |entity_data|. This must be the same tag
361 // that was/would have been generated in the SyncableService/Directory world
362 // for backward compatibility with pre-USS clients. The only time this
363 // theoretically needs to be called is on the creation of local data, however
364 // it is also used to verify the hash of remote data. If a data type was never
365 // launched pre-USS, then method does not need to be different from
366 // GetStorageKey().
367 std::string ReadingListStore::GetClientTag(
368 const syncer::EntityData& entity_data) {
369 DCHECK(CalledOnValidThread());
370 return GetStorageKey(entity_data);
371 }
372
373 // Get or generate a storage key for |entity_data|. This will only ever be
374 // called once when first encountering a remote entity. Local changes will
375 // provide their storage keys directly to Put instead of using this method.
376 // Theoretically this function doesn't need to be stable across multiple calls
377 // on the same or different clients, but to keep things simple, it probably
378 // should be.
379 std::string ReadingListStore::GetStorageKey(
380 const syncer::EntityData& entity_data) {
381 DCHECK(CalledOnValidThread());
382 return entity_data.specifics.reading_list().entry_id();
383 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698