metadata_database_index_on_disk.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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 "chrome/browser/sync_file_system/drive_backend/metadata_database_index_on_disk.h" 6 7#include "base/format_macros.h" 8#include "base/logging.h" 9#include "base/strings/string_number_conversions.h" 10#include "base/strings/string_util.h" 11#include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h" 12#include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h" 13#include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h" 14#include "chrome/browser/sync_file_system/logger.h" 15#include "third_party/leveldatabase/src/include/leveldb/db.h" 16#include "third_party/leveldatabase/src/include/leveldb/write_batch.h" 17 18// LevelDB database schema 19// ======================= 20// 21// NOTE 22// - Entries are sorted by keys. 23// - int64 value is serialized as a string by base::Int64ToString(). 24// - ServiceMetadata, FileMetadata, and FileTracker values are serialized 25// as a string by SerializeToString() of protocol buffers. 26// 27// Version 4: 28// # Version of this schema 29// key: "VERSION" 30// value: "4" 31// 32// # Metadata of the SyncFS service (compatible with version 3) 33// key: "SERVICE" 34// value: <ServiceMetadata 'service_metadata'> 35// 36// # Metadata of remote files (compatible with version 3) 37// key: "FILE: " + <string 'file_id'> 38// value: <FileMetadata 'metadata'> 39// 40// # Trackers of remote file updates (compatible with version 3) 41// key: "TRACKER: " + <int64 'tracker_id'> 42// value: <FileTracker 'tracker'> 43// 44// # Index from App ID to the tracker ID 45// key: "APP_ROOT: " + <string 'app_id'> 46// value: <int64 'app_root_tracker_id'> 47// 48// # Index from file ID to the active tracker ID 49// key: "ACTIVE_FILE: " + <string 'file_id'> 50// value: <int64 'active_tracker_id'> 51// 52// # Index from file ID to a tracker ID 53// key: "TRACKER_FILE: " + <string 'file_id'> + '\x00' + <int64 'tracker_id'> 54// value: <empty> 55// 56// # Tracker IDs; a file metadata linked to multiple tracker IDs. 57// key: "MULTI_FILE: " + <int64 'tracker_id'> 58// value: <empty> 59// 60// # Index from the parent tracker ID and the title to the active tracker ID 61// key: "ACTIVE_PATH: " + <int64 'parent_tracker_id'> + 62// '\x00' + <string 'title'> 63// value: <int64 'active_tracker_id'> 64// 65// # Index from the parent tracker ID and the title to a tracker ID 66// key: "TRACKER_PATH: " + <int64 'parent_tracker_id'> + 67// '\x00' + <string 'title'> + '\x00' + <int64 'tracker_id'> 68// value: <empty> 69// 70// # Tracker IDs; a parent tracker ID and a title figure multiple tracker IDs 71// key: "MULTI_PATH: " + <int64 'tracker_id'> 72// value: <empty> 73// 74// # Dirty tracker IDs 75// key: "DIRTY: " + <int64 'dirty_tracker_id'> 76// value: <empty> 77// 78// # Demoted dirty tracker IDs 79// key: "DEMOTED_DIRTY: " + <int64 'demoted_dirty_tracker_id'> 80// value: <empty> 81 82namespace sync_file_system { 83namespace drive_backend { 84 85namespace { 86 87std::string GenerateAppRootIDByAppIDKey(const std::string& app_id) { 88 return kAppRootIDByAppIDKeyPrefix + app_id; 89} 90 91std::string GenerateActiveTrackerIDByFileIDKey(const std::string& file_id) { 92 return kActiveTrackerIDByFileIDKeyPrefix + file_id; 93} 94 95std::string GenerateTrackerIDByFileIDKeyPrefix(const std::string& file_id) { 96 std::ostringstream oss; 97 oss << kTrackerIDByFileIDKeyPrefix << file_id << '\0'; 98 return oss.str(); 99} 100 101std::string GenerateMultiTrackerKey(const std::string& file_id) { 102 return kMultiTrackerByFileIDKeyPrefix + file_id; 103} 104 105std::string GenerateActiveTrackerIDByParentAndTitleKey( 106 int64 parent_id, const std::string& title) { 107 std::ostringstream oss; 108 oss << kActiveTrackerIDByParentAndTitleKeyPrefix << parent_id 109 << '\0' << title; 110 return oss.str(); 111} 112 113std::string GenerateTrackerIDByParentAndTitleKeyPrefix( 114 int64 parent_id, const std::string& title) { 115 std::ostringstream oss; 116 oss << kTrackerIDByParentAndTitleKeyPrefix << parent_id << '\0' 117 << title << '\0'; 118 return oss.str(); 119} 120 121std::string GenerateTrackerIDsByParentIDKeyPrefix(int64 parent_id) { 122 std::ostringstream oss; 123 oss << kTrackerIDByParentAndTitleKeyPrefix << parent_id << '\0'; 124 return oss.str(); 125} 126 127std::string GenerateMultiBackingParentAndTitleKey( 128 int64 parent_id, const std::string& title) { 129 std::ostringstream oss; 130 oss << kMultiBackingParentAndTitleKeyPrefix << parent_id << '\0' 131 << title; 132 return oss.str(); 133} 134 135std::string GenerateDirtyIDKey(int64 tracker_id) { 136 return kDirtyIDKeyPrefix + base::Int64ToString(tracker_id); 137} 138 139std::string GenerateDemotedDirtyIDKey(int64 tracker_id) { 140 return kDemotedDirtyIDKeyPrefix + base::Int64ToString(tracker_id); 141} 142 143} // namespace 144 145// static 146scoped_ptr<MetadataDatabaseIndexOnDisk> 147MetadataDatabaseIndexOnDisk::Create( 148 leveldb::DB* db, leveldb::WriteBatch* batch) { 149 DCHECK(db); 150 151 PutVersionToBatch(kDatabaseOnDiskVersion, batch); 152 scoped_ptr<MetadataDatabaseIndexOnDisk> 153 index(new MetadataDatabaseIndexOnDisk(db)); 154 return index.Pass(); 155} 156 157MetadataDatabaseIndexOnDisk::~MetadataDatabaseIndexOnDisk() {} 158 159bool MetadataDatabaseIndexOnDisk::GetFileMetadata( 160 const std::string& file_id, FileMetadata* metadata) const { 161 const std::string key = kFileMetadataKeyPrefix + file_id; 162 std::string value; 163 leveldb::Status status = db_->Get(leveldb::ReadOptions(), key, &value); 164 165 if (status.IsNotFound()) 166 return false; 167 168 if (!status.ok()) { 169 util::Log(logging::LOG_WARNING, FROM_HERE, 170 "LevelDB error (%s) in getting FileMetadata for ID: %s", 171 status.ToString().c_str(), 172 file_id.c_str()); 173 return false; 174 } 175 176 FileMetadata tmp_metadata; 177 if (!tmp_metadata.ParseFromString(value)) { 178 util::Log(logging::LOG_WARNING, FROM_HERE, 179 "Failed to parse a FileMetadata for ID: %s", 180 file_id.c_str()); 181 return false; 182 } 183 if (metadata) 184 metadata->CopyFrom(tmp_metadata); 185 186 return true; 187} 188 189bool MetadataDatabaseIndexOnDisk::GetFileTracker( 190 int64 tracker_id, FileTracker* tracker) const { 191 const std::string key = 192 kFileTrackerKeyPrefix + base::Int64ToString(tracker_id); 193 std::string value; 194 leveldb::Status status = db_->Get(leveldb::ReadOptions(), key, &value); 195 196 if (status.IsNotFound()) 197 return false; 198 199 if (!status.ok()) { 200 util::Log(logging::LOG_WARNING, FROM_HERE, 201 "LevelDB error (%s) in getting FileTracker for ID: %" PRId64, 202 status.ToString().c_str(), 203 tracker_id); 204 return false; 205 } 206 207 FileTracker tmp_tracker; 208 if (!tmp_tracker.ParseFromString(value)) { 209 util::Log(logging::LOG_WARNING, FROM_HERE, 210 "Failed to parse a Tracker for ID: %" PRId64, 211 tracker_id); 212 return false; 213 } 214 if (tracker) 215 tracker->CopyFrom(tmp_tracker); 216 217 return true; 218} 219 220void MetadataDatabaseIndexOnDisk::StoreFileMetadata( 221 scoped_ptr<FileMetadata> metadata, leveldb::WriteBatch* batch) { 222 DCHECK(metadata); 223 PutFileMetadataToBatch(*metadata, batch); 224} 225 226void MetadataDatabaseIndexOnDisk::StoreFileTracker( 227 scoped_ptr<FileTracker> tracker, leveldb::WriteBatch* batch) { 228 DCHECK(tracker); 229 PutFileTrackerToBatch(*tracker, batch); 230 231 int64 tracker_id = tracker->tracker_id(); 232 FileTracker old_tracker; 233 if (!GetFileTracker(tracker_id, &old_tracker)) { 234 DVLOG(3) << "Adding new tracker: " << tracker->tracker_id() 235 << " " << GetTrackerTitle(*tracker); 236 AddToAppIDIndex(*tracker, batch); 237 AddToFileIDIndexes(*tracker, batch); 238 AddToPathIndexes(*tracker, batch); 239 AddToDirtyTrackerIndexes(*tracker, batch); 240 } else { 241 DVLOG(3) << "Updating tracker: " << tracker->tracker_id() 242 << " " << GetTrackerTitle(*tracker); 243 UpdateInAppIDIndex(old_tracker, *tracker, batch); 244 UpdateInFileIDIndexes(old_tracker, *tracker, batch); 245 UpdateInPathIndexes(old_tracker, *tracker, batch); 246 UpdateInDirtyTrackerIndexes(old_tracker, *tracker, batch); 247 } 248} 249 250void MetadataDatabaseIndexOnDisk::RemoveFileMetadata( 251 const std::string& file_id, leveldb::WriteBatch* batch) { 252 PutFileMetadataDeletionToBatch(file_id, batch); 253} 254 255void MetadataDatabaseIndexOnDisk::RemoveFileTracker( 256 int64 tracker_id, leveldb::WriteBatch* batch) { 257 PutFileTrackerDeletionToBatch(tracker_id, batch); 258 259 FileTracker tracker; 260 if (!GetFileTracker(tracker_id, &tracker)) { 261 NOTREACHED(); 262 return; 263 } 264 265 DVLOG(1) << "Removing tracker: " 266 << tracker.tracker_id() << " " << GetTrackerTitle(tracker); 267 RemoveFromAppIDIndex(tracker, batch); 268 RemoveFromFileIDIndexes(tracker, batch); 269 RemoveFromPathIndexes(tracker, batch); 270 RemoveFromDirtyTrackerIndexes(tracker, batch); 271} 272 273TrackerIDSet MetadataDatabaseIndexOnDisk::GetFileTrackerIDsByFileID( 274 const std::string& file_id) const { 275 return GetTrackerIDSetByPrefix( 276 GenerateActiveTrackerIDByFileIDKey(file_id), 277 GenerateTrackerIDByFileIDKeyPrefix(file_id)); 278} 279 280int64 MetadataDatabaseIndexOnDisk::GetAppRootTracker( 281 const std::string& app_id) const { 282 const std::string key = GenerateAppRootIDByAppIDKey(app_id); 283 std::string value; 284 leveldb::Status status = db_->Get(leveldb::ReadOptions(), key, &value); 285 286 if (status.IsNotFound()) 287 return kInvalidTrackerID; 288 289 if (!status.ok()) { 290 util::Log(logging::LOG_WARNING, FROM_HERE, 291 "LevelDB error (%s) in getting AppRoot for AppID: %s", 292 status.ToString().c_str(), 293 app_id.c_str()); 294 return kInvalidTrackerID; 295 } 296 297 int64 root_id; 298 if (!base::StringToInt64(value, &root_id)) { 299 util::Log(logging::LOG_WARNING, FROM_HERE, 300 "Failed to parse a root ID (%s) for an App ID: %s", 301 value.c_str(), 302 app_id.c_str()); 303 return kInvalidTrackerID; 304 } 305 306 return root_id; 307} 308 309TrackerIDSet MetadataDatabaseIndexOnDisk::GetFileTrackerIDsByParentAndTitle( 310 int64 parent_tracker_id, const std::string& title) const { 311 return GetTrackerIDSetByPrefix( 312 GenerateActiveTrackerIDByParentAndTitleKey(parent_tracker_id, title), 313 GenerateTrackerIDByParentAndTitleKeyPrefix(parent_tracker_id, title)); 314} 315 316std::vector<int64> MetadataDatabaseIndexOnDisk::GetFileTrackerIDsByParent( 317 int64 parent_id) const { 318 std::vector<int64> result; 319 320 const std::string prefix = GenerateTrackerIDsByParentIDKeyPrefix(parent_id); 321 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); 322 for (itr->Seek(prefix); itr->Valid(); itr->Next()) { 323 const std::string& key(itr->key().ToString()); 324 std::string title_and_id; 325 if (!RemovePrefix(key, prefix, &title_and_id)) 326 break; 327 328 size_t pos = title_and_id.rfind('\0'); 329 DCHECK(pos != std::string::npos); 330 331 int64 tracker_id; 332 if (!base::StringToInt64(title_and_id.substr(pos + 1), &tracker_id)) 333 continue; 334 result.push_back(tracker_id); 335 } 336 return result; 337} 338 339std::string MetadataDatabaseIndexOnDisk::PickMultiTrackerFileID() const { 340 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); 341 itr->Seek(kMultiTrackerByFileIDKeyPrefix); 342 if (!itr->Valid()) 343 return std::string(); 344 345 std::string file_id; 346 if (!RemovePrefix(itr->key().ToString(), 347 kMultiTrackerByFileIDKeyPrefix, &file_id)) 348 return std::string(); 349 350 return file_id; 351} 352 353ParentIDAndTitle MetadataDatabaseIndexOnDisk::PickMultiBackingFilePath() const { 354 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); 355 itr->Seek(kMultiBackingParentAndTitleKeyPrefix); 356 if (!itr->Valid()) 357 return ParentIDAndTitle(); 358 359 std::string value; 360 if (!RemovePrefix(itr->key().ToString(), 361 kMultiBackingParentAndTitleKeyPrefix, &value)) 362 return ParentIDAndTitle(); 363 364 size_t pos = value.find('\0'); // '\0' is a separator. 365 int64 parent_id; 366 if (pos == std::string::npos || 367 !base::StringToInt64(value.substr(0, pos), &parent_id)) 368 return ParentIDAndTitle(); 369 370 // Successfully found an entry. 371 return ParentIDAndTitle(parent_id, value.substr(pos + 1)); 372} 373 374int64 MetadataDatabaseIndexOnDisk::PickDirtyTracker() const { 375 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); 376 itr->Seek(kDirtyIDKeyPrefix); 377 if (!itr->Valid()) 378 return kInvalidTrackerID; 379 380 std::string id_str; 381 if (!RemovePrefix(itr->key().ToString(), kDirtyIDKeyPrefix, &id_str)) 382 return kInvalidTrackerID; 383 384 int64 tracker_id; 385 if (!base::StringToInt64(id_str, &tracker_id)) 386 return kInvalidTrackerID; 387 388 return tracker_id; 389} 390 391void MetadataDatabaseIndexOnDisk::DemoteDirtyTracker( 392 int64 tracker_id, leveldb::WriteBatch* batch) { 393 const std::string key = GenerateDirtyIDKey(tracker_id); 394 395 std::string value; 396 leveldb::Status status = db_->Get(leveldb::ReadOptions(), key, &value); 397 if (status.IsNotFound()) 398 return; 399 if (!status.ok()) { 400 util::Log(logging::LOG_WARNING, FROM_HERE, 401 "LevelDB error (%s) in getting a dirty tracker for ID: %" PRId64, 402 status.ToString().c_str(), 403 tracker_id); 404 return; 405 } 406 407 batch->Delete(key); 408 batch->Put(GenerateDemotedDirtyIDKey(tracker_id), std::string()); 409} 410 411bool MetadataDatabaseIndexOnDisk::HasDemotedDirtyTracker() const { 412 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); 413 itr->Seek(kDemotedDirtyIDKeyPrefix); 414 if (!itr->Valid()) 415 return false; 416 return StartsWithASCII(itr->key().ToString(), kDemotedDirtyIDKeyPrefix, true); 417} 418 419void MetadataDatabaseIndexOnDisk::PromoteDemotedDirtyTrackers( 420 leveldb::WriteBatch* batch) { 421 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); 422 for (itr->Seek(kDirtyIDKeyPrefix); itr->Valid(); itr->Next()) { 423 std::string id_str; 424 if (!RemovePrefix(itr->key().ToString(), kDirtyIDKeyPrefix, &id_str)) 425 break; 426 427 int64 tracker_id; 428 if (!base::StringToInt64(id_str, &tracker_id)) 429 continue; 430 431 batch->Delete(itr->key()); 432 batch->Put(GenerateDemotedDirtyIDKey(tracker_id), std::string()); 433 } 434} 435 436size_t MetadataDatabaseIndexOnDisk::CountDirtyTracker() const { 437 size_t num_dirty_trackers = 0; 438 439 // TODO(peria): Store the number of dirty trackers, and do not iterate 440 // everytime. 441 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); 442 for (itr->Seek(kDirtyIDKeyPrefix); itr->Valid(); itr->Next()) { 443 if (!StartsWithASCII(itr->key().ToString(), kDirtyIDKeyPrefix, true)) 444 break; 445 ++num_dirty_trackers; 446 } 447 448 for (itr->Seek(kDemotedDirtyIDKeyPrefix); itr->Valid(); itr->Next()) { 449 if (!StartsWithASCII(itr->key().ToString(), kDemotedDirtyIDKeyPrefix, true)) 450 break; 451 ++num_dirty_trackers; 452 } 453 454 return num_dirty_trackers; 455} 456 457size_t MetadataDatabaseIndexOnDisk::CountFileMetadata() const { 458 // TODO(peria): Cache the number of FileMetadata in the DB. 459 size_t count = 0; 460 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); 461 for (itr->Seek(kFileMetadataKeyPrefix); itr->Valid(); itr->Next()) { 462 if (!StartsWithASCII(itr->key().ToString(), kFileMetadataKeyPrefix, true)) 463 break; 464 ++count; 465 } 466 return count; 467} 468 469size_t MetadataDatabaseIndexOnDisk::CountFileTracker() const { 470 // TODO(peria): Cache the number of FileTracker in the DB. 471 size_t count = 0; 472 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); 473 for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) { 474 if (!StartsWithASCII(itr->key().ToString(), kFileTrackerKeyPrefix, true)) 475 break; 476 ++count; 477 } 478 return count; 479} 480 481void MetadataDatabaseIndexOnDisk::SetSyncRootTrackerID( 482 int64 sync_root_id, leveldb::WriteBatch* batch) const { 483 service_metadata_->set_sync_root_tracker_id(sync_root_id); 484 PutServiceMetadataToBatch(*service_metadata_, batch); 485} 486 487void MetadataDatabaseIndexOnDisk::SetLargestChangeID( 488 int64 largest_change_id, leveldb::WriteBatch* batch) const { 489 service_metadata_->set_largest_change_id(largest_change_id); 490 PutServiceMetadataToBatch(*service_metadata_, batch); 491} 492 493void MetadataDatabaseIndexOnDisk::SetNextTrackerID( 494 int64 next_tracker_id, leveldb::WriteBatch* batch) const { 495 service_metadata_->set_next_tracker_id(next_tracker_id); 496 PutServiceMetadataToBatch(*service_metadata_, batch); 497} 498 499int64 MetadataDatabaseIndexOnDisk::GetSyncRootTrackerID() const { 500 if (!service_metadata_->has_sync_root_tracker_id()) 501 return kInvalidTrackerID; 502 return service_metadata_->sync_root_tracker_id(); 503} 504 505int64 MetadataDatabaseIndexOnDisk::GetLargestChangeID() const { 506 if (!service_metadata_->has_largest_change_id()) 507 return kInvalidTrackerID; 508 return service_metadata_->largest_change_id(); 509} 510 511int64 MetadataDatabaseIndexOnDisk::GetNextTrackerID() const { 512 if (!service_metadata_->has_next_tracker_id()) 513 return kInvalidTrackerID; 514 return service_metadata_->next_tracker_id(); 515} 516 517std::vector<std::string> 518MetadataDatabaseIndexOnDisk::GetRegisteredAppIDs() const { 519 std::vector<std::string> result; 520 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); 521 for (itr->Seek(kAppRootIDByAppIDKeyPrefix); itr->Valid(); itr->Next()) { 522 std::string id; 523 if (!RemovePrefix(itr->key().ToString(), kAppRootIDByAppIDKeyPrefix, &id)) 524 break; 525 result.push_back(id); 526 } 527 return result; 528} 529 530std::vector<int64> MetadataDatabaseIndexOnDisk::GetAllTrackerIDs() const { 531 std::vector<int64> tracker_ids; 532 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); 533 for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) { 534 std::string id_str; 535 if (!RemovePrefix(itr->key().ToString(), kFileTrackerKeyPrefix, &id_str)) 536 break; 537 538 int64 tracker_id; 539 if (!base::StringToInt64(id_str, &tracker_id)) 540 continue; 541 tracker_ids.push_back(tracker_id); 542 } 543 return tracker_ids; 544} 545 546std::vector<std::string> 547MetadataDatabaseIndexOnDisk::GetAllMetadataIDs() const { 548 std::vector<std::string> file_ids; 549 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); 550 for (itr->Seek(kFileMetadataKeyPrefix); itr->Valid(); itr->Next()) { 551 std::string file_id; 552 if (!RemovePrefix(itr->key().ToString(), kFileMetadataKeyPrefix, &file_id)) 553 break; 554 file_ids.push_back(file_id); 555 } 556 return file_ids; 557} 558 559void MetadataDatabaseIndexOnDisk::BuildTrackerIndexes( 560 leveldb::WriteBatch* batch) { 561 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); 562 for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) { 563 if (!RemovePrefix(itr->key().ToString(), kFileTrackerKeyPrefix, NULL)) 564 break; 565 566 FileTracker tracker; 567 if (!tracker.ParseFromString(itr->value().ToString())) { 568 util::Log(logging::LOG_WARNING, FROM_HERE, 569 "Failed to parse a Tracker"); 570 continue; 571 } 572 573 AddToAppIDIndex(tracker, batch); 574 AddToFileIDIndexes(tracker, batch); 575 AddToPathIndexes(tracker, batch); 576 AddToDirtyTrackerIndexes(tracker, batch); 577 } 578} 579 580MetadataDatabaseIndexOnDisk::MetadataDatabaseIndexOnDisk(leveldb::DB* db) 581 : db_(db) { 582 // TODO(peria): Add UMA to measure the number of FileMetadata, FileTracker, 583 // and AppRootId. 584 // TODO(peria): If the DB version is 3, build up index lists. 585 service_metadata_ = InitializeServiceMetadata(db_); 586} 587 588void MetadataDatabaseIndexOnDisk::AddToAppIDIndex( 589 const FileTracker& tracker, leveldb::WriteBatch* batch) { 590 if (!IsAppRoot(tracker)) { 591 DVLOG(3) << " Tracker for " << tracker.file_id() << " is not an App root."; 592 return; 593 } 594 595 DVLOG(1) << " Add to App root by App ID: " << tracker.app_id(); 596 597 const std::string db_key = GenerateAppRootIDByAppIDKey(tracker.app_id()); 598 DCHECK(tracker.active()); 599 DCHECK(!DBHasKey(db_key)); 600 batch->Put(db_key, base::Int64ToString(tracker.tracker_id())); 601} 602 603void MetadataDatabaseIndexOnDisk::UpdateInAppIDIndex( 604 const FileTracker& old_tracker, 605 const FileTracker& new_tracker, 606 leveldb::WriteBatch* batch) { 607 DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id()); 608 609 if (IsAppRoot(old_tracker) && !IsAppRoot(new_tracker)) { 610 DCHECK(old_tracker.active()); 611 DCHECK(!new_tracker.active()); 612 const std::string key = GenerateAppRootIDByAppIDKey(old_tracker.app_id()); 613 DCHECK(DBHasKey(key)); 614 615 DVLOG(1) << " Remove from App root by App ID: " << old_tracker.app_id(); 616 batch->Delete(key); 617 } else if (!IsAppRoot(old_tracker) && IsAppRoot(new_tracker)) { 618 DCHECK(!old_tracker.active()); 619 DCHECK(new_tracker.active()); 620 const std::string key = GenerateAppRootIDByAppIDKey(new_tracker.app_id()); 621 DCHECK(!DBHasKey(key)); 622 623 DVLOG(1) << " Add to App root by App ID: " << new_tracker.app_id(); 624 batch->Put(key, base::Int64ToString(new_tracker.tracker_id())); 625 } 626} 627 628void MetadataDatabaseIndexOnDisk::RemoveFromAppIDIndex( 629 const FileTracker& tracker, leveldb::WriteBatch* batch) { 630 if (!IsAppRoot(tracker)) { 631 DVLOG(3) << " Tracker for " << tracker.file_id() << " is not an App root."; 632 return; 633 } 634 635 DCHECK(tracker.active()); 636 const std::string key = GenerateAppRootIDByAppIDKey(tracker.app_id()); 637 DCHECK(DBHasKey(key)); 638 639 DVLOG(1) << " Remove from App root by App ID: " << tracker.app_id(); 640 batch->Delete(key); 641} 642 643void MetadataDatabaseIndexOnDisk::AddToFileIDIndexes( 644 const FileTracker& new_tracker, leveldb::WriteBatch* batch) { 645 const std::string& file_id = new_tracker.file_id(); 646 647 DVLOG(1) << " Add to trackers by file ID: " << file_id; 648 const std::string prefix = GenerateTrackerIDByFileIDKeyPrefix(file_id); 649 AddToTrackerIDSetWithPrefix( 650 GenerateActiveTrackerIDByFileIDKey(file_id), 651 prefix, new_tracker, batch); 652 653 const std::string multi_tracker_key = GenerateMultiTrackerKey(file_id); 654 if (!DBHasKey(multi_tracker_key) && 655 CountWithPrefix(prefix, new_tracker.tracker_id()) != NONE) { 656 DVLOG(1) << " Add to multi-tracker file IDs: " << file_id; 657 batch->Put(multi_tracker_key, std::string()); 658 } 659} 660 661void MetadataDatabaseIndexOnDisk::UpdateInFileIDIndexes( 662 const FileTracker& old_tracker, 663 const FileTracker& new_tracker, 664 leveldb::WriteBatch* batch) { 665 DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id()); 666 DCHECK_EQ(old_tracker.file_id(), new_tracker.file_id()); 667 668 const std::string& file_id = new_tracker.file_id(); 669 const std::string prefix = GenerateTrackerIDByFileIDKeyPrefix(file_id); 670 DCHECK(DBHasKey(prefix + base::Int64ToString(new_tracker.tracker_id()))); 671 672 if (old_tracker.active() && !new_tracker.active()) { 673 DeactivateInTrackerIDSetWithPrefix( 674 GenerateActiveTrackerIDByFileIDKey(file_id), prefix, 675 new_tracker.tracker_id(), batch); 676 } else if (!old_tracker.active() && new_tracker.active()) { 677 ActivateInTrackerIDSetWithPrefix( 678 GenerateActiveTrackerIDByFileIDKey(file_id), prefix, 679 new_tracker.tracker_id(), batch); 680 } 681} 682 683void MetadataDatabaseIndexOnDisk::RemoveFromFileIDIndexes( 684 const FileTracker& tracker, leveldb::WriteBatch* batch) { 685 const std::string& file_id = tracker.file_id(); 686 const std::string prefix = 687 GenerateTrackerIDByFileIDKeyPrefix(file_id); 688 689 if (!EraseInTrackerIDSetWithPrefix( 690 GenerateActiveTrackerIDByFileIDKey(file_id), 691 prefix, tracker.tracker_id(), batch)) 692 return; 693 694 DVLOG(1) << " Remove from trackers by file ID: " << tracker.tracker_id(); 695 696 const std::string multi_key = GenerateMultiTrackerKey(file_id); 697 if (DBHasKey(multi_key) && 698 CountWithPrefix(prefix, tracker.tracker_id()) != MULTIPLE) { 699 DVLOG(1) << " Remove from multi-tracker file IDs: " << file_id; 700 batch->Delete(multi_key); 701 } 702} 703 704void MetadataDatabaseIndexOnDisk::AddToPathIndexes( 705 const FileTracker& new_tracker, leveldb::WriteBatch* batch) { 706 int64 parent_id = new_tracker.parent_tracker_id(); 707 std::string title = GetTrackerTitle(new_tracker); 708 709 DVLOG(1) << " Add to trackers by parent and title: " 710 << parent_id << " " << title; 711 712 const std::string prefix = 713 GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, title); 714 AddToTrackerIDSetWithPrefix( 715 GenerateActiveTrackerIDByParentAndTitleKey(parent_id, title), 716 prefix, new_tracker, batch); 717 718 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); 719 for (itr->Seek(prefix); itr->Valid(); itr->Next()) { 720 std::string id_str; 721 if (!RemovePrefix(itr->key().ToString(), prefix, &id_str)) 722 break; 723 724 int64 tracker_id; 725 if (!base::StringToInt64(id_str, &tracker_id)) 726 continue; 727 if (tracker_id == new_tracker.tracker_id()) { 728 NOTREACHED(); 729 continue; 730 } 731 732 const std::string multi_key = 733 GenerateMultiBackingParentAndTitleKey(parent_id, title); 734 DVLOG_IF(1, !DBHasKey(multi_key)) 735 << " Add to multi backing file paths: " << parent_id << " " << title; 736 batch->Put(GenerateMultiBackingParentAndTitleKey(parent_id, title), 737 std::string()); 738 break; 739 } 740} 741 742void MetadataDatabaseIndexOnDisk::UpdateInPathIndexes( 743 const FileTracker& old_tracker, 744 const FileTracker& new_tracker, 745 leveldb::WriteBatch* batch) { 746 DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id()); 747 DCHECK_EQ(old_tracker.parent_tracker_id(), new_tracker.parent_tracker_id()); 748 DCHECK(GetTrackerTitle(old_tracker) == GetTrackerTitle(new_tracker) || 749 !old_tracker.has_synced_details()); 750 751 int64 tracker_id = new_tracker.tracker_id(); 752 int64 parent_id = new_tracker.parent_tracker_id(); 753 const std::string old_title = GetTrackerTitle(old_tracker); 754 const std::string title = GetTrackerTitle(new_tracker); 755 756 if (old_title != title) { 757 const std::string old_prefix = 758 GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, old_title); 759 EraseInTrackerIDSetWithPrefix( 760 GenerateActiveTrackerIDByParentAndTitleKey(parent_id, old_title), 761 old_prefix, tracker_id, batch); 762 763 if (!old_title.empty() && 764 CountWithPrefix(old_prefix, tracker_id) != MULTIPLE) { 765 const std::string old_multi_backing_key = 766 GenerateMultiBackingParentAndTitleKey(parent_id, old_title); 767 DVLOG_IF(1, DBHasKey(old_multi_backing_key)) 768 << " Remove from multi backing file paths: " 769 << parent_id << " " << old_title; 770 batch->Delete(old_multi_backing_key); 771 } 772 773 DVLOG(1) << " Add to trackers by parent and title: " 774 << parent_id << " " << title; 775 776 const std::string prefix = 777 GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, title); 778 AddToTrackerIDSetWithPrefix( 779 GenerateActiveTrackerIDByParentAndTitleKey(parent_id, title), 780 prefix, new_tracker, batch); 781 782 if (CountWithPrefix(prefix, tracker_id) != NONE) { 783 const std::string multi_backing_key = 784 GenerateMultiBackingParentAndTitleKey(parent_id, title); 785 DVLOG_IF(1, !DBHasKey(multi_backing_key)) 786 << " Add to multi backing file_paths: " 787 << parent_id << " " << title; 788 batch->Put(multi_backing_key, std::string()); 789 } 790 791 return; 792 } 793 794 const std::string active_tracker_key = 795 GenerateActiveTrackerIDByParentAndTitleKey(parent_id, title); 796 const std::string prefix = 797 GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, title); 798 if (old_tracker.active() && !new_tracker.active()) { 799 DeactivateInTrackerIDSetWithPrefix( 800 active_tracker_key, prefix, tracker_id, batch); 801 } else if (!old_tracker.active() && new_tracker.active()) { 802 ActivateInTrackerIDSetWithPrefix( 803 active_tracker_key, prefix, tracker_id, batch); 804 } 805} 806 807void MetadataDatabaseIndexOnDisk::RemoveFromPathIndexes( 808 const FileTracker& tracker, leveldb::WriteBatch* batch) { 809 int64 tracker_id = tracker.tracker_id(); 810 int64 parent_id = tracker.parent_tracker_id(); 811 std::string title = GetTrackerTitle(tracker); 812 813 DVLOG(1) << " Remove from trackers by parent and title: " 814 << parent_id << " " << title; 815 816 const std::string active_tracker_key = 817 GenerateActiveTrackerIDByParentAndTitleKey(parent_id, title); 818 const std::string key_prefix = 819 GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, title); 820 if (!EraseInTrackerIDSetWithPrefix( 821 active_tracker_key, key_prefix, tracker_id, batch)) 822 return; 823 824 const std::string multi_key = 825 GenerateMultiBackingParentAndTitleKey(parent_id, title); 826 if (!title.empty() && DBHasKey(multi_key) && 827 CountWithPrefix(key_prefix, tracker_id) != MULTIPLE) { 828 DVLOG(1) << " Remove from multi backing file paths: " 829 << parent_id << " " << title; 830 batch->Delete(multi_key); 831 } 832} 833 834void MetadataDatabaseIndexOnDisk::AddToDirtyTrackerIndexes( 835 const FileTracker& new_tracker, 836 leveldb::WriteBatch* batch) { 837 const std::string dirty_key = GenerateDirtyIDKey(new_tracker.tracker_id()); 838 DCHECK(!DBHasKey(dirty_key)); 839 DCHECK(!DBHasKey(GenerateDemotedDirtyIDKey(new_tracker.tracker_id()))); 840 841 if (new_tracker.dirty()) { 842 DVLOG(1) << " Add to dirty tracker IDs: " << new_tracker.tracker_id(); 843 batch->Put(dirty_key, std::string()); 844 } 845} 846 847void MetadataDatabaseIndexOnDisk::UpdateInDirtyTrackerIndexes( 848 const FileTracker& old_tracker, 849 const FileTracker& new_tracker, 850 leveldb::WriteBatch* batch) { 851 DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id()); 852 853 int64 tracker_id = new_tracker.tracker_id(); 854 const std::string dirty_key = GenerateDirtyIDKey(tracker_id); 855 const std::string demoted_key = GenerateDemotedDirtyIDKey(tracker_id); 856 if (old_tracker.dirty() && !new_tracker.dirty()) { 857 DCHECK(DBHasKey(dirty_key) || DBHasKey(demoted_key)); 858 859 DVLOG(1) << " Remove from dirty trackers IDs: " << tracker_id; 860 861 batch->Delete(dirty_key); 862 batch->Delete(demoted_key); 863 } else if (!old_tracker.dirty() && new_tracker.dirty()) { 864 DCHECK(!DBHasKey(dirty_key)); 865 DCHECK(!DBHasKey(demoted_key)); 866 867 DVLOG(1) << " Add to dirty tracker IDs: " << tracker_id; 868 869 batch->Put(dirty_key, std::string()); 870 } 871} 872 873void MetadataDatabaseIndexOnDisk::RemoveFromDirtyTrackerIndexes( 874 const FileTracker& tracker, leveldb::WriteBatch* batch) { 875 if (tracker.dirty()) { 876 int64 tracker_id = tracker.tracker_id(); 877 const std::string dirty_key = GenerateDirtyIDKey(tracker_id); 878 const std::string demoted_key = GenerateDemotedDirtyIDKey(tracker_id); 879 DCHECK(DBHasKey(dirty_key) || DBHasKey(demoted_key)); 880 881 DVLOG(1) << " Remove from dirty tracker IDs: " << tracker_id; 882 batch->Delete(dirty_key); 883 batch->Delete(demoted_key); 884 } 885} 886 887TrackerIDSet MetadataDatabaseIndexOnDisk::GetTrackerIDSetByPrefix( 888 const std::string& active_tracker_key, 889 const std::string& ids_prefix) const { 890 TrackerIDSet trackers; 891 892 // Seek IDs. 893 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); 894 for (itr->Seek(ids_prefix); itr->Valid(); itr->Next()) { 895 const std::string& key(itr->key().ToString()); 896 std::string id_str; 897 if (!RemovePrefix(key, ids_prefix, &id_str)) 898 break; 899 900 int64 tracker_id; 901 if (!base::StringToInt64(id_str, &tracker_id)) 902 continue; 903 trackers.InsertInactiveTracker(tracker_id); 904 } 905 906 // Set an active tracker ID, if available. 907 std::string value; 908 leveldb::Status status = db_->Get(leveldb::ReadOptions(), 909 active_tracker_key, &value); 910 int64 active_tracker; 911 if (status.ok() && base::StringToInt64(value, &active_tracker) && 912 active_tracker != kInvalidTrackerID) { 913 trackers.Activate(active_tracker); 914 } 915 916 return trackers; 917} 918 919void MetadataDatabaseIndexOnDisk::AddToTrackerIDSetWithPrefix( 920 const std::string& active_tracker_key, const std::string& key_prefix, 921 const FileTracker& tracker, leveldb::WriteBatch* batch) { 922 DCHECK(tracker.tracker_id()); 923 924 const std::string id_str = base::Int64ToString(tracker.tracker_id()); 925 batch->Put(key_prefix + id_str, std::string()); 926 if (tracker.active()) 927 batch->Put(active_tracker_key, id_str); 928} 929 930bool MetadataDatabaseIndexOnDisk::EraseInTrackerIDSetWithPrefix( 931 const std::string& active_tracker_key, const std::string& key_prefix, 932 int64 tracker_id, leveldb::WriteBatch* batch) { 933 std::string value; 934 const std::string del_key = key_prefix + base::Int64ToString(tracker_id); 935 leveldb::Status status = db_->Get(leveldb::ReadOptions(), del_key, &value); 936 if (status.IsNotFound()) 937 return false; 938 939 batch->Delete(del_key); 940 941 size_t count = 0; 942 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); 943 for (itr->Seek(key_prefix); itr->Valid(); itr->Next()) { 944 const std::string key = itr->key().ToString(); 945 if (!StartsWithASCII(key, key_prefix, true)) 946 break; 947 // Entry for |del_key| is not deleted yet. 948 if (key == del_key) 949 continue; 950 ++count; 951 break; 952 } 953 954 if (count > 0) { 955 // TrackerIDSet is still alive. Deactivate if the tracker is active. 956 leveldb::Status status = 957 db_->Get(leveldb::ReadOptions(), active_tracker_key, &value); 958 int64 active_tracker_id; 959 if (status.ok() && base::StringToInt64(value, &active_tracker_id) && 960 active_tracker_id == tracker_id) { 961 batch->Put(active_tracker_key, base::Int64ToString(kInvalidTrackerID)); 962 } 963 } else { 964 // TrackerIDSet is no longer alive. Erase active tracker entry. 965 batch->Delete(active_tracker_key); 966 } 967 968 return true; 969} 970 971void MetadataDatabaseIndexOnDisk::ActivateInTrackerIDSetWithPrefix( 972 const std::string& active_tracker_key, const std::string& key_prefix, 973 int64 tracker_id, leveldb::WriteBatch* batch) { 974 DCHECK(DBHasKey(key_prefix + base::Int64ToString(tracker_id))); 975 976 std::string value; 977 leveldb::Status status = db_->Get(leveldb::ReadOptions(), 978 active_tracker_key, &value); 979 int64 active_tracker_id; 980 if (status.ok() && base::StringToInt64(value, &active_tracker_id)) { 981 DCHECK(active_tracker_id != tracker_id); 982 batch->Put(active_tracker_key, base::Int64ToString(tracker_id)); 983 } 984} 985 986void MetadataDatabaseIndexOnDisk::DeactivateInTrackerIDSetWithPrefix( 987 const std::string& active_tracker_key, const std::string& key_prefix, 988 int64 tracker_id, leveldb::WriteBatch* batch) { 989 DCHECK(DBHasKey(key_prefix + base::Int64ToString(tracker_id))); 990 991 std::string value; 992 leveldb::Status status = db_->Get(leveldb::ReadOptions(), 993 active_tracker_key, &value); 994 int64 active_tracker_id; 995 if (status.ok() && base::StringToInt64(value, &active_tracker_id)) { 996 DCHECK(active_tracker_id == tracker_id); 997 batch->Put(active_tracker_key, base::Int64ToString(kInvalidTrackerID)); 998 } 999} 1000 1001bool MetadataDatabaseIndexOnDisk::DBHasKey(const std::string& key) { 1002 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); 1003 itr->Seek(key); 1004 return itr->Valid() && (itr->key() == key); 1005} 1006 1007MetadataDatabaseIndexOnDisk::NumEntries 1008MetadataDatabaseIndexOnDisk::CountWithPrefix( 1009 const std::string& prefix, int64 ignored_id) { 1010 const std::string ignored = base::Int64ToString(ignored_id); 1011 1012 size_t count = 0; 1013 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); 1014 for (itr->Seek(prefix); itr->Valid() && count <= 1; itr->Next()) { 1015 std::string value; 1016 if (!RemovePrefix(itr->key().ToString(), prefix, &value)) 1017 break; 1018 if (value == ignored) 1019 continue; 1020 1021 ++count; 1022 } 1023 1024 if (count >= 2) 1025 return MULTIPLE; 1026 return count == 0 ? NONE : SINGLE; 1027} 1028 1029} // namespace drive_backend 1030} // namespace sync_file_system 1031