diff --git a/db/db_bench.cc b/db/db_bench.cc index cd0107287..c33179a94 100644 --- a/db/db_bench.cc +++ b/db/db_bench.cc @@ -201,6 +201,9 @@ static int FLAGS_stats_per_interval = 0; // less than or equal to this value. static double FLAGS_rate_limit = 0; +// Run read only benchmarks. +static bool FLAGS_read_only = false; + extern bool useOsBuffer; extern bool useFsReadAhead; extern bool useMmapRead; @@ -942,7 +945,12 @@ class Benchmark { FLAGS_delete_obsolete_files_period_micros; options.rate_limit = FLAGS_rate_limit; options.table_cache_numshardbits = FLAGS_table_cache_numshardbits; - Status s = DB::Open(options, FLAGS_db, &db_); + Status s; + if(FLAGS_read_only) { + s = DB::OpenForReadOnly(options, FLAGS_db, &db_); + } else { + s = DB::Open(options, FLAGS_db, &db_); + } if (!s.ok()) { fprintf(stderr, "open error: %s\n", s.ToString().c_str()); exit(1); @@ -1374,6 +1382,9 @@ int main(int argc, char** argv) { } else if (sscanf(argv[i], "--rate_limit=%lf%c", &d, &junk) == 1 && d > 1.0) { FLAGS_rate_limit = d; + } else if (sscanf(argv[i], "--readonly=%d%c", &n, &junk) == 1 && + (n == 0 || n ==1 )) { + FLAGS_read_only = n; } else { fprintf(stderr, "Invalid flag '%s'\n", argv[i]); exit(1); diff --git a/db/db_impl.cc b/db/db_impl.cc index df561f758..45ed6516c 100644 --- a/db/db_impl.cc +++ b/db/db_impl.cc @@ -167,13 +167,13 @@ Options SanitizeOptions(const std::string& dbname, DBImpl::DBImpl(const Options& options, const std::string& dbname) : env_(options.env), + dbname_(dbname), internal_comparator_(options.comparator), - internal_filter_policy_(options.filter_policy), options_(SanitizeOptions( dbname, &internal_comparator_, &internal_filter_policy_, options)), + internal_filter_policy_(options.filter_policy), owns_info_log_(options_.info_log != options.info_log), owns_cache_(options_.block_cache != options.block_cache), - dbname_(dbname), db_lock_(NULL), shutting_down_(NULL), bg_cv_(&mutex_), @@ -430,7 +430,8 @@ void DBImpl::DeleteObsoleteFiles() { EvictObsoleteFiles(deletion_state); } -Status DBImpl::Recover(VersionEdit* edit) { +Status DBImpl::Recover(VersionEdit* edit, bool no_log_recory, + bool error_if_log_file_exist) { mutex_.AssertHeld(); // Ignore error from CreateDir since the creation of the DB is @@ -489,6 +490,16 @@ Status DBImpl::Recover(VersionEdit* edit) { } } + if (logs.size() > 0 && error_if_log_file_exist) { + return Status::Corruption("" + "The db was opened in readonly mode with error_if_log_file_exist" + "flag but a log file already exists"); + } + + if (no_log_recory) { + return s; + } + // Recover in the order in which the logs were generated std::sort(logs.begin(), logs.end()); for (size_t i = 0; i < logs.size(); i++) { diff --git a/db/db_impl.h b/db/db_impl.h index a12b94f7a..c792cae4c 100644 --- a/db/db_impl.h +++ b/db/db_impl.h @@ -77,6 +77,18 @@ class DBImpl : public DB { // file at a level >= 1. int64_t TEST_MaxNextLevelOverlappingBytes(); +protected: + + Env* const env_; + const std::string dbname_; + VersionSet* versions_; + const InternalKeyComparator internal_comparator_; + const Options options_; // options_.comparator == &internal_comparator_ + + const Comparator* user_comparator() const { + return internal_comparator_.user_comparator(); + } + private: friend class DB; struct CompactionState; @@ -91,7 +103,9 @@ class DBImpl : public DB { // Recover the descriptor from persistent storage. May do a significant // amount of work to recover recently logged updates. Any changes to // be made to the descriptor are added to *edit. - Status Recover(VersionEdit* edit); + Status Recover(VersionEdit* edit, + bool no_log_recory = false, + bool error_if_log_file_exist = false); void MaybeIgnoreError(Status* s) const; @@ -145,13 +159,9 @@ class DBImpl : public DB { void EvictObsoleteFiles(DeletionState& deletion_state); // Constant after construction - Env* const env_; - const InternalKeyComparator internal_comparator_; const InternalFilterPolicy internal_filter_policy_; - const Options options_; // options_.comparator == &internal_comparator_ bool owns_info_log_; bool owns_cache_; - const std::string dbname_; // table_cache_ provides its own synchronization TableCache* table_cache_; @@ -198,8 +208,6 @@ class DBImpl : public DB { }; ManualCompaction* manual_compaction_; - VersionSet* versions_; - // Have we encountered a background error in paranoid mode? Status bg_error_; @@ -276,9 +284,6 @@ class DBImpl : public DB { DBImpl(const DBImpl&); void operator=(const DBImpl&); - const Comparator* user_comparator() const { - return internal_comparator_.user_comparator(); - } }; // Sanitize db options. The caller should delete result.info_log if diff --git a/db/db_impl_readonly.cc b/db/db_impl_readonly.cc new file mode 100644 index 000000000..18f62b805 --- /dev/null +++ b/db/db_impl_readonly.cc @@ -0,0 +1,92 @@ +// Copyright (c) 2012 Facebook. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "db/db_impl_readonly.h" +#include "db/db_impl.h" + +#include +#include +#include +#include +#include +#include +#include +#include "db/db_iter.h" +#include "db/dbformat.h" +#include "db/filename.h" +#include "db/log_reader.h" +#include "db/log_writer.h" +#include "db/memtable.h" +#include "db/table_cache.h" +#include "db/version_set.h" +#include "db/write_batch_internal.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/status.h" +#include "leveldb/table.h" +#include "leveldb/table_builder.h" +#include "port/port.h" +#include "table/block.h" +#include "table/merger.h" +#include "table/two_level_iterator.h" +#include "util/coding.h" +#include "util/logging.h" +#include "util/build_version.h" + +namespace leveldb { + +DBImplReadOnly::DBImplReadOnly(const Options& options, + const std::string& dbname) + : DBImpl(options, dbname) { + Log(options_.info_log, "Opening the db in read only mode"); +} + +DBImplReadOnly::~DBImplReadOnly() { +} + +// Implementations of the DB interface +Status DBImplReadOnly::Get(const ReadOptions& options, + const Slice& key, + std::string* value) { + Status s; + Version* current = versions_->current(); + SequenceNumber snapshot = versions_->LastSequence(); + LookupKey lkey(key, snapshot); + Version::GetStats stats; + s = current->Get(options, lkey, value, &stats); + return s; +} + +Iterator* DBImplReadOnly::NewIterator(const ReadOptions& options) { + std::vector list; + versions_->current()->AddIterators(options, &list); + Iterator* internal_iter = + NewMergingIterator(&internal_comparator_, &list[0], list.size()); + return NewDBIterator( + &dbname_, env_, user_comparator(), internal_iter, + reinterpret_cast(options.snapshot)->number_); +} + + +Status DB::OpenForReadOnly(const Options& options, const std::string& dbname, + DB** dbptr, bool no_log_recory, bool error_if_log_file_exist) { + *dbptr = NULL; + + DBImplReadOnly* impl = new DBImplReadOnly(options, dbname); + impl->mutex_.Lock(); + VersionEdit edit(impl->NumberLevels()); + Status s = impl->Recover(&edit, no_log_recory, error_if_log_file_exist); + if (s.ok() && !no_log_recory) { + s = impl->versions_->LogAndApply(&edit, &impl->mutex_); + } + impl->mutex_.Unlock(); + if (s.ok()) { + *dbptr = impl; + } else { + delete impl; + } + return s; +} + +} diff --git a/db/db_impl_readonly.h b/db/db_impl_readonly.h new file mode 100644 index 000000000..403b35452 --- /dev/null +++ b/db/db_impl_readonly.h @@ -0,0 +1,72 @@ +// Copyright (c) 2012 Facebook. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef STORAGE_LEVELDB_DB_DB_IMPL_READONLY_H_ +#define STORAGE_LEVELDB_DB_DB_IMPL_READONLY_H_ + +#include "db/db_impl.h" + +#include +#include +#include "db/dbformat.h" +#include "db/log_writer.h" +#include "db/snapshot.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "port/port.h" +#include "util/stats_logger.h" + +#ifdef USE_SCRIBE +#include "scribe/scribe_logger.h" +#endif + +namespace leveldb { + +class DBImplReadOnly : public DBImpl { +public: + DBImplReadOnly(const Options& options, const std::string& dbname); + virtual ~DBImplReadOnly(); + + // Implementations of the DB interface + virtual Status Get(const ReadOptions& options, + const Slice& key, + std::string* value); + virtual Iterator* NewIterator(const ReadOptions&); + + virtual Status Put(const WriteOptions&, const Slice& key, const Slice& value) { + return Status::NotSupported("Not supported operation in read only mode."); + } + virtual Status Delete(const WriteOptions&, const Slice& key) { + return Status::NotSupported("Not supported operation in read only mode."); + } + virtual Status Write(const WriteOptions& options, WriteBatch* updates) { + return Status::NotSupported("Not supported operation in read only mode."); + } + virtual void CompactRange(const Slice* begin, const Slice* end) { + } + virtual Status DisableFileDeletions() { + return Status::NotSupported("Not supported operation in read only mode."); + } + virtual Status EnableFileDeletions() { + return Status::NotSupported("Not supported operation in read only mode."); + } + virtual Status GetLiveFiles(std::vector&, + uint64_t* manifest_file_size) { + return Status::NotSupported("Not supported operation in read only mode."); + } + virtual Status Flush(const FlushOptions& options) { + return Status::NotSupported("Not supported operation in read only mode."); + } + +private: + friend class DB; + + // No copying allowed + DBImplReadOnly(const DBImplReadOnly&); + void operator=(const DBImplReadOnly&); +}; + +} + +#endif diff --git a/include/leveldb/db.h b/include/leveldb/db.h index b80c1fc40..e4e2a30c4 100644 --- a/include/leveldb/db.h +++ b/include/leveldb/db.h @@ -54,6 +54,14 @@ class DB { const std::string& name, DB** dbptr); + // Open the database for read only. All DB interfaces + // that modify data, like put/delete, will return error. + // If the db is opened in read only mode, then no compactions + // will happen. + static Status OpenForReadOnly(const Options& options, + const std::string& name, DB** dbptr, + bool no_log_recory = true, bool error_if_log_file_exist = false); + DB() { } virtual ~DB();