mirror of
https://github.com/facebook/rocksdb.git
synced 2026-10-23 08:09:02 +03:00
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/15086 Why: An application built on top of RocksDB could use cache population reads through explicit snapshots and needs to know when the returned value has already been superseded, without a second point read. Previous behavior: Get and MultiGet returned the snapshot-visible value and optional timestamp but could not report a newer point update or covering range deletion. New behavior: experimental C++, C, and Java metadata-read APIs report newer committed puts, deletes, merges, and covering range tombstones; ordinary primary-DB writes completed before the read are guaranteed to be reported, racing writes remain best-effort, and true has no false positives. Implementation: metadata reads widen lookup behind read-callback hooks while keeping metadata-only state off passive common object layouts; per-key state stays in KeyContext/GetContext, ordinary MultiGet row-cache replay semantics are preserved, BlobDB/TTL use shared output helpers, dead single-CF plumbing and an invalid cross-key stress assertion are removed, and the range-tombstone ordering invariant is documented without changing behavior. Safety and unsupported cases: tracking is opt-in and requires an explicit snapshot; persisted-tier reads, custom transactional visibility callbacks, secondary DBs, and unsupported subclasses return NotSupported, while read-only/compacted DBs return false because their in-process views are static. Tests and limitations: 1,265 Buck tests plus focused metadata, 100x coerced-context-switch row-cache, write-committed/write-prepared crash tests, builds, and lint passed. Current-parent release db_bench found no material passive regression: readrandom +0.65%, MultiGet batch 4 -1.40%, and batch 32 +0.48%. Reviewed By: pdillinger Differential Revision: D104182397 fbshipit-source-id: 36f7be1cd50fe2383689adaae31a6bb7d7e462dc
176 lines
6.2 KiB
C++
176 lines
6.2 KiB
C++
// Copyright (c) 2011-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).
|
|
|
|
#include "db/db_impl/compacted_db_impl.h"
|
|
|
|
#include "db/blob/blob_fetcher.h"
|
|
#include "db/db_impl/db_impl.h"
|
|
#include "db/db_impl/db_impl_metadata.h"
|
|
#include "db/version_set.h"
|
|
#include "logging/logging.h"
|
|
#include "table/get_context.h"
|
|
#include "util/cast_util.h"
|
|
|
|
namespace ROCKSDB_NAMESPACE {
|
|
|
|
CompactedDBImpl::CompactedDBImpl(const DBOptions& options,
|
|
const std::string& dbname)
|
|
: DBImpl(options, dbname, /*seq_per_batch*/ false, +/*batch_per_txn*/ true,
|
|
/*read_only*/ true),
|
|
cfd_(nullptr),
|
|
version_(nullptr),
|
|
user_comparator_(nullptr) {}
|
|
|
|
CompactedDBImpl::~CompactedDBImpl() = default;
|
|
|
|
size_t CompactedDBImpl::FindFile(const Slice& key) {
|
|
size_t right = files_.num_files - 1;
|
|
auto cmp = [&](const FdWithKeyRange& f, const Slice& k) -> bool {
|
|
return user_comparator_->Compare(ExtractUserKey(f.largest_key), k) < 0;
|
|
};
|
|
return static_cast<size_t>(
|
|
std::lower_bound(files_.files, files_.files + right, key, cmp) -
|
|
files_.files);
|
|
}
|
|
|
|
Status CompactedDBImpl::GetWithMetadata(const ReadOptions& options,
|
|
ColumnFamilyHandle* column_family,
|
|
const Slice& key, PinnableSlice* value,
|
|
OutputMetadata* output_metadata) {
|
|
std::string* timestamp = GetOutputTimestamp(output_metadata);
|
|
bool* newer_version_present = GetOutputNewerVersionPresent(output_metadata);
|
|
if (newer_version_present != nullptr) {
|
|
*newer_version_present = false;
|
|
}
|
|
if (value == nullptr) {
|
|
return Status::InvalidArgument(
|
|
"Cannot call GetWithMetadata with a null value");
|
|
}
|
|
return Get(options, column_family, key, value, timestamp);
|
|
}
|
|
|
|
void CompactedDBImpl::MultiGetWithMetadata(
|
|
const ReadOptions& options, size_t num_keys,
|
|
ColumnFamilyHandle* const* column_families, const Slice* keys,
|
|
PinnableSlice* values, Status* statuses,
|
|
MultiGetOutputMetadata* output_metadata, const bool sorted_input) {
|
|
std::vector<std::string>* timestamps = GetOutputTimestamps(output_metadata);
|
|
if (timestamps != nullptr) {
|
|
timestamps->resize(num_keys);
|
|
}
|
|
std::vector<uint8_t>* newer_version_present =
|
|
GetOutputNewerVersionPresent(output_metadata);
|
|
if (newer_version_present != nullptr) {
|
|
newer_version_present->assign(num_keys, false);
|
|
}
|
|
autovector<ColumnFamilyHandle*, MultiGetContext::MAX_BATCH_SIZE>
|
|
stack_column_families;
|
|
std::vector<ColumnFamilyHandle*> heap_column_families;
|
|
ColumnFamilyHandle** mutable_column_families = MakeMutableCfHandles(
|
|
column_families, num_keys, &stack_column_families, &heap_column_families);
|
|
MultiGet(options, num_keys, mutable_column_families, keys, values,
|
|
timestamps != nullptr ? timestamps->data() : nullptr, statuses,
|
|
sorted_input);
|
|
}
|
|
|
|
Status CompactedDBImpl::Init(const Options& options) {
|
|
SuperVersionContext sv_context(/* create_superversion */ true);
|
|
mutex_.Lock();
|
|
ColumnFamilyDescriptor cf(kDefaultColumnFamilyName,
|
|
ColumnFamilyOptions(options));
|
|
Status s = Recover({cf}, true /* read only */, false, true);
|
|
if (s.ok()) {
|
|
cfd_ = static_cast_with_check<ColumnFamilyHandleImpl>(DefaultColumnFamily())
|
|
->cfd();
|
|
cfd_->InstallSuperVersion(&sv_context, &mutex_);
|
|
}
|
|
mutex_.Unlock();
|
|
sv_context.Clean();
|
|
if (!s.ok()) {
|
|
return s;
|
|
}
|
|
NewThreadStatusCfInfo(cfd_);
|
|
version_ = cfd_->GetSuperVersion()->current;
|
|
user_comparator_ = cfd_->user_comparator();
|
|
auto* vstorage = version_->storage_info();
|
|
if (vstorage->num_non_empty_levels() == 0) {
|
|
return Status::NotSupported("no file exists");
|
|
}
|
|
const LevelFilesBrief& l0 = vstorage->LevelFilesBrief(0);
|
|
// L0 should not have files
|
|
if (l0.num_files > 1) {
|
|
return Status::NotSupported("L0 contain more than 1 file");
|
|
}
|
|
if (l0.num_files == 1) {
|
|
if (vstorage->num_non_empty_levels() > 1) {
|
|
return Status::NotSupported("Both L0 and other level contain files");
|
|
}
|
|
files_ = l0;
|
|
files_level_ = 0;
|
|
return Status::OK();
|
|
}
|
|
|
|
for (int i = 1; i < vstorage->num_non_empty_levels() - 1; ++i) {
|
|
if (vstorage->LevelFilesBrief(i).num_files > 0) {
|
|
return Status::NotSupported("Other levels also contain files");
|
|
}
|
|
}
|
|
|
|
int level = vstorage->num_non_empty_levels() - 1;
|
|
if (vstorage->LevelFilesBrief(level).num_files > 0) {
|
|
files_ = vstorage->LevelFilesBrief(level);
|
|
files_level_ = level;
|
|
return Status::OK();
|
|
}
|
|
return Status::NotSupported("no file exists");
|
|
}
|
|
|
|
Status CompactedDBImpl::Open(const Options& options, const std::string& dbname,
|
|
std::unique_ptr<DB>* dbptr) {
|
|
*dbptr = nullptr;
|
|
|
|
if (options.max_open_files != -1) {
|
|
return Status::InvalidArgument("require max_open_files = -1");
|
|
}
|
|
if (options.merge_operator.get() != nullptr) {
|
|
return Status::InvalidArgument("merge operator is not supported");
|
|
}
|
|
DBOptions db_options(options);
|
|
std::unique_ptr<CompactedDBImpl> db(new CompactedDBImpl(db_options, dbname));
|
|
Status s = db->Init(options);
|
|
if (s.ok()) {
|
|
{
|
|
InstrumentedMutexLock l(&db->mutex_);
|
|
db->opened_successfully_ = true;
|
|
if (db->immutable_db_options_.open_files_async) {
|
|
db->ScheduleAsyncFileOpening();
|
|
}
|
|
}
|
|
s = db->StartPeriodicTaskScheduler();
|
|
}
|
|
if (s.ok()) {
|
|
ROCKS_LOG_INFO(db->immutable_db_options_.info_log,
|
|
"Opened the db as fully compacted mode");
|
|
LogFlush(db->immutable_db_options_.info_log);
|
|
*dbptr = std::move(db);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
} // namespace ROCKSDB_NAMESPACE
|
|
|
|
// Generate the regular and coroutine versions of the functions defined in
|
|
// compacted_db_impl_sync_and_async.h by including it twice. The macros expand
|
|
// differently based on whether WITH_COROUTINES or WITHOUT_COROUTINES is
|
|
// defined.
|
|
// clang-format off
|
|
#define WITHOUT_COROUTINES
|
|
#include "db/db_impl/compacted_db_impl_sync_and_async.h"
|
|
#undef WITHOUT_COROUTINES
|
|
#define WITH_COROUTINES
|
|
#include "db/db_impl/compacted_db_impl_sync_and_async.h"
|
|
#undef WITH_COROUTINES
|
|
// clang-format on
|