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

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

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

Powered by Google App Engine
This is Rietveld 408576698