summaryrefslogtreecommitdiffstats
path: root/extensions/browser
diff options
context:
space:
mode:
authorcmumford <cmumford@chromium.org>2016-03-15 10:18:23 -0700
committerCommit bot <commit-bot@chromium.org>2016-03-15 17:19:29 +0000
commit215d116d00a9f349bb9cae4d7f16671c8f67e5f8 (patch)
tree31fb4f895916492f2ffca1302b4dd1cec2a66fc3 /extensions/browser
parent7a1f43f10cf484c74382383f3827e74462fb4e2d (diff)
downloadchromium_src-215d116d00a9f349bb9cae4d7f16671c8f67e5f8.zip
chromium_src-215d116d00a9f349bb9cae4d7f16671c8f67e5f8.tar.gz
chromium_src-215d116d00a9f349bb9cae4d7f16671c8f67e5f8.tar.bz2
Extensions: Extract lazy leveldb operations into LazyLevelDb.
Extracted (the bulk of) the leveldb operations from LeveldbValueStore into a new base class: LazyLevelDb. The purpose of this change is to support an upcoming shared database class that also needs to read/write from/to a leveldb, but partitioned for different key spaces. This is part 2 from crrev.com/1658833003 BUG=453946 Review URL: https://codereview.chromium.org/1788893002 Cr-Commit-Position: refs/heads/master@{#381239}
Diffstat (limited to 'extensions/browser')
-rw-r--r--extensions/browser/value_store/lazy_leveldb.cc271
-rw-r--r--extensions/browser/value_store/lazy_leveldb.h89
-rw-r--r--extensions/browser/value_store/leveldb_value_store.cc319
-rw-r--r--extensions/browser/value_store/leveldb_value_store.h49
4 files changed, 384 insertions, 344 deletions
diff --git a/extensions/browser/value_store/lazy_leveldb.cc b/extensions/browser/value_store/lazy_leveldb.cc
new file mode 100644
index 0000000..da37f08
--- /dev/null
+++ b/extensions/browser/value_store/lazy_leveldb.cc
@@ -0,0 +1,271 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/value_store/lazy_leveldb.h"
+
+#include "base/files/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/strings/string_util.h"
+#include "content/public/browser/browser_thread.h"
+#include "third_party/leveldatabase/env_chromium.h"
+#include "third_party/leveldatabase/src/include/leveldb/iterator.h"
+#include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
+
+using base::StringPiece;
+using content::BrowserThread;
+
+namespace {
+
+const char kInvalidJson[] = "Invalid JSON";
+const char kRestoredDuringOpen[] = "Database corruption repaired during open";
+
+// UMA values used when recovering from a corrupted leveldb.
+// Do not change/delete these values as you will break reporting for older
+// copies of Chrome. Only add new values to the end.
+enum LevelDBDatabaseCorruptionRecoveryValue {
+ LEVELDB_DB_RESTORE_DELETE_SUCCESS = 0,
+ LEVELDB_DB_RESTORE_DELETE_FAILURE,
+ LEVELDB_DB_RESTORE_REPAIR_SUCCESS,
+ LEVELDB_DB_RESTORE_MAX
+};
+
+// UMA values used when recovering from a corrupted leveldb.
+// Do not change/delete these values as you will break reporting for older
+// copies of Chrome. Only add new values to the end.
+enum LevelDBValueCorruptionRecoveryValue {
+ LEVELDB_VALUE_RESTORE_DELETE_SUCCESS,
+ LEVELDB_VALUE_RESTORE_DELETE_FAILURE,
+ LEVELDB_VALUE_RESTORE_MAX
+};
+
+ValueStore::StatusCode LevelDbToValueStoreStatusCode(
+ const leveldb::Status& status) {
+ if (status.ok())
+ return ValueStore::OK;
+ if (status.IsCorruption())
+ return ValueStore::CORRUPTION;
+ return ValueStore::OTHER_ERROR;
+}
+
+} // namespace
+
+LazyLevelDb::LazyLevelDb(const std::string& uma_client_name,
+ const base::FilePath& path)
+ : db_path_(path) {
+ open_options_.create_if_missing = true;
+ open_options_.paranoid_checks = true;
+ open_options_.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
+
+ read_options_.verify_checksums = true;
+
+ // Used in lieu of UMA_HISTOGRAM_ENUMERATION because the histogram name is
+ // not a constant.
+ open_histogram_ = base::LinearHistogram::FactoryGet(
+ "Extensions.Database.Open." + uma_client_name, 1,
+ leveldb_env::LEVELDB_STATUS_MAX, leveldb_env::LEVELDB_STATUS_MAX + 1,
+ base::Histogram::kUmaTargetedHistogramFlag);
+ db_restore_histogram_ = base::LinearHistogram::FactoryGet(
+ "Extensions.Database.Database.Restore." + uma_client_name, 1,
+ LEVELDB_DB_RESTORE_MAX, LEVELDB_DB_RESTORE_MAX + 1,
+ base::Histogram::kUmaTargetedHistogramFlag);
+ value_restore_histogram_ = base::LinearHistogram::FactoryGet(
+ "Extensions.Database.Value.Restore." + uma_client_name, 1,
+ LEVELDB_VALUE_RESTORE_MAX, LEVELDB_VALUE_RESTORE_MAX + 1,
+ base::Histogram::kUmaTargetedHistogramFlag);
+}
+
+LazyLevelDb::~LazyLevelDb() {
+ if (db_ && !BrowserThread::CurrentlyOn(BrowserThread::FILE))
+ BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, db_.release());
+}
+
+ValueStore::Status LazyLevelDb::Read(const std::string& key,
+ scoped_ptr<base::Value>* value) {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ DCHECK(value);
+
+ std::string value_as_json;
+ leveldb::Status s = db_->Get(read_options_, key, &value_as_json);
+
+ if (s.IsNotFound()) {
+ // Despite there being no value, it was still a success. Check this first
+ // because ok() is false on IsNotFound.
+ return ValueStore::Status();
+ }
+
+ if (!s.ok())
+ return ToValueStoreError(s);
+
+ scoped_ptr<base::Value> val = base::JSONReader().ReadToValue(value_as_json);
+ if (!val)
+ return ValueStore::Status(ValueStore::CORRUPTION, FixCorruption(&key),
+ kInvalidJson);
+
+ *value = std::move(val);
+ return ValueStore::Status();
+}
+
+leveldb::Status LazyLevelDb::Delete(const std::string& key) {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ DCHECK(db_.get());
+
+ leveldb::WriteBatch batch;
+ batch.Delete(key);
+
+ return db_->Write(leveldb::WriteOptions(), &batch);
+}
+
+ValueStore::BackingStoreRestoreStatus LazyLevelDb::LogRestoreStatus(
+ ValueStore::BackingStoreRestoreStatus restore_status) const {
+ switch (restore_status) {
+ case ValueStore::RESTORE_NONE:
+ NOTREACHED();
+ break;
+ case ValueStore::DB_RESTORE_DELETE_SUCCESS:
+ db_restore_histogram_->Add(LEVELDB_DB_RESTORE_DELETE_SUCCESS);
+ break;
+ case ValueStore::DB_RESTORE_DELETE_FAILURE:
+ db_restore_histogram_->Add(LEVELDB_DB_RESTORE_DELETE_FAILURE);
+ break;
+ case ValueStore::DB_RESTORE_REPAIR_SUCCESS:
+ db_restore_histogram_->Add(LEVELDB_DB_RESTORE_REPAIR_SUCCESS);
+ break;
+ case ValueStore::VALUE_RESTORE_DELETE_SUCCESS:
+ value_restore_histogram_->Add(LEVELDB_VALUE_RESTORE_DELETE_SUCCESS);
+ break;
+ case ValueStore::VALUE_RESTORE_DELETE_FAILURE:
+ value_restore_histogram_->Add(LEVELDB_VALUE_RESTORE_DELETE_FAILURE);
+ break;
+ }
+ return restore_status;
+}
+
+ValueStore::BackingStoreRestoreStatus LazyLevelDb::FixCorruption(
+ const std::string* key) {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ leveldb::Status s;
+ if (key && db_) {
+ s = Delete(*key);
+ // Deleting involves writing to the log, so it's possible to have a
+ // perfectly OK database but still have a delete fail.
+ if (s.ok())
+ return LogRestoreStatus(ValueStore::VALUE_RESTORE_DELETE_SUCCESS);
+ else if (s.IsIOError())
+ return LogRestoreStatus(ValueStore::VALUE_RESTORE_DELETE_FAILURE);
+ // Any other kind of failure triggers a db repair.
+ }
+
+ // Make sure database is closed.
+ db_.reset();
+
+ // First try the less lossy repair.
+ ValueStore::BackingStoreRestoreStatus restore_status =
+ ValueStore::RESTORE_NONE;
+
+ leveldb::Options repair_options;
+ repair_options.create_if_missing = true;
+ repair_options.paranoid_checks = true;
+
+ // RepairDB can drop an unbounded number of leveldb tables (key/value sets).
+ s = leveldb::RepairDB(db_path_.AsUTF8Unsafe(), repair_options);
+
+ leveldb::DB* db = nullptr;
+ if (s.ok()) {
+ restore_status = ValueStore::DB_RESTORE_REPAIR_SUCCESS;
+ s = leveldb::DB::Open(open_options_, db_path_.AsUTF8Unsafe(), &db);
+ }
+
+ if (!s.ok()) {
+ if (DeleteDbFile()) {
+ restore_status = ValueStore::DB_RESTORE_DELETE_SUCCESS;
+ s = leveldb::DB::Open(open_options_, db_path_.AsUTF8Unsafe(), &db);
+ } else {
+ restore_status = ValueStore::DB_RESTORE_DELETE_FAILURE;
+ }
+ }
+
+ if (s.ok())
+ db_.reset(db);
+ else
+ db_unrecoverable_ = true;
+
+ if (s.ok() && key) {
+ s = Delete(*key);
+ if (s.ok()) {
+ restore_status = ValueStore::VALUE_RESTORE_DELETE_SUCCESS;
+ } else if (s.IsIOError()) {
+ restore_status = ValueStore::VALUE_RESTORE_DELETE_FAILURE;
+ } else {
+ db_.reset(db);
+ if (!DeleteDbFile())
+ db_unrecoverable_ = true;
+ restore_status = ValueStore::DB_RESTORE_DELETE_FAILURE;
+ }
+ }
+
+ // Only log for the final and most extreme form of database restoration.
+ LogRestoreStatus(restore_status);
+
+ return restore_status;
+}
+
+ValueStore::Status LazyLevelDb::EnsureDbIsOpen() {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+
+ if (db_)
+ return ValueStore::Status();
+
+ if (db_unrecoverable_) {
+ return ValueStore::Status(ValueStore::CORRUPTION,
+ ValueStore::DB_RESTORE_DELETE_FAILURE,
+ "Database corrupted");
+ }
+
+ leveldb::DB* db = nullptr;
+ leveldb::Status ldb_status =
+ leveldb::DB::Open(open_options_, db_path_.AsUTF8Unsafe(), &db);
+ open_histogram_->Add(leveldb_env::GetLevelDBStatusUMAValue(ldb_status));
+ ValueStore::Status status = ToValueStoreError(ldb_status);
+ if (ldb_status.ok()) {
+ db_.reset(db);
+ } else if (ldb_status.IsCorruption()) {
+ status.restore_status = FixCorruption(nullptr);
+ if (status.restore_status != ValueStore::DB_RESTORE_DELETE_FAILURE) {
+ status.code = ValueStore::OK;
+ status.message = kRestoredDuringOpen;
+ }
+ }
+
+ return status;
+}
+
+ValueStore::Status LazyLevelDb::ToValueStoreError(
+ const leveldb::Status& status) {
+ CHECK(!status.IsNotFound()); // not an error
+
+ std::string message = status.ToString();
+ // The message may contain |db_path_|, which may be considered sensitive
+ // data, and those strings are passed to the extension, so strip it out.
+ base::ReplaceSubstringsAfterOffset(&message, 0u, db_path_.AsUTF8Unsafe(),
+ "...");
+
+ return ValueStore::Status(LevelDbToValueStoreStatusCode(status), message);
+}
+
+bool LazyLevelDb::DeleteDbFile() {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ db_.reset(); // release any lock on the directory
+ if (!base::DeleteFile(db_path_, true /* recursive */)) {
+ LOG(WARNING) << "Failed to delete leveldb database at " << db_path_.value();
+ return false;
+ }
+ return true;
+}
+
+scoped_ptr<leveldb::Iterator> LazyLevelDb::CreateIterator(
+ const leveldb::ReadOptions& read_options) {
+ if (!EnsureDbIsOpen().ok())
+ return nullptr;
+ return make_scoped_ptr(db_->NewIterator(read_options));
+}
diff --git a/extensions/browser/value_store/lazy_leveldb.h b/extensions/browser/value_store/lazy_leveldb.h
new file mode 100644
index 0000000..f435faf
--- /dev/null
+++ b/extensions/browser/value_store/lazy_leveldb.h
@@ -0,0 +1,89 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_BROWSER_VALUE_STORE_LAZY_LEVELDB_H_
+#define EXTENSIONS_BROWSER_VALUE_STORE_LAZY_LEVELDB_H_
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram_base.h"
+#include "extensions/browser/value_store/value_store.h"
+#include "third_party/leveldatabase/src/include/leveldb/db.h"
+
+namespace leveldb {
+class Iterator;
+} // namespace leveldb
+
+class LazyLevelDb {
+ public:
+ // Create a new database iterator. This iterator *must* be deleted before
+ // this database is closed.
+ scoped_ptr<leveldb::Iterator> CreateIterator(
+ const leveldb::ReadOptions& read_options);
+
+ // Convert a leveldb::Status to a ValueStore::Status. Will also sanitize path
+ // to eliminate user data path.
+ ValueStore::Status ToValueStoreError(const leveldb::Status& status);
+
+ protected:
+ LazyLevelDb(const std::string& uma_client_name, const base::FilePath& path);
+ ~LazyLevelDb();
+
+ // Close, if necessary, and delete the database directory.
+ bool DeleteDbFile();
+
+ // Fix the |key| or database. If |key| is not null and the database is open
+ // then the key will be deleted. Otherwise the database will be repaired, and
+ // failing that will be deleted.
+ ValueStore::BackingStoreRestoreStatus FixCorruption(const std::string* key);
+
+ // Delete a value (identified by |key|) from the database.
+ leveldb::Status Delete(const std::string& key);
+
+ // Read a value from the database.
+ ValueStore::Status Read(const std::string& key,
+ scoped_ptr<base::Value>* value);
+
+ // Ensure that the database is open.
+ ValueStore::Status EnsureDbIsOpen();
+
+ const std::string& open_histogram_name() const {
+ return open_histogram_->histogram_name();
+ }
+
+ leveldb::DB* db() { return db_.get(); }
+
+ const leveldb::ReadOptions& read_options() const { return read_options_; }
+
+ const leveldb::WriteOptions& write_options() const { return write_options_; }
+
+ private:
+ ValueStore::BackingStoreRestoreStatus LogRestoreStatus(
+ ValueStore::BackingStoreRestoreStatus restore_status) const;
+
+ // The leveldb to which this class reads/writes.
+ scoped_ptr<leveldb::DB> db_;
+ // The path to the underlying leveldb.
+ const base::FilePath db_path_;
+ // The options to be used when this database is lazily opened.
+ leveldb::Options open_options_;
+ // The options to be used for all database read operations.
+ leveldb::ReadOptions read_options_;
+ // The options to be used for all database write operations.
+ leveldb::WriteOptions write_options_;
+ // Set when this database has tried to repair (and failed) to prevent
+ // unbounded attempts to open a bad/unrecoverable database.
+ bool db_unrecoverable_ = false;
+ // Used for UMA logging.
+ base::HistogramBase* open_histogram_ = nullptr;
+ base::HistogramBase* db_restore_histogram_ = nullptr;
+ base::HistogramBase* value_restore_histogram_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(LazyLevelDb);
+};
+
+#endif // EXTENSIONS_BROWSER_VALUE_STORE_LAZY_LEVELDB_H_
diff --git a/extensions/browser/value_store/leveldb_value_store.cc b/extensions/browser/value_store/leveldb_value_store.cc
index e2529ce..c5a989a 100644
--- a/extensions/browser/value_store/leveldb_value_store.cc
+++ b/extensions/browser/value_store/leveldb_value_store.cc
@@ -31,89 +31,12 @@ namespace {
const char kInvalidJson[] = "Invalid JSON";
const char kCannotSerialize[] = "Cannot serialize value to JSON";
-const char kRestoredDuringOpen[] = "Database corruption repaired during open";
-
-// UMA values used when recovering from a corrupted leveldb.
-// Do not change/delete these values as you will break reporting for older
-// copies of Chrome. Only add new values to the end.
-enum LevelDBDatabaseCorruptionRecoveryValue {
- LEVELDB_DB_RESTORE_DELETE_SUCCESS = 0,
- LEVELDB_DB_RESTORE_DELETE_FAILURE,
- LEVELDB_DB_RESTORE_REPAIR_SUCCESS,
- LEVELDB_DB_RESTORE_MAX
-};
-
-// UMA values used when recovering from a corrupted leveldb.
-// Do not change/delete these values as you will break reporting for older
-// copies of Chrome. Only add new values to the end.
-enum LevelDBValueCorruptionRecoveryValue {
- LEVELDB_VALUE_RESTORE_DELETE_SUCCESS,
- LEVELDB_VALUE_RESTORE_DELETE_FAILURE,
- LEVELDB_VALUE_RESTORE_MAX
-};
-
-// Scoped leveldb snapshot which releases the snapshot on destruction.
-class ScopedSnapshot {
- public:
- explicit ScopedSnapshot(leveldb::DB* db)
- : db_(db), snapshot_(db->GetSnapshot()) {}
-
- ~ScopedSnapshot() {
- db_->ReleaseSnapshot(snapshot_);
- }
-
- const leveldb::Snapshot* get() {
- return snapshot_;
- }
-
- private:
- leveldb::DB* db_;
- const leveldb::Snapshot* snapshot_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedSnapshot);
-};
-
-ValueStore::StatusCode LevelDbToValueStoreStatus(
- const leveldb::Status& status) {
- if (status.ok())
- return ValueStore::OK;
- if (status.IsCorruption())
- return ValueStore::CORRUPTION;
- return ValueStore::OTHER_ERROR;
-}
} // namespace
LeveldbValueStore::LeveldbValueStore(const std::string& uma_client_name,
const base::FilePath& db_path)
- : db_path_(db_path),
- db_unrecoverable_(false),
- open_histogram_(nullptr),
- db_restore_histogram_(nullptr),
- value_restore_histogram_(nullptr) {
- DCHECK_CURRENTLY_ON(BrowserThread::FILE);
-
- open_options_.max_open_files = 0; // Use minimum.
- open_options_.create_if_missing = true;
- open_options_.paranoid_checks = true;
- open_options_.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
-
- read_options_.verify_checksums = true;
-
- // Used in lieu of UMA_HISTOGRAM_ENUMERATION because the histogram name is
- // not a constant.
- open_histogram_ = base::LinearHistogram::FactoryGet(
- "Extensions.Database.Open." + uma_client_name, 1,
- leveldb_env::LEVELDB_STATUS_MAX, leveldb_env::LEVELDB_STATUS_MAX + 1,
- base::Histogram::kUmaTargetedHistogramFlag);
- db_restore_histogram_ = base::LinearHistogram::FactoryGet(
- "Extensions.Database.Database.Restore." + uma_client_name, 1,
- LEVELDB_DB_RESTORE_MAX, LEVELDB_DB_RESTORE_MAX + 1,
- base::Histogram::kUmaTargetedHistogramFlag);
- value_restore_histogram_ = base::LinearHistogram::FactoryGet(
- "Extensions.Database.Value.Restore." + uma_client_name, 1,
- LEVELDB_VALUE_RESTORE_MAX, LEVELDB_VALUE_RESTORE_MAX + 1,
- base::Histogram::kUmaTargetedHistogramFlag);
+ : LazyLevelDb(uma_client_name, db_path) {
base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
this, "LeveldbValueStore", base::ThreadTaskRunnerHandle::Get());
}
@@ -122,12 +45,6 @@ LeveldbValueStore::~LeveldbValueStore() {
DCHECK_CURRENTLY_ON(BrowserThread::FILE);
base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
this);
-
- // Delete the database from disk if it's empty (but only if we managed to
- // open it!). This is safe on destruction, assuming that we have exclusive
- // access to the database.
- if (db_ && IsEmpty())
- DeleteDbFile();
}
size_t LeveldbValueStore::GetBytesInUse(const std::string& key) {
@@ -157,7 +74,7 @@ ValueStore::ReadResult LeveldbValueStore::Get(const std::string& key) {
return MakeReadResult(status);
scoped_ptr<base::Value> setting;
- status.Merge(ReadFromDb(key, &setting));
+ status.Merge(Read(key, &setting));
if (!status.ok())
return MakeReadResult(status);
@@ -179,7 +96,7 @@ ValueStore::ReadResult LeveldbValueStore::Get(
for (const std::string& key : keys) {
scoped_ptr<base::Value> setting;
- status.Merge(ReadFromDb(key, &setting));
+ status.Merge(Read(key, &setting));
if (!status.ok())
return MakeReadResult(status);
if (setting)
@@ -197,14 +114,9 @@ ValueStore::ReadResult LeveldbValueStore::Get() {
return MakeReadResult(status);
base::JSONReader json_reader;
- leveldb::ReadOptions options = leveldb::ReadOptions();
- // All interaction with the db is done on the same thread, so snapshotting
- // isn't strictly necessary. This is just defensive.
scoped_ptr<base::DictionaryValue> settings(new base::DictionaryValue());
- ScopedSnapshot snapshot(db_.get());
- options.snapshot = snapshot.get();
- scoped_ptr<leveldb::Iterator> it(db_->NewIterator(options));
+ scoped_ptr<leveldb::Iterator> it(db()->NewIterator(read_options()));
for (it->SeekToFirst(); it->Valid(); it->Next()) {
std::string key = it->key().ToString();
scoped_ptr<base::Value> value =
@@ -231,8 +143,9 @@ ValueStore::ReadResult LeveldbValueStore::Get() {
return MakeReadResult(std::move(settings), status);
}
-ValueStore::WriteResult LeveldbValueStore::Set(
- WriteOptions options, const std::string& key, const base::Value& value) {
+ValueStore::WriteResult LeveldbValueStore::Set(WriteOptions options,
+ const std::string& key,
+ const base::Value& value) {
DCHECK_CURRENTLY_ON(BrowserThread::FILE);
Status status = EnsureDbIsOpen();
@@ -251,7 +164,8 @@ ValueStore::WriteResult LeveldbValueStore::Set(
}
ValueStore::WriteResult LeveldbValueStore::Set(
- WriteOptions options, const base::DictionaryValue& settings) {
+ WriteOptions options,
+ const base::DictionaryValue& settings) {
DCHECK_CURRENTLY_ON(BrowserThread::FILE);
Status status = EnsureDbIsOpen();
@@ -292,7 +206,7 @@ ValueStore::WriteResult LeveldbValueStore::Remove(
for (const std::string& key : keys) {
scoped_ptr<base::Value> old_value;
- status.Merge(ReadFromDb(key, &old_value));
+ status.Merge(Read(key, &old_value));
if (!status.ok())
return MakeWriteResult(status);
@@ -302,7 +216,7 @@ ValueStore::WriteResult LeveldbValueStore::Remove(
}
}
- leveldb::Status ldb_status = db_->Write(leveldb::WriteOptions(), &batch);
+ leveldb::Status ldb_status = db()->Write(leveldb::WriteOptions(), &batch);
if (!ldb_status.ok() && !ldb_status.IsNotFound()) {
status.Merge(ToValueStoreError(ldb_status));
return MakeWriteResult(status);
@@ -332,6 +246,9 @@ ValueStore::WriteResult LeveldbValueStore::Clear() {
}
bool LeveldbValueStore::WriteToDbForTest(leveldb::WriteBatch* batch) {
+ Status status = EnsureDbIsOpen();
+ if (!status.ok())
+ return false;
return WriteToDb(batch).ok();
}
@@ -341,19 +258,18 @@ bool LeveldbValueStore::OnMemoryDump(
DCHECK_CURRENTLY_ON(BrowserThread::FILE);
// Return true so that the provider is not disabled.
- if (!db_)
+ if (!db())
return true;
std::string value;
uint64_t size;
- bool res = db_->GetProperty("leveldb.approximate-memory-usage", &value);
+ bool res = db()->GetProperty("leveldb.approximate-memory-usage", &value);
DCHECK(res);
res = base::StringToUint64(value, &size);
DCHECK(res);
- auto dump = pmd->CreateAllocatorDump(
- base::StringPrintf("leveldb/value_store/%s/%p",
- open_histogram_->histogram_name().c_str(), this));
+ auto dump = pmd->CreateAllocatorDump(base::StringPrintf(
+ "leveldb/value_store/%s/%p", open_histogram_name().c_str(), this));
dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
base::trace_event::MemoryAllocatorDump::kUnitsBytes, size);
@@ -367,154 +283,6 @@ bool LeveldbValueStore::OnMemoryDump(
return true;
}
-ValueStore::BackingStoreRestoreStatus LeveldbValueStore::LogRestoreStatus(
- BackingStoreRestoreStatus restore_status) {
- switch (restore_status) {
- case RESTORE_NONE:
- NOTREACHED();
- break;
- case DB_RESTORE_DELETE_SUCCESS:
- db_restore_histogram_->Add(LEVELDB_DB_RESTORE_DELETE_SUCCESS);
- break;
- case DB_RESTORE_DELETE_FAILURE:
- db_restore_histogram_->Add(LEVELDB_DB_RESTORE_DELETE_FAILURE);
- break;
- case DB_RESTORE_REPAIR_SUCCESS:
- db_restore_histogram_->Add(LEVELDB_DB_RESTORE_REPAIR_SUCCESS);
- break;
- case VALUE_RESTORE_DELETE_SUCCESS:
- value_restore_histogram_->Add(LEVELDB_VALUE_RESTORE_DELETE_SUCCESS);
- break;
- case VALUE_RESTORE_DELETE_FAILURE:
- value_restore_histogram_->Add(LEVELDB_VALUE_RESTORE_DELETE_FAILURE);
- break;
- }
- return restore_status;
-}
-
-ValueStore::BackingStoreRestoreStatus LeveldbValueStore::FixCorruption(
- const std::string* key) {
- leveldb::Status s;
- if (key && db_) {
- s = Delete(*key);
- // Deleting involves writing to the log, so it's possible to have a
- // perfectly OK database but still have a delete fail.
- if (s.ok())
- return LogRestoreStatus(VALUE_RESTORE_DELETE_SUCCESS);
- else if (s.IsIOError())
- return LogRestoreStatus(VALUE_RESTORE_DELETE_FAILURE);
- // Any other kind of failure triggers a db repair.
- }
-
- // Make sure database is closed.
- db_.reset();
-
- // First try the less lossy repair.
- BackingStoreRestoreStatus restore_status = RESTORE_NONE;
-
- leveldb::Options repair_options;
- repair_options.create_if_missing = true;
- repair_options.paranoid_checks = true;
-
- // RepairDB can drop an unbounded number of leveldb tables (key/value sets).
- s = leveldb::RepairDB(db_path_.AsUTF8Unsafe(), repair_options);
-
- leveldb::DB* db = nullptr;
- if (s.ok()) {
- restore_status = DB_RESTORE_REPAIR_SUCCESS;
- s = leveldb::DB::Open(open_options_, db_path_.AsUTF8Unsafe(), &db);
- }
-
- if (!s.ok()) {
- if (DeleteDbFile()) {
- restore_status = DB_RESTORE_DELETE_SUCCESS;
- s = leveldb::DB::Open(open_options_, db_path_.AsUTF8Unsafe(), &db);
- } else {
- restore_status = DB_RESTORE_DELETE_FAILURE;
- }
- }
-
- if (s.ok())
- db_.reset(db);
- else
- db_unrecoverable_ = true;
-
- if (s.ok() && key) {
- s = Delete(*key);
- if (s.ok()) {
- restore_status = VALUE_RESTORE_DELETE_SUCCESS;
- } else if (s.IsIOError()) {
- restore_status = VALUE_RESTORE_DELETE_FAILURE;
- } else {
- db_.reset(db);
- if (!DeleteDbFile())
- db_unrecoverable_ = true;
- restore_status = DB_RESTORE_DELETE_FAILURE;
- }
- }
-
- // Only log for the final and most extreme form of database restoration.
- LogRestoreStatus(restore_status);
-
- return restore_status;
-}
-
-ValueStore::Status LeveldbValueStore::EnsureDbIsOpen() {
- DCHECK_CURRENTLY_ON(BrowserThread::FILE);
-
- if (db_)
- return Status();
-
- if (db_unrecoverable_) {
- return ValueStore::Status(ValueStore::CORRUPTION,
- ValueStore::DB_RESTORE_DELETE_FAILURE,
- "Database corrupted");
- }
-
- leveldb::DB* db = NULL;
- leveldb::Status ldb_status =
- leveldb::DB::Open(open_options_, db_path_.AsUTF8Unsafe(), &db);
- open_histogram_->Add(leveldb_env::GetLevelDBStatusUMAValue(ldb_status));
- Status status = ToValueStoreError(ldb_status);
- if (ldb_status.ok()) {
- db_.reset(db);
- } else if (ldb_status.IsCorruption()) {
- status.restore_status = FixCorruption(nullptr);
- if (status.restore_status != DB_RESTORE_DELETE_FAILURE) {
- status.code = OK;
- status.message = kRestoredDuringOpen;
- }
- }
-
- return status;
-}
-
-ValueStore::Status LeveldbValueStore::ReadFromDb(
- const std::string& key,
- scoped_ptr<base::Value>* setting) {
- DCHECK_CURRENTLY_ON(BrowserThread::FILE);
- DCHECK(setting);
-
- std::string value_as_json;
- leveldb::Status s = db_->Get(read_options_, key, &value_as_json);
-
- if (s.IsNotFound()) {
- // Despite there being no value, it was still a success. Check this first
- // because ok() is false on IsNotFound.
- return Status();
- }
-
- if (!s.ok())
- return ToValueStoreError(s);
-
- scoped_ptr<base::Value> value = base::JSONReader().ReadToValue(value_as_json);
- if (!value)
- return Status(CORRUPTION, FixCorruption(&key), kInvalidJson);
-
- *setting = std::move(value);
- return Status();
-}
-
ValueStore::Status LeveldbValueStore::AddToBatch(
ValueStore::WriteOptions options,
const std::string& key,
@@ -525,7 +293,7 @@ ValueStore::Status LeveldbValueStore::AddToBatch(
if (!(options & NO_GENERATE_CHANGES)) {
scoped_ptr<base::Value> old_value;
- Status status = ReadFromDb(key, &old_value);
+ Status status = Read(key, &old_value);
if (!status.ok())
return status;
if (!old_value || !old_value->Equals(&value)) {
@@ -547,52 +315,5 @@ ValueStore::Status LeveldbValueStore::AddToBatch(
}
ValueStore::Status LeveldbValueStore::WriteToDb(leveldb::WriteBatch* batch) {
- leveldb::Status status = db_->Write(leveldb::WriteOptions(), batch);
- return ToValueStoreError(status);
-}
-
-bool LeveldbValueStore::IsEmpty() {
- DCHECK_CURRENTLY_ON(BrowserThread::FILE);
- scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions()));
-
- it->SeekToFirst();
- bool is_empty = !it->Valid();
- if (!it->status().ok()) {
- LOG(ERROR) << "Checking DB emptiness failed: " << it->status().ToString();
- return false;
- }
- return is_empty;
-}
-
-leveldb::Status LeveldbValueStore::Delete(const std::string& key) {
- DCHECK_CURRENTLY_ON(BrowserThread::FILE);
- DCHECK(db_.get());
-
- leveldb::WriteBatch batch;
- batch.Delete(key);
-
- return db_->Write(leveldb::WriteOptions(), &batch);
-}
-
-bool LeveldbValueStore::DeleteDbFile() {
- db_.reset(); // release any lock on the directory
- if (!base::DeleteFile(db_path_, true /* recursive */)) {
- LOG(WARNING) << "Failed to delete LeveldbValueStore database at " <<
- db_path_.value();
- return false;
- }
- return true;
-}
-
-ValueStore::Status LeveldbValueStore::ToValueStoreError(
- const leveldb::Status& status) {
- CHECK(!status.IsNotFound()); // not an error
-
- std::string message = status.ToString();
- // The message may contain |db_path_|, which may be considered sensitive
- // data, and those strings are passed to the extension, so strip it out.
- base::ReplaceSubstringsAfterOffset(
- &message, 0u, db_path_.AsUTF8Unsafe(), "...");
-
- return Status(LevelDbToValueStoreStatus(status), message);
+ return ToValueStoreError(db()->Write(write_options(), batch));
}
diff --git a/extensions/browser/value_store/leveldb_value_store.h b/extensions/browser/value_store/leveldb_value_store.h
index 058e282..41e91a3 100644
--- a/extensions/browser/value_store/leveldb_value_store.h
+++ b/extensions/browser/value_store/leveldb_value_store.h
@@ -15,6 +15,7 @@
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "base/trace_event/memory_dump_provider.h"
+#include "extensions/browser/value_store/lazy_leveldb.h"
#include "extensions/browser/value_store/value_store.h"
#include "third_party/leveldatabase/src/include/leveldb/db.h"
@@ -25,6 +26,7 @@ class HistogramBase;
// Value store area, backed by a leveldb database.
// All methods must be run on the FILE thread.
class LeveldbValueStore : public ValueStore,
+ public LazyLevelDb,
public base::trace_event::MemoryDumpProvider {
public:
// Creates a database bound to |path|. The underlying database won't be
@@ -46,10 +48,10 @@ class LeveldbValueStore : public ValueStore,
ReadResult Get(const std::string& key) override;
ReadResult Get(const std::vector<std::string>& keys) override;
ReadResult Get() override;
- WriteResult Set(WriteOptions options,
+ WriteResult Set(ValueStore::WriteOptions options,
const std::string& key,
const base::Value& value) override;
- WriteResult Set(WriteOptions options,
+ WriteResult Set(ValueStore::WriteOptions options,
const base::DictionaryValue& values) override;
WriteResult Remove(const std::string& key) override;
WriteResult Remove(const std::vector<std::string>& keys) override;
@@ -64,23 +66,6 @@ class LeveldbValueStore : public ValueStore,
base::trace_event::ProcessMemoryDump* pmd) override;
private:
- // Fix the |key| or database. If |key| is not null and the database is open
- // then the key will be deleted. Otherwise the database will be repaired, and
- // failing that will be deleted.
- BackingStoreRestoreStatus FixCorruption(const std::string* key);
-
- // Log, to UMA, the status of an attempt to restore a database.
- BackingStoreRestoreStatus LogRestoreStatus(
- BackingStoreRestoreStatus restore_status);
-
- // Tries to open the database if it hasn't been opened already.
- ValueStore::Status EnsureDbIsOpen();
-
- // Reads a setting from the database.
- ValueStore::Status ReadFromDb(const std::string& key,
- // Will be reset() with the result, if any.
- scoped_ptr<base::Value>* setting);
-
// Adds a setting to a WriteBatch, and logs the change in |changes|. For use
// with WriteToDb.
ValueStore::Status AddToBatch(ValueStore::WriteOptions options,
@@ -92,32 +77,6 @@ class LeveldbValueStore : public ValueStore,
// Commits the changes in |batch| to the database.
ValueStore::Status WriteToDb(leveldb::WriteBatch* batch);
- // Converts an error leveldb::Status to a ValueStore::Error.
- ValueStore::Status ToValueStoreError(const leveldb::Status& status);
-
- // Delete a value (identified by |key|) from the value store.
- leveldb::Status Delete(const std::string& key);
-
- // Removes the on-disk database at |db_path_|. Any file system locks should
- // be released before calling this method.
- bool DeleteDbFile();
-
- // Returns whether the database is empty.
- bool IsEmpty();
-
- // The location of the leveldb backend.
- const base::FilePath db_path_;
- leveldb::Options open_options_;
- leveldb::ReadOptions read_options_;
-
- // leveldb backend.
- scoped_ptr<leveldb::DB> db_;
- // Database is corrupt - restoration failed.
- bool db_unrecoverable_;
- base::HistogramBase* open_histogram_;
- base::HistogramBase* db_restore_histogram_;
- base::HistogramBase* value_restore_histogram_;
-
DISALLOW_COPY_AND_ASSIGN(LeveldbValueStore);
};