cachelib/allocator/NvmCacheState.cpp (143 lines of code) (raw):

/* * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <folly/io/RecordIO.h> #include <folly/logging/xlog.h> #include <fstream> #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wconversion" #include <folly/Format.h> #pragma GCC diagnostic pop #include "cachelib/allocator/CacheVersion.h" #include "cachelib/allocator/NvmCacheState.h" #include "cachelib/allocator/serialize/gen-cpp2/objects_types.h" #include "cachelib/common/Serialization.h" #include "cachelib/common/Time.h" #include "cachelib/common/Utils.h" namespace facebook { namespace cachelib { namespace { const folly::StringPiece kNvmCacheState = "NvmCacheState"; const folly::StringPiece kShouldDropNvmCache = "IceRoll"; bool fileExists(const std::string& file) { return util::getStatIfExists(file, nullptr); } std::string constructFilePath(folly::StringPiece cacheDir, folly::StringPiece name) { return folly::sformat("{}/{}", cacheDir, name); } serialization::NvmCacheMetadata loadMetadata(folly::StringPiece fileName) { folly::File shutDownFile{fileName}; folly::RecordIOReader rr{std::move(shutDownFile)}; auto metadataIoBuf = folly::IOBuf::copyBuffer(rr.begin()->first); if (metadataIoBuf->length() == 0) { throw std::runtime_error( folly::sformat("no content in file: {}", fileName)); } Deserializer deserializer{metadataIoBuf->data(), metadataIoBuf->data() + metadataIoBuf->length()}; return deserializer.deserialize<serialization::NvmCacheMetadata>(); } void saveMetadata(const folly::File& file, const serialization::NvmCacheMetadata& metadata) { auto metadataIoBuf = Serializer::serializeToIOBuf(metadata); folly::File shutDownFile{file.fd()}; folly::RecordIOWriter rw{std::move(shutDownFile)}; rw.write(std::move(metadataIoBuf)); } } // namespace std::string NvmCacheState::getNvmCacheStateFilePath( folly::StringPiece cacheDir) { return constructFilePath(cacheDir, kNvmCacheState); } NvmCacheState::NvmCacheState(const std::string& cacheDir, bool encryptionEnabled, bool truncateAllocSize) : cacheDir_(cacheDir), creationTime_{util::getCurrentTimeSec()}, encryptionEnabled_{encryptionEnabled}, truncateAllocSize_{truncateAllocSize} { if (cacheDir_.empty()) { return; } if (!fileExists(cacheDir_)) { util::makeDir(cacheDir_); } else if (!util::isDir(cacheDir_)) { throw std::invalid_argument( folly::sformat("Expected {} to be directory", cacheDir_)); } restoreState(); // Keep a stream open for the metadata metadataFile_ = std::make_unique<folly::File>(getFileNameFor(kNvmCacheState), O_CREAT | O_TRUNC | O_RDWR); XDCHECK(metadataFile_); } void NvmCacheState::restoreState() { // Read previous instance state from nvm state file try { shouldDropNvmCache_ = fileExists(getFileNameFor(kShouldDropNvmCache)); if (!fileExists(getFileNameFor(kNvmCacheState))) { // No nvm cache state supplied, we return early. Nvm cache will still be // started afresh due to wasCleanshutDown_ == false return; } auto metadata = loadMetadata(getFileNameFor(kNvmCacheState)); wasCleanshutDown_ = *metadata.safeShutDown_ref(); if (!shouldStartFresh()) { if (*metadata.nvmFormatVersion_ref() == kCacheNvmFormatVersion && encryptionEnabled_ == *metadata.encryptionEnabled_ref() && truncateAllocSize_ == *metadata.truncateAllocSize_ref()) { creationTime_ = *metadata.creationTime_ref(); } else { XLOGF(ERR, "Expected nvm format version {}, but found {}. Expected " "encryption to be {}, but found {}. Expected truncateAllocSize " "to be {}, but found {}. Dropping NvmCache", kCacheNvmFormatVersion, *metadata.nvmFormatVersion_ref(), encryptionEnabled_ ? "true" : "false", *metadata.encryptionEnabled_ref() ? "true" : "false", truncateAllocSize_ ? "true" : "false", *metadata.truncateAllocSize_ref() ? "true" : "false"); shouldDropNvmCache_ = true; } } } catch (const std::exception& ex) { XLOGF(ERR, "unable to deserialize nvm metadata file: {}", ex.what()); shouldDropNvmCache_ = true; } } bool NvmCacheState::shouldStartFresh() const { return shouldDropNvmCache() || !wasCleanShutDown(); } bool NvmCacheState::shouldDropNvmCache() const { return shouldDropNvmCache_; } bool NvmCacheState::wasCleanShutDown() const { return wasCleanshutDown_; } time_t NvmCacheState::getCreationTime() const { return creationTime_; } void NvmCacheState::clearPrevState() { auto dropFile = getFileNameFor(kShouldDropNvmCache); if (::unlink(dropFile.data()) != 0 && errno != ENOENT) { util::throwSystemError(errno, "Failed to delete nvm run file"); } XDCHECK(metadataFile_); ftruncate(metadataFile_->fd(), 0); } void NvmCacheState::markSafeShutDown() { XDCHECK(metadataFile_); serialization::NvmCacheMetadata metadata; *metadata.nvmFormatVersion_ref() = kCacheNvmFormatVersion; *metadata.creationTime_ref() = creationTime_; *metadata.safeShutDown_ref() = true; *metadata.encryptionEnabled_ref() = encryptionEnabled_; *metadata.truncateAllocSize_ref() = truncateAllocSize_; saveMetadata(*metadataFile_, metadata); } std::string NvmCacheState::getFileNameFor(folly::StringPiece name) const { return constructFilePath(cacheDir_, name); } std::string NvmCacheState::getFileForNvmCacheDrop(folly::StringPiece cacheDir) { return constructFilePath(cacheDir, kShouldDropNvmCache); } std::string NvmCacheState::toString() const { return folly::sformat("cleanShutDown={}, shouldDrop={}, creationTime={}", wasCleanShutDown(), shouldDropNvmCache(), getCreationTime()); } void NvmCacheState::markTruncated() { wasCleanshutDown_ = false; creationTime_ = util::getCurrentTimeSec(); } } // namespace cachelib } // namespace facebook