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

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

Issue 2451843002: Add Store+Sync to reading list. (Closed)
Patch Set: experimental_flags 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_CURRENTLY_ON(web::WebThread::UI);
37 model_ = model;
38 create_store_callback_.Run(
39 base::Bind(&ReadingListStore::OnStoreCreated, base::AsWeakPtr(this)));
40 }
41
42 void ReadingListStore::BeginTransaction() {
43 DCHECK_CURRENTLY_ON(web::WebThread::UI);
44 pending_transaction_++;
45 if (pending_transaction_ == 1) {
46 batch_ = store_->CreateWriteBatch();
47 }
48 }
49
50 void ReadingListStore::CommitTransaction() {
51 DCHECK_CURRENTLY_ON(web::WebThread::UI);
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_CURRENTLY_ON(web::WebThread::UI);
65 BeginTransaction();
skym 2016/11/01 18:27:26 Aaah, I finally understand why you have multiple t
66
67 std::unique_ptr<reading_list::ReadingListLocal> pb_entry =
68 entry.AsReadingListLocal(read);
69 std::unique_ptr<sync_pb::ReadingListSpecifics> pb_entry_sync =
skym 2016/11/01 18:27:26 I'd personally move this down to below the if(..)
Olivier 2016/11/02 14:57:11 Done.
70 entry.AsReadingListSpecifics(read);
71 // Unref the URL before making asynchronous call.
skym 2016/11/01 18:27:26 Is this really needed? I'm having a difficult time
Olivier 2016/11/02 14:57:11 This was part of a fix that revealed to be unrelat
72 std::string local_key = entry.URL().spec();
73
74 store_->WriteData(batch_.get(), local_key, pb_entry->SerializeAsString());
75
76 if (!change_processor() || from_sync) {
77 CommitTransaction();
78 return;
79 }
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());
skym 2016/11/01 18:27:26 Max/Pavel, this use case of MetadataChangeList cou
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_CURRENTLY_ON(web::WebThread::UI);
99 BeginTransaction();
100 std::string local_key = entry.URL().spec();
skym 2016/11/01 18:27:26 Same as above.
Olivier 2016/11/02 14:57:11 Done.
101 store_->DeleteData(batch_.get(), local_key);
102 if (!change_processor() || from_sync) {
103 CommitTransaction();
104 return;
105 }
106 std::unique_ptr<syncer::MetadataChangeList> metadata_change_list =
107 CreateMetadataChangeList();
108
109 change_processor()->Delete(local_key, metadata_change_list.get());
110 static_cast<syncer::SimpleMetadataChangeList*>(metadata_change_list.get())
111 ->TransferChanges(store_.get(), batch_.get());
112
113 CommitTransaction();
114 }
115
116 void ReadingListStore::OnDatabaseLoad(
117 syncer::ModelTypeStore::Result result,
118 std::unique_ptr<syncer::ModelTypeStore::RecordList> entries) {
119 DCHECK_CURRENTLY_ON(web::WebThread::UI);
120 if (result != syncer::ModelTypeStore::Result::SUCCESS) {
121 return;
122 }
123 database_loaded_ = true;
124 auto read = base::MakeUnique<ReadingListEntries>();
125 auto unread = base::MakeUnique<ReadingListEntries>();
126
127 for (const syncer::ModelTypeStore::Record& r : *entries.get()) {
128 // for (const reading_list::ReadingListLocal& pb_entry : *entries) {
129 std::unique_ptr<reading_list::ReadingListLocal> proto =
130 base::MakeUnique<reading_list::ReadingListLocal>();
131 if (!proto->ParseFromString(r.value)) {
132 continue;
133 // TODO(skym, crbug.com/582460): Handle unrecoverable initialization
134 // failure.
135 }
136
137 std::unique_ptr<ReadingListEntry> entry(
138 ReadingListEntry::FromReadingListLocal(*proto));
139 if (!entry) {
140 continue;
141 }
142 if (proto->status() == reading_list::ReadingListLocal::READ) {
143 read->push_back(std::move(*entry));
144 } else {
145 unread->push_back(std::move(*entry));
146 }
147 }
148
149 model_->ModelLoaded(std::move(unread), std::move(read));
150
151 store_->ReadAllMetadata(
152 base::Bind(&ReadingListStore::OnReadAllMetadata, base::AsWeakPtr(this)));
153 }
154
155 void ReadingListStore::OnReadAllMetadata(
156 syncer::ModelTypeStore::Result result,
157 std::unique_ptr<syncer::ModelTypeStore::RecordList> metadata_records,
158 const std::string& global_metadata) {
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 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 // Keep track of the last update of each item.
237 std::map<GURL, int64_t> last_update;
238 std::unique_ptr<ReadingListModel::ScopedReadingListBatchUpdate> batch =
239 model_->BeginBatchUpdates();
240
241 // Merge sync to local data.
242 for (const auto& kv : entity_data_map) {
243 const sync_pb::ReadingListSpecifics& specifics =
244 kv.second.value().specifics.reading_list();
245 std::unique_ptr<ReadingListEntry> entry(
246 ReadingListEntry::FromReadingListSpecifics(specifics));
247 DCHECK(entry->URL().spec() == kv.first);
248 DCHECK(specifics.entry_id() == kv.first);
249 last_update[entry->URL()] = entry->UpdateTime();
250 // This method will update the model and the local store.
251 model_->SyncAddEntry(
252 std::move(entry),
253 specifics.status() == sync_pb::ReadingListSpecifics::READ);
254 }
255
256 int unread_count = model_->unread_size();
257 int read_count = model_->read_size();
258 for (int index = 0; index < unread_count + read_count; index++) {
skym 2016/11/01 18:27:26 This is a lot of complexity to just iterate over a
259 bool read = index >= unread_count;
260 const ReadingListEntry& entry =
261 read ? model_->GetReadEntryAtIndex(index - unread_count)
262 : model_->GetUnreadEntryAtIndex(index);
263 if (last_update.count(entry.URL()) &&
264 last_update[entry.URL()] >= entry.UpdateTime()) {
265 // The synced entry is up to date.
266 continue;
267 }
268 std::unique_ptr<sync_pb::ReadingListSpecifics> entry_pb =
269 entry.AsReadingListSpecifics(read);
270
271 std::unique_ptr<syncer::EntityData> entity_data(new syncer::EntityData());
skym 2016/11/01 18:27:26 auto and base::MakeUnique?
Olivier 2016/11/02 14:57:11 Done.
272 *(entity_data->specifics.mutable_reading_list()) = *entry_pb;
273 entity_data->non_unique_name = entry_pb->entry_id();
274
275 static_cast<syncer::SimpleMetadataChangeList*>(metadata_change_list.get())
276 ->TransferChanges(store_.get(), batch_.get());
277 change_processor()->Put(entry_pb->entry_id(), std::move(entity_data),
278 metadata_change_list.get());
279 }
280 return syncer::SyncError();
281 }
282
283 // Apply changes from the sync server locally.
284 // Please note that |entity_changes| might have fewer entries than
285 // |metadata_change_list| in case when some of the data changes are filtered
286 // out, or even be empty in case when a commit confirmation is processed and
287 // only the metadata needs to persisted.
288 syncer::SyncError ReadingListStore::ApplySyncChanges(
289 std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
290 syncer::EntityChangeList entity_changes) {
291 std::unique_ptr<ReadingListModel::ScopedReadingListBatchUpdate> batch =
292 model_->BeginBatchUpdates();
293 for (syncer::EntityChange& change : entity_changes) {
294 const sync_pb::ReadingListSpecifics& specifics =
295 change.data().specifics.reading_list();
296 if (change.type() == syncer::EntityChange::ACTION_DELETE) {
297 model_->SyncRemoveEntry(GURL(specifics.url()));
298 continue;
299 }
300
301 std::unique_ptr<ReadingListEntry> entry(
302 ReadingListEntry::FromReadingListSpecifics(specifics));
303 model_->SyncAddEntry(
304 std::move(entry),
305 specifics.status() == sync_pb::ReadingListSpecifics::READ);
306 }
pavely 2016/11/01 06:14:10 metadata_change_list contains metadata updates and
Olivier 2016/11/02 14:57:11 Done.
307 return syncer::SyncError();
308 }
309
310 // Asynchronously retrieve the corresponding sync data for |storage_keys|.
skym 2016/11/01 18:27:26 This implementation is not async.
Olivier 2016/11/02 14:57:11 Done.
311 void ReadingListStore::GetData(StorageKeyList storage_keys,
312 DataCallback callback) {
313 auto batch = base::MakeUnique<syncer::MutableDataBatch>();
314 for (std::string url_string : storage_keys) {
315 auto add_to_batch_callback =
316 base::Bind(&ReadingListStore::AddEntryToBatch, base::Unretained(this),
317 batch.get());
318 model_->CallbackEntryReadStatusURL(GURL(url_string), add_to_batch_callback);
319 }
320
321 callback.Run(syncer::SyncError(), std::move(batch));
322 }
323
324 // Asynchronously retrieve all of the local sync data.
skym 2016/11/01 18:27:26 This implementation is not async.
Olivier 2016/11/02 14:57:10 Done.
325 void ReadingListStore::GetAllData(DataCallback callback) {
326 auto batch = base::MakeUnique<syncer::MutableDataBatch>();
327 int unread_count = model_->unread_size();
328 int read_count = model_->read_size();
329 for (int index = 0; index < unread_count + read_count; index++) {
skym 2016/11/01 18:27:26 I feel like I've seen this logic before! :)
330 bool read = index >= unread_count;
331 const ReadingListEntry& entry =
332 read ? model_->GetReadEntryAtIndex(index - unread_count)
333 : model_->GetUnreadEntryAtIndex(index);
334 AddEntryToBatch(batch.get(), entry, read);
335 }
336
337 callback.Run(syncer::SyncError(), std::move(batch));
338 }
339
340 void ReadingListStore::AddEntryToBatch(syncer::MutableDataBatch* batch,
341 const ReadingListEntry& entry,
342 bool read) {
343 std::unique_ptr<sync_pb::ReadingListSpecifics> entry_pb =
344 entry.AsReadingListSpecifics(read);
345
346 std::unique_ptr<syncer::EntityData> entity_data(new syncer::EntityData());
347 *(entity_data->specifics.mutable_reading_list()) = *entry_pb;
348 entity_data->non_unique_name = entry_pb->entry_id();
349
350 batch->Put(entry_pb->entry_id(), std::move(entity_data));
351 }
352
353 // Get or generate a client tag for |entity_data|. This must be the same tag
354 // that was/would have been generated in the SyncableService/Directory world
355 // for backward compatibility with pre-USS clients. The only time this
356 // theoretically needs to be called is on the creation of local data, however
357 // it is also used to verify the hash of remote data. If a data type was never
358 // launched pre-USS, then method does not need to be different from
359 // GetStorageKey().
360 std::string ReadingListStore::GetClientTag(
361 const syncer::EntityData& entity_data) {
362 return entity_data.specifics.reading_list().entry_id();
363 }
364
365 // Get or generate a storage key for |entity_data|. This will only ever be
366 // called once when first encountering a remote entity. Local changes will
367 // provide their storage keys directly to Put instead of using this method.
368 // Theoretically this function doesn't need to be stable across multiple calls
369 // on the same or different clients, but to keep things simple, it probably
370 // should be.
371 std::string ReadingListStore::GetStorageKey(
372 const syncer::EntityData& entity_data) {
373 return entity_data.specifics.reading_list().entry_id();
374 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698