// Copyright (c) 2021-present, Facebook, Inc. All rights reserved. // This source code is licensed under both the GPLv2 (found in the // COPYING file in the root directory) and Apache 2.0 License // (found in the LICENSE.Apache file in the root directory). #ifdef GFLAGS #pragma once #include #include #include #include "db/dbformat.h" #include "db_stress_tool/expected_value.h" #include "file/file_util.h" #include "rocksdb/db.h" #include "rocksdb/env.h" #include "rocksdb/file_system.h" #include "rocksdb/rocksdb_namespace.h" #include "rocksdb/types.h" #include "util/string_util.h" namespace ROCKSDB_NAMESPACE { // `ExpectedState` provides read/write access to expected values stored in // `ExpectedState` for every key. class ExpectedState { public: explicit ExpectedState(size_t max_key, size_t num_column_families); virtual ~ExpectedState() {} // Requires external locking preventing concurrent execution with any other // member function. virtual Status Open(bool create) = 0; // Requires external locking covering all keys in `cf`. void ClearColumnFamily(int cf); // Prepare a Put that will be started but not finished yet // This is useful for crash-recovery testing when the process may crash // before updating the corresponding expected value // // Requires external locking covering `key` in `cf` to prevent concurrent // write or delete to the same `key`. PendingExpectedValue PreparePut(int cf, int64_t key); // Does not requires external locking. ExpectedValue Get(int cf, int64_t key); // Prepare a Delete that will be started but not finished yet. // This is useful for crash-recovery testing when the process may crash // before updating the corresponding expected value // // Requires external locking covering `key` in `cf` to prevent concurrent // write or delete to the same `key`. PendingExpectedValue PrepareDelete(int cf, int64_t key); // Requires external locking covering `key` in `cf` to prevent concurrent // write or delete to the same `key`. PendingExpectedValue PrepareSingleDelete(int cf, int64_t key); // Requires external locking covering keys in `[begin_key, end_key)` in `cf` // to prevent concurrent write or delete to the same `key`. std::vector PrepareDeleteRange(int cf, int64_t begin_key, int64_t end_key); // Update the expected value for start of an incomplete write or delete // operation on the key assoicated with this expected value void Precommit(int cf, int64_t key, const ExpectedValue& value); // Requires external locking covering `key` in `cf` to prevent concurrent // delete to the same `key`. bool Exists(int cf, int64_t key); // Sync the `value_base` to the corresponding expected value // // Requires external locking covering `key` in `cf` or be in single thread // to prevent concurrent write or delete to the same `key` void SyncPut(int cf, int64_t key, uint32_t value_base); // Sync the corresponding expected value to be pending Put // // Requires external locking covering `key` in `cf` or be in single thread // to prevent concurrent write or delete to the same `key` void SyncPendingPut(int cf, int64_t key); // Sync the corresponding expected value to be deleted // // Requires external locking covering `key` in `cf` or be in single thread // to prevent concurrent write or delete to the same `key` void SyncDelete(int cf, int64_t key); // Sync the corresponding expected values to be deleted // // Requires external locking covering keys in `[begin_key, end_key)` in `cf` // to prevent concurrent write or delete to the same `key` void SyncDeleteRange(int cf, int64_t begin_key, int64_t end_key); private: // Does not requires external locking. std::atomic& Value(int cf, int64_t key) const { return values_[cf * max_key_ + key]; } // Does not requires external locking ExpectedValue Load(int cf, int64_t key) const { return ExpectedValue(Value(cf, key).load()); } const size_t max_key_; const size_t num_column_families_; protected: size_t GetValuesLen() const { return sizeof(std::atomic) * num_column_families_ * max_key_; } // Requires external locking preventing concurrent execution with any other // member function. void Reset(); std::atomic* values_; }; // A `FileExpectedState` implements `ExpectedState` backed by a file. class FileExpectedState : public ExpectedState { public: explicit FileExpectedState(std::string expected_state_file_path, size_t max_key, size_t num_column_families); // Requires external locking preventing concurrent execution with any other // member function. Status Open(bool create) override; private: const std::string expected_state_file_path_; std::unique_ptr expected_state_mmap_buffer_; }; // An `AnonExpectedState` implements `ExpectedState` backed by a memory // allocation. class AnonExpectedState : public ExpectedState { public: explicit AnonExpectedState(size_t max_key, size_t num_column_families); // Requires external locking preventing concurrent execution with any other // member function. Status Open(bool create) override; private: std::unique_ptr[]> values_allocation_; }; // An `ExpectedStateManager` manages data about the expected state of the // database. It exposes operations for reading and modifying the latest // expected state. class ExpectedStateManager { public: explicit ExpectedStateManager(size_t max_key, size_t num_column_families); virtual ~ExpectedStateManager(); // Requires external locking preventing concurrent execution with any other // member function. virtual Status Open() = 0; // Saves expected values for the current state of `db` and begins tracking // changes. Following a successful `SaveAtAndAfter()`, `Restore()` can be // called on the same DB, as long as its state does not roll back to before // its current state. // // Requires external locking preventing concurrent execution with any other // member function. Furthermore, `db` must not be mutated while this function // is executing. virtual Status SaveAtAndAfter(DB* db) = 0; // Returns true if at least one state of historical expected values can be // restored. // // Requires external locking preventing concurrent execution with any other // member function. virtual bool HasHistory() = 0; // Restores expected values according to the current state of `db`. See // `SaveAtAndAfter()` for conditions where this can be called. // // Requires external locking preventing concurrent execution with any other // member function. Furthermore, `db` must not be mutated while this function // is executing. virtual Status Restore(DB* db) = 0; // Requires external locking covering all keys in `cf`. void ClearColumnFamily(int cf) { return latest_->ClearColumnFamily(cf); } // See ExpectedState::PreparePut() PendingExpectedValue PreparePut(int cf, int64_t key) { return latest_->PreparePut(cf, key); } // See ExpectedState::Get() ExpectedValue Get(int cf, int64_t key) { return latest_->Get(cf, key); } // See ExpectedState::PrepareDelete() PendingExpectedValue PrepareDelete(int cf, int64_t key) { return latest_->PrepareDelete(cf, key); } // See ExpectedState::PrepareSingleDelete() PendingExpectedValue PrepareSingleDelete(int cf, int64_t key) { return latest_->PrepareSingleDelete(cf, key); } // See ExpectedState::PrepareDeleteRange() std::vector PrepareDeleteRange(int cf, int64_t begin_key, int64_t end_key) { return latest_->PrepareDeleteRange(cf, begin_key, end_key); } // See ExpectedState::Exists() bool Exists(int cf, int64_t key) { return latest_->Exists(cf, key); } // See ExpectedState::SyncPut() void SyncPut(int cf, int64_t key, uint32_t value_base) { return latest_->SyncPut(cf, key, value_base); } // See ExpectedState::SyncPendingPut() void SyncPendingPut(int cf, int64_t key) { return latest_->SyncPendingPut(cf, key); } // See ExpectedState::SyncDelete() void SyncDelete(int cf, int64_t key) { return latest_->SyncDelete(cf, key); } // See ExpectedState::SyncDeleteRange() void SyncDeleteRange(int cf, int64_t begin_key, int64_t end_key) { return latest_->SyncDeleteRange(cf, begin_key, end_key); } protected: const size_t max_key_; const size_t num_column_families_; std::unique_ptr latest_; }; // A `FileExpectedStateManager` implements an `ExpectedStateManager` backed by // a directory of files containing data about the expected state of the // database. class FileExpectedStateManager : public ExpectedStateManager { public: explicit FileExpectedStateManager(size_t max_key, size_t num_column_families, std::string expected_state_dir_path); // Requires external locking preventing concurrent execution with any other // member function. Status Open() override; // See `ExpectedStateManager::SaveAtAndAfter()` API doc. // // This implementation makes a copy of "LATEST.state" into // ".state", and starts a trace in ".trace". // Due to using external files, a following `Restore()` can happen even // from a different process. Status SaveAtAndAfter(DB* db) override; // See `ExpectedStateManager::HasHistory()` API doc. bool HasHistory() override; // See `ExpectedStateManager::Restore()` API doc. // // Say `db->GetLatestSequenceNumber()` was `a` last time `SaveAtAndAfter()` // was called and now it is `b`. Then this function replays `b - a` write // operations from "`a`.trace" onto "`a`.state", and then copies the resulting // file into "LATEST.state". Status Restore(DB* db) override; private: // Requires external locking preventing concurrent execution with any other // member function. Status Clean(); std::string GetTempPathForFilename(const std::string& filename); std::string GetPathForFilename(const std::string& filename); static const std::string kLatestBasename; static const std::string kStateFilenameSuffix; static const std::string kTraceFilenameSuffix; static const std::string kTempFilenamePrefix; static const std::string kTempFilenameSuffix; const std::string expected_state_dir_path_; SequenceNumber saved_seqno_ = kMaxSequenceNumber; }; // An `AnonExpectedStateManager` implements an `ExpectedStateManager` backed by // a memory allocation containing data about the expected state of the database. class AnonExpectedStateManager : public ExpectedStateManager { public: explicit AnonExpectedStateManager(size_t max_key, size_t num_column_families); // See `ExpectedStateManager::SaveAtAndAfter()` API doc. // // This implementation returns `Status::NotSupported` since we do not // currently have a need to keep history of expected state within a process. Status SaveAtAndAfter(DB* /* db */) override { return Status::NotSupported(); } // See `ExpectedStateManager::HasHistory()` API doc. bool HasHistory() override { return false; } // See `ExpectedStateManager::Restore()` API doc. // // This implementation returns `Status::NotSupported` since we do not // currently have a need to keep history of expected state within a process. Status Restore(DB* /* db */) override { return Status::NotSupported(); } // Requires external locking preventing concurrent execution with any other // member function. Status Open() override; }; } // namespace ROCKSDB_NAMESPACE #endif // GFLAGS