Home | History | Annotate | Download | only in engine
      1 // Copyright 2014 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 "sync/engine/entity_tracker.h"
      6 
      7 #include "base/logging.h"
      8 #include "sync/internal_api/public/base/model_type.h"
      9 #include "sync/internal_api/public/non_blocking_sync_common.h"
     10 #include "sync/syncable/syncable_util.h"
     11 #include "sync/util/time.h"
     12 
     13 namespace syncer {
     14 
     15 EntityTracker* EntityTracker::FromServerUpdate(
     16     const std::string& id_string,
     17     const std::string& client_tag_hash,
     18     int64 received_version) {
     19   return new EntityTracker(id_string, client_tag_hash, 0, received_version);
     20 }
     21 
     22 EntityTracker* EntityTracker::FromCommitRequest(
     23     const std::string& id_string,
     24     const std::string& client_tag_hash,
     25     int64 sequence_number,
     26     int64 base_version,
     27     base::Time ctime,
     28     base::Time mtime,
     29     const std::string& non_unique_name,
     30     bool deleted,
     31     const sync_pb::EntitySpecifics& specifics) {
     32   return new EntityTracker(id_string,
     33                            client_tag_hash,
     34                            0,
     35                            0,
     36                            true,
     37                            sequence_number,
     38                            base_version,
     39                            ctime,
     40                            mtime,
     41                            non_unique_name,
     42                            deleted,
     43                            specifics);
     44 }
     45 
     46 // Constructor that does not set any pending commit fields.
     47 EntityTracker::EntityTracker(const std::string& id,
     48                              const std::string& client_tag_hash,
     49                              int64 highest_commit_response_version,
     50                              int64 highest_gu_response_version)
     51     : id_(id),
     52       client_tag_hash_(client_tag_hash),
     53       highest_commit_response_version_(highest_commit_response_version),
     54       highest_gu_response_version_(highest_gu_response_version),
     55       is_commit_pending_(false),
     56       sequence_number_(0),
     57       base_version_(0),
     58       deleted_(false) {
     59 }
     60 
     61 EntityTracker::EntityTracker(const std::string& id,
     62                              const std::string& client_tag_hash,
     63                              int64 highest_commit_response_version,
     64                              int64 highest_gu_response_version,
     65                              bool is_commit_pending,
     66                              int64 sequence_number,
     67                              int64 base_version,
     68                              base::Time ctime,
     69                              base::Time mtime,
     70                              const std::string& non_unique_name,
     71                              bool deleted,
     72                              const sync_pb::EntitySpecifics& specifics)
     73     : id_(id),
     74       client_tag_hash_(client_tag_hash),
     75       highest_commit_response_version_(highest_commit_response_version),
     76       highest_gu_response_version_(highest_gu_response_version),
     77       is_commit_pending_(is_commit_pending),
     78       sequence_number_(sequence_number),
     79       base_version_(base_version),
     80       ctime_(ctime),
     81       mtime_(mtime),
     82       non_unique_name_(non_unique_name),
     83       deleted_(deleted),
     84       specifics_(specifics) {
     85 }
     86 
     87 EntityTracker::~EntityTracker() {
     88 }
     89 
     90 bool EntityTracker::IsCommitPending() const {
     91   return is_commit_pending_;
     92 }
     93 
     94 void EntityTracker::PrepareCommitProto(sync_pb::SyncEntity* commit_entity,
     95                                        int64* sequence_number) const {
     96   // Set ID if we have a server-assigned ID.  Otherwise, it will be up to
     97   // our caller to assign a client-unique initial ID.
     98   if (base_version_ != kUncommittedVersion) {
     99     commit_entity->set_id_string(id_);
    100   }
    101 
    102   commit_entity->set_client_defined_unique_tag(client_tag_hash_);
    103   commit_entity->set_version(base_version_);
    104   commit_entity->set_deleted(deleted_);
    105   commit_entity->set_folder(false);
    106   commit_entity->set_name(non_unique_name_);
    107   if (!deleted_) {
    108     commit_entity->set_ctime(TimeToProtoTime(ctime_));
    109     commit_entity->set_mtime(TimeToProtoTime(mtime_));
    110     commit_entity->mutable_specifics()->CopyFrom(specifics_);
    111   }
    112 
    113   *sequence_number = sequence_number_;
    114 }
    115 
    116 void EntityTracker::RequestCommit(const std::string& id,
    117                                   const std::string& client_tag_hash,
    118                                   int64 sequence_number,
    119                                   int64 base_version,
    120                                   base::Time ctime,
    121                                   base::Time mtime,
    122                                   const std::string& non_unique_name,
    123                                   bool deleted,
    124                                   const sync_pb::EntitySpecifics& specifics) {
    125   DCHECK_GE(base_version, base_version_)
    126       << "Base version should never decrease";
    127 
    128   DCHECK_GE(sequence_number, sequence_number_)
    129       << "Sequence number should never decrease";
    130 
    131   // Update our book-keeping counters.
    132   base_version_ = base_version;
    133   sequence_number_ = sequence_number;
    134 
    135   // Do our counter values indicate a conflict?  If so, don't commit.
    136   //
    137   // There's no need to inform the model thread of the conflict.  The
    138   // conflicting update has already been posted to its task runner; it will
    139   // figure it out as soon as it runs that task.
    140   is_commit_pending_ = true;
    141   if (IsInConflict()) {
    142     ClearPendingCommit();
    143     return;
    144   }
    145 
    146   // We don't commit deletions of server-unknown items.
    147   if (deleted && !IsServerKnown()) {
    148     ClearPendingCommit();
    149     return;
    150   }
    151 
    152   // Otherwise, we should store the data associated with this pending commit
    153   // so we're ready to commit at the next possible opportunity.
    154 
    155   // We intentionally don't update the id_ here.  Good ID values come from the
    156   // server and always pass through the sync thread first.  There's no way the
    157   // model thread could have a better ID value than we do.
    158 
    159   // This entity is identified by its client tag.  That value can never change.
    160   DCHECK_EQ(client_tag_hash_, client_tag_hash);
    161 
    162   // Set the fields for the pending commit.
    163   ctime_ = ctime;
    164   mtime_ = mtime;
    165   non_unique_name_ = non_unique_name;
    166   deleted_ = deleted;
    167   specifics_ = specifics;
    168 }
    169 
    170 void EntityTracker::ReceiveCommitResponse(const std::string& response_id,
    171                                           int64 response_version,
    172                                           int64 sequence_number) {
    173   // Commit responses, especially after the first commit, can update our ID.
    174   id_ = response_id;
    175 
    176   DCHECK_GT(response_version, highest_commit_response_version_)
    177       << "Had expected higher response version."
    178       << " id: " << id_;
    179 
    180   // Commits are synchronous, so there's no reason why the sequence numbers
    181   // wouldn't match.
    182   DCHECK_EQ(sequence_number_, sequence_number)
    183       << "Unexpected sequence number mismatch."
    184       << " id: " << id_;
    185 
    186   highest_commit_response_version_ = response_version;
    187 
    188   // Because an in-progress commit blocks the sync thread, we can assume that
    189   // the item we just committed successfully is exactly the one we have now.
    190   // Nothing changed it while the commit was happening.  Since we're now in
    191   // sync with the server, we can clear the pending commit.
    192   ClearPendingCommit();
    193 }
    194 
    195 void EntityTracker::ReceiveUpdate(int64 version) {
    196   if (version <= highest_gu_response_version_)
    197     return;
    198 
    199   highest_gu_response_version_ = version;
    200 
    201   // Got an applicable update newer than any pending updates.  It must be safe
    202   // to discard the old pending update, if there was one.
    203   ClearPendingUpdate();
    204 
    205   if (IsInConflict()) {
    206     // Incoming update clobbers the pending commit on the sync thread.
    207     // The model thread can re-request this commit later if it wants to.
    208     ClearPendingCommit();
    209   }
    210 }
    211 
    212 bool EntityTracker::ReceivePendingUpdate(const UpdateResponseData& data) {
    213   if (data.response_version < highest_gu_response_version_)
    214     return false;
    215 
    216   highest_gu_response_version_ = data.response_version;
    217   pending_update_.reset(new UpdateResponseData(data));
    218   ClearPendingCommit();
    219   return true;
    220 }
    221 
    222 bool EntityTracker::HasPendingUpdate() const {
    223   return !!pending_update_;
    224 }
    225 
    226 UpdateResponseData EntityTracker::GetPendingUpdate() const {
    227   return *pending_update_;
    228 }
    229 
    230 void EntityTracker::ClearPendingUpdate() {
    231   pending_update_.reset();
    232 }
    233 
    234 bool EntityTracker::IsInConflict() const {
    235   if (!is_commit_pending_)
    236     return false;
    237 
    238   if (HasPendingUpdate())
    239     return true;
    240 
    241   if (highest_gu_response_version_ <= highest_commit_response_version_) {
    242     // The most recent server state was created in a commit made by this
    243     // client.  We're fully up to date, and therefore not in conflict.
    244     return false;
    245   } else {
    246     // The most recent server state was written by someone else.
    247     // Did the model thread have the most up to date version when it issued the
    248     // commit request?
    249     if (base_version_ >= highest_gu_response_version_) {
    250       return false;  // Yes.
    251     } else {
    252       return true;  // No.
    253     }
    254   }
    255 }
    256 
    257 bool EntityTracker::IsServerKnown() const {
    258   return base_version_ != kUncommittedVersion;
    259 }
    260 
    261 void EntityTracker::ClearPendingCommit() {
    262   is_commit_pending_ = false;
    263 
    264   // Clearing the specifics might free up some memory.  It can't hurt to try.
    265   specifics_.Clear();
    266 }
    267 
    268 }  // namespace syncer
    269