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

Unified Diff: chrome/browser/sync/engine/syncer_util.cc

Issue 2844037: Fix handling of undeletion within the syncer. (Closed) Base URL: http://src.chromium.org/git/chromium.git
Patch Set: Whitespace. Created 10 years, 5 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/sync/engine/syncer_unittest.cc ('k') | chrome/browser/sync/engine/syncproto.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/sync/engine/syncer_util.cc
diff --git a/chrome/browser/sync/engine/syncer_util.cc b/chrome/browser/sync/engine/syncer_util.cc
index 8b5f7b9294c6f76ddb74954d5e97cf6a8a0a8ce9..3133a337bbf35e70f4e4cb10b09dc0228cc5b32b 100644
--- a/chrome/browser/sync/engine/syncer_util.cc
+++ b/chrome/browser/sync/engine/syncer_util.cc
@@ -132,6 +132,10 @@ void SyncerUtil::ChangeEntryIDAndUpdateChildren(
// static
void SyncerUtil::AttemptReuniteClientTag(syncable::WriteTransaction* trans,
const SyncEntity& server_entry) {
+ if (!server_entry.has_client_defined_unique_tag() ||
+ server_entry.client_defined_unique_tag().empty()) {
+ return;
+ }
// Expected entry points of this function:
// SyncEntity has NOT been applied to SERVER fields.
@@ -148,9 +152,9 @@ void SyncerUtil::AttemptReuniteClientTag(syncable::WriteTransaction* trans,
// work just fine. Update will end up in the proper entry, via ID lookup.
// Case 2 - Should never happen. We'd need to change the
// ID of the local entry, we refuse. We'll skip this in VERIFY.
- // Case 3 - We need to replace the local ID with the server ID. Conflict
- // resolution must occur, but this is prior to update application! This case
- // should be rare. For now, clobber client changes entirely.
+ // Case 3 - We need to replace the local ID with the server ID so that
+ // this update gets targeted at the correct local entry; we expect conflict
+ // resolution to occur.
// Case 4 - Perfect. Same as case 1.
syncable::MutableEntry local_entry(trans, syncable::GET_BY_CLIENT_TAG,
@@ -167,18 +171,13 @@ void SyncerUtil::AttemptReuniteClientTag(syncable::WriteTransaction* trans,
DCHECK(local_entry.Get(ID) == server_entry.id());
} else {
// Case 3: We have a local entry with the same client tag.
- // We can't have two updates with the same client tag though.
- // One of these has to go. Let's delete the client entry and move it
- // aside. This will cause a delete + create. The client API user must
- // handle this correctly. In this situation the client must have created
- // this entry but not yet committed it for the first time. Usually the
- // client probably wants the server data for this instead.
- // Other strategies to handle this are a bit flaky.
- DCHECK(local_entry.Get(IS_UNAPPLIED_UPDATE) == false);
- local_entry.Put(IS_UNSYNCED, false);
- local_entry.Put(IS_DEL, true);
- // Needs to get out of the index before our update can be put in.
- local_entry.Put(UNIQUE_CLIENT_TAG, "");
+ // We should change the ID of the local entry to the server entry.
+ // This will result in an server ID with base version == 0, but that's
+ // a legal state for an item with a client tag. By changing the ID,
+ // server_entry will now be applied to local_entry.
+ ChangeEntryIDAndUpdateChildren(trans, &local_entry, server_entry.id());
+ DCHECK(0 == local_entry.Get(BASE_VERSION) ||
+ CHANGES_VERSION == local_entry.Get(BASE_VERSION));
}
}
// Case 4: Client has no entry for tag, all green.
@@ -201,7 +200,7 @@ void SyncerUtil::AttemptReuniteLostCommitResponses(
// a reasonable span, our commit batches have to be small enough
// to process within our HTTP response "assumed alive" time.
- // We need to check if we have a that didn't get its server
+ // We need to check if we have an entry that didn't get its server
// id updated correctly. The server sends down a client ID
// and a local (negative) id. If we have a entry by that
// description, we should update the ID and version to the
@@ -210,21 +209,21 @@ void SyncerUtil::AttemptReuniteLostCommitResponses(
server_entry.originator_cache_guid() == client_id) {
syncable::Id server_id = syncable::Id::CreateFromClientString(
server_entry.originator_client_item_id());
- CHECK(!server_id.ServerKnows());
+ DCHECK(!server_id.ServerKnows());
syncable::MutableEntry local_entry(trans, GET_BY_ID, server_id);
// If it exists, then our local client lost a commit response.
if (local_entry.good() && !local_entry.Get(IS_DEL)) {
int64 old_version = local_entry.Get(BASE_VERSION);
int64 new_version = server_entry.version();
- CHECK(old_version <= 0);
- CHECK(new_version > 0);
+ DCHECK(old_version <= 0);
+ DCHECK(new_version > 0);
// Otherwise setting the base version could cause a consistency failure.
// An entry should never be version 0 and SYNCED.
- CHECK(local_entry.Get(IS_UNSYNCED));
+ DCHECK(local_entry.Get(IS_UNSYNCED));
// Just a quick sanity check.
- CHECK(!local_entry.Get(ID).ServerKnows());
+ DCHECK(!local_entry.Get(ID).ServerKnows());
LOG(INFO) << "Reuniting lost commit response IDs" <<
" server id: " << server_entry.id() << " local id: " <<
@@ -238,8 +237,8 @@ void SyncerUtil::AttemptReuniteLostCommitResponses(
// reunited its ID.
}
// !local_entry.Good() means we don't have a left behind entry for this
- // ID. We successfully committed before. In the future we should get rid
- // of this system and just have client side generated IDs as a whole.
+ // ID in sync database. This could happen if we crashed after successfully
+ // committing an item that never was flushed to disk.
}
}
@@ -352,17 +351,31 @@ void SyncerUtil::UpdateServerFieldsFromUpdate(
const SyncEntity& server_entry,
const string& name) {
if (server_entry.deleted()) {
+ if (local_entry->Get(SERVER_IS_DEL)) {
+ // If we already think the item is server-deleted, we're done.
+ // Skipping these cases prevents our committed deletions from coming
+ // back and overriding subsequent undeletions. For non-deleted items,
+ // the version number check has a similar effect.
+ return;
+ }
// The server returns very lightweight replies for deletions, so we don't
// clobber a bunch of fields on delete.
local_entry->Put(SERVER_IS_DEL, true);
- local_entry->Put(SERVER_VERSION,
- std::max(local_entry->Get(SERVER_VERSION),
- local_entry->Get(BASE_VERSION)) + 1L);
+ if (!local_entry->Get(UNIQUE_CLIENT_TAG).empty()) {
+ // Items identified by the client unique tag are undeletable; when
+ // they're deleted, they go back to version 0.
+ local_entry->Put(SERVER_VERSION, 0);
+ } else {
+ // Otherwise, fake a server version by bumping the local number.
+ local_entry->Put(SERVER_VERSION,
+ std::max(local_entry->Get(SERVER_VERSION),
+ local_entry->Get(BASE_VERSION)) + 1);
+ }
local_entry->Put(IS_UNAPPLIED_UPDATE, true);
return;
}
- CHECK(local_entry->Get(ID) == server_entry.id())
+ DCHECK(local_entry->Get(ID) == server_entry.id())
<< "ID Changing not supported here";
local_entry->Put(SERVER_PARENT_ID, server_entry.parent_id());
local_entry->Put(SERVER_NON_UNIQUE_NAME, name);
@@ -505,8 +518,8 @@ void SyncerUtil::SplitServerInformationIntoNewEntry(
void SyncerUtil::UpdateLocalDataFromServerData(
syncable::WriteTransaction* trans,
syncable::MutableEntry* entry) {
- CHECK(!entry->Get(IS_UNSYNCED));
- CHECK(entry->Get(IS_UNAPPLIED_UPDATE));
+ DCHECK(!entry->Get(IS_UNSYNCED));
+ DCHECK(entry->Get(IS_UNAPPLIED_UPDATE));
LOG(INFO) << "Updating entry : " << *entry;
// Start by setting the properties that determine the model_type.
@@ -723,9 +736,8 @@ VerifyResult SyncerUtil::VerifyUpdateConsistency(
same_id->Get(BASE_VERSION) > 0))) {
// An undelete. The latter case in the above condition is for
// when the server does not give us an update following the
- // commit of a delete, before undeleting. Undeletion is possible
- // in the server's storage backend, so it's possible on the client,
- // though not expected to be something that is commonly possible.
+ // commit of a delete, before undeleting.
+ // Undeletion is common for items that reuse the client-unique tag.
VerifyResult result =
SyncerUtil::VerifyUndelete(trans, entry, same_id);
if (VERIFY_UNDECIDED != result)
@@ -769,12 +781,22 @@ VerifyResult SyncerUtil::VerifyUpdateConsistency(
VerifyResult SyncerUtil::VerifyUndelete(syncable::WriteTransaction* trans,
const SyncEntity& entry,
syncable::MutableEntry* same_id) {
+ // TODO(nick): We hit this path for items deleted items that the server
+ // tells us to re-create; only deleted items with positive base versions
+ // will hit this path. However, it's not clear how such an undeletion
+ // would actually succeed on the server; in the protocol, a base
+ // version of 0 is required to undelete an object. This codepath
+ // should be deprecated in favor of client-tag style undeletion
+ // (where items go to version 0 when they're deleted), or else
+ // removed entirely (if this type of undeletion is indeed impossible).
CHECK(same_id->good());
LOG(INFO) << "Server update is attempting undelete. " << *same_id
<< "Update:" << SyncerProtoUtil::SyncEntityDebugString(entry);
// Move the old one aside and start over. It's too tricky to get the old one
// back into a state that would pass CheckTreeInvariants().
if (same_id->Get(IS_DEL)) {
+ DCHECK(same_id->Get(UNIQUE_CLIENT_TAG).empty())
+ << "Doing move-aside undeletion on client-tagged item.";
same_id->Put(ID, trans->directory()->NextId());
same_id->Put(UNIQUE_CLIENT_TAG, "");
same_id->Put(BASE_VERSION, CHANGES_VERSION);
« no previous file with comments | « chrome/browser/sync/engine/syncer_unittest.cc ('k') | chrome/browser/sync/engine/syncproto.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698