mirror of
https://github.com/facebook/rocksdb.git
synced 2026-07-07 14:47:40 +08:00
Compare commits
10 Commits
f2c0eb41ef
...
v8.3.3
| Author | SHA1 | Date | |
|---|---|---|---|
| 791a7fe402 | |||
| ecfb8779c2 | |||
| 869801945b | |||
| a89bd81df4 | |||
| 3f7c92b975 | |||
| e978dccd7a | |||
| 32c6de7bc3 | |||
| b36f691e58 | |||
| 0f6d363db9 | |||
| 031714aa83 |
+15
-1
@@ -1,5 +1,19 @@
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
## 8.3.3 (09/01/2023)
|
||||
### Bug Fixes
|
||||
* Fix a bug where if there is an error reading from offset 0 of a file from L1+ and that the file is not the first file in the sorted run, data can be lost in compaction and read/scan can return incorrect results.
|
||||
* Fix a bug where iterator may return incorrect result for DeleteRange() users if there was an error reading from a file.
|
||||
* Fixed a race condition in `GenericRateLimiter` that could cause it to stop granting requests
|
||||
|
||||
## 8.3.2 (06/14/2023)
|
||||
### Bug Fixes
|
||||
* Reduced cases of illegally using Env::Default() during static destruction by never destroying the internal PosixEnv itself (except for builds checking for memory leaks). (#11538)
|
||||
|
||||
## 8.3.1 (06/07/2023)
|
||||
### Performance Improvements
|
||||
* Fixed higher read QPS during DB::Open() reading files created prior to #11406, especially when reading many small file (size < 52 MB) during DB::Open() and partitioned filter or index is used.
|
||||
|
||||
## 8.3.0 (05/19/2023)
|
||||
### New Features
|
||||
* Introduced a new option `block_protection_bytes_per_key`, which can be used to enable per key-value integrity protection for in-memory blocks in block cache (#11287).
|
||||
* Added `JemallocAllocatorOptions::num_arenas`. Setting `num_arenas > 1` may mitigate mutex contention in the allocator, particularly in scenarios where block allocations commonly bypass jemalloc tcache.
|
||||
|
||||
Vendored
+12
-8
@@ -213,13 +213,14 @@ class PosixEnv : public CompositeEnv {
|
||||
const char* Name() const override { return kClassName(); }
|
||||
const char* NickName() const override { return kDefaultName(); }
|
||||
|
||||
~PosixEnv() override {
|
||||
if (this == Env::Default()) {
|
||||
for (const auto tid : threads_to_join_) {
|
||||
struct JoinThreadsOnExit {
|
||||
explicit JoinThreadsOnExit(PosixEnv& _deflt) : deflt(_deflt) {}
|
||||
~JoinThreadsOnExit() {
|
||||
for (const auto tid : deflt.threads_to_join_) {
|
||||
pthread_join(tid, nullptr);
|
||||
}
|
||||
for (int pool_id = 0; pool_id < Env::Priority::TOTAL; ++pool_id) {
|
||||
thread_pools_[pool_id].JoinAllThreads();
|
||||
deflt.thread_pools_[pool_id].JoinAllThreads();
|
||||
}
|
||||
// Do not delete the thread_status_updater_ in order to avoid the
|
||||
// free after use when Env::Default() is destructed while some other
|
||||
@@ -227,7 +228,8 @@ class PosixEnv : public CompositeEnv {
|
||||
// PosixEnv instances use the same thread_status_updater_, so never
|
||||
// explicitly delete it.
|
||||
}
|
||||
}
|
||||
PosixEnv& deflt;
|
||||
};
|
||||
|
||||
void SetFD_CLOEXEC(int fd, const EnvOptions* options) {
|
||||
if ((options == nullptr || options->set_fd_cloexec) && fd > 0) {
|
||||
@@ -501,9 +503,11 @@ Env* Env::Default() {
|
||||
ThreadLocalPtr::InitSingletons();
|
||||
CompressionContextCache::InitSingleton();
|
||||
INIT_SYNC_POINT_SINGLETONS();
|
||||
// ~PosixEnv must be called on exit
|
||||
//**TODO: Can we make this a STATIC_AVOID_DESTRUCTION?
|
||||
static PosixEnv default_env;
|
||||
// Avoid problems with accessing most members of Env::Default() during
|
||||
// static destruction.
|
||||
STATIC_AVOID_DESTRUCTION(PosixEnv, default_env);
|
||||
// This destructor must be called on exit
|
||||
static PosixEnv::JoinThreadsOnExit thread_joiner(default_env);
|
||||
return &default_env;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+17
@@ -3551,6 +3551,23 @@ TEST_F(TestAsyncRead, ReadAsync) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct StaticDestructionTester {
|
||||
bool activated = false;
|
||||
~StaticDestructionTester() {
|
||||
if (activated && !kMustFreeHeapAllocations) {
|
||||
// Make sure we can still call some things on default Env.
|
||||
std::string hostname;
|
||||
Env::Default()->GetHostNameString(&hostname);
|
||||
}
|
||||
}
|
||||
} static_destruction_tester;
|
||||
|
||||
TEST(EnvTestMisc, StaticDestruction) {
|
||||
// Check for any crashes during static destruction.
|
||||
static_destruction_tester.activated = true;
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -234,6 +234,8 @@ class FilePrefetchBuffer {
|
||||
// tracked if track_min_offset = true.
|
||||
size_t min_offset_read() const { return min_offset_read_; }
|
||||
|
||||
size_t GetPrefetchOffset() const { return bufs_[curr_].offset_; }
|
||||
|
||||
// Called in case of implicit auto prefetching.
|
||||
void UpdateReadPattern(const uint64_t& offset, const size_t& len,
|
||||
bool decrease_readaheadsize) {
|
||||
|
||||
@@ -374,7 +374,6 @@ inline std::shared_ptr<SecondaryCache> NewCompressedSecondaryCache(
|
||||
// * Requires an extra tuning parameter: see estimated_entry_charge below.
|
||||
// Similarly, substantially changing the capacity with SetCapacity could
|
||||
// harm efficiency.
|
||||
// * SecondaryCache is not yet supported.
|
||||
// * Cache priorities are less aggressively enforced, which could cause
|
||||
// cache dilution from long range scans (unless they use fill_cache=false).
|
||||
// * Can be worse for small caches, because if almost all of a cache shard is
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// minor or major version number planned for release.
|
||||
#define ROCKSDB_MAJOR 8
|
||||
#define ROCKSDB_MINOR 3
|
||||
#define ROCKSDB_PATCH 0
|
||||
#define ROCKSDB_PATCH 3
|
||||
|
||||
// Do not use these. We made the mistake of declaring macros starting with
|
||||
// double underscore. Now we have to live with our choice. We'll deprecate these
|
||||
|
||||
@@ -828,10 +828,7 @@ Status BlockBasedTable::PrefetchTail(
|
||||
// index/filter is enabled and top-level partition pinning is enabled.
|
||||
// That's because we need to issue readahead before we read the
|
||||
// properties, at which point we don't yet know the index type.
|
||||
tail_prefetch_size =
|
||||
prefetch_all || preload_all
|
||||
? static_cast<size_t>(4 * 1024 + 0.01 * file_size)
|
||||
: 4 * 1024;
|
||||
tail_prefetch_size = prefetch_all || preload_all ? 512 * 1024 : 4 * 1024;
|
||||
|
||||
ROCKS_LOG_WARN(logger,
|
||||
"Tail prefetch size %zu is calculated based on heuristics",
|
||||
|
||||
@@ -484,7 +484,8 @@ Status PartitionedFilterBlockReader::CacheDependencies(
|
||||
handle.offset() + handle.size() + BlockBasedTable::kBlockTrailerSize;
|
||||
uint64_t prefetch_len = last_off - prefetch_off;
|
||||
std::unique_ptr<FilePrefetchBuffer> prefetch_buffer;
|
||||
if (tail_prefetch_buffer == nullptr || !tail_prefetch_buffer->Enabled()) {
|
||||
if (tail_prefetch_buffer == nullptr || !tail_prefetch_buffer->Enabled() ||
|
||||
tail_prefetch_buffer->GetPrefetchOffset() > prefetch_off) {
|
||||
rep->CreateFilePrefetchBuffer(
|
||||
0, 0, &prefetch_buffer, false /* Implicit autoreadahead */,
|
||||
0 /*num_reads_*/, 0 /*num_file_reads_for_auto_readahead*/);
|
||||
|
||||
@@ -162,7 +162,8 @@ Status PartitionIndexReader::CacheDependencies(
|
||||
handle.offset() + BlockBasedTable::BlockSizeWithTrailer(handle);
|
||||
uint64_t prefetch_len = last_off - prefetch_off;
|
||||
std::unique_ptr<FilePrefetchBuffer> prefetch_buffer;
|
||||
if (tail_prefetch_buffer == nullptr || !tail_prefetch_buffer->Enabled()) {
|
||||
if (tail_prefetch_buffer == nullptr || !tail_prefetch_buffer->Enabled() ||
|
||||
tail_prefetch_buffer->GetPrefetchOffset() > prefetch_off) {
|
||||
rep->CreateFilePrefetchBuffer(
|
||||
0, 0, &prefetch_buffer, false /*Implicit auto readahead*/,
|
||||
0 /*num_reads_*/, 0 /*num_file_reads_for_auto_readahead*/);
|
||||
|
||||
@@ -329,6 +329,7 @@ void CompactionMergingIterator::FindNextVisibleKey() {
|
||||
assert(current->iter.status().ok());
|
||||
minHeap_.replace_top(current);
|
||||
} else {
|
||||
considerStatus(current->iter.status());
|
||||
minHeap_.pop();
|
||||
}
|
||||
if (range_tombstone_iters_[current->level]) {
|
||||
|
||||
@@ -308,6 +308,7 @@ class MergingIterator : public InternalIterator {
|
||||
// holds after this call, and minHeap_.top().iter points to the
|
||||
// first key >= target among children_ that is not covered by any range
|
||||
// tombstone.
|
||||
status_ = Status::OK();
|
||||
SeekImpl(target);
|
||||
FindNextVisibleKey();
|
||||
|
||||
@@ -321,6 +322,7 @@ class MergingIterator : public InternalIterator {
|
||||
void SeekForPrev(const Slice& target) override {
|
||||
assert(range_tombstone_iters_.empty() ||
|
||||
range_tombstone_iters_.size() == children_.size());
|
||||
status_ = Status::OK();
|
||||
SeekForPrevImpl(target);
|
||||
FindPrevVisibleKey();
|
||||
|
||||
@@ -798,7 +800,6 @@ void MergingIterator::SeekImpl(const Slice& target, size_t starting_level,
|
||||
active_.erase(active_.lower_bound(starting_level), active_.end());
|
||||
}
|
||||
|
||||
status_ = Status::OK();
|
||||
IterKey current_search_key;
|
||||
current_search_key.SetInternalKey(target, false /* copy */);
|
||||
// Seek target might change to some range tombstone end key, so
|
||||
@@ -931,6 +932,7 @@ bool MergingIterator::SkipNextDeleted() {
|
||||
InsertRangeTombstoneToMinHeap(current->level, true /* start_key */,
|
||||
true /* replace_top */);
|
||||
} else {
|
||||
// TruncatedRangeDelIterator does not have status
|
||||
minHeap_.pop();
|
||||
}
|
||||
return true /* current key deleted */;
|
||||
@@ -988,6 +990,9 @@ bool MergingIterator::SkipNextDeleted() {
|
||||
if (current->iter.Valid()) {
|
||||
assert(current->iter.status().ok());
|
||||
minHeap_.push(current);
|
||||
} else {
|
||||
// TODO(cbi): check status and early return if non-ok.
|
||||
considerStatus(current->iter.status());
|
||||
}
|
||||
// Invariants (rti) and (phi)
|
||||
if (range_tombstone_iters_[current->level] &&
|
||||
@@ -1027,6 +1032,7 @@ bool MergingIterator::SkipNextDeleted() {
|
||||
if (current->iter.Valid()) {
|
||||
minHeap_.replace_top(current);
|
||||
} else {
|
||||
considerStatus(current->iter.status());
|
||||
minHeap_.pop();
|
||||
}
|
||||
return true /* current key deleted */;
|
||||
@@ -1078,7 +1084,6 @@ void MergingIterator::SeekForPrevImpl(const Slice& target,
|
||||
active_.erase(active_.lower_bound(starting_level), active_.end());
|
||||
}
|
||||
|
||||
status_ = Status::OK();
|
||||
IterKey current_search_key;
|
||||
current_search_key.SetInternalKey(target, false /* copy */);
|
||||
// Seek target might change to some range tombstone end key, so
|
||||
@@ -1199,6 +1204,8 @@ bool MergingIterator::SkipPrevDeleted() {
|
||||
if (current->iter.Valid()) {
|
||||
assert(current->iter.status().ok());
|
||||
maxHeap_->push(current);
|
||||
} else {
|
||||
considerStatus(current->iter.status());
|
||||
}
|
||||
|
||||
if (range_tombstone_iters_[current->level] &&
|
||||
@@ -1241,6 +1248,7 @@ bool MergingIterator::SkipPrevDeleted() {
|
||||
if (current->iter.Valid()) {
|
||||
maxHeap_->replace_top(current);
|
||||
} else {
|
||||
considerStatus(current->iter.status());
|
||||
maxHeap_->pop();
|
||||
}
|
||||
return true /* current key deleted */;
|
||||
|
||||
+10
-10
@@ -179,16 +179,16 @@ void GenericRateLimiter::Request(int64_t bytes, const Env::IOPriority pri,
|
||||
// Whichever thread reaches here first performs duty (2) as described
|
||||
// above.
|
||||
RefillBytesAndGrantRequestsLocked();
|
||||
if (r.request_bytes == 0) {
|
||||
// If there is any remaining requests, make sure there exists at least
|
||||
// one candidate is awake for future duties by signaling a front request
|
||||
// of a queue.
|
||||
for (int i = Env::IO_TOTAL - 1; i >= Env::IO_LOW; --i) {
|
||||
std::deque<Req*> queue = queue_[i];
|
||||
if (!queue.empty()) {
|
||||
queue.front()->cv.Signal();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (r.request_bytes == 0) {
|
||||
// If there is any remaining requests, make sure there exists at least
|
||||
// one candidate is awake for future duties by signaling a front request
|
||||
// of a queue.
|
||||
for (int i = Env::IO_TOTAL - 1; i >= Env::IO_LOW; --i) {
|
||||
auto& queue = queue_[i];
|
||||
if (!queue.empty()) {
|
||||
queue.front()->cv.Signal();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user