blob: 043ed02b8964f96ee8d6e759a907a05228fe9fa3 [file] [log] [blame]
// Copyright 2015 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 "components/data_reduction_proxy/core/browser/data_store_impl.h"
#include <stdint.h>
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/metrics/histogram_macros.h"
#include "components/data_reduction_proxy/proto/data_store.pb.h"
#include "third_party/leveldatabase/env_chromium.h"
#include "third_party/leveldatabase/leveldb_chrome.h"
#include "third_party/leveldatabase/src/include/leveldb/db.h"
#include "third_party/leveldatabase/src/include/leveldb/options.h"
#include "third_party/leveldatabase/src/include/leveldb/status.h"
#include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
namespace {
const base::FilePath::CharType kDBName[] =
FILE_PATH_LITERAL("data_reduction_proxy_leveldb");
data_reduction_proxy::DataStore::Status LevelDbToDRPStoreStatus(
leveldb::Status leveldb_status) {
if (leveldb_status.ok())
return data_reduction_proxy::DataStore::Status::OK;
if (leveldb_status.IsNotFound())
return data_reduction_proxy::DataStore::Status::NOT_FOUND;
if (leveldb_status.IsCorruption())
return data_reduction_proxy::DataStore::Status::CORRUPTED;
if (leveldb_status.IsIOError())
return data_reduction_proxy::DataStore::Status::IO_ERROR;
return data_reduction_proxy::DataStore::Status::MISC_ERROR;
}
} // namespace
namespace data_reduction_proxy {
DataStoreImpl::DataStoreImpl(const base::FilePath& profile_path)
: profile_path_(profile_path) {
sequence_checker_.DetachFromSequence();
}
DataStoreImpl::~DataStoreImpl() {
DCHECK(sequence_checker_.CalledOnValidSequence());
}
void DataStoreImpl::InitializeOnDBThread() {
DCHECK(sequence_checker_.CalledOnValidSequence());
DCHECK(!db_);
DataStore::Status status = OpenDB();
if (status == CORRUPTED)
RecreateDB();
}
DataStore::Status DataStoreImpl::Get(base::StringPiece key,
std::string* value) {
DCHECK(sequence_checker_.CalledOnValidSequence());
if (!db_)
return MISC_ERROR;
leveldb::ReadOptions read_options;
read_options.verify_checksums = true;
leveldb::Slice slice(key.data(), key.size());
leveldb::Status status = db_->Get(read_options, slice, value);
if (status.IsCorruption())
RecreateDB();
return LevelDbToDRPStoreStatus(status);
}
DataStore::Status DataStoreImpl::Put(
const std::map<std::string, std::string>& map) {
DCHECK(sequence_checker_.CalledOnValidSequence());
if (!db_)
return MISC_ERROR;
leveldb::WriteBatch batch;
for (const auto& iter : map) {
batch.Put(iter.first, iter.second);
}
leveldb::WriteOptions write_options;
leveldb::Status status = db_->Write(write_options, &batch);
if (status.IsCorruption())
RecreateDB();
return LevelDbToDRPStoreStatus(status);
}
DataStore::Status DataStoreImpl::Delete(base::StringPiece key) {
DCHECK(sequence_checker_.CalledOnValidSequence());
if (!db_)
return MISC_ERROR;
leveldb::Slice slice(key.data(), key.size());
leveldb::WriteOptions write_options;
leveldb::Status status = db_->Delete(write_options, slice);
if (status.IsCorruption())
RecreateDB();
return LevelDbToDRPStoreStatus(status);
}
DataStore::Status DataStoreImpl::OpenDB() {
DCHECK(sequence_checker_.CalledOnValidSequence());
leveldb_env::Options options;
options.create_if_missing = true;
options.paranoid_checks = true;
// Deletes to buckets not found are stored in the log. Use a new log so that
// these log entries are deleted.
options.reuse_logs = false;
std::string db_name = profile_path_.Append(kDBName).AsUTF8Unsafe();
db_.reset();
Status status =
LevelDbToDRPStoreStatus(leveldb_env::OpenDB(options, db_name, &db_));
UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.LevelDBOpenStatus", status,
STATUS_MAX);
if (status != OK)
LOG(ERROR) << "Failed to open Data Reduction Proxy DB: " << status;
if (db_) {
leveldb::Range range;
uint64_t size;
// We try to capture the size of the entire DB by using the highest and
// lowest keys.
range.start = "";
range.limit = "z"; // Keys starting with 'z' will not be included.
db_->GetApproximateSizes(&range, 1, &size);
UMA_HISTOGRAM_MEMORY_KB("DataReductionProxy.LevelDBSize", size / 1024);
}
return status;
}
DataStore::Status DataStoreImpl::RecreateDB() {
DCHECK(sequence_checker_.CalledOnValidSequence());
db_.reset();
const base::FilePath db_path = profile_path_.Append(kDBName);
leveldb::Status s = leveldb_chrome::DeleteDB(db_path, leveldb::Options());
if (!s.ok())
return LevelDbToDRPStoreStatus(s);
return OpenDB();
}
} // namespace data_reduction_proxy