2020-10-15 20:02:44 +00:00
|
|
|
// 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/blob/blob_file_cache.h"
|
|
|
|
|
|
|
|
#include <cassert>
|
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
#include "db/blob/blob_file_reader.h"
|
|
|
|
#include "options/cf_options.h"
|
|
|
|
#include "rocksdb/cache.h"
|
|
|
|
#include "rocksdb/slice.h"
|
|
|
|
#include "test_util/sync_point.h"
|
2021-02-16 17:47:12 +00:00
|
|
|
#include "trace_replay/io_tracer.h"
|
2020-10-15 20:02:44 +00:00
|
|
|
#include "util/hash.h"
|
|
|
|
|
|
|
|
namespace ROCKSDB_NAMESPACE {
|
|
|
|
|
|
|
|
BlobFileCache::BlobFileCache(Cache* cache,
|
|
|
|
const ImmutableCFOptions* immutable_cf_options,
|
|
|
|
const FileOptions* file_options,
|
|
|
|
uint32_t column_family_id,
|
2021-02-16 17:47:12 +00:00
|
|
|
HistogramImpl* blob_file_read_hist,
|
|
|
|
const std::shared_ptr<IOTracer>& io_tracer)
|
2020-10-15 20:02:44 +00:00
|
|
|
: cache_(cache),
|
Integrity protection for live updates to WriteBatch (#7748)
Summary:
This PR adds the foundation classes for key-value integrity protection and the first use case: protecting live updates from the source buffers added to `WriteBatch` through the destination buffer in `MemTable`. The width of the protection info is not yet configurable -- only eight bytes per key is supported. This PR allows users to enable protection by constructing `WriteBatch` with `protection_bytes_per_key == 8`. It does not yet expose a way for users to get integrity protection via other write APIs (e.g., `Put()`, `Merge()`, `Delete()`, etc.).
The foundation classes (`ProtectionInfo.*`) embed the coverage info in their type, and provide `Protect.*()` and `Strip.*()` functions to navigate between types with different coverage. For making bytes per key configurable (for powers of two up to eight) in the future, these classes are templated on the unsigned integer type used to store the protection info. That integer contains the XOR'd result of hashes with independent seeds for all covered fields. For integer fields, the hash is computed on the raw unadjusted bytes, so the result is endian-dependent. The most significant bytes are truncated when the hash value (8 bytes) is wider than the protection integer.
When `WriteBatch` is constructed with `protection_bytes_per_key == 8`, we hold a `ProtectionInfoKVOTC` (i.e., one that covers key, value, optype aka `ValueType`, timestamp, and CF ID) for each entry added to the batch. The protection info is generated from the original buffers passed by the user, as well as the original metadata generated internally. When writing to memtable, each entry is transformed to a `ProtectionInfoKVOTS` (i.e., dropping coverage of CF ID and adding coverage of sequence number), since at that point we know the sequence number, and have already selected a memtable corresponding to a particular CF. This protection info is verified once the entry is encoded in the `MemTable` buffer.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/7748
Test Plan:
- an integration test to verify a wide variety of single-byte changes to the encoded `MemTable` buffer are caught
- add to stress/crash test to verify it works in variety of configs/operations without intentional corruption
- [deferred] unit tests for `ProtectionInfo.*` classes for edge cases like KV swap, `SliceParts` and `Slice` APIs are interchangeable, etc.
Reviewed By: pdillinger
Differential Revision: D25754492
Pulled By: ajkr
fbshipit-source-id: e481bac6c03c2ab268be41359730f1ceb9964866
2021-01-29 20:17:17 +00:00
|
|
|
mutex_(kNumberOfMutexStripes, kGetSliceNPHash64UnseededFnPtr),
|
2020-10-15 20:02:44 +00:00
|
|
|
immutable_cf_options_(immutable_cf_options),
|
|
|
|
file_options_(file_options),
|
|
|
|
column_family_id_(column_family_id),
|
2021-02-16 17:47:12 +00:00
|
|
|
blob_file_read_hist_(blob_file_read_hist),
|
|
|
|
io_tracer_(io_tracer) {
|
2020-10-15 20:02:44 +00:00
|
|
|
assert(cache_);
|
|
|
|
assert(immutable_cf_options_);
|
|
|
|
assert(file_options_);
|
|
|
|
}
|
|
|
|
|
|
|
|
Status BlobFileCache::GetBlobFileReader(
|
|
|
|
uint64_t blob_file_number,
|
|
|
|
CacheHandleGuard<BlobFileReader>* blob_file_reader) {
|
|
|
|
assert(blob_file_reader);
|
|
|
|
assert(blob_file_reader->IsEmpty());
|
|
|
|
|
|
|
|
const Slice key = GetSlice(&blob_file_number);
|
|
|
|
|
|
|
|
assert(cache_);
|
|
|
|
|
|
|
|
Cache::Handle* handle = cache_->Lookup(key);
|
|
|
|
if (handle) {
|
|
|
|
*blob_file_reader = CacheHandleGuard<BlobFileReader>(cache_, handle);
|
|
|
|
return Status::OK();
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_SYNC_POINT("BlobFileCache::GetBlobFileReader:DoubleCheck");
|
|
|
|
|
|
|
|
// Check again while holding mutex
|
|
|
|
MutexLock lock(mutex_.get(key));
|
|
|
|
|
|
|
|
handle = cache_->Lookup(key);
|
|
|
|
if (handle) {
|
|
|
|
*blob_file_reader = CacheHandleGuard<BlobFileReader>(cache_, handle);
|
|
|
|
return Status::OK();
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(immutable_cf_options_);
|
|
|
|
Statistics* const statistics = immutable_cf_options_->statistics;
|
|
|
|
|
|
|
|
RecordTick(statistics, NO_FILE_OPENS);
|
|
|
|
|
|
|
|
std::unique_ptr<BlobFileReader> reader;
|
|
|
|
|
|
|
|
{
|
|
|
|
assert(file_options_);
|
|
|
|
const Status s = BlobFileReader::Create(
|
|
|
|
*immutable_cf_options_, *file_options_, column_family_id_,
|
2021-02-16 17:47:12 +00:00
|
|
|
blob_file_read_hist_, blob_file_number, io_tracer_, &reader);
|
2020-10-15 20:02:44 +00:00
|
|
|
if (!s.ok()) {
|
|
|
|
RecordTick(statistics, NO_FILE_ERRORS);
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
constexpr size_t charge = 1;
|
|
|
|
|
|
|
|
const Status s = cache_->Insert(key, reader.get(), charge,
|
|
|
|
&DeleteCacheEntry<BlobFileReader>, &handle);
|
|
|
|
if (!s.ok()) {
|
|
|
|
RecordTick(statistics, NO_FILE_ERRORS);
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
reader.release();
|
|
|
|
|
|
|
|
*blob_file_reader = CacheHandleGuard<BlobFileReader>(cache_, handle);
|
|
|
|
|
|
|
|
return Status::OK();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace ROCKSDB_NAMESPACE
|