drive: Delete unused ID entries from an already updated DB

BUG=374648
TEST=unit_tests

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@272495 0039d316-1c4b-4281-b951-d872f2087c98
parent 10d1b26e
......@@ -285,8 +285,34 @@ bool ResourceMetadataStorage::UpgradeOldDB(
UMA_HISTOGRAM_SPARSE_SLOWLY("Drive.MetadataDBVersionBeforeUpgradeCheck",
header.version());
if (header.version() == kDBVersion) { // Nothing to do.
return true;
if (header.version() == kDBVersion) {
// Before r272134, UpgradeOldDB() was not deleting unused ID entries.
// Delete unused ID entries to fix crbug.com/374648.
std::set<std::string> used_ids;
scoped_ptr<leveldb::Iterator> it(
resource_map->NewIterator(leveldb::ReadOptions()));
it->Seek(leveldb::Slice(GetHeaderDBKey()));
it->Next();
for (; it->Valid(); it->Next()) {
if (IsCacheEntryKey(it->key())) {
used_ids.insert(GetIdFromCacheEntryKey(it->key()));
} else if (!IsChildEntryKey(it->key()) && !IsIdEntryKey(it->key())) {
used_ids.insert(it->key().ToString());
}
}
if (!it->status().ok())
return false;
leveldb::WriteBatch batch;
for (it->SeekToFirst(); it->Valid(); it->Next()) {
if (IsIdEntryKey(it->key()) && !used_ids.count(it->value().ToString()))
batch.Delete(it->key());
}
if (!it->status().ok())
return false;
return resource_map->Write(leveldb::WriteOptions(), &batch).ok();
} else if (header.version() < 6) { // Too old, nothing can be done.
return false;
} else if (header.version() < 11) { // Cache entries can be reused.
......
......@@ -402,6 +402,48 @@ TEST_F(ResourceMetadataStorageTest, IncompatibleDB_Unknown) {
EXPECT_EQ(FILE_ERROR_NOT_FOUND, storage_->GetEntry(key1, &entry));
}
TEST_F(ResourceMetadataStorageTest, DeleteUnusedIDEntries) {
leveldb::WriteBatch batch;
// Put an ID entry with a corresponding ResourceEntry.
ResourceEntry entry;
entry.set_local_id("id1");
entry.set_resource_id("resource_id1");
std::string serialized_entry;
EXPECT_TRUE(entry.SerializeToString(&serialized_entry));
batch.Put("id1", serialized_entry);
batch.Put('\0' + std::string("ID") + '\0' + "resource_id1", "id1");
// Put an ID entry with a corresponding FileCacheEntry.
FileCacheEntry cache_entry;
EXPECT_TRUE(cache_entry.SerializeToString(&serialized_entry));
batch.Put(std::string("id2") + '\0' + "CACHE", serialized_entry);
batch.Put('\0' + std::string("ID") + '\0' + "resource_id2", "id2");
// Put an ID entry without any corresponding entries.
batch.Put('\0' + std::string("ID") + '\0' + "resource_id3", "id3");
EXPECT_TRUE(resource_map()->Write(leveldb::WriteOptions(), &batch).ok());
// Upgrade and reopen.
storage_.reset();
EXPECT_TRUE(ResourceMetadataStorage::UpgradeOldDB(
temp_dir_.path(), base::Bind(&util::CanonicalizeResourceId)));
storage_.reset(new ResourceMetadataStorage(
temp_dir_.path(), base::MessageLoopProxy::current().get()));
ASSERT_TRUE(storage_->Initialize());
// Only the unused entry is deleted.
std::string id;
EXPECT_EQ(FILE_ERROR_OK, storage_->GetIdByResourceId("resource_id1", &id));
EXPECT_EQ("id1", id);
EXPECT_EQ(FILE_ERROR_OK, storage_->GetIdByResourceId("resource_id2", &id));
EXPECT_EQ("id2", id);
EXPECT_EQ(FILE_ERROR_NOT_FOUND,
storage_->GetIdByResourceId("resource_id3", &id));
}
TEST_F(ResourceMetadataStorageTest, WrongPath) {
// Create a file.
base::FilePath path;
......
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