| OLD | NEW | 
|    1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |    1 // Copyright (c) 2012 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/process_updates_command.h" |    5 #include "sync/engine/process_updates_command.h" | 
|    6  |    6  | 
|    7 #include <vector> |    7 #include <vector> | 
|    8  |    8  | 
|    9 #include "base/basictypes.h" |    9 #include "base/basictypes.h" | 
|   10 #include "base/location.h" |   10 #include "base/location.h" | 
|   11 #include "sync/engine/syncer.h" |   11 #include "sync/engine/syncer.h" | 
|   12 #include "sync/engine/syncer_proto_util.h" |   12 #include "sync/engine/syncer_proto_util.h" | 
|   13 #include "sync/engine/syncer_util.h" |   13 #include "sync/engine/syncer_util.h" | 
|   14 #include "sync/sessions/sync_session.h" |   14 #include "sync/sessions/sync_session.h" | 
|   15 #include "sync/syncable/directory.h" |   15 #include "sync/syncable/directory.h" | 
|   16 #include "sync/syncable/mutable_entry.h" |   16 #include "sync/syncable/mutable_entry.h" | 
|   17 #include "sync/syncable/syncable_proto_util.h" |   17 #include "sync/syncable/syncable_proto_util.h" | 
|   18 #include "sync/syncable/syncable_util.h" |   18 #include "sync/syncable/syncable_util.h" | 
|   19 #include "sync/syncable/write_transaction.h" |   19 #include "sync/syncable/write_transaction.h" | 
|   20 #include "sync/util/cryptographer.h" |   20 #include "sync/util/cryptographer.h" | 
|   21  |   21  | 
|   22 using std::vector; |   22 using std::vector; | 
|   23  |   23  | 
|   24 namespace syncer { |   24 namespace syncer { | 
|   25  |   25  | 
|   26 using sessions::SyncSession; |   26 using sessions::SyncSession; | 
|   27 using sessions::StatusController; |   27 using sessions::StatusController; | 
|   28 using sessions::UpdateProgress; |   28  | 
 |   29 using syncable::GET_BY_ID; | 
|   29  |   30  | 
|   30 ProcessUpdatesCommand::ProcessUpdatesCommand() {} |   31 ProcessUpdatesCommand::ProcessUpdatesCommand() {} | 
|   31 ProcessUpdatesCommand::~ProcessUpdatesCommand() {} |   32 ProcessUpdatesCommand::~ProcessUpdatesCommand() {} | 
|   32  |   33  | 
|   33 std::set<ModelSafeGroup> ProcessUpdatesCommand::GetGroupsToChange( |   34 std::set<ModelSafeGroup> ProcessUpdatesCommand::GetGroupsToChange( | 
|   34     const sessions::SyncSession& session) const { |   35     const sessions::SyncSession& session) const { | 
|   35   return session.GetEnabledGroupsWithVerifiedUpdates(); |   36   std::set<ModelSafeGroup> groups_with_updates; | 
|   36 } |   37  | 
 |   38   const sync_pb::GetUpdatesResponse& updates = | 
 |   39       session.status_controller().updates_response().get_updates(); | 
 |   40   for (int i = 0; i < updates.entries().size(); i++) { | 
 |   41     groups_with_updates.insert( | 
 |   42         GetGroupForModelType(GetModelType(updates.entries(i)), | 
 |   43                              session.routing_info())); | 
 |   44   } | 
 |   45  | 
 |   46   return groups_with_updates; | 
 |   47 } | 
 |   48  | 
 |   49 namespace { | 
 |   50  | 
 |   51 // This function attempts to determine whether or not this update is genuinely | 
 |   52 // new, or if it is a reflection of one of our own commits. | 
 |   53 // | 
 |   54 // There is a known inaccuracy in its implementation.  If this update ends up | 
 |   55 // being applied to a local item with a different ID, we will count the change | 
 |   56 // as being a non-reflection update.  Fortunately, the server usually updates | 
 |   57 // our IDs correctly in its commit response, so a new ID during GetUpdate should | 
 |   58 // be rare. | 
 |   59 // | 
 |   60 // The only secnarios I can think of where this might happen are: | 
 |   61 // - We commit a  new item to the server, but we don't persist the | 
 |   62 // server-returned new ID to the database before we shut down.  On the GetUpdate | 
 |   63 // following the next restart, we will receive an update from the server that | 
 |   64 // updates its local ID. | 
 |   65 // - When two attempts to create an item with identical UNIQUE_CLIENT_TAG values | 
 |   66 // collide at the server.  I have seen this in testing.  When it happens, the | 
 |   67 // test server will send one of the clients a response to upate its local ID so | 
 |   68 // that both clients will refer to the item using the same ID going forward.  In | 
 |   69 // this case, we're right to assume that the update is not a reflection. | 
 |   70 // | 
 |   71 // For more information, see FindLocalIdToUpdate(). | 
 |   72 bool UpdateContainsNewVersion(syncable::BaseTransaction *trans, | 
 |   73                               const sync_pb::SyncEntity &update) { | 
 |   74   int64 existing_version = -1; // The server always sends positive versions. | 
 |   75   syncable::Entry existing_entry(trans, GET_BY_ID, | 
 |   76                                  SyncableIdFromProto(update.id_string())); | 
 |   77   if (existing_entry.good()) | 
 |   78     existing_version = existing_entry.Get(syncable::BASE_VERSION); | 
 |   79  | 
 |   80   if (!existing_entry.good() && update.deleted()) { | 
 |   81     // There are several possible explanations for this.  The most common cases | 
 |   82     // will be first time sync and the redelivery of deletions we've already | 
 |   83     // synced, accepted, and purged from our database.  In either case, the | 
 |   84     // update is useless to us.  Let's count them all as "not new", even though | 
 |   85     // that may not always be entirely accurate. | 
 |   86     return false; | 
 |   87   } | 
 |   88  | 
 |   89   if (existing_entry.good() && | 
 |   90       !existing_entry.Get(syncable::UNIQUE_CLIENT_TAG).empty() && | 
 |   91       existing_entry.Get(syncable::IS_DEL) && | 
 |   92       update.deleted()) { | 
 |   93     // Unique client tags will have their version set to zero when they're | 
 |   94     // deleted.  The usual version comparison logic won't be able to detect | 
 |   95     // reflections of these items.  Instead, we assume any received tombstones | 
 |   96     // are reflections.  That should be correct most of the time. | 
 |   97     return false; | 
 |   98   } | 
 |   99  | 
 |  100   return existing_version < update.version(); | 
 |  101 } | 
 |  102  | 
 |  103 }  // namespace | 
|   37  |  104  | 
|   38 SyncerError ProcessUpdatesCommand::ModelChangingExecuteImpl( |  105 SyncerError ProcessUpdatesCommand::ModelChangingExecuteImpl( | 
|   39     SyncSession* session) { |  106     SyncSession* session) { | 
|   40   syncable::Directory* dir = session->context()->directory(); |  107   syncable::Directory* dir = session->context()->directory(); | 
|   41  |  108  | 
|   42   const sessions::UpdateProgress* progress = |  | 
|   43       session->status_controller().update_progress(); |  | 
|   44   if (!progress) |  | 
|   45     return SYNCER_OK;  // Nothing to do. |  | 
|   46  |  | 
|   47   syncable::WriteTransaction trans(FROM_HERE, syncable::SYNCER, dir); |  109   syncable::WriteTransaction trans(FROM_HERE, syncable::SYNCER, dir); | 
|   48   vector<sessions::VerifiedUpdate>::const_iterator it; |  110  | 
|   49   for (it = progress->VerifiedUpdatesBegin(); |  111   sessions::StatusController* status = session->mutable_status_controller(); | 
|   50        it != progress->VerifiedUpdatesEnd(); |  112   const sync_pb::GetUpdatesResponse& updates = | 
|   51        ++it) { |  113       status->updates_response().get_updates(); | 
|   52     const sync_pb::SyncEntity& update = it->second; |  114   int update_count = updates.entries().size(); | 
|   53  |  115  | 
|   54     if (it->first != VERIFY_SUCCESS && it->first != VERIFY_UNDELETE) |  116   ModelTypeSet requested_types = GetRoutingInfoTypes( | 
 |  117       session->routing_info()); | 
 |  118  | 
 |  119   DVLOG(1) << update_count << " entries to verify"; | 
 |  120   for (int i = 0; i < update_count; i++) { | 
 |  121     const sync_pb::SyncEntity& update = updates.entries(i); | 
 |  122     ModelSafeGroup g = GetGroupForModelType(GetModelType(update), | 
 |  123                                             session->routing_info()); | 
 |  124     if (g != status->group_restriction()) | 
|   55       continue; |  125       continue; | 
|   56     switch (ProcessUpdate(update, |  126  | 
|   57                           dir->GetCryptographer(&trans), |  127     VerifyResult verify_result = VerifyUpdate(&trans, update, | 
|   58                           &trans)) { |  128                                               requested_types, | 
|   59       case SUCCESS_PROCESSED: |  129                                               session->routing_info()); | 
|   60       case SUCCESS_STORED: |  130     status->increment_num_updates_downloaded_by(1); | 
|   61         break; |  131     if (!UpdateContainsNewVersion(&trans, update)) | 
|   62       default: |  132       status->increment_num_reflected_updates_downloaded_by(1); | 
|   63         NOTREACHED(); |  133     if (update.deleted()) | 
|   64         break; |  134       status->increment_num_tombstone_updates_downloaded_by(1); | 
 |  135  | 
 |  136     if (verify_result != VERIFY_SUCCESS && verify_result != VERIFY_UNDELETE) | 
 |  137       continue; | 
 |  138  | 
 |  139     ServerUpdateProcessingResult process_result = | 
 |  140        ProcessUpdate(update, dir->GetCryptographer(&trans), &trans); | 
 |  141  | 
 |  142     DCHECK(process_result == SUCCESS_PROCESSED || | 
 |  143            process_result == SUCCESS_STORED); | 
 |  144   } | 
 |  145  | 
 |  146   return SYNCER_OK; | 
 |  147 } | 
 |  148  | 
 |  149 namespace { | 
 |  150  | 
 |  151 // In the event that IDs match, but tags differ AttemptReuniteClient tag | 
 |  152 // will have refused to unify the update. | 
 |  153 // We should not attempt to apply it at all since it violates consistency | 
 |  154 // rules. | 
 |  155 VerifyResult VerifyTagConsistency(const sync_pb::SyncEntity& entry, | 
 |  156                                   const syncable::MutableEntry& same_id) { | 
 |  157   if (entry.has_client_defined_unique_tag() && | 
 |  158       entry.client_defined_unique_tag() != | 
 |  159           same_id.Get(syncable::UNIQUE_CLIENT_TAG)) { | 
 |  160     return VERIFY_FAIL; | 
 |  161   } | 
 |  162   return VERIFY_UNDECIDED; | 
 |  163 } | 
 |  164  | 
 |  165 }  // namespace | 
 |  166  | 
 |  167 VerifyResult ProcessUpdatesCommand::VerifyUpdate( | 
 |  168     syncable::WriteTransaction* trans, const sync_pb::SyncEntity& entry, | 
 |  169     ModelTypeSet requested_types, | 
 |  170     const ModelSafeRoutingInfo& routes) { | 
 |  171   syncable::Id id = SyncableIdFromProto(entry.id_string()); | 
 |  172   VerifyResult result = VERIFY_FAIL; | 
 |  173  | 
 |  174   const bool deleted = entry.has_deleted() && entry.deleted(); | 
 |  175   const bool is_directory = IsFolder(entry); | 
 |  176   const ModelType model_type = GetModelType(entry); | 
 |  177  | 
 |  178   if (!id.ServerKnows()) { | 
 |  179     LOG(ERROR) << "Illegal negative id in received updates"; | 
 |  180     return result; | 
 |  181   } | 
 |  182   { | 
 |  183     const std::string name = SyncerProtoUtil::NameFromSyncEntity(entry); | 
 |  184     if (name.empty() && !deleted) { | 
 |  185       LOG(ERROR) << "Zero length name in non-deleted update"; | 
 |  186       return result; | 
|   65     } |  187     } | 
|   66   } |  188   } | 
|   67  |  189  | 
|   68   StatusController* status = session->mutable_status_controller(); |  190   syncable::MutableEntry same_id(trans, GET_BY_ID, id); | 
|   69   status->mutable_update_progress()->ClearVerifiedUpdates(); |  191   result = VerifyNewEntry(entry, &same_id, deleted); | 
|   70   return SYNCER_OK; |  192  | 
 |  193   ModelType placement_type = !deleted ? GetModelType(entry) | 
 |  194       : same_id.good() ? same_id.GetModelType() : UNSPECIFIED; | 
 |  195  | 
 |  196   if (VERIFY_UNDECIDED == result) { | 
 |  197     result = VerifyTagConsistency(entry, same_id); | 
 |  198   } | 
 |  199  | 
 |  200   if (VERIFY_UNDECIDED == result) { | 
 |  201     if (deleted) { | 
 |  202       // For deletes the server could send tombostones for items that | 
 |  203       // the client did not request. If so ignore those items. | 
 |  204       if (IsRealDataType(placement_type) && | 
 |  205           !requested_types.Has(placement_type)) { | 
 |  206         result = VERIFY_SKIP; | 
 |  207       } else { | 
 |  208         result = VERIFY_SUCCESS; | 
 |  209       } | 
 |  210     } | 
 |  211   } | 
 |  212  | 
 |  213   // If we have an existing entry, we check here for updates that break | 
 |  214   // consistency rules. | 
 |  215   if (VERIFY_UNDECIDED == result) { | 
 |  216     result = VerifyUpdateConsistency(trans, entry, &same_id, | 
 |  217                                      deleted, is_directory, model_type); | 
 |  218   } | 
 |  219  | 
 |  220   if (VERIFY_UNDECIDED == result) | 
 |  221     result = VERIFY_SUCCESS;  // No news is good news. | 
 |  222  | 
 |  223   return result;  // This might be VERIFY_SUCCESS as well | 
|   71 } |  224 } | 
|   72  |  225  | 
|   73 namespace { |  226 namespace { | 
|   74 // Returns true if the entry is still ok to process. |  227 // Returns true if the entry is still ok to process. | 
|   75 bool ReverifyEntry(syncable::WriteTransaction* trans, |  228 bool ReverifyEntry(syncable::WriteTransaction* trans, | 
|   76                    const sync_pb::SyncEntity& entry, |  229                    const sync_pb::SyncEntity& entry, | 
|   77                    syncable::MutableEntry* same_id) { |  230                    syncable::MutableEntry* same_id) { | 
|   78  |  231  | 
|   79   const bool deleted = entry.has_deleted() && entry.deleted(); |  232   const bool deleted = entry.has_deleted() && entry.deleted(); | 
|   80   const bool is_directory = IsFolder(entry); |  233   const bool is_directory = IsFolder(entry); | 
| (...skipping 22 matching lines...) Expand all  Loading... | 
|  103  |  256  | 
|  104   // FindLocalEntryToUpdate has veto power. |  257   // FindLocalEntryToUpdate has veto power. | 
|  105   if (local_id.IsNull()) { |  258   if (local_id.IsNull()) { | 
|  106     return SUCCESS_PROCESSED;  // The entry has become irrelevant. |  259     return SUCCESS_PROCESSED;  // The entry has become irrelevant. | 
|  107   } |  260   } | 
|  108  |  261  | 
|  109   CreateNewEntry(trans, local_id); |  262   CreateNewEntry(trans, local_id); | 
|  110  |  263  | 
|  111   // We take a two step approach. First we store the entries data in the |  264   // We take a two step approach. First we store the entries data in the | 
|  112   // server fields of a local entry and then move the data to the local fields |  265   // server fields of a local entry and then move the data to the local fields | 
|  113   syncable::MutableEntry target_entry(trans, syncable::GET_BY_ID, local_id); |  266   syncable::MutableEntry target_entry(trans, GET_BY_ID, local_id); | 
|  114  |  267  | 
|  115   // We need to run the Verify checks again; the world could have changed |  268   // We need to run the Verify checks again; the world could have changed | 
|  116   // since VerifyUpdatesCommand. |  269   // since VerifyUpdatesCommand. | 
|  117   if (!ReverifyEntry(trans, update, &target_entry)) { |  270   if (!ReverifyEntry(trans, update, &target_entry)) { | 
|  118     return SUCCESS_PROCESSED;  // The entry has become irrelevant. |  271     return SUCCESS_PROCESSED;  // The entry has become irrelevant. | 
|  119   } |  272   } | 
|  120  |  273  | 
|  121   // If we're repurposing an existing local entry with a new server ID, |  274   // If we're repurposing an existing local entry with a new server ID, | 
|  122   // change the ID now, after we're sure that the update can succeed. |  275   // change the ID now, after we're sure that the update can succeed. | 
|  123   if (local_id != server_id) { |  276   if (local_id != server_id) { | 
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  176     target_entry.Put(syncable::BASE_SERVER_SPECIFICS, |  329     target_entry.Put(syncable::BASE_SERVER_SPECIFICS, | 
|  177                      sync_pb::EntitySpecifics()); |  330                      sync_pb::EntitySpecifics()); | 
|  178   } |  331   } | 
|  179  |  332  | 
|  180   UpdateServerFieldsFromUpdate(&target_entry, update, name); |  333   UpdateServerFieldsFromUpdate(&target_entry, update, name); | 
|  181  |  334  | 
|  182   return SUCCESS_PROCESSED; |  335   return SUCCESS_PROCESSED; | 
|  183 } |  336 } | 
|  184  |  337  | 
|  185 }  // namespace syncer |  338 }  // namespace syncer | 
| OLD | NEW |