Chromium Code Reviews| Index: chrome/browser/value_store/leveldb_value_store.cc |
| diff --git a/chrome/browser/value_store/leveldb_value_store.cc b/chrome/browser/value_store/leveldb_value_store.cc |
| index 5d31f8178da8d9706a71bf8ef007fa23e903c89b..9287c37fa072d1717e45dc990291e32ea18e74bc 100644 |
| --- a/chrome/browser/value_store/leveldb_value_store.cc |
| +++ b/chrome/browser/value_store/leveldb_value_store.cc |
| @@ -8,6 +8,7 @@ |
| #include "base/json/json_reader.h" |
| #include "base/json/json_writer.h" |
| #include "base/logging.h" |
| +#include "base/stringprintf.h" |
| #include "base/sys_string_conversions.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "third_party/leveldatabase/src/include/leveldb/iterator.h" |
| @@ -17,8 +18,39 @@ using content::BrowserThread; |
| namespace { |
| -// Generic error message on failure. |
| -const char kGenericOnFailureMessage[] = "Failure accessing database"; |
| +const char* kInvalidJson = "Invalid JSON"; |
| + |
| +ValueStore::ReadResult ReadFailure(const std::string& action, |
| + const std::string& reason) { |
| + CHECK_NE("", reason); |
| + return ValueStore::MakeReadResult(base::StringPrintf( |
| + "Failure to %s: %s", action.c_str(), reason.c_str())); |
| +} |
| + |
| +ValueStore::ReadResult ReadFailureForKey(const std::string& action, |
| + const std::string& key, |
| + const std::string& reason) { |
| + CHECK_NE("", reason); |
| + return ValueStore::MakeReadResult(base::StringPrintf( |
| + "Failure to %s for key %s: %s", |
| + action.c_str(), key.c_str(), reason.c_str())); |
| +} |
| + |
| +ValueStore::WriteResult WriteFailure(const std::string& action, |
| + const std::string& reason) { |
| + CHECK_NE("", reason); |
| + return ValueStore::MakeWriteResult(base::StringPrintf( |
| + "Failure to %s: %s", action.c_str(), reason.c_str())); |
| +} |
| + |
| +ValueStore::WriteResult WriteFailureForKey(const std::string& action, |
| + const std::string& key, |
| + const std::string& reason) { |
| + CHECK_NE("", reason); |
| + return ValueStore::MakeWriteResult(base::StringPrintf( |
| + "Failure to %s for key %s: %s", |
| + action.c_str(), key.c_str(), reason.c_str())); |
| +} |
| // Scoped leveldb snapshot which releases the snapshot on destruction. |
| class ScopedSnapshot { |
| @@ -108,20 +140,22 @@ size_t LeveldbValueStore::GetBytesInUse() { |
| ValueStore::ReadResult LeveldbValueStore::Get( |
| const std::string& key) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + |
| scoped_ptr<Value> setting; |
| - if (!ReadFromDb(leveldb::ReadOptions(), key, &setting)) { |
| - return MakeReadResult(kGenericOnFailureMessage); |
| - } |
| + std::string error = ReadFromDb(leveldb::ReadOptions(), key, &setting); |
| + if (!error.empty()) |
| + return ReadFailureForKey("get", key, error); |
| + |
| DictionaryValue* settings = new DictionaryValue(); |
| - if (setting.get()) { |
| + if (setting.get()) |
| settings->SetWithoutPathExpansion(key, setting.release()); |
| - } |
| return MakeReadResult(settings); |
| } |
| ValueStore::ReadResult LeveldbValueStore::Get( |
| const std::vector<std::string>& keys) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + |
| leveldb::ReadOptions options; |
| scoped_ptr<DictionaryValue> settings(new DictionaryValue()); |
| @@ -132,12 +166,11 @@ ValueStore::ReadResult LeveldbValueStore::Get( |
| for (std::vector<std::string>::const_iterator it = keys.begin(); |
| it != keys.end(); ++it) { |
| scoped_ptr<Value> setting; |
| - if (!ReadFromDb(options, *it, &setting)) { |
| - return MakeReadResult(kGenericOnFailureMessage); |
| - } |
| - if (setting.get()) { |
| + std::string error = ReadFromDb(options, *it, &setting); |
| + if (!error.empty()) |
| + return ReadFailureForKey("get multiple keys", *it, error); |
| + if (setting.get()) |
| settings->SetWithoutPathExpansion(*it, setting.release()); |
| - } |
| } |
| return MakeReadResult(settings.release()); |
| @@ -145,6 +178,7 @@ ValueStore::ReadResult LeveldbValueStore::Get( |
| ValueStore::ReadResult LeveldbValueStore::Get() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + |
| base::JSONReader json_reader; |
| leveldb::ReadOptions options = leveldb::ReadOptions(); |
| // All interaction with the db is done on the same thread, so snapshotting |
| @@ -155,20 +189,23 @@ ValueStore::ReadResult LeveldbValueStore::Get() { |
| options.snapshot = snapshot.get(); |
| scoped_ptr<leveldb::Iterator> it(db_->NewIterator(options)); |
| for (it->SeekToFirst(); it->Valid(); it->Next()) { |
| + std::string key = it->key().ToString(); |
| Value* value = json_reader.ReadToValue(it->value().ToString()); |
| - if (value != NULL) { |
| - settings->SetWithoutPathExpansion(it->key().ToString(), value); |
| - } else { |
| + if (value == NULL) { |
| // TODO(kalman): clear the offending non-JSON value from the database. |
| - LOG(ERROR) << "Invalid JSON: " << it->value().ToString(); |
| + return ReadFailureForKey("get all", key, kInvalidJson); |
| } |
| + settings->SetWithoutPathExpansion(key, value); |
| } |
| - if (!it->status().ok()) { |
| - LOG(ERROR) << "DB iteration failed: " << it->status().ToString(); |
| - return MakeReadResult(kGenericOnFailureMessage); |
| + if (it->status().IsNotFound()) { |
| + NOTREACHED() << "IsNotFound() but iterating over all keys?!"; |
| + return MakeReadResult(settings.release()); |
| } |
| + if (!it->status().ok()) |
| + return ReadFailure("get all", it->status().ToString()); |
| + |
| return MakeReadResult(settings.release()); |
| } |
| @@ -178,8 +215,15 @@ ValueStore::WriteResult LeveldbValueStore::Set( |
| leveldb::WriteBatch batch; |
| scoped_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList()); |
| - AddToBatch(options, key, value, &batch, changes.get()); |
| - return WriteToDb(&batch, changes.Pass()); |
| + std::string read_error = |
| + AddToBatch(options, key, value, &batch, changes.get()); |
| + if (!read_error.empty()) |
| + return WriteFailureForKey("set (read)", key, read_error); |
| + |
| + std::string write_error = WriteToDb(&batch); |
| + return write_error.empty() ? |
| + MakeWriteResult(changes.release()) : |
| + WriteFailureForKey("set (write)", key, write_error); |
| } |
| ValueStore::WriteResult LeveldbValueStore::Set( |
| @@ -190,19 +234,22 @@ ValueStore::WriteResult LeveldbValueStore::Set( |
| scoped_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList()); |
| for (DictionaryValue::Iterator it(settings); it.HasNext(); it.Advance()) { |
| - if (!AddToBatch(options, it.key(), it.value(), &batch, changes.get())) |
| - return MakeWriteResult(kGenericOnFailureMessage); |
| + std::string read_error = |
| + AddToBatch(options, it.key(), it.value(), &batch, changes.get()); |
| + if (!read_error.empty()) |
| + return WriteFailureForKey("set multi (read)", it.key(), read_error); |
|
Matt Perry
2012/09/17 19:21:35
since this is a user-visible error, I would make i
not at google - send to devlin
2012/09/20 00:11:53
Done.
The "read" is because it's the reading part
|
| } |
| - return WriteToDb(&batch, changes.Pass()); |
| + std::string write_error = WriteToDb(&batch); |
| + return write_error.empty() ? |
| + MakeWriteResult(changes.release()) : |
| + WriteFailure("set multi (write)", write_error); |
| } |
| ValueStore::WriteResult LeveldbValueStore::Remove( |
| const std::string& key) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| - std::vector<std::string> keys; |
| - keys.push_back(key); |
| - return Remove(keys); |
| + return Remove(std::vector<std::string>(1, key)); |
| } |
| ValueStore::WriteResult LeveldbValueStore::Remove( |
| @@ -215,24 +262,21 @@ ValueStore::WriteResult LeveldbValueStore::Remove( |
| for (std::vector<std::string>::const_iterator it = keys.begin(); |
| it != keys.end(); ++it) { |
| scoped_ptr<Value> old_value; |
| - if (!ReadFromDb(leveldb::ReadOptions(), *it, &old_value)) { |
| - return MakeWriteResult(kGenericOnFailureMessage); |
| - } |
| + std::string read_error = |
| + ReadFromDb(leveldb::ReadOptions(), *it, &old_value); |
| + if (!read_error.empty()) |
| + return WriteFailureForKey("remove multi (read)", *it, read_error); |
| if (old_value.get()) { |
| - changes->push_back( |
| - ValueStoreChange(*it, old_value.release(), NULL)); |
| + changes->push_back(ValueStoreChange(*it, old_value.release(), NULL)); |
| batch.Delete(*it); |
| } |
| } |
| leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); |
| - if (!status.ok() && !status.IsNotFound()) { |
| - LOG(WARNING) << "DB batch delete failed: " << status.ToString(); |
| - return MakeWriteResult(kGenericOnFailureMessage); |
| - } |
| - |
| - return MakeWriteResult(changes.release()); |
| + return (status.ok() || status.IsNotFound()) ? |
| + MakeWriteResult(changes.release()) : |
| + WriteFailure("remove multi (write)", status.ToString()); |
| } |
| ValueStore::WriteResult LeveldbValueStore::Clear() { |
| @@ -251,29 +295,30 @@ ValueStore::WriteResult LeveldbValueStore::Clear() { |
| const std::string key = it->key().ToString(); |
| const std::string old_value_json = it->value().ToString(); |
| Value* old_value = base::JSONReader().ReadToValue(old_value_json); |
| - if (old_value) { |
| - changes->push_back(ValueStoreChange(key, old_value, NULL)); |
| - } else { |
| - LOG(ERROR) << "Invalid JSON in database: " << old_value_json; |
| + if (!old_value) { |
| + // TODO: delete the bad JSON. |
| + return WriteFailureForKey("clear (read)", key, kInvalidJson); |
| } |
| + changes->push_back(ValueStoreChange(key, old_value, NULL)); |
| batch.Delete(key); |
| } |
| - if (!it->status().ok()) { |
| - LOG(WARNING) << "Clear iteration failed: " << it->status().ToString(); |
| - return MakeWriteResult(kGenericOnFailureMessage); |
| - } |
| + if (it->status().IsNotFound()) |
| + NOTREACHED() << "IsNotFound() but clearing?!"; |
| + else if (!it->status().ok()) |
| + return WriteFailure("clear (read)", it->status().ToString()); |
| leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); |
| - if (!status.ok() && !status.IsNotFound()) { |
| - LOG(WARNING) << "Clear failed: " << status.ToString(); |
| - return MakeWriteResult(kGenericOnFailureMessage); |
| + if (status.IsNotFound()) { |
| + NOTREACHED() << "IsNotFound() but clearing?!"; |
| + return MakeWriteResult(changes.release()); |
| } |
| - |
| - return MakeWriteResult(changes.release()); |
| + return status.ok() ? |
| + MakeWriteResult(changes.release()) : |
| + WriteFailure("clear", status.ToString()); |
| } |
| -bool LeveldbValueStore::ReadFromDb( |
| +std::string LeveldbValueStore::ReadFromDb( |
| leveldb::ReadOptions options, |
| const std::string& key, |
| scoped_ptr<Value>* setting) { |
| @@ -284,26 +329,24 @@ bool LeveldbValueStore::ReadFromDb( |
| if (s.IsNotFound()) { |
| // Despite there being no value, it was still a success. |
| - return true; |
| + // Check this first because ok() is false on IsNotFound. |
| + return ""; |
| } |
| - if (!s.ok()) { |
| - LOG(ERROR) << "Error reading from database: " << s.ToString(); |
| - return false; |
| - } |
| + if (!s.ok()) |
| + return s.ToString(); |
| Value* value = base::JSONReader().ReadToValue(value_as_json); |
| if (value == NULL) { |
| // TODO(kalman): clear the offending non-JSON value from the database. |
| - LOG(ERROR) << "Invalid JSON in database: " << value_as_json; |
| - return false; |
| + return kInvalidJson; |
| } |
| setting->reset(value); |
| - return true; |
| + return ""; |
| } |
| -bool LeveldbValueStore::AddToBatch( |
| +std::string LeveldbValueStore::AddToBatch( |
| ValueStore::WriteOptions options, |
| const std::string& key, |
| const base::Value& value, |
| @@ -311,8 +354,9 @@ bool LeveldbValueStore::AddToBatch( |
| ValueStoreChangeList* changes) { |
| scoped_ptr<Value> old_value; |
| if (!(options & NO_CHECK_OLD_VALUE)) { |
| - if (!ReadFromDb(leveldb::ReadOptions(), key, &old_value)) |
| - return false; |
| + std::string error = ReadFromDb(leveldb::ReadOptions(), key, &old_value); |
| + if (!error.empty()) |
| + return error; |
| } |
| if (!old_value.get() || !old_value->Equals(&value)) { |
| @@ -325,19 +369,16 @@ bool LeveldbValueStore::AddToBatch( |
| batch->Put(key, value_as_json); |
| } |
| - return true; |
| + return ""; |
| } |
| -ValueStore::WriteResult LeveldbValueStore::WriteToDb( |
| - leveldb::WriteBatch* batch, |
| - scoped_ptr<ValueStoreChangeList> changes) { |
| +std::string LeveldbValueStore::WriteToDb(leveldb::WriteBatch* batch) { |
| leveldb::Status status = db_->Write(leveldb::WriteOptions(), batch); |
| - if (!status.ok()) { |
| - LOG(WARNING) << "DB batch write failed: " << status.ToString(); |
| - return MakeWriteResult(kGenericOnFailureMessage); |
| + if (status.IsNotFound()) { |
| + NOTREACHED() << "IsNotFound() but writing?!"; |
|
not at google - send to devlin
2012/09/17 22:17:36
Here (and a few other places checking IsNotFound)
|
| + return ""; |
| } |
| - |
| - return MakeWriteResult(changes.release()); |
| + return status.ok() ? "" : status.ToString(); |
| } |
| bool LeveldbValueStore::IsEmpty() { |