OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "sync/engine/get_commit_ids.h" | 5 #include "sync/engine/get_commit_ids.h" |
6 | 6 |
7 #include <set> | 7 #include <set> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/basictypes.h" | 10 #include "base/basictypes.h" |
(...skipping 232 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
243 void AddItemThenPredecessors( | 243 void AddItemThenPredecessors( |
244 const std::set<int64>& ready_unsynced_set, | 244 const std::set<int64>& ready_unsynced_set, |
245 const syncable::Entry& item, | 245 const syncable::Entry& item, |
246 syncable::Directory::Metahandles* result) const; | 246 syncable::Directory::Metahandles* result) const; |
247 | 247 |
248 void AddPredecessorsThenItem( | 248 void AddPredecessorsThenItem( |
249 const std::set<int64>& ready_unsynced_set, | 249 const std::set<int64>& ready_unsynced_set, |
250 const syncable::Entry& item, | 250 const syncable::Entry& item, |
251 syncable::Directory::Metahandles* result) const; | 251 syncable::Directory::Metahandles* result) const; |
252 | 252 |
| 253 bool AddDeletedParents(const std::set<int64>& ready_unsynced_set, |
| 254 const syncable::Entry& item, |
| 255 const syncable::Directory::Metahandles& traversed, |
| 256 syncable::Directory::Metahandles* result) const; |
| 257 |
253 // Returns true if we've collected enough items. | 258 // Returns true if we've collected enough items. |
254 bool IsFull() const; | 259 bool IsFull() const; |
255 | 260 |
256 // Returns true if the specified handle is already in the traversal. | 261 // Returns true if the specified handle is already in the traversal. |
257 bool HaveItem(int64 handle) const; | 262 bool HaveItem(int64 handle) const; |
258 | 263 |
259 // Adds the specified handles to the traversal. | 264 // Adds the specified handles to the traversal. |
260 void AppendManyToTraversal(const syncable::Directory::Metahandles& handles); | 265 void AppendManyToTraversal(const syncable::Directory::Metahandles& handles); |
261 | 266 |
262 // Adds the specifed handle to the traversal. | 267 // Adds the specifed handle to the traversal. |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
369 const std::set<int64>& ready_unsynced_set, | 374 const std::set<int64>& ready_unsynced_set, |
370 const syncable::Entry& item, | 375 const syncable::Entry& item, |
371 syncable::Directory::Metahandles* result) const { | 376 syncable::Directory::Metahandles* result) const { |
372 syncable::Directory::Metahandles dependencies; | 377 syncable::Directory::Metahandles dependencies; |
373 AddItemThenPredecessors(ready_unsynced_set, item, &dependencies); | 378 AddItemThenPredecessors(ready_unsynced_set, item, &dependencies); |
374 | 379 |
375 // Reverse what we added to get the correct order. | 380 // Reverse what we added to get the correct order. |
376 result->insert(result->end(), dependencies.rbegin(), dependencies.rend()); | 381 result->insert(result->end(), dependencies.rbegin(), dependencies.rend()); |
377 } | 382 } |
378 | 383 |
| 384 // Traverses the tree from bottom to top, adding the deleted parents of the |
| 385 // given |item|. Stops traversing if it encounters a non-deleted node, or |
| 386 // a node that was already listed in the |traversed| list. Returns an error |
| 387 // (false) if a node along the traversal is in a conflict state. |
| 388 // |
| 389 // The result list is reversed before it is returned, so the resulting |
| 390 // traversal is in top to bottom order. Also note that this function appends |
| 391 // to the result list without clearing it. |
| 392 bool Traversal::AddDeletedParents( |
| 393 const std::set<int64>& ready_unsynced_set, |
| 394 const syncable::Entry& item, |
| 395 const syncable::Directory::Metahandles& traversed, |
| 396 syncable::Directory::Metahandles* result) const { |
| 397 syncable::Directory::Metahandles dependencies; |
| 398 syncable::Id parent_id = item.GetParentId(); |
| 399 |
| 400 // Climb the tree adding entries leaf -> root. |
| 401 while (!parent_id.IsRoot()) { |
| 402 syncable::Entry parent(trans_, syncable::GET_BY_ID, parent_id); |
| 403 |
| 404 if (!parent.good()) { |
| 405 // This is valid because the parent could have gone away a long time ago |
| 406 // |
| 407 // Consider the case where a folder is server-unknown and locally |
| 408 // deleted, and has a child that is server-known, deleted, and unsynced. |
| 409 // The parent could be dropped from memory at any time, but its child |
| 410 // needs to be committed first. |
| 411 break; |
| 412 } |
| 413 int64 handle = parent.GetMetahandle(); |
| 414 if (!parent.GetIsUnsynced()) { |
| 415 // In some rare cases, our parent can be both deleted and unsynced. |
| 416 // (ie. the server-unknown parent case). |
| 417 break; |
| 418 } |
| 419 if (!parent.GetIsDel()) { |
| 420 // We're not intersted in non-deleted parents. |
| 421 break; |
| 422 } |
| 423 if (std::find(traversed.begin(), traversed.end(), handle) != |
| 424 traversed.end()) { |
| 425 // We've already added this parent (and therefore all of its parents). |
| 426 // We can return early. |
| 427 break; |
| 428 } |
| 429 if (IsEntryInConflict(parent)) { |
| 430 // We ignore all entries that are children of a conflicing item. Return |
| 431 // false immediately to forget the traversal we've built up so far. |
| 432 DVLOG(1) << "Parent was in conflict, omitting " << item; |
| 433 return false; |
| 434 } |
| 435 TryAddItem(ready_unsynced_set, parent, &dependencies); |
| 436 parent_id = parent.GetParentId(); |
| 437 } |
| 438 |
| 439 // Reverse what we added to get the correct order. |
| 440 result->insert(result->end(), dependencies.rbegin(), dependencies.rend()); |
| 441 return true; |
| 442 } |
| 443 |
379 bool Traversal::IsFull() const { | 444 bool Traversal::IsFull() const { |
380 return out_->size() >= max_entries_; | 445 return out_->size() >= max_entries_; |
381 } | 446 } |
382 | 447 |
383 bool Traversal::HaveItem(int64 handle) const { | 448 bool Traversal::HaveItem(int64 handle) const { |
384 return added_handles_.find(handle) != added_handles_.end(); | 449 return added_handles_.find(handle) != added_handles_.end(); |
385 } | 450 } |
386 | 451 |
387 void Traversal::AppendManyToTraversal( | 452 void Traversal::AppendManyToTraversal( |
388 const syncable::Directory::Metahandles& handles) { | 453 const syncable::Directory::Metahandles& handles) { |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
422 } | 487 } |
423 } | 488 } |
424 } | 489 } |
425 | 490 |
426 // It's possible that we overcommitted while trying to expand dependent | 491 // It's possible that we overcommitted while trying to expand dependent |
427 // items. If so, truncate the set down to the allowed size. | 492 // items. If so, truncate the set down to the allowed size. |
428 if (out_->size() > max_entries_) | 493 if (out_->size() > max_entries_) |
429 out_->resize(max_entries_); | 494 out_->resize(max_entries_); |
430 } | 495 } |
431 | 496 |
432 void Traversal::AddDeletes( | 497 void Traversal::AddDeletes(const std::set<int64>& ready_unsynced_set) { |
433 const std::set<int64>& ready_unsynced_set) { | 498 syncable::Directory::Metahandles deletion_list; |
434 set<syncable::Id> legal_delete_parents; | |
435 | 499 |
436 for (std::set<int64>::const_iterator iter = ready_unsynced_set.begin(); | 500 for (std::set<int64>::const_iterator iter = ready_unsynced_set.begin(); |
437 !IsFull() && iter != ready_unsynced_set.end(); ++iter) { | 501 !IsFull() && iter != ready_unsynced_set.end(); ++iter) { |
438 int64 metahandle = *iter; | 502 int64 metahandle = *iter; |
| 503 |
439 if (HaveItem(metahandle)) | 504 if (HaveItem(metahandle)) |
440 continue; | 505 continue; |
441 | 506 |
| 507 if (std::find(deletion_list.begin(), deletion_list.end(), metahandle) != |
| 508 deletion_list.end()) { |
| 509 continue; |
| 510 } |
| 511 |
442 syncable::Entry entry(trans_, syncable::GET_BY_HANDLE, | 512 syncable::Entry entry(trans_, syncable::GET_BY_HANDLE, |
443 metahandle); | 513 metahandle); |
444 | 514 |
445 if (entry.GetIsDel()) { | 515 if (entry.GetIsDel()) { |
446 syncable::Entry parent(trans_, syncable::GET_BY_ID, | 516 syncable::Directory::Metahandles parents; |
447 entry.GetParentId()); | 517 if (AddDeletedParents( |
448 // If the parent is deleted and unsynced, then any children of that | 518 ready_unsynced_set, entry, deletion_list, &parents)) { |
449 // parent don't need to be added to the delete queue. | 519 // Append parents and chilren in top to bottom order. |
450 // | 520 deletion_list.insert(deletion_list.end(), |
451 // Note: the parent could be synced if there was an update deleting a | 521 parents.begin(), |
452 // folder when we had a deleted all items in it. | 522 parents.end()); |
453 // We may get more updates, or we may want to delete the entry. | 523 deletion_list.push_back(metahandle); |
454 if (parent.good() && parent.GetIsDel() && parent.GetIsUnsynced()) { | 524 } |
455 // However, if an entry is moved, these rules can apply differently. | 525 } |
456 // | |
457 // If the entry was moved, then the destination parent was deleted, | |
458 // then we'll miss it in the roll up. We have to add it in manually. | |
459 // TODO(chron): Unit test for move / delete cases: | |
460 // Case 1: Locally moved, then parent deleted | |
461 // Case 2: Server moved, then locally issue recursive delete. | |
462 if (entry.GetId().ServerKnows() && | |
463 entry.GetParentId() != entry.GetServerParentId()) { | |
464 DVLOG(1) << "Inserting moved and deleted entry, will be missed by " | |
465 << "delete roll." << entry.GetId(); | |
466 | 526 |
467 AppendToTraversal(metahandle); | 527 if (deletion_list.size() + out_->size() > max_entries_) |
468 } | 528 break; |
469 | |
470 // Skip this entry since it's a child of a parent that will be | |
471 // deleted. The server will unroll the delete and delete the | |
472 // child as well. | |
473 continue; | |
474 } | |
475 | |
476 legal_delete_parents.insert(entry.GetParentId()); | |
477 } | |
478 } | 529 } |
479 | 530 |
480 // We could store all the potential entries with a particular parent during | 531 // We've been gathering deletions in top to bottom order. Now we reverse the |
481 // the above scan, but instead we rescan here. This is less efficient, but | 532 // order as we prepare the list. |
482 // we're dropping memory alloc/dealloc in favor of linear scans of recently | 533 std::reverse(deletion_list.begin(), deletion_list.end()); |
483 // examined entries. | 534 AppendManyToTraversal(deletion_list); |
484 // | 535 |
485 // Scan through the UnsyncedMetaHandles again. If we have a deleted | 536 // It's possible that we overcommitted while trying to expand dependent |
486 // entry, then check if the parent is in legal_delete_parents. | 537 // items. If so, truncate the set down to the allowed size. |
487 // | 538 if (out_->size() > max_entries_) |
488 // Parent being in legal_delete_parents means for the child: | 539 out_->resize(max_entries_); |
489 // a recursive delete is not currently happening (no recent deletes in same | |
490 // folder) | |
491 // parent did expect at least one old deleted child | |
492 // parent was not deleted | |
493 for (std::set<int64>::const_iterator iter = ready_unsynced_set.begin(); | |
494 !IsFull() && iter != ready_unsynced_set.end(); ++iter) { | |
495 int64 metahandle = *iter; | |
496 if (HaveItem(metahandle)) | |
497 continue; | |
498 syncable::Entry entry(trans_, syncable::GET_BY_HANDLE, metahandle); | |
499 if (entry.GetIsDel()) { | |
500 syncable::Id parent_id = entry.GetParentId(); | |
501 if (legal_delete_parents.count(parent_id)) { | |
502 AppendToTraversal(metahandle); | |
503 } | |
504 } | |
505 } | |
506 } | 540 } |
507 | 541 |
508 void OrderCommitIds( | 542 void OrderCommitIds( |
509 syncable::BaseTransaction* trans, | 543 syncable::BaseTransaction* trans, |
510 size_t max_entries, | 544 size_t max_entries, |
511 const std::set<int64>& ready_unsynced_set, | 545 const std::set<int64>& ready_unsynced_set, |
512 syncable::Directory::Metahandles* out) { | 546 syncable::Directory::Metahandles* out) { |
513 // Commits follow these rules: | 547 // Commits follow these rules: |
514 // 1. Moves or creates are preceded by needed folder creates, from | 548 // 1. Moves or creates are preceded by needed folder creates, from |
515 // root to leaf. For folders whose contents are ordered, moves | 549 // root to leaf. For folders whose contents are ordered, moves |
516 // and creates appear in order. | 550 // and creates appear in order. |
517 // 2. Moves/Creates before deletes. | 551 // 2. Moves/Creates before deletes. |
518 // 3. Deletes, collapsed. | 552 // 3. Deletes, collapsed. |
519 // We commit deleted moves under deleted items as moves when collapsing | 553 // We commit deleted moves under deleted items as moves when collapsing |
520 // delete trees. | 554 // delete trees. |
521 | 555 |
522 Traversal traversal(trans, max_entries, out); | 556 Traversal traversal(trans, max_entries, out); |
523 | 557 |
524 // Add moves and creates, and prepend their uncommitted parents. | 558 // Add moves and creates, and prepend their uncommitted parents. |
525 traversal.AddCreatesAndMoves(ready_unsynced_set); | 559 traversal.AddCreatesAndMoves(ready_unsynced_set); |
526 | 560 |
527 // Add all deletes. | 561 // Add all deletes. |
528 traversal.AddDeletes(ready_unsynced_set); | 562 traversal.AddDeletes(ready_unsynced_set); |
529 } | 563 } |
530 | 564 |
531 } // namespace | 565 } // namespace |
532 | 566 |
533 } // namespace syncer | 567 } // namespace syncer |
OLD | NEW |