mutable_entry.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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 3// found in the LICENSE file. 4 5#include "sync/syncable/mutable_entry.h" 6 7#include "base/memory/scoped_ptr.h" 8#include "sync/internal_api/public/base/node_ordinal.h" 9#include "sync/syncable/directory.h" 10#include "sync/syncable/scoped_index_updater.h" 11#include "sync/syncable/scoped_kernel_lock.h" 12#include "sync/syncable/syncable-inl.h" 13#include "sync/syncable/syncable_changes_version.h" 14#include "sync/syncable/syncable_util.h" 15#include "sync/syncable/write_transaction.h" 16 17using std::string; 18 19namespace syncer { 20namespace syncable { 21 22MutableEntry::MutableEntry(WriteTransaction* trans, Create, 23 const Id& parent_id, const string& name) 24 : Entry(trans), 25 write_transaction_(trans) { 26 Init(trans, parent_id, name); 27} 28 29 30void MutableEntry::Init(WriteTransaction* trans, const Id& parent_id, 31 const string& name) { 32 scoped_ptr<EntryKernel> kernel(new EntryKernel); 33 kernel_ = NULL; 34 35 kernel->put(ID, trans->directory_->NextId()); 36 kernel->put(META_HANDLE, trans->directory_->NextMetahandle()); 37 kernel->mark_dirty(trans->directory_->kernel_->dirty_metahandles); 38 kernel->put(PARENT_ID, parent_id); 39 kernel->put(NON_UNIQUE_NAME, name); 40 const base::Time& now = base::Time::Now(); 41 kernel->put(CTIME, now); 42 kernel->put(MTIME, now); 43 // We match the database defaults here 44 kernel->put(BASE_VERSION, CHANGES_VERSION); 45 kernel->put(SERVER_ORDINAL_IN_PARENT, NodeOrdinal::CreateInitialOrdinal()); 46 if (!trans->directory()->InsertEntry(trans, kernel.get())) { 47 return; // We failed inserting, nothing more to do. 48 } 49 // Because this entry is new, it was originally deleted. 50 kernel->put(IS_DEL, true); 51 trans->SaveOriginal(kernel.get()); 52 kernel->put(IS_DEL, false); 53 54 // Now swap the pointers. 55 kernel_ = kernel.release(); 56} 57 58MutableEntry::MutableEntry(WriteTransaction* trans, CreateNewUpdateItem, 59 const Id& id) 60 : Entry(trans), write_transaction_(trans) { 61 Entry same_id(trans, GET_BY_ID, id); 62 kernel_ = NULL; 63 if (same_id.good()) { 64 return; // already have an item with this ID. 65 } 66 scoped_ptr<EntryKernel> kernel(new EntryKernel()); 67 68 kernel->put(ID, id); 69 kernel->put(META_HANDLE, trans->directory_->NextMetahandle()); 70 kernel->mark_dirty(trans->directory_->kernel_->dirty_metahandles); 71 kernel->put(SERVER_ORDINAL_IN_PARENT, NodeOrdinal::CreateInitialOrdinal()); 72 kernel->put(IS_DEL, true); 73 // We match the database defaults here 74 kernel->put(BASE_VERSION, CHANGES_VERSION); 75 if (!trans->directory()->InsertEntry(trans, kernel.get())) { 76 return; // Failed inserting. 77 } 78 trans->SaveOriginal(kernel.get()); 79 80 kernel_ = kernel.release(); 81} 82 83MutableEntry::MutableEntry(WriteTransaction* trans, GetById, const Id& id) 84 : Entry(trans, GET_BY_ID, id), write_transaction_(trans) { 85} 86 87MutableEntry::MutableEntry(WriteTransaction* trans, GetByHandle, 88 int64 metahandle) 89 : Entry(trans, GET_BY_HANDLE, metahandle), write_transaction_(trans) { 90} 91 92MutableEntry::MutableEntry(WriteTransaction* trans, GetByClientTag, 93 const std::string& tag) 94 : Entry(trans, GET_BY_CLIENT_TAG, tag), write_transaction_(trans) { 95} 96 97MutableEntry::MutableEntry(WriteTransaction* trans, GetByServerTag, 98 const string& tag) 99 : Entry(trans, GET_BY_SERVER_TAG, tag), write_transaction_(trans) { 100} 101 102bool MutableEntry::PutIsDel(bool is_del) { 103 DCHECK(kernel_); 104 write_transaction_->SaveOriginal(kernel_); 105 if (is_del == kernel_->ref(IS_DEL)) { 106 return true; 107 } 108 if (is_del) { 109 if (!UnlinkFromOrder()) { 110 return false; 111 } 112 113 // If the server never knew about this item and it's deleted then we don't 114 // need to keep it around. Unsetting IS_UNSYNCED will: 115 // - Ensure that the item is never committed to the server. 116 // - Allow any items with the same UNIQUE_CLIENT_TAG created on other 117 // clients to override this entry. 118 // - Let us delete this entry permanently through 119 // DirectoryBackingStore::DropDeletedEntries() when we next restart sync. 120 // This will save memory and avoid crbug.com/125381. 121 if (!Get(ID).ServerKnows()) { 122 Put(IS_UNSYNCED, false); 123 } 124 } 125 126 { 127 ScopedKernelLock lock(dir()); 128 // Some indices don't include deleted items and must be updated 129 // upon a value change. 130 ScopedIndexUpdater<ParentIdAndHandleIndexer> updater(lock, kernel_, 131 dir()->kernel_->parent_id_child_index); 132 133 kernel_->put(IS_DEL, is_del); 134 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); 135 } 136 137 if (!is_del) 138 // Restores position to the 0th index. 139 if (!PutPredecessor(Id())) { 140 // TODO(lipalani) : Propagate the error to caller. crbug.com/100444. 141 NOTREACHED(); 142 } 143 144 return true; 145} 146 147bool MutableEntry::Put(Int64Field field, const int64& value) { 148 DCHECK(kernel_); 149 write_transaction_->SaveOriginal(kernel_); 150 if (kernel_->ref(field) != value) { 151 ScopedKernelLock lock(dir()); 152 kernel_->put(field, value); 153 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); 154 } 155 return true; 156} 157 158bool MutableEntry::Put(TimeField field, const base::Time& value) { 159 DCHECK(kernel_); 160 write_transaction_->SaveOriginal(kernel_); 161 if (kernel_->ref(field) != value) { 162 kernel_->put(field, value); 163 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); 164 } 165 return true; 166} 167 168bool MutableEntry::Put(IdField field, const Id& value) { 169 DCHECK(kernel_); 170 write_transaction_->SaveOriginal(kernel_); 171 if (kernel_->ref(field) != value) { 172 if (ID == field) { 173 if (!dir()->ReindexId(write_transaction(), kernel_, value)) 174 return false; 175 } else if (PARENT_ID == field) { 176 PutParentIdPropertyOnly(value); // Makes sibling order inconsistent. 177 // Fixes up the sibling order inconsistency. 178 if (!PutPredecessor(Id())) { 179 // TODO(lipalani) : Propagate the error to caller. crbug.com/100444. 180 NOTREACHED(); 181 } 182 } else { 183 kernel_->put(field, value); 184 } 185 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); 186 } 187 return true; 188} 189 190bool MutableEntry::Put(OrdinalField field, const NodeOrdinal& value) { 191 DCHECK(kernel_); 192 DCHECK(value.IsValid()); 193 write_transaction_->SaveOriginal(kernel_); 194 if(!kernel_->ref(field).Equals(value)) { 195 ScopedKernelLock lock(dir()); 196 if (SERVER_ORDINAL_IN_PARENT == field) { 197 ScopedIndexUpdater<ParentIdAndHandleIndexer> updater( 198 lock, kernel_, dir()->kernel_->parent_id_child_index); 199 kernel_->put(field, value); 200 } else { 201 kernel_->put(field, value); 202 } 203 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); 204 } 205 return true; 206} 207 208void MutableEntry::PutParentIdPropertyOnly(const Id& parent_id) { 209 write_transaction_->SaveOriginal(kernel_); 210 dir()->ReindexParentId(write_transaction(), kernel_, parent_id); 211 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); 212} 213 214bool MutableEntry::Put(BaseVersion field, int64 value) { 215 DCHECK(kernel_); 216 write_transaction_->SaveOriginal(kernel_); 217 if (kernel_->ref(field) != value) { 218 kernel_->put(field, value); 219 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); 220 } 221 return true; 222} 223 224bool MutableEntry::Put(StringField field, const string& value) { 225 DCHECK(kernel_); 226 write_transaction_->SaveOriginal(kernel_); 227 if (field == UNIQUE_CLIENT_TAG) { 228 return PutUniqueClientTag(value); 229 } 230 231 if (kernel_->ref(field) != value) { 232 kernel_->put(field, value); 233 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); 234 } 235 return true; 236} 237 238bool MutableEntry::Put(ProtoField field, 239 const sync_pb::EntitySpecifics& value) { 240 DCHECK(kernel_); 241 write_transaction_->SaveOriginal(kernel_); 242 // TODO(ncarter): This is unfortunately heavyweight. Can we do 243 // better? 244 if (kernel_->ref(field).SerializeAsString() != value.SerializeAsString()) { 245 const bool update_unapplied_updates_index = 246 (field == SERVER_SPECIFICS) && kernel_->ref(IS_UNAPPLIED_UPDATE); 247 if (update_unapplied_updates_index) { 248 // Remove ourselves from unapplied_update_metahandles with our 249 // old server type. 250 const ModelType old_server_type = kernel_->GetServerModelType(); 251 const int64 metahandle = kernel_->ref(META_HANDLE); 252 size_t erase_count = 253 dir()->kernel_->unapplied_update_metahandles[old_server_type] 254 .erase(metahandle); 255 DCHECK_EQ(erase_count, 1u); 256 } 257 258 kernel_->put(field, value); 259 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); 260 261 if (update_unapplied_updates_index) { 262 // Add ourselves back into unapplied_update_metahandles with our 263 // new server type. 264 const ModelType new_server_type = kernel_->GetServerModelType(); 265 const int64 metahandle = kernel_->ref(META_HANDLE); 266 dir()->kernel_->unapplied_update_metahandles[new_server_type] 267 .insert(metahandle); 268 } 269 } 270 return true; 271} 272 273bool MutableEntry::Put(BitField field, bool value) { 274 DCHECK(kernel_); 275 write_transaction_->SaveOriginal(kernel_); 276 if (kernel_->ref(field) != value) { 277 kernel_->put(field, value); 278 kernel_->mark_dirty(GetDirtyIndexHelper()); 279 } 280 return true; 281} 282 283MetahandleSet* MutableEntry::GetDirtyIndexHelper() { 284 return dir()->kernel_->dirty_metahandles; 285} 286 287bool MutableEntry::PutUniqueClientTag(const string& new_tag) { 288 write_transaction_->SaveOriginal(kernel_); 289 // There is no SERVER_UNIQUE_CLIENT_TAG. This field is similar to ID. 290 string old_tag = kernel_->ref(UNIQUE_CLIENT_TAG); 291 if (old_tag == new_tag) { 292 return true; 293 } 294 295 ScopedKernelLock lock(dir()); 296 if (!new_tag.empty()) { 297 // Make sure your new value is not in there already. 298 EntryKernel lookup_kernel_ = *kernel_; 299 lookup_kernel_.put(UNIQUE_CLIENT_TAG, new_tag); 300 bool new_tag_conflicts = 301 (dir()->kernel_->client_tag_index->count(&lookup_kernel_) > 0); 302 if (new_tag_conflicts) { 303 return false; 304 } 305 } 306 307 { 308 ScopedIndexUpdater<ClientTagIndexer> index_updater(lock, kernel_, 309 dir()->kernel_->client_tag_index); 310 kernel_->put(UNIQUE_CLIENT_TAG, new_tag); 311 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); 312 } 313 return true; 314} 315 316bool MutableEntry::Put(IndexedBitField field, bool value) { 317 DCHECK(kernel_); 318 write_transaction_->SaveOriginal(kernel_); 319 if (kernel_->ref(field) != value) { 320 MetahandleSet* index; 321 if (IS_UNSYNCED == field) { 322 index = dir()->kernel_->unsynced_metahandles; 323 } else { 324 // Use kernel_->GetServerModelType() instead of 325 // GetServerModelType() as we may trigger some DCHECKs in the 326 // latter. 327 index = 328 &dir()->kernel_->unapplied_update_metahandles[ 329 kernel_->GetServerModelType()]; 330 } 331 332 ScopedKernelLock lock(dir()); 333 if (value) { 334 if (!SyncAssert(index->insert(kernel_->ref(META_HANDLE)).second, 335 FROM_HERE, 336 "Could not insert", 337 write_transaction())) { 338 return false; 339 } 340 } else { 341 if (!SyncAssert(1U == index->erase(kernel_->ref(META_HANDLE)), 342 FROM_HERE, 343 "Entry Not succesfully erased", 344 write_transaction())) { 345 return false; 346 } 347 } 348 kernel_->put(field, value); 349 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles); 350 } 351 return true; 352} 353 354bool MutableEntry::UnlinkFromOrder() { 355 ScopedKernelLock lock(dir()); 356 return dir()->UnlinkEntryFromOrder(kernel_, 357 write_transaction(), 358 &lock, 359 NODE_MANIPULATION); 360} 361 362bool MutableEntry::PutPredecessor(const Id& predecessor_id) { 363 if (!UnlinkFromOrder()) 364 return false; 365 366 if (Get(IS_DEL)) { 367 DCHECK(predecessor_id.IsNull()); 368 return true; 369 } 370 371 // TODO(ncarter): It should be possible to not maintain position for 372 // non-bookmark items. However, we'd need to robustly handle all possible 373 // permutations of setting IS_DEL and the SPECIFICS to identify the 374 // object type; or else, we'd need to add a ModelType to the 375 // MutableEntry's Create ctor. 376 // if (!ShouldMaintainPosition()) { 377 // return false; 378 // } 379 380 // This is classic insert-into-doubly-linked-list from CS 101 and your last 381 // job interview. An "IsRoot" Id signifies the head or tail. 382 Id successor_id; 383 if (!predecessor_id.IsRoot()) { 384 MutableEntry predecessor(write_transaction(), GET_BY_ID, predecessor_id); 385 if (!predecessor.good()) { 386 LOG(ERROR) << "Predecessor is not good : " 387 << predecessor_id.GetServerId(); 388 return false; 389 } 390 if (predecessor.Get(PARENT_ID) != Get(PARENT_ID)) 391 return false; 392 successor_id = predecessor.Get(NEXT_ID); 393 predecessor.Put(NEXT_ID, Get(ID)); 394 } else { 395 syncable::Directory* dir = trans()->directory(); 396 if (!dir->GetFirstChildId(trans(), Get(PARENT_ID), &successor_id)) { 397 return false; 398 } 399 } 400 if (!successor_id.IsRoot()) { 401 MutableEntry successor(write_transaction(), GET_BY_ID, successor_id); 402 if (!successor.good()) { 403 LOG(ERROR) << "Successor is not good: " 404 << successor_id.GetServerId(); 405 return false; 406 } 407 if (successor.Get(PARENT_ID) != Get(PARENT_ID)) 408 return false; 409 successor.Put(PREV_ID, Get(ID)); 410 } 411 DCHECK(predecessor_id != Get(ID)); 412 DCHECK(successor_id != Get(ID)); 413 Put(PREV_ID, predecessor_id); 414 Put(NEXT_ID, successor_id); 415 return true; 416} 417 418bool MutableEntry::Put(BitTemp field, bool value) { 419 DCHECK(kernel_); 420 kernel_->put(field, value); 421 return true; 422} 423 424// This function sets only the flags needed to get this entry to sync. 425bool MarkForSyncing(MutableEntry* e) { 426 DCHECK_NE(static_cast<MutableEntry*>(NULL), e); 427 DCHECK(!e->IsRoot()) << "We shouldn't mark a permanent object for syncing."; 428 if (!(e->Put(IS_UNSYNCED, true))) 429 return false; 430 e->Put(SYNCING, false); 431 return true; 432} 433 434} // namespace syncable 435} // namespace syncer 436