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

Side by Side Diff: components/reading_list/ios/reading_list_store.cc

Issue 2763233003: Move ReadingList model to components/reading_list/core (Closed)
Patch Set: last? Created 3 years, 9 months 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 "components/reading_list/ios/reading_list_store.h"
6
7 #include <set>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "base/memory/ptr_util.h"
13 #include "base/time/clock.h"
14 #include "components/reading_list/ios/proto/reading_list.pb.h"
15 #include "components/reading_list/ios/reading_list_model_impl.h"
16 #include "components/sync/model/entity_change.h"
17 #include "components/sync/model/metadata_batch.h"
18 #include "components/sync/model/metadata_change_list.h"
19 #include "components/sync/model/model_type_change_processor.h"
20 #include "components/sync/model/mutable_data_batch.h"
21 #include "components/sync/model_impl/accumulating_metadata_change_list.h"
22 #include "components/sync/protocol/model_type_state.pb.h"
23
24 ReadingListStore::ReadingListStore(
25 StoreFactoryFunction create_store_callback,
26 const ChangeProcessorFactory& change_processor_factory)
27 : ReadingListModelStorage(change_processor_factory, syncer::READING_LIST),
28 create_store_callback_(create_store_callback),
29 pending_transaction_count_(0) {}
30
31 ReadingListStore::~ReadingListStore() {
32 DCHECK_EQ(0, pending_transaction_count_);
33 }
34
35 void ReadingListStore::SetReadingListModel(ReadingListModel* model,
36 ReadingListStoreDelegate* delegate,
37 base::Clock* clock) {
38 DCHECK(CalledOnValidThread());
39 model_ = model;
40 delegate_ = delegate;
41 clock_ = clock;
42 create_store_callback_.Run(
43 base::Bind(&ReadingListStore::OnStoreCreated, base::AsWeakPtr(this)));
44 }
45
46 std::unique_ptr<ReadingListModelStorage::ScopedBatchUpdate>
47 ReadingListStore::EnsureBatchCreated() {
48 return base::WrapUnique<ReadingListModelStorage::ScopedBatchUpdate>(
49 new ScopedBatchUpdate(this));
50 }
51
52 ReadingListStore::ScopedBatchUpdate::ScopedBatchUpdate(ReadingListStore* store)
53 : store_(store) {
54 store_->BeginTransaction();
55 }
56
57 ReadingListStore::ScopedBatchUpdate::~ScopedBatchUpdate() {
58 store_->CommitTransaction();
59 }
60
61 void ReadingListStore::BeginTransaction() {
62 DCHECK(CalledOnValidThread());
63 pending_transaction_count_++;
64 if (pending_transaction_count_ == 1) {
65 batch_ = store_->CreateWriteBatch();
66 }
67 }
68
69 void ReadingListStore::CommitTransaction() {
70 DCHECK(CalledOnValidThread());
71 pending_transaction_count_--;
72 if (pending_transaction_count_ == 0) {
73 store_->CommitWriteBatch(
74 std::move(batch_),
75 base::Bind(&ReadingListStore::OnDatabaseSave, base::AsWeakPtr(this)));
76 batch_.reset();
77 }
78 }
79
80 void ReadingListStore::SaveEntry(const ReadingListEntry& entry) {
81 DCHECK(CalledOnValidThread());
82 auto token = EnsureBatchCreated();
83
84 std::unique_ptr<reading_list::ReadingListLocal> pb_entry =
85 entry.AsReadingListLocal(clock_->Now());
86
87 batch_->WriteData(entry.URL().spec(), pb_entry->SerializeAsString());
88
89 if (!change_processor()->IsTrackingMetadata()) {
90 return;
91 }
92 std::unique_ptr<sync_pb::ReadingListSpecifics> pb_entry_sync =
93 entry.AsReadingListSpecifics();
94
95 std::unique_ptr<syncer::MetadataChangeList> metadata_change_list =
96 CreateMetadataChangeList();
97
98 std::unique_ptr<syncer::EntityData> entity_data(new syncer::EntityData());
99 *entity_data->specifics.mutable_reading_list() = *pb_entry_sync;
100 entity_data->non_unique_name = pb_entry_sync->entry_id();
101
102 change_processor()->Put(entry.URL().spec(), std::move(entity_data),
103 metadata_change_list.get());
104 batch_->TransferMetadataChanges(std::move(metadata_change_list));
105 }
106
107 void ReadingListStore::RemoveEntry(const ReadingListEntry& entry) {
108 DCHECK(CalledOnValidThread());
109 auto token = EnsureBatchCreated();
110
111 batch_->DeleteData(entry.URL().spec());
112 if (!change_processor()->IsTrackingMetadata()) {
113 return;
114 }
115 std::unique_ptr<syncer::MetadataChangeList> metadata_change_list =
116 CreateMetadataChangeList();
117
118 change_processor()->Delete(entry.URL().spec(), metadata_change_list.get());
119 batch_->TransferMetadataChanges(std::move(metadata_change_list));
120 }
121
122 void ReadingListStore::OnDatabaseLoad(
123 syncer::ModelTypeStore::Result result,
124 std::unique_ptr<syncer::ModelTypeStore::RecordList> entries) {
125 DCHECK(CalledOnValidThread());
126 if (result != syncer::ModelTypeStore::Result::SUCCESS) {
127 change_processor()->ReportError(FROM_HERE,
128 "Cannot load Reading List Database.");
129 return;
130 }
131 auto loaded_entries =
132 base::MakeUnique<ReadingListStoreDelegate::ReadingListEntries>();
133
134 for (const syncer::ModelTypeStore::Record& r : *entries.get()) {
135 reading_list::ReadingListLocal proto;
136 if (!proto.ParseFromString(r.value)) {
137 continue;
138 // TODO(skym, crbug.com/582460): Handle unrecoverable initialization
139 // failure.
140 }
141
142 std::unique_ptr<ReadingListEntry> entry(
143 ReadingListEntry::FromReadingListLocal(proto, clock_->Now()));
144 if (!entry) {
145 continue;
146 }
147 GURL url = entry->URL();
148 DCHECK(!loaded_entries->count(url));
149 loaded_entries->insert(std::make_pair(url, std::move(*entry)));
150 }
151
152 delegate_->StoreLoaded(std::move(loaded_entries));
153
154 store_->ReadAllMetadata(
155 base::Bind(&ReadingListStore::OnReadAllMetadata, base::AsWeakPtr(this)));
156 }
157
158 void ReadingListStore::OnReadAllMetadata(
159 base::Optional<syncer::ModelError> error,
160 std::unique_ptr<syncer::MetadataBatch> metadata_batch) {
161 DCHECK(CalledOnValidThread());
162 if (error) {
163 change_processor()->ReportError(FROM_HERE, "Failed to read metadata.");
164 } else {
165 change_processor()->ModelReadyToSync(std::move(metadata_batch));
166 }
167 }
168
169 void ReadingListStore::OnDatabaseSave(syncer::ModelTypeStore::Result result) {
170 return;
171 }
172
173 void ReadingListStore::OnStoreCreated(
174 syncer::ModelTypeStore::Result result,
175 std::unique_ptr<syncer::ModelTypeStore> store) {
176 DCHECK(CalledOnValidThread());
177 if (result != syncer::ModelTypeStore::Result::SUCCESS) {
178 // TODO(crbug.com/664926): handle store creation error.
179 return;
180 }
181 store_ = std::move(store);
182 store_->ReadAllData(
183 base::Bind(&ReadingListStore::OnDatabaseLoad, base::AsWeakPtr(this)));
184 return;
185 }
186
187 // Creates an object used to communicate changes in the sync metadata to the
188 // model type store.
189 std::unique_ptr<syncer::MetadataChangeList>
190 ReadingListStore::CreateMetadataChangeList() {
191 return syncer::ModelTypeStore::WriteBatch::CreateMetadataChangeList();
192 }
193
194 // Perform the initial merge between local and sync data. This should only be
195 // called when a data type is first enabled to start syncing, and there is no
196 // sync metadata. Best effort should be made to match local and sync data. The
197 // keys in the |entity_data_map| will have been created via GetClientTag(...),
198 // and if a local and sync data should match/merge but disagree on tags, the
199 // service should use the sync data's tag. Any local pieces of data that are
200 // not present in sync should immediately be Put(...) to the processor before
201 // returning. The same MetadataChangeList that was passed into this function
202 // can be passed to Put(...) calls. Delete(...) can also be called but should
203 // not be needed for most model types. Durable storage writes, if not able to
204 // combine all change atomically, should save the metadata after the data
205 // changes, so that this merge will be re-driven by sync if is not completely
206 // saved during the current run.
207 base::Optional<syncer::ModelError> ReadingListStore::MergeSyncData(
208 std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
209 syncer::EntityDataMap entity_data_map) {
210 DCHECK(CalledOnValidThread());
211 auto token = EnsureBatchCreated();
212 // Keep track of the last update of each item.
213 std::set<std::string> synced_entries;
214 std::unique_ptr<ReadingListModel::ScopedReadingListBatchUpdate>
215 model_batch_updates = model_->BeginBatchUpdates();
216
217 // Merge sync to local data.
218 for (const auto& kv : entity_data_map) {
219 synced_entries.insert(kv.first);
220 const sync_pb::ReadingListSpecifics& specifics =
221 kv.second.value().specifics.reading_list();
222 // Deserialize entry.
223 std::unique_ptr<ReadingListEntry> entry(
224 ReadingListEntry::FromReadingListSpecifics(specifics, clock_->Now()));
225
226 const ReadingListEntry* existing_entry =
227 model_->GetEntryByURL(entry->URL());
228
229 if (!existing_entry) {
230 // This entry is new. Add it to the store and model.
231 // Convert to local store format and write to store.
232 std::unique_ptr<reading_list::ReadingListLocal> entry_pb =
233 entry->AsReadingListLocal(clock_->Now());
234 batch_->WriteData(entry->URL().spec(), entry_pb->SerializeAsString());
235
236 // Notify model about updated entry.
237 delegate_->SyncAddEntry(std::move(entry));
238 } else {
239 // Merge the local data and the sync data and store the result.
240 ReadingListEntry* merged_entry =
241 delegate_->SyncMergeEntry(std::move(entry));
242
243 // Write to the store.
244 std::unique_ptr<reading_list::ReadingListLocal> entry_local_pb =
245 merged_entry->AsReadingListLocal(clock_->Now());
246 batch_->WriteData(merged_entry->URL().spec(),
247 entry_local_pb->SerializeAsString());
248
249 // Send to sync
250 std::unique_ptr<sync_pb::ReadingListSpecifics> entry_sync_pb =
251 merged_entry->AsReadingListSpecifics();
252 DCHECK(CompareEntriesForSync(specifics, *entry_sync_pb));
253 auto entity_data = base::MakeUnique<syncer::EntityData>();
254 *(entity_data->specifics.mutable_reading_list()) = *entry_sync_pb;
255 entity_data->non_unique_name = entry_sync_pb->entry_id();
256
257 // TODO(crbug.com/666232): Investigate if there is a risk of sync
258 // ping-pong.
259 change_processor()->Put(entry_sync_pb->entry_id(), std::move(entity_data),
260 metadata_change_list.get());
261 }
262 }
263
264 // Commit local only entries to server.
265 for (const auto& url : model_->Keys()) {
266 const ReadingListEntry* entry = model_->GetEntryByURL(url);
267 if (synced_entries.count(url.spec())) {
268 // Entry already exists and has been merged above.
269 continue;
270 }
271
272 // Local entry has later timestamp. It should be committed to server.
273 std::unique_ptr<sync_pb::ReadingListSpecifics> entry_pb =
274 entry->AsReadingListSpecifics();
275
276 auto entity_data = base::MakeUnique<syncer::EntityData>();
277 *(entity_data->specifics.mutable_reading_list()) = *entry_pb;
278 entity_data->non_unique_name = entry_pb->entry_id();
279
280 change_processor()->Put(entry_pb->entry_id(), std::move(entity_data),
281 metadata_change_list.get());
282 }
283 batch_->TransferMetadataChanges(std::move(metadata_change_list));
284
285 return {};
286 }
287
288 // Apply changes from the sync server locally.
289 // Please note that |entity_changes| might have fewer entries than
290 // |metadata_change_list| in case when some of the data changes are filtered
291 // out, or even be empty in case when a commit confirmation is processed and
292 // only the metadata needs to persisted.
293 base::Optional<syncer::ModelError> ReadingListStore::ApplySyncChanges(
294 std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
295 syncer::EntityChangeList entity_changes) {
296 DCHECK(CalledOnValidThread());
297 std::unique_ptr<ReadingListModel::ScopedReadingListBatchUpdate> batch =
298 model_->BeginBatchUpdates();
299 auto token = EnsureBatchCreated();
300
301 for (syncer::EntityChange& change : entity_changes) {
302 if (change.type() == syncer::EntityChange::ACTION_DELETE) {
303 batch_->DeleteData(change.storage_key());
304 // Need to notify model that entry is deleted.
305 delegate_->SyncRemoveEntry(GURL(change.storage_key()));
306 } else {
307 // Deserialize entry.
308 const sync_pb::ReadingListSpecifics& specifics =
309 change.data().specifics.reading_list();
310 std::unique_ptr<ReadingListEntry> entry(
311 ReadingListEntry::FromReadingListSpecifics(specifics, clock_->Now()));
312
313 const ReadingListEntry* existing_entry =
314 model_->GetEntryByURL(entry->URL());
315
316 if (!existing_entry) {
317 // This entry is new. Add it to the store and model.
318 // Convert to local store format and write to store.
319 std::unique_ptr<reading_list::ReadingListLocal> entry_pb =
320 entry->AsReadingListLocal(clock_->Now());
321 batch_->WriteData(entry->URL().spec(), entry_pb->SerializeAsString());
322
323 // Notify model about updated entry.
324 delegate_->SyncAddEntry(std::move(entry));
325 } else {
326 // Merge the local data and the sync data and store the result.
327 ReadingListEntry* merged_entry =
328 delegate_->SyncMergeEntry(std::move(entry));
329
330 // Write to the store.
331 std::unique_ptr<reading_list::ReadingListLocal> entry_local_pb =
332 merged_entry->AsReadingListLocal(clock_->Now());
333 batch_->WriteData(merged_entry->URL().spec(),
334 entry_local_pb->SerializeAsString());
335
336 // Send to sync
337 std::unique_ptr<sync_pb::ReadingListSpecifics> entry_sync_pb =
338 merged_entry->AsReadingListSpecifics();
339 DCHECK(CompareEntriesForSync(specifics, *entry_sync_pb));
340 auto entity_data = base::MakeUnique<syncer::EntityData>();
341 *(entity_data->specifics.mutable_reading_list()) = *entry_sync_pb;
342 entity_data->non_unique_name = entry_sync_pb->entry_id();
343
344 // TODO(crbug.com/666232): Investigate if there is a risk of sync
345 // ping-pong.
346 change_processor()->Put(entry_sync_pb->entry_id(),
347 std::move(entity_data),
348 metadata_change_list.get());
349 }
350 }
351 }
352
353 batch_->TransferMetadataChanges(std::move(metadata_change_list));
354 return {};
355 }
356
357 void ReadingListStore::GetData(StorageKeyList storage_keys,
358 DataCallback callback) {
359 DCHECK(CalledOnValidThread());
360 auto batch = base::MakeUnique<syncer::MutableDataBatch>();
361 for (const std::string& url_string : storage_keys) {
362 const ReadingListEntry* entry = model_->GetEntryByURL(GURL(url_string));
363 if (entry) {
364 AddEntryToBatch(batch.get(), *entry);
365 }
366 }
367
368 callback.Run(std::move(batch));
369 }
370
371 void ReadingListStore::GetAllData(DataCallback callback) {
372 DCHECK(CalledOnValidThread());
373 auto batch = base::MakeUnique<syncer::MutableDataBatch>();
374
375 for (const auto& url : model_->Keys()) {
376 const ReadingListEntry* entry = model_->GetEntryByURL(GURL(url));
377 AddEntryToBatch(batch.get(), *entry);
378 }
379
380 callback.Run(std::move(batch));
381 }
382
383 void ReadingListStore::AddEntryToBatch(syncer::MutableDataBatch* batch,
384 const ReadingListEntry& entry) {
385 DCHECK(CalledOnValidThread());
386 std::unique_ptr<sync_pb::ReadingListSpecifics> entry_pb =
387 entry.AsReadingListSpecifics();
388
389 std::unique_ptr<syncer::EntityData> entity_data(new syncer::EntityData());
390 *(entity_data->specifics.mutable_reading_list()) = *entry_pb;
391 entity_data->non_unique_name = entry_pb->entry_id();
392
393 batch->Put(entry_pb->entry_id(), std::move(entity_data));
394 }
395
396 // Get or generate a client tag for |entity_data|. This must be the same tag
397 // that was/would have been generated in the SyncableService/Directory world
398 // for backward compatibility with pre-USS clients. The only time this
399 // theoretically needs to be called is on the creation of local data, however
400 // it is also used to verify the hash of remote data. If a data type was never
401 // launched pre-USS, then method does not need to be different from
402 // GetStorageKey().
403 std::string ReadingListStore::GetClientTag(
404 const syncer::EntityData& entity_data) {
405 DCHECK(CalledOnValidThread());
406 return GetStorageKey(entity_data);
407 }
408
409 // Get or generate a storage key for |entity_data|. This will only ever be
410 // called once when first encountering a remote entity. Local changes will
411 // provide their storage keys directly to Put instead of using this method.
412 // Theoretically this function doesn't need to be stable across multiple calls
413 // on the same or different clients, but to keep things simple, it probably
414 // should be.
415 std::string ReadingListStore::GetStorageKey(
416 const syncer::EntityData& entity_data) {
417 DCHECK(CalledOnValidThread());
418 return entity_data.specifics.reading_list().entry_id();
419 }
420
421 bool ReadingListStore::CompareEntriesForSync(
422 const sync_pb::ReadingListSpecifics& lhs,
423 const sync_pb::ReadingListSpecifics& rhs) {
424 DCHECK(lhs.entry_id() == rhs.entry_id());
425 DCHECK(lhs.has_update_time_us());
426 DCHECK(rhs.has_update_time_us());
427 DCHECK(lhs.has_update_title_time_us());
428 DCHECK(rhs.has_update_title_time_us());
429 DCHECK(lhs.has_creation_time_us());
430 DCHECK(rhs.has_creation_time_us());
431 DCHECK(lhs.has_url());
432 DCHECK(rhs.has_url());
433 DCHECK(lhs.has_title());
434 DCHECK(rhs.has_title());
435 DCHECK(lhs.has_status());
436 DCHECK(rhs.has_status());
437 if (rhs.url() != lhs.url() ||
438 rhs.update_title_time_us() < lhs.update_title_time_us() ||
439 rhs.creation_time_us() < lhs.creation_time_us() ||
440 rhs.update_time_us() < lhs.update_time_us()) {
441 return false;
442 }
443 if (rhs.update_time_us() == lhs.update_time_us()) {
444 if ((rhs.status() == sync_pb::ReadingListSpecifics::UNSEEN &&
445 lhs.status() != sync_pb::ReadingListSpecifics::UNSEEN) ||
446 (rhs.status() == sync_pb::ReadingListSpecifics::UNREAD &&
447 lhs.status() == sync_pb::ReadingListSpecifics::READ))
448 return false;
449 }
450 if (rhs.update_title_time_us() == lhs.update_title_time_us()) {
451 if (rhs.title().compare(lhs.title()) < 0)
452 return false;
453 }
454 if (rhs.creation_time_us() == lhs.creation_time_us()) {
455 if (rhs.first_read_time_us() == 0 && lhs.first_read_time_us() != 0) {
456 return false;
457 }
458 if (rhs.first_read_time_us() > lhs.first_read_time_us() &&
459 lhs.first_read_time_us() != 0) {
460 return false;
461 }
462 }
463 return true;
464 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698