mutable_entry.cc revision 868fa2fe829687343ffae624259930155e16dbd8
1// Copyright 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/unique_position.h" 9#include "sync/syncable/directory.h" 10#include "sync/syncable/scoped_kernel_lock.h" 11#include "sync/syncable/scoped_parent_child_index_updater.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/syncable_write_transaction.h" 16 17using std::string; 18 19namespace syncer { 20namespace syncable { 21 22void MutableEntry::Init(WriteTransaction* trans, 23 ModelType model_type, 24 const Id& parent_id, 25 const string& name) { 26 scoped_ptr<EntryKernel> kernel(new EntryKernel); 27 kernel_ = NULL; 28 29 kernel->put(ID, trans->directory_->NextId()); 30 kernel->put(META_HANDLE, trans->directory_->NextMetahandle()); 31 kernel->mark_dirty(&trans->directory_->kernel_->dirty_metahandles); 32 kernel->put(PARENT_ID, parent_id); 33 kernel->put(NON_UNIQUE_NAME, name); 34 const base::Time& now = base::Time::Now(); 35 kernel->put(CTIME, now); 36 kernel->put(MTIME, now); 37 // We match the database defaults here 38 kernel->put(BASE_VERSION, CHANGES_VERSION); 39 40 // Normally the SPECIFICS setting code is wrapped in logic to deal with 41 // unknown fields and encryption. Since all we want to do here is ensure that 42 // GetModelType() returns a correct value from the very beginning, these 43 // few lines are sufficient. 44 sync_pb::EntitySpecifics specifics; 45 AddDefaultFieldValue(model_type, &specifics); 46 kernel->put(SPECIFICS, specifics); 47 48 // Because this entry is new, it was originally deleted. 49 kernel->put(IS_DEL, true); 50 trans->SaveOriginal(kernel.get()); 51 kernel->put(IS_DEL, false); 52 53 // Now swap the pointers. 54 kernel_ = kernel.release(); 55} 56 57MutableEntry::MutableEntry(WriteTransaction* trans, 58 Create, 59 ModelType model_type, 60 const Id& parent_id, 61 const string& name) 62 : Entry(trans), 63 write_transaction_(trans) { 64 Init(trans, model_type, parent_id, name); 65 // We need to have a valid position ready before we can index the item. 66 if (model_type == BOOKMARKS) { 67 // Base the tag off of our cache-guid and local "c-" style ID. 68 std::string unique_tag = syncable::GenerateSyncableBookmarkHash( 69 trans->directory()->cache_guid(), Get(ID).GetServerId()); 70 kernel_->put(UNIQUE_BOOKMARK_TAG, unique_tag); 71 kernel_->put(UNIQUE_POSITION, UniquePosition::InitialPosition(unique_tag)); 72 } else { 73 DCHECK(!ShouldMaintainPosition()); 74 } 75 76 bool result = trans->directory()->InsertEntry(trans, kernel_); 77 DCHECK(result); 78} 79 80MutableEntry::MutableEntry(WriteTransaction* trans, CreateNewUpdateItem, 81 const Id& id) 82 : Entry(trans), write_transaction_(trans) { 83 Entry same_id(trans, GET_BY_ID, id); 84 kernel_ = NULL; 85 if (same_id.good()) { 86 return; // already have an item with this ID. 87 } 88 scoped_ptr<EntryKernel> kernel(new EntryKernel()); 89 90 kernel->put(ID, id); 91 kernel->put(META_HANDLE, trans->directory_->NextMetahandle()); 92 kernel->mark_dirty(&trans->directory_->kernel_->dirty_metahandles); 93 kernel->put(IS_DEL, true); 94 // We match the database defaults here 95 kernel->put(BASE_VERSION, CHANGES_VERSION); 96 if (!trans->directory()->InsertEntry(trans, kernel.get())) { 97 return; // Failed inserting. 98 } 99 trans->SaveOriginal(kernel.get()); 100 101 kernel_ = kernel.release(); 102} 103 104MutableEntry::MutableEntry(WriteTransaction* trans, GetById, const Id& id) 105 : Entry(trans, GET_BY_ID, id), write_transaction_(trans) { 106} 107 108MutableEntry::MutableEntry(WriteTransaction* trans, GetByHandle, 109 int64 metahandle) 110 : Entry(trans, GET_BY_HANDLE, metahandle), write_transaction_(trans) { 111} 112 113MutableEntry::MutableEntry(WriteTransaction* trans, GetByClientTag, 114 const std::string& tag) 115 : Entry(trans, GET_BY_CLIENT_TAG, tag), write_transaction_(trans) { 116} 117 118MutableEntry::MutableEntry(WriteTransaction* trans, GetByServerTag, 119 const string& tag) 120 : Entry(trans, GET_BY_SERVER_TAG, tag), write_transaction_(trans) { 121} 122 123bool MutableEntry::PutIsDel(bool is_del) { 124 DCHECK(kernel_); 125 write_transaction_->SaveOriginal(kernel_); 126 if (is_del == kernel_->ref(IS_DEL)) { 127 return true; 128 } 129 if (is_del) { 130 // If the server never knew about this item and it's deleted then we don't 131 // need to keep it around. Unsetting IS_UNSYNCED will: 132 // - Ensure that the item is never committed to the server. 133 // - Allow any items with the same UNIQUE_CLIENT_TAG created on other 134 // clients to override this entry. 135 // - Let us delete this entry permanently through 136 // DirectoryBackingStore::DropDeletedEntries() when we next restart sync. 137 // This will save memory and avoid crbug.com/125381. 138 if (!Get(ID).ServerKnows()) { 139 Put(IS_UNSYNCED, false); 140 } 141 } 142 143 { 144 ScopedKernelLock lock(dir()); 145 // Some indices don't include deleted items and must be updated 146 // upon a value change. 147 ScopedParentChildIndexUpdater updater(lock, kernel_, 148 &dir()->kernel_->parent_child_index); 149 150 kernel_->put(IS_DEL, is_del); 151 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 152 } 153 154 return true; 155} 156 157bool MutableEntry::Put(Int64Field field, const int64& value) { 158 DCHECK(kernel_); 159 160 // We shouldn't set TRANSACTION_VERSION here. See UpdateTransactionVersion. 161 DCHECK_NE(TRANSACTION_VERSION, field); 162 163 write_transaction_->SaveOriginal(kernel_); 164 if (kernel_->ref(field) != value) { 165 ScopedKernelLock lock(dir()); 166 kernel_->put(field, value); 167 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 168 } 169 return true; 170} 171 172bool MutableEntry::Put(TimeField field, const base::Time& value) { 173 DCHECK(kernel_); 174 write_transaction_->SaveOriginal(kernel_); 175 if (kernel_->ref(field) != value) { 176 kernel_->put(field, value); 177 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 178 } 179 return true; 180} 181 182bool MutableEntry::Put(IdField field, const Id& value) { 183 DCHECK(kernel_); 184 write_transaction_->SaveOriginal(kernel_); 185 if (kernel_->ref(field) != value) { 186 if (ID == field) { 187 if (!dir()->ReindexId(write_transaction(), kernel_, value)) 188 return false; 189 } else if (PARENT_ID == field) { 190 PutParentIdPropertyOnly(value); 191 if (!Get(IS_DEL)) { 192 if (!PutPredecessor(Id())) { 193 // TODO(lipalani) : Propagate the error to caller. crbug.com/100444. 194 NOTREACHED(); 195 } 196 } 197 } else { 198 kernel_->put(field, value); 199 } 200 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 201 } 202 return true; 203} 204 205bool MutableEntry::Put(UniquePositionField field, const UniquePosition& value) { 206 DCHECK(kernel_); 207 write_transaction_->SaveOriginal(kernel_); 208 if(!kernel_->ref(field).Equals(value)) { 209 // We should never overwrite a valid position with an invalid one. 210 DCHECK(value.IsValid()); 211 ScopedKernelLock lock(dir()); 212 if (UNIQUE_POSITION == field) { 213 ScopedParentChildIndexUpdater updater( 214 lock, kernel_, &dir()->kernel_->parent_child_index); 215 kernel_->put(field, value); 216 } else { 217 kernel_->put(field, value); 218 } 219 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 220 } 221 return true; 222} 223 224void MutableEntry::PutParentIdPropertyOnly(const Id& parent_id) { 225 write_transaction_->SaveOriginal(kernel_); 226 dir()->ReindexParentId(write_transaction(), kernel_, parent_id); 227 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 228} 229 230bool MutableEntry::Put(BaseVersion field, int64 value) { 231 DCHECK(kernel_); 232 write_transaction_->SaveOriginal(kernel_); 233 if (kernel_->ref(field) != value) { 234 kernel_->put(field, value); 235 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 236 } 237 return true; 238} 239 240bool MutableEntry::Put(StringField field, const string& value) { 241 DCHECK(kernel_); 242 write_transaction_->SaveOriginal(kernel_); 243 if (field == UNIQUE_CLIENT_TAG) { 244 return PutUniqueClientTag(value); 245 } 246 247 if (field == UNIQUE_SERVER_TAG) { 248 return PutUniqueServerTag(value); 249 } 250 251 DCHECK_NE(UNIQUE_BOOKMARK_TAG, field) 252 << "Should use PutUniqueBookmarkTag instead of Put(UNIQUE_BOOKMARK_TAG)"; 253 254 if (kernel_->ref(field) != value) { 255 kernel_->put(field, value); 256 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 257 } 258 return true; 259} 260 261bool MutableEntry::Put(ProtoField field, 262 const sync_pb::EntitySpecifics& value) { 263 DCHECK(kernel_); 264 write_transaction_->SaveOriginal(kernel_); 265 // TODO(ncarter): This is unfortunately heavyweight. Can we do 266 // better? 267 if (kernel_->ref(field).SerializeAsString() != value.SerializeAsString()) { 268 const bool update_unapplied_updates_index = 269 (field == SERVER_SPECIFICS) && kernel_->ref(IS_UNAPPLIED_UPDATE); 270 if (update_unapplied_updates_index) { 271 // Remove ourselves from unapplied_update_metahandles with our 272 // old server type. 273 const ModelType old_server_type = kernel_->GetServerModelType(); 274 const int64 metahandle = kernel_->ref(META_HANDLE); 275 size_t erase_count = 276 dir()->kernel_->unapplied_update_metahandles[old_server_type] 277 .erase(metahandle); 278 DCHECK_EQ(erase_count, 1u); 279 } 280 281 kernel_->put(field, value); 282 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 283 284 if (update_unapplied_updates_index) { 285 // Add ourselves back into unapplied_update_metahandles with our 286 // new server type. 287 const ModelType new_server_type = kernel_->GetServerModelType(); 288 const int64 metahandle = kernel_->ref(META_HANDLE); 289 dir()->kernel_->unapplied_update_metahandles[new_server_type] 290 .insert(metahandle); 291 } 292 } 293 return true; 294} 295 296bool MutableEntry::Put(BitField field, bool value) { 297 DCHECK(kernel_); 298 write_transaction_->SaveOriginal(kernel_); 299 bool old_value = kernel_->ref(field); 300 if (old_value != value) { 301 kernel_->put(field, value); 302 kernel_->mark_dirty(GetDirtyIndexHelper()); 303 } 304 305 // Update delete journal for existence status change on server side here 306 // instead of in PutIsDel() because IS_DEL may not be updated due to 307 // early returns when processing updates. And because 308 // UpdateDeleteJournalForServerDelete() checks for SERVER_IS_DEL, it has 309 // to be called on sync thread. 310 if (field == SERVER_IS_DEL) { 311 dir()->delete_journal()->UpdateDeleteJournalForServerDelete( 312 write_transaction(), old_value, *kernel_); 313 } 314 315 return true; 316} 317 318MetahandleSet* MutableEntry::GetDirtyIndexHelper() { 319 return &dir()->kernel_->dirty_metahandles; 320} 321 322bool MutableEntry::PutUniqueClientTag(const string& new_tag) { 323 if (new_tag == kernel_->ref(UNIQUE_CLIENT_TAG)) { 324 return true; 325 } 326 327 write_transaction_->SaveOriginal(kernel_); 328 ScopedKernelLock lock(dir()); 329 // Make sure your new value is not in there already. 330 if (dir()->kernel_->client_tags_map.find(new_tag) != 331 dir()->kernel_->client_tags_map.end()) { 332 DVLOG(1) << "Detected duplicate client tag"; 333 return false; 334 } 335 dir()->kernel_->client_tags_map.erase( 336 kernel_->ref(UNIQUE_CLIENT_TAG)); 337 kernel_->put(UNIQUE_CLIENT_TAG, new_tag); 338 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 339 if (!new_tag.empty()) { 340 dir()->kernel_->client_tags_map[new_tag] = kernel_; 341 } 342 343 return true; 344} 345 346bool MutableEntry::PutUniqueServerTag(const string& new_tag) { 347 if (new_tag == kernel_->ref(UNIQUE_SERVER_TAG)) { 348 return true; 349 } 350 351 write_transaction_->SaveOriginal(kernel_); 352 ScopedKernelLock lock(dir()); 353 // Make sure your new value is not in there already. 354 if (dir()->kernel_->server_tags_map.find(new_tag) != 355 dir()->kernel_->server_tags_map.end()) { 356 DVLOG(1) << "Detected duplicate server tag"; 357 return false; 358 } 359 dir()->kernel_->server_tags_map.erase( 360 kernel_->ref(UNIQUE_SERVER_TAG)); 361 kernel_->put(UNIQUE_SERVER_TAG, new_tag); 362 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 363 if (!new_tag.empty()) { 364 dir()->kernel_->server_tags_map[new_tag] = kernel_; 365 } 366 367 return true; 368} 369 370bool MutableEntry::Put(IndexedBitField field, bool value) { 371 DCHECK(kernel_); 372 write_transaction_->SaveOriginal(kernel_); 373 if (kernel_->ref(field) != value) { 374 MetahandleSet* index; 375 if (IS_UNSYNCED == field) { 376 index = &dir()->kernel_->unsynced_metahandles; 377 } else { 378 // Use kernel_->GetServerModelType() instead of 379 // GetServerModelType() as we may trigger some DCHECKs in the 380 // latter. 381 index = 382 &dir()->kernel_->unapplied_update_metahandles[ 383 kernel_->GetServerModelType()]; 384 } 385 386 ScopedKernelLock lock(dir()); 387 if (value) { 388 if (!SyncAssert(index->insert(kernel_->ref(META_HANDLE)).second, 389 FROM_HERE, 390 "Could not insert", 391 write_transaction())) { 392 return false; 393 } 394 } else { 395 if (!SyncAssert(1U == index->erase(kernel_->ref(META_HANDLE)), 396 FROM_HERE, 397 "Entry Not succesfully erased", 398 write_transaction())) { 399 return false; 400 } 401 } 402 kernel_->put(field, value); 403 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 404 } 405 return true; 406} 407 408void MutableEntry::PutUniqueBookmarkTag(const std::string& tag) { 409 // This unique tag will eventually be used as the unique suffix when adjusting 410 // this bookmark's position. Let's make sure it's a valid suffix. 411 if (!UniquePosition::IsValidSuffix(tag)) { 412 NOTREACHED(); 413 return; 414 } 415 416 if (!kernel_->ref(UNIQUE_BOOKMARK_TAG).empty() 417 && tag != kernel_->ref(UNIQUE_BOOKMARK_TAG)) { 418 // There is only one scenario where our tag is expected to change. That 419 // scenario occurs when our current tag is a non-correct tag assigned during 420 // the UniquePosition migration. 421 std::string migration_generated_tag = 422 GenerateSyncableBookmarkHash(std::string(), 423 kernel_->ref(ID).GetServerId()); 424 DCHECK_EQ(migration_generated_tag, kernel_->ref(UNIQUE_BOOKMARK_TAG)); 425 } 426 427 kernel_->put(UNIQUE_BOOKMARK_TAG, tag); 428 kernel_->mark_dirty(&dir()->kernel_->dirty_metahandles); 429} 430 431bool MutableEntry::PutPredecessor(const Id& predecessor_id) { 432 MutableEntry predecessor(write_transaction_, GET_BY_ID, predecessor_id); 433 if (!predecessor.good()) 434 return false; 435 dir()->PutPredecessor(kernel_, predecessor.kernel_); 436 return true; 437} 438 439bool MutableEntry::Put(BitTemp field, bool value) { 440 DCHECK(kernel_); 441 kernel_->put(field, value); 442 return true; 443} 444 445void MutableEntry::UpdateTransactionVersion(int64 value) { 446 ScopedKernelLock lock(dir()); 447 kernel_->put(TRANSACTION_VERSION, value); 448 kernel_->mark_dirty(&(dir()->kernel_->dirty_metahandles)); 449} 450 451// This function sets only the flags needed to get this entry to sync. 452bool MarkForSyncing(MutableEntry* e) { 453 DCHECK_NE(static_cast<MutableEntry*>(NULL), e); 454 DCHECK(!e->IsRoot()) << "We shouldn't mark a permanent object for syncing."; 455 if (!(e->Put(IS_UNSYNCED, true))) 456 return false; 457 e->Put(SYNCING, false); 458 return true; 459} 460 461} // namespace syncable 462} // namespace syncer 463