metadata_database_index_on_disk.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
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/leveldb_wrapper.h" 14#include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h" 15#include "chrome/browser/sync_file_system/logger.h" 16#include "third_party/leveldatabase/src/include/leveldb/status.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// 82// # Timestamp when the last validation ran 83// key: "LAST_VALID" 84// value: <time_t 'last_valid_time'> 85 86namespace sync_file_system { 87namespace drive_backend { 88 89namespace { 90 91std::string GenerateAppRootIDByAppIDKey(const std::string& app_id) { 92 return kAppRootIDByAppIDKeyPrefix + app_id; 93} 94 95std::string GenerateActiveTrackerIDByFileIDKey(const std::string& file_id) { 96 return kActiveTrackerIDByFileIDKeyPrefix + file_id; 97} 98 99std::string GenerateTrackerIDByFileIDKeyPrefix(const std::string& file_id) { 100 std::ostringstream oss; 101 oss << kTrackerIDByFileIDKeyPrefix << file_id << '\0'; 102 return oss.str(); 103} 104 105std::string GenerateMultiTrackerKey(const std::string& file_id) { 106 return kMultiTrackerByFileIDKeyPrefix + file_id; 107} 108 109std::string GenerateActiveTrackerIDByParentAndTitleKey( 110 int64 parent_id, const std::string& title) { 111 std::ostringstream oss; 112 oss << kActiveTrackerIDByParentAndTitleKeyPrefix << parent_id 113 << '\0' << title; 114 return oss.str(); 115} 116 117std::string GenerateTrackerIDByParentAndTitleKeyPrefix( 118 int64 parent_id, const std::string& title) { 119 std::ostringstream oss; 120 oss << kTrackerIDByParentAndTitleKeyPrefix << parent_id << '\0' 121 << title << '\0'; 122 return oss.str(); 123} 124 125std::string GenerateTrackerIDsByParentIDKeyPrefix(int64 parent_id) { 126 std::ostringstream oss; 127 oss << kTrackerIDByParentAndTitleKeyPrefix << parent_id << '\0'; 128 return oss.str(); 129} 130 131std::string GenerateMultiBackingParentAndTitleKey( 132 int64 parent_id, const std::string& title) { 133 std::ostringstream oss; 134 oss << kMultiBackingParentAndTitleKeyPrefix << parent_id << '\0' 135 << title; 136 return oss.str(); 137} 138 139std::string GenerateDirtyIDKey(int64 tracker_id) { 140 return kDirtyIDKeyPrefix + base::Int64ToString(tracker_id); 141} 142 143std::string GenerateDemotedDirtyIDKey(int64 tracker_id) { 144 return kDemotedDirtyIDKeyPrefix + base::Int64ToString(tracker_id); 145} 146 147void RemoveUnreachableItems(LevelDBWrapper* db) { 148 DCHECK(db); 149 150 typedef std::map<int64, std::set<int64> > ChildTrackersByParent; 151 ChildTrackersByParent trackers_by_parent; 152 { 153 // Set up links from parent tracker to child trackers. 154 std::set<int64> inactive_trackers; 155 scoped_ptr<LevelDBWrapper::Iterator> itr = db->NewIterator(); 156 for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) { 157 if (!RemovePrefix(itr->key().ToString(), kFileTrackerKeyPrefix, NULL)) 158 break; 159 160 scoped_ptr<FileTracker> tracker(new FileTracker); 161 if (!tracker->ParseFromString(itr->value().ToString())) { 162 util::Log(logging::LOG_WARNING, FROM_HERE, 163 "Failed to parse a Tracker"); 164 continue; 165 } 166 167 int64 parent_tracker_id = tracker->parent_tracker_id(); 168 int64 tracker_id = tracker->tracker_id(); 169 trackers_by_parent[parent_tracker_id].insert(tracker_id); 170 if (!tracker->active()) 171 inactive_trackers.insert(tracker_id); 172 } 173 174 // Drop links from inactive trackers. 175 for (std::set<int64>::iterator iter = inactive_trackers.begin(); 176 iter != inactive_trackers.end(); ++iter) { 177 trackers_by_parent.erase(*iter); 178 } 179 } 180 181 // Traverse tracker tree from sync-root. 182 std::set<int64> visited_trackers; 183 { 184 scoped_ptr<ServiceMetadata> service_metadata = 185 InitializeServiceMetadata(db); 186 int64 sync_root_tracker_id = service_metadata->sync_root_tracker_id(); 187 std::vector<int64> pending; 188 if (sync_root_tracker_id != kInvalidTrackerID) 189 pending.push_back(sync_root_tracker_id); 190 191 while (!pending.empty()) { 192 int64 tracker_id = pending.back(); 193 DCHECK_NE(kInvalidTrackerID, tracker_id); 194 pending.pop_back(); 195 196 if (!visited_trackers.insert(tracker_id).second) { 197 NOTREACHED(); 198 continue; 199 } 200 201 AppendContents( 202 LookUpMap(trackers_by_parent, tracker_id, std::set<int64>()), 203 &pending); 204 } 205 } 206 207 // Delete all unreachable trackers, and list all |file_id| referred by 208 // remained trackers. 209 base::hash_set<std::string> referred_file_ids; 210 { 211 scoped_ptr<LevelDBWrapper::Iterator> itr = db->NewIterator(); 212 for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) { 213 if (!RemovePrefix(itr->key().ToString(), kFileTrackerKeyPrefix, NULL)) 214 break; 215 216 scoped_ptr<FileTracker> tracker(new FileTracker); 217 if (!tracker->ParseFromString(itr->value().ToString())) { 218 util::Log(logging::LOG_WARNING, FROM_HERE, 219 "Failed to parse a Tracker"); 220 continue; 221 } 222 223 if (ContainsKey(visited_trackers, tracker->tracker_id())) { 224 referred_file_ids.insert(tracker->file_id()); 225 } else { 226 PutFileTrackerDeletionToDB(tracker->tracker_id(), db); 227 } 228 } 229 } 230 231 // Delete all unreferred metadata. 232 { 233 scoped_ptr<LevelDBWrapper::Iterator> itr = db->NewIterator(); 234 for (itr->Seek(kFileMetadataKeyPrefix); itr->Valid(); itr->Next()) { 235 if (!RemovePrefix(itr->key().ToString(), kFileMetadataKeyPrefix, NULL)) 236 break; 237 238 scoped_ptr<FileMetadata> metadata(new FileMetadata); 239 if (!metadata->ParseFromString(itr->value().ToString())) { 240 util::Log(logging::LOG_WARNING, FROM_HERE, 241 "Failed to parse a Tracker"); 242 continue; 243 } 244 245 if (!ContainsKey(referred_file_ids, metadata->file_id())) 246 PutFileMetadataDeletionToDB(metadata->file_id(), db); 247 } 248 } 249} 250 251} // namespace 252 253// static 254scoped_ptr<MetadataDatabaseIndexOnDisk> 255MetadataDatabaseIndexOnDisk::Create(LevelDBWrapper* db) { 256 DCHECK(db); 257 258 PutVersionToDB(kDatabaseOnDiskVersion, db); 259 // TODO(peria): It is not good to call RemoveUnreachableItems on every 260 // creation. 261 RemoveUnreachableItems(db); 262 scoped_ptr<MetadataDatabaseIndexOnDisk> 263 index(new MetadataDatabaseIndexOnDisk(db)); 264 265 return index.Pass(); 266} 267 268MetadataDatabaseIndexOnDisk::~MetadataDatabaseIndexOnDisk() {} 269 270bool MetadataDatabaseIndexOnDisk::GetFileMetadata( 271 const std::string& file_id, FileMetadata* metadata) const { 272 const std::string key = kFileMetadataKeyPrefix + file_id; 273 std::string value; 274 leveldb::Status status = db_->Get(key, &value); 275 276 if (status.IsNotFound()) 277 return false; 278 279 if (!status.ok()) { 280 util::Log(logging::LOG_WARNING, FROM_HERE, 281 "LevelDB error (%s) in getting FileMetadata for ID: %s", 282 status.ToString().c_str(), 283 file_id.c_str()); 284 return false; 285 } 286 287 FileMetadata tmp_metadata; 288 if (!tmp_metadata.ParseFromString(value)) { 289 util::Log(logging::LOG_WARNING, FROM_HERE, 290 "Failed to parse a FileMetadata for ID: %s", 291 file_id.c_str()); 292 return false; 293 } 294 if (metadata) 295 metadata->CopyFrom(tmp_metadata); 296 297 return true; 298} 299 300bool MetadataDatabaseIndexOnDisk::GetFileTracker( 301 int64 tracker_id, FileTracker* tracker) const { 302 const std::string key = 303 kFileTrackerKeyPrefix + base::Int64ToString(tracker_id); 304 std::string value; 305 leveldb::Status status = db_->Get(key, &value); 306 307 if (status.IsNotFound()) 308 return false; 309 310 if (!status.ok()) { 311 util::Log(logging::LOG_WARNING, FROM_HERE, 312 "LevelDB error (%s) in getting FileTracker for ID: %" PRId64, 313 status.ToString().c_str(), 314 tracker_id); 315 return false; 316 } 317 318 FileTracker tmp_tracker; 319 if (!tmp_tracker.ParseFromString(value)) { 320 util::Log(logging::LOG_WARNING, FROM_HERE, 321 "Failed to parse a Tracker for ID: %" PRId64, 322 tracker_id); 323 return false; 324 } 325 if (tracker) 326 tracker->CopyFrom(tmp_tracker); 327 328 return true; 329} 330 331void MetadataDatabaseIndexOnDisk::StoreFileMetadata( 332 scoped_ptr<FileMetadata> metadata) { 333 DCHECK(metadata); 334 PutFileMetadataToDB(*metadata, db_); 335} 336 337void MetadataDatabaseIndexOnDisk::StoreFileTracker( 338 scoped_ptr<FileTracker> tracker) { 339 DCHECK(tracker); 340 341 int64 tracker_id = tracker->tracker_id(); 342 FileTracker old_tracker; 343 if (!GetFileTracker(tracker_id, &old_tracker)) { 344 DVLOG(3) << "Adding new tracker: " << tracker->tracker_id() 345 << " " << GetTrackerTitle(*tracker); 346 AddToAppIDIndex(*tracker); 347 AddToFileIDIndexes(*tracker); 348 AddToPathIndexes(*tracker); 349 AddToDirtyTrackerIndexes(*tracker); 350 } else { 351 DVLOG(3) << "Updating tracker: " << tracker->tracker_id() 352 << " " << GetTrackerTitle(*tracker); 353 UpdateInAppIDIndex(old_tracker, *tracker); 354 UpdateInFileIDIndexes(old_tracker, *tracker); 355 UpdateInPathIndexes(old_tracker, *tracker); 356 UpdateInDirtyTrackerIndexes(old_tracker, *tracker); 357 358 } 359 360 PutFileTrackerToDB(*tracker, db_); 361} 362 363void MetadataDatabaseIndexOnDisk::RemoveFileMetadata( 364 const std::string& file_id) { 365 PutFileMetadataDeletionToDB(file_id, db_); 366} 367 368void MetadataDatabaseIndexOnDisk::RemoveFileTracker(int64 tracker_id) { 369 FileTracker tracker; 370 if (!GetFileTracker(tracker_id, &tracker)) { 371 NOTREACHED(); 372 return; 373 } 374 375 DVLOG(1) << "Removing tracker: " 376 << tracker.tracker_id() << " " << GetTrackerTitle(tracker); 377 RemoveFromAppIDIndex(tracker); 378 RemoveFromFileIDIndexes(tracker); 379 RemoveFromPathIndexes(tracker); 380 RemoveFromDirtyTrackerIndexes(tracker); 381 382 PutFileTrackerDeletionToDB(tracker_id, db_); 383} 384 385TrackerIDSet MetadataDatabaseIndexOnDisk::GetFileTrackerIDsByFileID( 386 const std::string& file_id) const { 387 return GetTrackerIDSetByPrefix( 388 GenerateActiveTrackerIDByFileIDKey(file_id), 389 GenerateTrackerIDByFileIDKeyPrefix(file_id)); 390} 391 392int64 MetadataDatabaseIndexOnDisk::GetAppRootTracker( 393 const std::string& app_id) const { 394 const std::string key = GenerateAppRootIDByAppIDKey(app_id); 395 std::string value; 396 leveldb::Status status = db_->Get(key, &value); 397 398 if (status.IsNotFound()) 399 return kInvalidTrackerID; 400 401 if (!status.ok()) { 402 util::Log(logging::LOG_WARNING, FROM_HERE, 403 "LevelDB error (%s) in getting AppRoot for AppID: %s", 404 status.ToString().c_str(), 405 app_id.c_str()); 406 return kInvalidTrackerID; 407 } 408 409 int64 root_id; 410 if (!base::StringToInt64(value, &root_id)) { 411 util::Log(logging::LOG_WARNING, FROM_HERE, 412 "Failed to parse a root ID (%s) for an App ID: %s", 413 value.c_str(), 414 app_id.c_str()); 415 return kInvalidTrackerID; 416 } 417 418 return root_id; 419} 420 421TrackerIDSet MetadataDatabaseIndexOnDisk::GetFileTrackerIDsByParentAndTitle( 422 int64 parent_tracker_id, const std::string& title) const { 423 return GetTrackerIDSetByPrefix( 424 GenerateActiveTrackerIDByParentAndTitleKey(parent_tracker_id, title), 425 GenerateTrackerIDByParentAndTitleKeyPrefix(parent_tracker_id, title)); 426} 427 428std::vector<int64> MetadataDatabaseIndexOnDisk::GetFileTrackerIDsByParent( 429 int64 parent_id) const { 430 std::vector<int64> result; 431 432 const std::string prefix = GenerateTrackerIDsByParentIDKeyPrefix(parent_id); 433 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator()); 434 for (itr->Seek(prefix); itr->Valid(); itr->Next()) { 435 const std::string& key(itr->key().ToString()); 436 std::string title_and_id; 437 if (!RemovePrefix(key, prefix, &title_and_id)) 438 break; 439 440 size_t pos = title_and_id.rfind('\0'); 441 DCHECK(pos != std::string::npos); 442 443 int64 tracker_id; 444 if (!base::StringToInt64(title_and_id.substr(pos + 1), &tracker_id)) 445 continue; 446 result.push_back(tracker_id); 447 } 448 return result; 449} 450 451std::string MetadataDatabaseIndexOnDisk::PickMultiTrackerFileID() const { 452 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator()); 453 itr->Seek(kMultiTrackerByFileIDKeyPrefix); 454 if (!itr->Valid()) 455 return std::string(); 456 457 std::string file_id; 458 if (!RemovePrefix(itr->key().ToString(), 459 kMultiTrackerByFileIDKeyPrefix, &file_id)) 460 return std::string(); 461 462 return file_id; 463} 464 465ParentIDAndTitle MetadataDatabaseIndexOnDisk::PickMultiBackingFilePath() const { 466 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator()); 467 itr->Seek(kMultiBackingParentAndTitleKeyPrefix); 468 if (!itr->Valid()) 469 return ParentIDAndTitle(); 470 471 std::string value; 472 if (!RemovePrefix(itr->key().ToString(), 473 kMultiBackingParentAndTitleKeyPrefix, &value)) 474 return ParentIDAndTitle(); 475 476 size_t pos = value.find('\0'); // '\0' is a separator. 477 if (pos == std::string::npos) 478 return ParentIDAndTitle(); 479 480 int64 parent_id; 481 return base::StringToInt64(value.substr(0, pos), &parent_id) ? 482 ParentIDAndTitle(parent_id, value.substr(pos + 1)) : ParentIDAndTitle(); 483} 484 485int64 MetadataDatabaseIndexOnDisk::PickDirtyTracker() const { 486 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator()); 487 itr->Seek(kDirtyIDKeyPrefix); 488 if (!itr->Valid()) 489 return kInvalidTrackerID; 490 491 std::string id_str; 492 if (!RemovePrefix(itr->key().ToString(), kDirtyIDKeyPrefix, &id_str)) 493 return kInvalidTrackerID; 494 495 int64 tracker_id; 496 if (!base::StringToInt64(id_str, &tracker_id)) 497 return kInvalidTrackerID; 498 499 return tracker_id; 500} 501 502void MetadataDatabaseIndexOnDisk::DemoteDirtyTracker(int64 tracker_id) { 503 const std::string key = GenerateDirtyIDKey(tracker_id); 504 505 std::string value; 506 leveldb::Status status = db_->Get(key, &value); 507 if (status.IsNotFound()) 508 return; 509 if (!status.ok()) { 510 util::Log(logging::LOG_WARNING, FROM_HERE, 511 "LevelDB error (%s) in getting a dirty tracker for ID: %" PRId64, 512 status.ToString().c_str(), 513 tracker_id); 514 return; 515 } 516 517 db_->Delete(key); 518 db_->Put(GenerateDemotedDirtyIDKey(tracker_id), std::string()); 519} 520 521bool MetadataDatabaseIndexOnDisk::HasDemotedDirtyTracker() const { 522 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator()); 523 itr->Seek(kDemotedDirtyIDKeyPrefix); 524 if (!itr->Valid()) 525 return false; 526 return StartsWithASCII(itr->key().ToString(), kDemotedDirtyIDKeyPrefix, true); 527} 528 529void MetadataDatabaseIndexOnDisk::PromoteDemotedDirtyTracker(int64 tracker_id) { 530 std::string demoted_key = GenerateDemotedDirtyIDKey(tracker_id); 531 532 std::string empty; 533 if (db_->Get(demoted_key, &empty).ok()) { 534 db_->Delete(demoted_key); 535 db_->Put(GenerateDirtyIDKey(tracker_id), std::string()); 536 } 537} 538 539bool MetadataDatabaseIndexOnDisk::PromoteDemotedDirtyTrackers() { 540 bool promoted = false; 541 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator()); 542 for (itr->Seek(kDirtyIDKeyPrefix); itr->Valid(); itr->Next()) { 543 std::string id_str; 544 if (!RemovePrefix(itr->key().ToString(), kDirtyIDKeyPrefix, &id_str)) 545 break; 546 547 int64 tracker_id; 548 if (!base::StringToInt64(id_str, &tracker_id)) 549 continue; 550 551 db_->Delete(itr->key().ToString()); 552 db_->Put(GenerateDemotedDirtyIDKey(tracker_id), std::string()); 553 promoted = true; 554 } 555 return promoted; 556} 557 558size_t MetadataDatabaseIndexOnDisk::CountDirtyTracker() const { 559 size_t num_dirty_trackers = 0; 560 561 // TODO(peria): Store the number of dirty trackers, and do not iterate 562 // everytime. 563 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator()); 564 for (itr->Seek(kDirtyIDKeyPrefix); itr->Valid(); itr->Next()) { 565 if (!StartsWithASCII(itr->key().ToString(), kDirtyIDKeyPrefix, true)) 566 break; 567 ++num_dirty_trackers; 568 } 569 570 for (itr->Seek(kDemotedDirtyIDKeyPrefix); itr->Valid(); itr->Next()) { 571 if (!StartsWithASCII(itr->key().ToString(), kDemotedDirtyIDKeyPrefix, true)) 572 break; 573 ++num_dirty_trackers; 574 } 575 576 return num_dirty_trackers; 577} 578 579size_t MetadataDatabaseIndexOnDisk::CountFileMetadata() const { 580 // TODO(peria): Cache the number of FileMetadata in the DB. 581 size_t count = 0; 582 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator()); 583 for (itr->Seek(kFileMetadataKeyPrefix); itr->Valid(); itr->Next()) { 584 if (!StartsWithASCII(itr->key().ToString(), kFileMetadataKeyPrefix, true)) 585 break; 586 ++count; 587 } 588 return count; 589} 590 591size_t MetadataDatabaseIndexOnDisk::CountFileTracker() const { 592 // TODO(peria): Cache the number of FileTracker in the DB. 593 size_t count = 0; 594 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator()); 595 for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) { 596 if (!StartsWithASCII(itr->key().ToString(), kFileTrackerKeyPrefix, true)) 597 break; 598 ++count; 599 } 600 return count; 601} 602 603void MetadataDatabaseIndexOnDisk::SetSyncRootTrackerID( 604 int64 sync_root_id) const { 605 service_metadata_->set_sync_root_tracker_id(sync_root_id); 606 PutServiceMetadataToDB(*service_metadata_, db_); 607} 608 609void MetadataDatabaseIndexOnDisk::SetLargestChangeID( 610 int64 largest_change_id) const { 611 service_metadata_->set_largest_change_id(largest_change_id); 612 PutServiceMetadataToDB(*service_metadata_, db_); 613} 614 615void MetadataDatabaseIndexOnDisk::SetNextTrackerID( 616 int64 next_tracker_id) const { 617 service_metadata_->set_next_tracker_id(next_tracker_id); 618 PutServiceMetadataToDB(*service_metadata_, db_); 619} 620 621int64 MetadataDatabaseIndexOnDisk::GetSyncRootTrackerID() const { 622 if (!service_metadata_->has_sync_root_tracker_id()) 623 return kInvalidTrackerID; 624 return service_metadata_->sync_root_tracker_id(); 625} 626 627int64 MetadataDatabaseIndexOnDisk::GetLargestChangeID() const { 628 if (!service_metadata_->has_largest_change_id()) 629 return kInvalidTrackerID; 630 return service_metadata_->largest_change_id(); 631} 632 633int64 MetadataDatabaseIndexOnDisk::GetNextTrackerID() const { 634 if (!service_metadata_->has_next_tracker_id()) 635 return kInvalidTrackerID; 636 return service_metadata_->next_tracker_id(); 637} 638 639std::vector<std::string> 640MetadataDatabaseIndexOnDisk::GetRegisteredAppIDs() const { 641 std::vector<std::string> result; 642 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator()); 643 for (itr->Seek(kAppRootIDByAppIDKeyPrefix); itr->Valid(); itr->Next()) { 644 std::string id; 645 if (!RemovePrefix(itr->key().ToString(), kAppRootIDByAppIDKeyPrefix, &id)) 646 break; 647 result.push_back(id); 648 } 649 return result; 650} 651 652std::vector<int64> MetadataDatabaseIndexOnDisk::GetAllTrackerIDs() const { 653 std::vector<int64> tracker_ids; 654 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator()); 655 for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) { 656 std::string id_str; 657 if (!RemovePrefix(itr->key().ToString(), kFileTrackerKeyPrefix, &id_str)) 658 break; 659 660 int64 tracker_id; 661 if (!base::StringToInt64(id_str, &tracker_id)) 662 continue; 663 tracker_ids.push_back(tracker_id); 664 } 665 return tracker_ids; 666} 667 668std::vector<std::string> 669MetadataDatabaseIndexOnDisk::GetAllMetadataIDs() const { 670 std::vector<std::string> file_ids; 671 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator()); 672 for (itr->Seek(kFileMetadataKeyPrefix); itr->Valid(); itr->Next()) { 673 std::string file_id; 674 if (!RemovePrefix(itr->key().ToString(), kFileMetadataKeyPrefix, &file_id)) 675 break; 676 file_ids.push_back(file_id); 677 } 678 return file_ids; 679} 680 681int64 MetadataDatabaseIndexOnDisk::BuildTrackerIndexes() { 682 int64 num_puts_before = db_->num_puts(); 683 684 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator()); 685 for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) { 686 if (!RemovePrefix(itr->key().ToString(), kFileTrackerKeyPrefix, NULL)) 687 break; 688 689 FileTracker tracker; 690 if (!tracker.ParseFromString(itr->value().ToString())) { 691 util::Log(logging::LOG_WARNING, FROM_HERE, 692 "Failed to parse a Tracker"); 693 continue; 694 } 695 696 AddToAppIDIndex(tracker); 697 AddToFileIDIndexes(tracker); 698 AddToPathIndexes(tracker); 699 AddToDirtyTrackerIndexes(tracker); 700 } 701 702 return db_->num_puts() - num_puts_before; 703} 704 705int64 MetadataDatabaseIndexOnDisk::DeleteTrackerIndexes() { 706 const char* kIndexPrefixes[] = { 707 kAppRootIDByAppIDKeyPrefix, kActiveTrackerIDByFileIDKeyPrefix, 708 kTrackerIDByFileIDKeyPrefix, kMultiTrackerByFileIDKeyPrefix, 709 kActiveTrackerIDByParentAndTitleKeyPrefix, 710 kTrackerIDByParentAndTitleKeyPrefix, kMultiBackingParentAndTitleKeyPrefix, 711 kDirtyIDKeyPrefix, kDemotedDirtyIDKeyPrefix 712 }; 713 714 int64 num_deletes_before = db_->num_deletes(); 715 for (size_t i = 0; i < arraysize(kIndexPrefixes); ++i) 716 DeleteKeyStartsWith(kIndexPrefixes[i]); 717 return db_->num_deletes() - num_deletes_before; 718} 719 720LevelDBWrapper* MetadataDatabaseIndexOnDisk::GetDBForTesting() { 721 return db_; 722} 723 724MetadataDatabaseIndexOnDisk::MetadataDatabaseIndexOnDisk(LevelDBWrapper* db) 725 : db_(db) { 726 // TODO(peria): Add UMA to measure the number of FileMetadata, FileTracker, 727 // and AppRootId. 728 service_metadata_ = InitializeServiceMetadata(db_); 729 730 // Check if index is valid, if no validations run in 7 days. 731 const int64 kThresholdToValidateInDays = 7; 732 733 int64 last_check_time = 0; 734 std::string value; 735 if (db_->Get(kLastValidationTimeKey, &value).ok()) 736 base::StringToInt64(value, &last_check_time); 737 base::TimeDelta since_last_check = 738 base::Time::Now() - base::Time::FromInternalValue(last_check_time); 739 int64 since_last_check_in_days = since_last_check.InDays(); 740 if (since_last_check_in_days >= kThresholdToValidateInDays || 741 since_last_check_in_days < 0) { 742 // TODO(peria): Add UMA to check if the number of deleted entries and the 743 // number of built entries are different or not. 744 DeleteTrackerIndexes(); 745 BuildTrackerIndexes(); 746 db_->Put(kLastValidationTimeKey, 747 base::Int64ToString(base::Time::Now().ToInternalValue())); 748 } 749} 750 751void MetadataDatabaseIndexOnDisk::AddToAppIDIndex(const FileTracker& tracker) { 752 if (!IsAppRoot(tracker)) { 753 DVLOG(3) << " Tracker for " << tracker.file_id() << " is not an App root."; 754 return; 755 } 756 757 DVLOG(1) << " Add to App root by App ID: " << tracker.app_id(); 758 759 const std::string db_key = GenerateAppRootIDByAppIDKey(tracker.app_id()); 760 DCHECK(tracker.active()); 761 DCHECK(!DBHasKey(db_key)); 762 db_->Put(db_key, base::Int64ToString(tracker.tracker_id())); 763} 764 765void MetadataDatabaseIndexOnDisk::UpdateInAppIDIndex( 766 const FileTracker& old_tracker, 767 const FileTracker& new_tracker) { 768 DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id()); 769 770 if (IsAppRoot(old_tracker) && !IsAppRoot(new_tracker)) { 771 DCHECK(old_tracker.active()); 772 DCHECK(!new_tracker.active()); 773 const std::string key = GenerateAppRootIDByAppIDKey(old_tracker.app_id()); 774 DCHECK(DBHasKey(key)); 775 776 DVLOG(1) << " Remove from App root by App ID: " << old_tracker.app_id(); 777 db_->Delete(key); 778 } else if (!IsAppRoot(old_tracker) && IsAppRoot(new_tracker)) { 779 DCHECK(!old_tracker.active()); 780 DCHECK(new_tracker.active()); 781 const std::string key = GenerateAppRootIDByAppIDKey(new_tracker.app_id()); 782 DCHECK(!DBHasKey(key)); 783 784 DVLOG(1) << " Add to App root by App ID: " << new_tracker.app_id(); 785 db_->Put(key, base::Int64ToString(new_tracker.tracker_id())); 786 } 787} 788 789void MetadataDatabaseIndexOnDisk::RemoveFromAppIDIndex( 790 const FileTracker& tracker) { 791 if (!IsAppRoot(tracker)) { 792 DVLOG(3) << " Tracker for " << tracker.file_id() << " is not an App root."; 793 return; 794 } 795 796 DCHECK(tracker.active()); 797 const std::string key = GenerateAppRootIDByAppIDKey(tracker.app_id()); 798 DCHECK(DBHasKey(key)); 799 800 DVLOG(1) << " Remove from App root by App ID: " << tracker.app_id(); 801 db_->Delete(key); 802} 803 804void MetadataDatabaseIndexOnDisk::AddToFileIDIndexes( 805 const FileTracker& new_tracker) { 806 const std::string& file_id = new_tracker.file_id(); 807 808 DVLOG(1) << " Add to trackers by file ID: " << file_id; 809 const std::string prefix = GenerateTrackerIDByFileIDKeyPrefix(file_id); 810 AddToTrackerIDSetWithPrefix( 811 GenerateActiveTrackerIDByFileIDKey(file_id), prefix, new_tracker); 812 813 const std::string multi_tracker_key = GenerateMultiTrackerKey(file_id); 814 if (!DBHasKey(multi_tracker_key) && 815 CountWithPrefix(prefix, new_tracker.tracker_id()) != NONE) { 816 DVLOG(1) << " Add to multi-tracker file IDs: " << file_id; 817 db_->Put(multi_tracker_key, std::string()); 818 } 819} 820 821void MetadataDatabaseIndexOnDisk::UpdateInFileIDIndexes( 822 const FileTracker& old_tracker, 823 const FileTracker& new_tracker) { 824 DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id()); 825 DCHECK_EQ(old_tracker.file_id(), new_tracker.file_id()); 826 827 const std::string& file_id = new_tracker.file_id(); 828 const std::string prefix = GenerateTrackerIDByFileIDKeyPrefix(file_id); 829 DCHECK(DBHasKey(prefix + base::Int64ToString(new_tracker.tracker_id()))); 830 831 if (old_tracker.active() && !new_tracker.active()) { 832 DeactivateInTrackerIDSetWithPrefix( 833 GenerateActiveTrackerIDByFileIDKey(file_id), prefix, 834 new_tracker.tracker_id()); 835 } else if (!old_tracker.active() && new_tracker.active()) { 836 ActivateInTrackerIDSetWithPrefix( 837 GenerateActiveTrackerIDByFileIDKey(file_id), prefix, 838 new_tracker.tracker_id()); 839 } 840} 841 842void MetadataDatabaseIndexOnDisk::RemoveFromFileIDIndexes( 843 const FileTracker& tracker) { 844 const std::string& file_id = tracker.file_id(); 845 const std::string prefix = 846 GenerateTrackerIDByFileIDKeyPrefix(file_id); 847 848 if (!EraseInTrackerIDSetWithPrefix( 849 GenerateActiveTrackerIDByFileIDKey(file_id), 850 prefix, tracker.tracker_id())) 851 return; 852 853 DVLOG(1) << " Remove from trackers by file ID: " << tracker.tracker_id(); 854 855 const std::string multi_key = GenerateMultiTrackerKey(file_id); 856 if (DBHasKey(multi_key) && 857 CountWithPrefix(prefix, tracker.tracker_id()) != MULTIPLE) { 858 DVLOG(1) << " Remove from multi-tracker file IDs: " << file_id; 859 db_->Delete(multi_key); 860 } 861} 862 863void MetadataDatabaseIndexOnDisk::AddToPathIndexes( 864 const FileTracker& new_tracker) { 865 int64 parent_id = new_tracker.parent_tracker_id(); 866 std::string title = GetTrackerTitle(new_tracker); 867 868 DVLOG(1) << " Add to trackers by parent and title: " 869 << parent_id << " " << title; 870 871 const std::string prefix = 872 GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, title); 873 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator()); 874 for (itr->Seek(prefix); itr->Valid(); itr->Next()) { 875 std::string id_str; 876 if (!RemovePrefix(itr->key().ToString(), prefix, &id_str)) 877 break; 878 879 int64 tracker_id; 880 if (!base::StringToInt64(id_str, &tracker_id)) 881 continue; 882 if (tracker_id == new_tracker.tracker_id()) { 883 NOTREACHED(); 884 continue; 885 } 886 887 const std::string multi_key = 888 GenerateMultiBackingParentAndTitleKey(parent_id, title); 889 DVLOG_IF(1, !DBHasKey(multi_key)) 890 << " Add to multi backing file paths: " << parent_id << " " << title; 891 db_->Put(GenerateMultiBackingParentAndTitleKey(parent_id, title), 892 std::string()); 893 break; 894 } 895 896 AddToTrackerIDSetWithPrefix( 897 GenerateActiveTrackerIDByParentAndTitleKey(parent_id, title), 898 prefix, new_tracker); 899} 900 901void MetadataDatabaseIndexOnDisk::UpdateInPathIndexes( 902 const FileTracker& old_tracker, 903 const FileTracker& new_tracker) { 904 DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id()); 905 DCHECK_EQ(old_tracker.parent_tracker_id(), new_tracker.parent_tracker_id()); 906 DCHECK(GetTrackerTitle(old_tracker) == GetTrackerTitle(new_tracker) || 907 !old_tracker.has_synced_details()); 908 909 int64 tracker_id = new_tracker.tracker_id(); 910 int64 parent_id = new_tracker.parent_tracker_id(); 911 const std::string old_title = GetTrackerTitle(old_tracker); 912 const std::string title = GetTrackerTitle(new_tracker); 913 914 if (old_title != title) { 915 const std::string old_prefix = 916 GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, old_title); 917 EraseInTrackerIDSetWithPrefix( 918 GenerateActiveTrackerIDByParentAndTitleKey(parent_id, old_title), 919 old_prefix, tracker_id); 920 921 if (!old_title.empty() && 922 CountWithPrefix(old_prefix, tracker_id) != MULTIPLE) { 923 const std::string old_multi_backing_key = 924 GenerateMultiBackingParentAndTitleKey(parent_id, old_title); 925 DVLOG_IF(1, DBHasKey(old_multi_backing_key)) 926 << " Remove from multi backing file paths: " 927 << parent_id << " " << old_title; 928 db_->Delete(old_multi_backing_key); 929 } 930 931 DVLOG(1) << " Add to trackers by parent and title: " 932 << parent_id << " " << title; 933 934 const std::string prefix = 935 GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, title); 936 AddToTrackerIDSetWithPrefix( 937 GenerateActiveTrackerIDByParentAndTitleKey(parent_id, title), 938 prefix, new_tracker); 939 940 if (CountWithPrefix(prefix, tracker_id) != NONE) { 941 const std::string multi_backing_key = 942 GenerateMultiBackingParentAndTitleKey(parent_id, title); 943 DVLOG_IF(1, !DBHasKey(multi_backing_key)) 944 << " Add to multi backing file_paths: " 945 << parent_id << " " << title; 946 db_->Put(multi_backing_key, std::string()); 947 } 948 949 return; 950 } 951 952 const std::string active_tracker_key = 953 GenerateActiveTrackerIDByParentAndTitleKey(parent_id, title); 954 const std::string prefix = 955 GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, title); 956 if (old_tracker.active() && !new_tracker.active()) { 957 DeactivateInTrackerIDSetWithPrefix( 958 active_tracker_key, prefix, tracker_id); 959 } else if (!old_tracker.active() && new_tracker.active()) { 960 ActivateInTrackerIDSetWithPrefix( 961 active_tracker_key, prefix, tracker_id); 962 } 963} 964 965void MetadataDatabaseIndexOnDisk::RemoveFromPathIndexes( 966 const FileTracker& tracker) { 967 int64 tracker_id = tracker.tracker_id(); 968 int64 parent_id = tracker.parent_tracker_id(); 969 std::string title = GetTrackerTitle(tracker); 970 971 DVLOG(1) << " Remove from trackers by parent and title: " 972 << parent_id << " " << title; 973 974 const std::string active_tracker_key = 975 GenerateActiveTrackerIDByParentAndTitleKey(parent_id, title); 976 const std::string key_prefix = 977 GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, title); 978 if (!EraseInTrackerIDSetWithPrefix( 979 active_tracker_key, key_prefix, tracker_id)) 980 return; 981 982 const std::string multi_key = 983 GenerateMultiBackingParentAndTitleKey(parent_id, title); 984 if (!title.empty() && DBHasKey(multi_key) && 985 CountWithPrefix(key_prefix, tracker_id) != MULTIPLE) { 986 DVLOG(1) << " Remove from multi backing file paths: " 987 << parent_id << " " << title; 988 db_->Delete(multi_key); 989 } 990} 991 992void MetadataDatabaseIndexOnDisk::AddToDirtyTrackerIndexes( 993 const FileTracker& new_tracker) { 994 const std::string dirty_key = GenerateDirtyIDKey(new_tracker.tracker_id()); 995 DCHECK(!DBHasKey(dirty_key)); 996 DCHECK(!DBHasKey(GenerateDemotedDirtyIDKey(new_tracker.tracker_id()))); 997 998 if (new_tracker.dirty()) { 999 DVLOG(1) << " Add to dirty tracker IDs: " << new_tracker.tracker_id(); 1000 db_->Put(dirty_key, std::string()); 1001 } 1002} 1003 1004void MetadataDatabaseIndexOnDisk::UpdateInDirtyTrackerIndexes( 1005 const FileTracker& old_tracker, 1006 const FileTracker& new_tracker) { 1007 DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id()); 1008 1009 int64 tracker_id = new_tracker.tracker_id(); 1010 const std::string dirty_key = GenerateDirtyIDKey(tracker_id); 1011 const std::string demoted_key = GenerateDemotedDirtyIDKey(tracker_id); 1012 if (old_tracker.dirty() && !new_tracker.dirty()) { 1013 DCHECK(DBHasKey(dirty_key) || DBHasKey(demoted_key)); 1014 1015 DVLOG(1) << " Remove from dirty trackers IDs: " << tracker_id; 1016 1017 db_->Delete(dirty_key); 1018 db_->Delete(demoted_key); 1019 } else if (!old_tracker.dirty() && new_tracker.dirty()) { 1020 DCHECK(!DBHasKey(dirty_key)); 1021 DCHECK(!DBHasKey(demoted_key)); 1022 1023 DVLOG(1) << " Add to dirty tracker IDs: " << tracker_id; 1024 1025 db_->Put(dirty_key, std::string()); 1026 } 1027} 1028 1029void MetadataDatabaseIndexOnDisk::RemoveFromDirtyTrackerIndexes( 1030 const FileTracker& tracker) { 1031 if (tracker.dirty()) { 1032 int64 tracker_id = tracker.tracker_id(); 1033 const std::string dirty_key = GenerateDirtyIDKey(tracker_id); 1034 const std::string demoted_key = GenerateDemotedDirtyIDKey(tracker_id); 1035 DCHECK(DBHasKey(dirty_key) || DBHasKey(demoted_key)); 1036 1037 DVLOG(1) << " Remove from dirty tracker IDs: " << tracker_id; 1038 db_->Delete(dirty_key); 1039 db_->Delete(demoted_key); 1040 } 1041} 1042 1043TrackerIDSet MetadataDatabaseIndexOnDisk::GetTrackerIDSetByPrefix( 1044 const std::string& active_tracker_key, 1045 const std::string& ids_prefix) const { 1046 TrackerIDSet trackers; 1047 1048 // Seek IDs. 1049 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator()); 1050 for (itr->Seek(ids_prefix); itr->Valid(); itr->Next()) { 1051 const std::string& key(itr->key().ToString()); 1052 std::string id_str; 1053 if (!RemovePrefix(key, ids_prefix, &id_str)) 1054 break; 1055 1056 int64 tracker_id; 1057 if (!base::StringToInt64(id_str, &tracker_id)) 1058 continue; 1059 trackers.InsertInactiveTracker(tracker_id); 1060 } 1061 1062 // Set an active tracker ID, if available. 1063 std::string value; 1064 leveldb::Status status = db_->Get(active_tracker_key, &value); 1065 int64 active_tracker; 1066 if (status.ok() && base::StringToInt64(value, &active_tracker)) { 1067 DCHECK_NE(kInvalidTrackerID, active_tracker); 1068 trackers.Activate(active_tracker); 1069 } 1070 1071 return trackers; 1072} 1073 1074void MetadataDatabaseIndexOnDisk::AddToTrackerIDSetWithPrefix( 1075 const std::string& active_tracker_key, const std::string& key_prefix, 1076 const FileTracker& tracker) { 1077 DCHECK(tracker.tracker_id()); 1078 1079 const std::string id_str = base::Int64ToString(tracker.tracker_id()); 1080 db_->Put(key_prefix + id_str, std::string()); 1081 if (tracker.active()) 1082 db_->Put(active_tracker_key, id_str); 1083} 1084 1085bool MetadataDatabaseIndexOnDisk::EraseInTrackerIDSetWithPrefix( 1086 const std::string& active_tracker_key, const std::string& key_prefix, 1087 int64 tracker_id) { 1088 std::string value; 1089 const std::string del_key = key_prefix + base::Int64ToString(tracker_id); 1090 leveldb::Status status = db_->Get(del_key, &value); 1091 if (status.IsNotFound()) 1092 return false; 1093 1094 db_->Delete(del_key); 1095 1096 status = db_->Get(active_tracker_key, &value); 1097 int64 active_tracker_id; 1098 if (status.ok() && base::StringToInt64(value, &active_tracker_id) && 1099 active_tracker_id == tracker_id) { 1100 db_->Delete(active_tracker_key); 1101 } 1102 1103 return true; 1104} 1105 1106void MetadataDatabaseIndexOnDisk::ActivateInTrackerIDSetWithPrefix( 1107 const std::string& active_tracker_key, const std::string& key_prefix, 1108 int64 tracker_id) { 1109 DCHECK(DBHasKey(key_prefix + base::Int64ToString(tracker_id))); 1110 1111 std::string value; 1112 leveldb::Status status = db_->Get(active_tracker_key, &value); 1113 int64 active_tracker_id = kInvalidTrackerID; 1114 if (status.IsNotFound() || 1115 (status.ok() && base::StringToInt64(value, &active_tracker_id))) { 1116 DCHECK(active_tracker_id != tracker_id); 1117 db_->Put(active_tracker_key, base::Int64ToString(tracker_id)); 1118 } 1119} 1120 1121void MetadataDatabaseIndexOnDisk::DeactivateInTrackerIDSetWithPrefix( 1122 const std::string& active_tracker_key, const std::string& key_prefix, 1123 int64 tracker_id) { 1124 DCHECK(DBHasKey(key_prefix + base::Int64ToString(tracker_id))); 1125 1126 std::string value; 1127 leveldb::Status status = db_->Get(active_tracker_key, &value); 1128 int64 active_tracker_id; 1129 if (status.ok() && base::StringToInt64(value, &active_tracker_id)) { 1130 DCHECK(active_tracker_id == tracker_id); 1131 db_->Delete(active_tracker_key); 1132 } 1133} 1134 1135bool MetadataDatabaseIndexOnDisk::DBHasKey(const std::string& key) { 1136 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator()); 1137 itr->Seek(key); 1138 return itr->Valid() && (itr->key() == key); 1139} 1140 1141MetadataDatabaseIndexOnDisk::NumEntries 1142MetadataDatabaseIndexOnDisk::CountWithPrefix( 1143 const std::string& prefix, int64 ignored_id) { 1144 const std::string ignored = base::Int64ToString(ignored_id); 1145 1146 size_t count = 0; 1147 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator()); 1148 for (itr->Seek(prefix); itr->Valid() && count <= 1; itr->Next()) { 1149 std::string value; 1150 if (!RemovePrefix(itr->key().ToString(), prefix, &value)) 1151 break; 1152 if (value == ignored) 1153 continue; 1154 1155 ++count; 1156 } 1157 1158 if (count >= 2) 1159 return MULTIPLE; 1160 return count == 0 ? NONE : SINGLE; 1161} 1162 1163void MetadataDatabaseIndexOnDisk::DeleteKeyStartsWith( 1164 const std::string& prefix) { 1165 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator()); 1166 for (itr->Seek(prefix); itr->Valid();) { 1167 const std::string key = itr->key().ToString(); 1168 if (!StartsWithASCII(key, prefix, true)) 1169 break; 1170 itr->Delete(); 1171 } 1172} 1173 1174} // namespace drive_backend 1175} // namespace sync_file_system 1176