diff --git a/eden/fs/rocksdb/RocksHandles.cpp b/eden/fs/rocksdb/RocksHandles.cpp index 39eda62188..78a2bbfd1d 100644 --- a/eden/fs/rocksdb/RocksHandles.cpp +++ b/eden/fs/rocksdb/RocksHandles.cpp @@ -11,6 +11,8 @@ #include +#include "eden/fs/rocksdb/RocksException.h" + using folly::StringPiece; using rocksdb::ColumnFamilyDescriptor; using rocksdb::ColumnFamilyHandle; @@ -26,6 +28,10 @@ namespace facebook { namespace eden { RocksHandles::~RocksHandles() { + close(); +} + +void RocksHandles::close() { // MUST destroy the column handles first columns.clear(); db.reset(); @@ -33,33 +39,12 @@ RocksHandles::~RocksHandles() { RocksHandles::RocksHandles( StringPiece dbPath, + const Options& options, const std::vector& columnDescriptors) { auto dbPathStr = dbPath.str(); - - Options options; - // Optimize RocksDB. This is the easiest way to get RocksDB to perform well. - options.IncreaseParallelism(); - - // Create the DB if it's not already present. - options.create_if_missing = true; - // Automatically create column families as we define new ones. - options.create_missing_column_families = true; - - // If we wanted we could set options.info_log to control where RocksDB - // log messages get sent. By default they are written to a file named "LOG" - // in the DB directory. - // options.info_log = make_shared(InfoLogLevel::INFO_LEVEL); - DB* dbRaw; - columns.reserve(columnDescriptors.size()); - std::vector columnHandles; - auto openDB = [&] { - return DB::Open( - options, dbPathStr, columnDescriptors, &columnHandles, &dbRaw); - }; - // This will create any newly defined column families automatically, // so we needn't make any special migration steps here; just define // a new family and start to use it. @@ -67,36 +52,17 @@ RocksHandles::RocksHandles( // and shout at us for not opening up the database with them defined. // We will need to do "something smarter" if we ever decide to perform // that kind of a migration. - auto status = openDB(); + auto status = + DB::Open(options, dbPathStr, columnDescriptors, &columnHandles, &dbRaw); if (!status.ok()) { XLOG(ERR) << "Error opening RocksDB storage at " << dbPathStr << ": " << status.ToString(); - XLOG(ERR) << "Attempting to repair RocksDB " << dbPathStr; - - rocksdb::ColumnFamilyOptions unknownColumFamilyOptions; - unknownColumFamilyOptions.OptimizeForPointLookup(8); - unknownColumFamilyOptions.OptimizeLevelStyleCompaction(); - - DBOptions dbOptions(options); - status = RepairDB( - dbPathStr, dbOptions, columnDescriptors, unknownColumFamilyOptions); - if (!status.ok()) { - throw std::runtime_error(folly::to( - "Unable to repair RocksDB at ", dbPathStr, ": ", status.ToString())); - } - - columnHandles.clear(); - status = openDB(); - if (!status.ok()) { - throw std::runtime_error(folly::to( - "Failed to open RocksDB at ", - dbPathStr, - " after repair attempt: ", - status.ToString())); - } + throw RocksException::build( + status, "error opening RocksDB storage at", dbPathStr); } db.reset(dbRaw); + columns.reserve(columnHandles.size()); for (auto h : columnHandles) { columns.emplace_back(h); } diff --git a/eden/fs/rocksdb/RocksHandles.h b/eden/fs/rocksdb/RocksHandles.h index 018bf4110f..b44165fa96 100644 --- a/eden/fs/rocksdb/RocksHandles.h +++ b/eden/fs/rocksdb/RocksHandles.h @@ -48,12 +48,10 @@ struct RocksHandles { */ RocksHandles( folly::StringPiece dbPath, + const rocksdb::Options& options, const std::vector& columnDescriptors); - RocksHandles(const RocksHandles&) = delete; - RocksHandles& operator=(const RocksHandles&) = delete; - RocksHandles(RocksHandles&&) = default; - RocksHandles& operator=(RocksHandles&&) = default; + void close(); }; } // namespace eden } // namespace facebook diff --git a/eden/fs/store/RocksDbLocalStore.cpp b/eden/fs/store/RocksDbLocalStore.cpp index 2be43e7932..7fc623c190 100644 --- a/eden/fs/store/RocksDbLocalStore.cpp +++ b/eden/fs/store/RocksDbLocalStore.cpp @@ -178,6 +178,52 @@ void RocksDbWriteBatch::put( flushIfNeeded(); } +rocksdb::Options getRocksdbOptions() { + rocksdb::Options options; + // Optimize RocksDB. This is the easiest way to get RocksDB to perform well. + options.IncreaseParallelism(); + + // Create the DB if it's not already present. + options.create_if_missing = true; + // Automatically create column families as we define new ones. + options.create_missing_column_families = true; + + return options; +} + +void repairDB(AbsolutePathPiece path) { + XLOG(ERR) << "Attempting to repair RocksDB " << path; + rocksdb::ColumnFamilyOptions unknownColumFamilyOptions; + unknownColumFamilyOptions.OptimizeForPointLookup(8); + unknownColumFamilyOptions.OptimizeLevelStyleCompaction(); + + const auto& columnDescriptors = columnFamilies(); + + auto dbPathStr = path.stringPiece().str(); + rocksdb::DBOptions dbOptions(getRocksdbOptions()); + auto status = RepairDB( + dbPathStr, dbOptions, columnDescriptors, unknownColumFamilyOptions); + if (!status.ok()) { + throw RocksException::build(status, "unable to repair RocksDB at ", path); + } +} + +RocksHandles openDB(AbsolutePathPiece path) { + auto options = getRocksdbOptions(); + try { + return RocksHandles(path.stringPiece(), options, columnFamilies()); + } catch (const RocksException& ex) { + XLOG(ERR) << "Error opening RocksDB storage at " << path << ": " + << ex.what(); + // Fall through and attempt to repair the DB + } + + repairDB(path); + + // Now try opening the DB again. + return RocksHandles(path.stringPiece(), options, columnFamilies()); +} + } // namespace namespace facebook { @@ -189,9 +235,8 @@ RocksDbLocalStore::RocksDbLocalStore( std::shared_ptr config) : LocalStore(std::move(config)), faultInjector_(*faultInjector), - dbHandles_(pathToRocksDb.stringPiece(), columnFamilies()), - ioPool_(12, "RocksLocalStore") { -} + dbHandles_(openDB(pathToRocksDb)), + ioPool_(12, "RocksLocalStore") {} RocksDbLocalStore::~RocksDbLocalStore() { #ifdef FOLLY_SANITIZE_ADDRESS @@ -209,8 +254,7 @@ RocksDbLocalStore::~RocksDbLocalStore() { } void RocksDbLocalStore::close() { - dbHandles_.columns.clear(); - dbHandles_.db.reset(); + dbHandles_.close(); } void RocksDbLocalStore::clearKeySpace(KeySpace keySpace) {