| 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 |