Commit db0df336 authored by peria@chromium.org's avatar peria@chromium.org

[SyncFS] Implement RemoveUnreachableItems for MetadataDatabaseIndexOnDisk


BUG=347425
TEST=./unit_tests --gtest_filter="Metadata*"

Review URL: https://codereview.chromium.org/423433006

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@287715 0039d316-1c4b-4281-b951-d872f2087c98
parent 71178808
...@@ -140,6 +140,110 @@ std::string GenerateDemotedDirtyIDKey(int64 tracker_id) { ...@@ -140,6 +140,110 @@ std::string GenerateDemotedDirtyIDKey(int64 tracker_id) {
return kDemotedDirtyIDKeyPrefix + base::Int64ToString(tracker_id); return kDemotedDirtyIDKeyPrefix + base::Int64ToString(tracker_id);
} }
void RemoveUnreachableItems(LevelDBWrapper* db) {
DCHECK(db);
typedef std::map<int64, std::set<int64> > ChildTrackersByParent;
ChildTrackersByParent trackers_by_parent;
{
// Set up links from parent tracker to child trackers.
std::set<int64> inactive_trackers;
scoped_ptr<LevelDBWrapper::Iterator> itr = db->NewIterator();
for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) {
if (!RemovePrefix(itr->key().ToString(), kFileTrackerKeyPrefix, NULL))
break;
scoped_ptr<FileTracker> tracker(new FileTracker);
if (!tracker->ParseFromString(itr->value().ToString())) {
util::Log(logging::LOG_WARNING, FROM_HERE,
"Failed to parse a Tracker");
continue;
}
int64 parent_tracker_id = tracker->parent_tracker_id();
int64 tracker_id = tracker->tracker_id();
trackers_by_parent[parent_tracker_id].insert(tracker_id);
if (!tracker->active())
inactive_trackers.insert(tracker_id);
}
// Drop links from inactive trackers.
for (std::set<int64>::iterator iter = inactive_trackers.begin();
iter != inactive_trackers.end(); ++iter) {
trackers_by_parent.erase(*iter);
}
}
// Traverse tracker tree from sync-root.
std::set<int64> visited_trackers;
{
scoped_ptr<ServiceMetadata> service_metadata =
InitializeServiceMetadata(db);
int64 sync_root_tracker_id = service_metadata->sync_root_tracker_id();
std::vector<int64> pending;
if (sync_root_tracker_id != kInvalidTrackerID)
pending.push_back(sync_root_tracker_id);
while (!pending.empty()) {
int64 tracker_id = pending.back();
DCHECK_NE(kInvalidTrackerID, tracker_id);
pending.pop_back();
if (!visited_trackers.insert(tracker_id).second) {
NOTREACHED();
continue;
}
AppendContents(
LookUpMap(trackers_by_parent, tracker_id, std::set<int64>()),
&pending);
}
}
// Delete all unreachable trackers, and list all |file_id| referred by
// remained trackers.
base::hash_set<std::string> referred_file_ids;
{
scoped_ptr<LevelDBWrapper::Iterator> itr = db->NewIterator();
for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) {
if (!RemovePrefix(itr->key().ToString(), kFileTrackerKeyPrefix, NULL))
break;
scoped_ptr<FileTracker> tracker(new FileTracker);
if (!tracker->ParseFromString(itr->value().ToString())) {
util::Log(logging::LOG_WARNING, FROM_HERE,
"Failed to parse a Tracker");
continue;
}
if (ContainsKey(visited_trackers, tracker->tracker_id())) {
referred_file_ids.insert(tracker->file_id());
} else {
PutFileTrackerDeletionToDB(tracker->tracker_id(), db);
}
}
}
// Delete all unreferred metadata.
{
scoped_ptr<LevelDBWrapper::Iterator> itr = db->NewIterator();
for (itr->Seek(kFileMetadataKeyPrefix); itr->Valid(); itr->Next()) {
if (!RemovePrefix(itr->key().ToString(), kFileMetadataKeyPrefix, NULL))
break;
scoped_ptr<FileMetadata> metadata(new FileMetadata);
if (!metadata->ParseFromString(itr->value().ToString())) {
util::Log(logging::LOG_WARNING, FROM_HERE,
"Failed to parse a Tracker");
continue;
}
if (!ContainsKey(referred_file_ids, metadata->file_id()))
PutFileMetadataDeletionToDB(metadata->file_id(), db);
}
}
}
} // namespace } // namespace
// static // static
...@@ -148,6 +252,9 @@ MetadataDatabaseIndexOnDisk::Create(LevelDBWrapper* db) { ...@@ -148,6 +252,9 @@ MetadataDatabaseIndexOnDisk::Create(LevelDBWrapper* db) {
DCHECK(db); DCHECK(db);
PutVersionToDB(kDatabaseOnDiskVersion, db); PutVersionToDB(kDatabaseOnDiskVersion, db);
// TODO(peria): It is not good to call RemoveUnreachableItems on every
// creation.
RemoveUnreachableItems(db);
scoped_ptr<MetadataDatabaseIndexOnDisk> scoped_ptr<MetadataDatabaseIndexOnDisk>
index(new MetadataDatabaseIndexOnDisk(db)); index(new MetadataDatabaseIndexOnDisk(db));
return index.Pass(); return index.Pass();
...@@ -573,6 +680,10 @@ void MetadataDatabaseIndexOnDisk::BuildTrackerIndexes() { ...@@ -573,6 +680,10 @@ void MetadataDatabaseIndexOnDisk::BuildTrackerIndexes() {
} }
} }
LevelDBWrapper* MetadataDatabaseIndexOnDisk::GetDBForTesting() {
return db_;
}
MetadataDatabaseIndexOnDisk::MetadataDatabaseIndexOnDisk(LevelDBWrapper* db) MetadataDatabaseIndexOnDisk::MetadataDatabaseIndexOnDisk(LevelDBWrapper* db)
: db_(db) { : db_(db) {
// TODO(peria): Add UMA to measure the number of FileMetadata, FileTracker, // TODO(peria): Add UMA to measure the number of FileMetadata, FileTracker,
......
...@@ -70,6 +70,8 @@ class MetadataDatabaseIndexOnDisk : public MetadataDatabaseIndexInterface { ...@@ -70,6 +70,8 @@ class MetadataDatabaseIndexOnDisk : public MetadataDatabaseIndexInterface {
// Builds on-disk indexes from FileTracker entries on disk. // Builds on-disk indexes from FileTracker entries on disk.
void BuildTrackerIndexes(); void BuildTrackerIndexes();
LevelDBWrapper* GetDBForTesting();
private: private:
enum NumEntries { enum NumEntries {
NONE, // No entries are found. NONE, // No entries are found.
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "chrome/browser/sync_file_system/drive_backend/metadata_database_index_on_disk.h" #include "chrome/browser/sync_file_system/drive_backend/metadata_database_index_on_disk.h"
#include "base/files/scoped_temp_dir.h" #include "base/files/scoped_temp_dir.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h" #include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
#include "chrome/browser/sync_file_system/drive_backend/drive_backend_test_util.h" #include "chrome/browser/sync_file_system/drive_backend/drive_backend_test_util.h"
#include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h" #include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h"
...@@ -35,7 +36,7 @@ class MetadataDatabaseIndexOnDiskTest : public testing::Test { ...@@ -35,7 +36,7 @@ class MetadataDatabaseIndexOnDiskTest : public testing::Test {
virtual void SetUp() OVERRIDE { virtual void SetUp() OVERRIDE {
ASSERT_TRUE(database_dir_.CreateUniqueTempDir()); ASSERT_TRUE(database_dir_.CreateUniqueTempDir());
in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default())); in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
InitializeLevelDB(); db_ = InitializeLevelDB();
index_ = MetadataDatabaseIndexOnDisk::Create(db_.get()); index_ = MetadataDatabaseIndexOnDisk::Create(db_.get());
} }
...@@ -45,7 +46,13 @@ class MetadataDatabaseIndexOnDiskTest : public testing::Test { ...@@ -45,7 +46,13 @@ class MetadataDatabaseIndexOnDiskTest : public testing::Test {
in_memory_env_.reset(); in_memory_env_.reset();
} }
void CreateTestDatabase(bool build_index) { void CreateTestDatabase(bool build_index, LevelDBWrapper* db) {
if (!db) {
DCHECK(index());
db = index()->GetDBForTesting();
}
DCHECK(db);
scoped_ptr<FileMetadata> sync_root_metadata = scoped_ptr<FileMetadata> sync_root_metadata =
test_util::CreateFolderMetadata("sync_root_folder_id", test_util::CreateFolderMetadata("sync_root_folder_id",
"Chrome Syncable FileSystem"); "Chrome Syncable FileSystem");
...@@ -72,8 +79,14 @@ class MetadataDatabaseIndexOnDiskTest : public testing::Test { ...@@ -72,8 +79,14 @@ class MetadataDatabaseIndexOnDiskTest : public testing::Test {
kPlaceholderTrackerID, kPlaceholderTrackerID,
app_root_tracker.get()); app_root_tracker.get());
scoped_ptr<ServiceMetadata> service_metadata =
InitializeServiceMetadata(db);
service_metadata->set_sync_root_tracker_id(kSyncRootTrackerID);
PutServiceMetadataToDB(*service_metadata, db);
if (build_index) { if (build_index) {
DCHECK(index()); DCHECK(index());
index()->StoreFileMetadata(sync_root_metadata.Pass()); index()->StoreFileMetadata(sync_root_metadata.Pass());
index()->StoreFileTracker(sync_root_tracker.Pass()); index()->StoreFileTracker(sync_root_tracker.Pass());
index()->StoreFileMetadata(app_root_metadata.Pass()); index()->StoreFileMetadata(app_root_metadata.Pass());
...@@ -82,25 +95,25 @@ class MetadataDatabaseIndexOnDiskTest : public testing::Test { ...@@ -82,25 +95,25 @@ class MetadataDatabaseIndexOnDiskTest : public testing::Test {
index()->StoreFileTracker(file_tracker.Pass()); index()->StoreFileTracker(file_tracker.Pass());
index()->StoreFileTracker(placeholder_tracker.Pass()); index()->StoreFileTracker(placeholder_tracker.Pass());
} else { } else {
PutFileMetadataToDB(*sync_root_metadata, db_.get()); PutFileMetadataToDB(*sync_root_metadata, db);
PutFileTrackerToDB(*sync_root_tracker, db_.get()); PutFileTrackerToDB(*sync_root_tracker, db);
PutFileMetadataToDB(*app_root_metadata, db_.get()); PutFileMetadataToDB(*app_root_metadata, db);
PutFileTrackerToDB(*app_root_tracker, db_.get()); PutFileTrackerToDB(*app_root_tracker, db);
PutFileMetadataToDB(*file_metadata, db_.get()); PutFileMetadataToDB(*file_metadata, db);
PutFileTrackerToDB(*file_tracker, db_.get()); PutFileTrackerToDB(*file_tracker, db);
PutFileTrackerToDB(*placeholder_tracker, db_.get()); PutFileTrackerToDB(*placeholder_tracker, db);
} }
ASSERT_TRUE(db_->Commit().ok()); ASSERT_TRUE(db->Commit().ok());
} }
MetadataDatabaseIndexOnDisk* index() { return index_.get(); } MetadataDatabaseIndexOnDisk* index() { return index_.get(); }
void WriteToDB() { void WriteToDB() {
ASSERT_TRUE(db_->Commit().ok()); ASSERT_TRUE(db_->Commit().ok());
} }
private: scoped_ptr<LevelDBWrapper> InitializeLevelDB() {
void InitializeLevelDB() {
leveldb::DB* db = NULL; leveldb::DB* db = NULL;
leveldb::Options options; leveldb::Options options;
options.create_if_missing = true; options.create_if_missing = true;
...@@ -108,10 +121,11 @@ class MetadataDatabaseIndexOnDiskTest : public testing::Test { ...@@ -108,10 +121,11 @@ class MetadataDatabaseIndexOnDiskTest : public testing::Test {
options.env = in_memory_env_.get(); options.env = in_memory_env_.get();
leveldb::Status status = leveldb::Status status =
leveldb::DB::Open(options, database_dir_.path().AsUTF8Unsafe(), &db); leveldb::DB::Open(options, database_dir_.path().AsUTF8Unsafe(), &db);
ASSERT_TRUE(status.ok()); EXPECT_TRUE(status.ok());
db_.reset(new LevelDBWrapper(make_scoped_ptr(db))); return make_scoped_ptr(new LevelDBWrapper(make_scoped_ptr(db)));
} }
private:
scoped_ptr<MetadataDatabaseIndexOnDisk> index_; scoped_ptr<MetadataDatabaseIndexOnDisk> index_;
base::ScopedTempDir database_dir_; base::ScopedTempDir database_dir_;
...@@ -120,7 +134,7 @@ class MetadataDatabaseIndexOnDiskTest : public testing::Test { ...@@ -120,7 +134,7 @@ class MetadataDatabaseIndexOnDiskTest : public testing::Test {
}; };
TEST_F(MetadataDatabaseIndexOnDiskTest, GetEntryTest) { TEST_F(MetadataDatabaseIndexOnDiskTest, GetEntryTest) {
CreateTestDatabase(false); CreateTestDatabase(false, NULL);
FileTracker tracker; FileTracker tracker;
EXPECT_FALSE(index()->GetFileTracker(kInvalidTrackerID, NULL)); EXPECT_FALSE(index()->GetFileTracker(kInvalidTrackerID, NULL));
...@@ -135,8 +149,7 @@ TEST_F(MetadataDatabaseIndexOnDiskTest, GetEntryTest) { ...@@ -135,8 +149,7 @@ TEST_F(MetadataDatabaseIndexOnDiskTest, GetEntryTest) {
} }
TEST_F(MetadataDatabaseIndexOnDiskTest, SetEntryTest) { TEST_F(MetadataDatabaseIndexOnDiskTest, SetEntryTest) {
// This test does not check updates of indexes. CreateTestDatabase(false, NULL);
CreateTestDatabase(false);
const int64 tracker_id = 10; const int64 tracker_id = 10;
scoped_ptr<FileMetadata> metadata = scoped_ptr<FileMetadata> metadata =
...@@ -176,8 +189,43 @@ TEST_F(MetadataDatabaseIndexOnDiskTest, SetEntryTest) { ...@@ -176,8 +189,43 @@ TEST_F(MetadataDatabaseIndexOnDiskTest, SetEntryTest) {
EXPECT_FALSE(index()->GetFileTracker(tracker_id, NULL)); EXPECT_FALSE(index()->GetFileTracker(tracker_id, NULL));
} }
TEST_F(MetadataDatabaseIndexOnDiskTest, RemoveUnreachableItemsTest) {
scoped_ptr<LevelDBWrapper> db = InitializeLevelDB();
CreateTestDatabase(false, db.get());
const int kOrphanedFileTrackerID = 13;
scoped_ptr<FileMetadata> orphaned_metadata =
test_util::CreateFileMetadata("orphaned_id", "orphaned", "md5");
scoped_ptr<FileTracker> orphaned_tracker =
test_util::CreateTracker(*orphaned_metadata,
kOrphanedFileTrackerID,
NULL);
PutFileMetadataToDB(*orphaned_metadata, db.get());
PutFileTrackerToDB(*orphaned_tracker, db.get());
EXPECT_TRUE(db->Commit().ok());
const std::string key =
kFileTrackerKeyPrefix + base::Int64ToString(kOrphanedFileTrackerID);
std::string value;
EXPECT_TRUE(db->Get(key, &value).ok());
// RemoveUnreachableItems() is expected to run on index creation.
scoped_ptr<MetadataDatabaseIndexOnDisk> index_on_disk =
MetadataDatabaseIndexOnDisk::Create(db.get());
EXPECT_TRUE(db->Commit().ok());
EXPECT_TRUE(db->Get(key, &value).IsNotFound());
EXPECT_FALSE(index_on_disk->GetFileTracker(kOrphanedFileTrackerID, NULL));
EXPECT_TRUE(index_on_disk->GetFileTracker(kSyncRootTrackerID, NULL));
EXPECT_TRUE(index_on_disk->GetFileTracker(kAppRootTrackerID, NULL));
EXPECT_TRUE(index_on_disk->GetFileTracker(kFileTrackerID, NULL));
}
TEST_F(MetadataDatabaseIndexOnDiskTest, BuildIndexTest) { TEST_F(MetadataDatabaseIndexOnDiskTest, BuildIndexTest) {
CreateTestDatabase(false); CreateTestDatabase(false, NULL);
TrackerIDSet tracker_ids; TrackerIDSet tracker_ids;
// Before building indexes, no references exist. // Before building indexes, no references exist.
...@@ -205,7 +253,7 @@ TEST_F(MetadataDatabaseIndexOnDiskTest, BuildIndexTest) { ...@@ -205,7 +253,7 @@ TEST_F(MetadataDatabaseIndexOnDiskTest, BuildIndexTest) {
} }
TEST_F(MetadataDatabaseIndexOnDiskTest, AllEntriesTest) { TEST_F(MetadataDatabaseIndexOnDiskTest, AllEntriesTest) {
CreateTestDatabase(true); CreateTestDatabase(true, NULL);
EXPECT_EQ(3U, index()->CountFileMetadata()); EXPECT_EQ(3U, index()->CountFileMetadata());
std::vector<std::string> file_ids(index()->GetAllMetadataIDs()); std::vector<std::string> file_ids(index()->GetAllMetadataIDs());
...@@ -226,7 +274,7 @@ TEST_F(MetadataDatabaseIndexOnDiskTest, AllEntriesTest) { ...@@ -226,7 +274,7 @@ TEST_F(MetadataDatabaseIndexOnDiskTest, AllEntriesTest) {
} }
TEST_F(MetadataDatabaseIndexOnDiskTest, IndexAppRootIDByAppIDTest) { TEST_F(MetadataDatabaseIndexOnDiskTest, IndexAppRootIDByAppIDTest) {
CreateTestDatabase(true); CreateTestDatabase(true, NULL);
std::vector<std::string> app_ids = index()->GetRegisteredAppIDs(); std::vector<std::string> app_ids = index()->GetRegisteredAppIDs();
ASSERT_EQ(1U, app_ids.size()); ASSERT_EQ(1U, app_ids.size());
...@@ -286,7 +334,7 @@ TEST_F(MetadataDatabaseIndexOnDiskTest, IndexAppRootIDByAppIDTest) { ...@@ -286,7 +334,7 @@ TEST_F(MetadataDatabaseIndexOnDiskTest, IndexAppRootIDByAppIDTest) {
} }
TEST_F(MetadataDatabaseIndexOnDiskTest, TrackerIDSetByFileIDTest) { TEST_F(MetadataDatabaseIndexOnDiskTest, TrackerIDSetByFileIDTest) {
CreateTestDatabase(true); CreateTestDatabase(true, NULL);
FileTracker app_root_tracker; FileTracker app_root_tracker;
EXPECT_TRUE(index()->GetFileTracker(kAppRootTrackerID, &app_root_tracker)); EXPECT_TRUE(index()->GetFileTracker(kAppRootTrackerID, &app_root_tracker));
...@@ -350,7 +398,7 @@ TEST_F(MetadataDatabaseIndexOnDiskTest, TrackerIDSetByFileIDTest) { ...@@ -350,7 +398,7 @@ TEST_F(MetadataDatabaseIndexOnDiskTest, TrackerIDSetByFileIDTest) {
} }
TEST_F(MetadataDatabaseIndexOnDiskTest, TrackerIDSetByParentIDAndTitleTest) { TEST_F(MetadataDatabaseIndexOnDiskTest, TrackerIDSetByParentIDAndTitleTest) {
CreateTestDatabase(true); CreateTestDatabase(true, NULL);
FileTracker app_root_tracker; FileTracker app_root_tracker;
EXPECT_TRUE(index()->GetFileTracker(kAppRootTrackerID, &app_root_tracker)); EXPECT_TRUE(index()->GetFileTracker(kAppRootTrackerID, &app_root_tracker));
...@@ -427,7 +475,7 @@ TEST_F(MetadataDatabaseIndexOnDiskTest, TrackerIDSetByParentIDAndTitleTest) { ...@@ -427,7 +475,7 @@ TEST_F(MetadataDatabaseIndexOnDiskTest, TrackerIDSetByParentIDAndTitleTest) {
} }
TEST_F(MetadataDatabaseIndexOnDiskTest, DirtyTrackersTest) { TEST_F(MetadataDatabaseIndexOnDiskTest, DirtyTrackersTest) {
CreateTestDatabase(true); CreateTestDatabase(true, NULL);
// Testing public methods // Testing public methods
EXPECT_EQ(1U, index()->CountDirtyTracker()); EXPECT_EQ(1U, index()->CountDirtyTracker());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment