From 32b4d4ad47caf312eabe563776913ab67e08710c Mon Sep 17 00:00:00 2001 From: Andrew Kryczka Date: Mon, 15 Oct 2018 16:18:55 -0700 Subject: [PATCH] Avoid per-key linear scan over snapshots in compaction (#4495) Summary: `CompactionIterator::snapshots_` is ordered by ascending seqnum, just like `DBImpl`'s linked list of snapshots from which it was copied. This PR exploits this ordering to make `findEarliestVisibleSnapshot` do binary search rather than linear scan. This can make flush/compaction significantly faster when many snapshots exist since that function is called on every single key. Pull Request resolved: https://github.com/facebook/rocksdb/pull/4495 Differential Revision: D10386470 Pulled By: ajkr fbshipit-source-id: 29734991631227b6b7b677e156ac567690118a8b --- HISTORY.md | 1 + db/compaction_iterator.cc | 29 ++++++++++++++++++++--------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 38eade89c..cda6baf5c 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -6,6 +6,7 @@ ### Bug Fixes * Fix corner case where a write group leader blocked due to write stall blocks other writers in queue with WriteOptions::no_slowdown set. * Fix in-memory range tombstone truncation to avoid erroneously covering newer keys at a lower level, and include range tombstones in compacted files whose largest key is the range tombstone's start key. +* Fix slow flush/compaction when DB contains many snapshots. The problem became noticeable to us in DBs with 100,000+ snapshots, though it will affect others at different thresholds. ## 5.17.0 (10/05/2018) ### Public API Change diff --git a/db/compaction_iterator.cc b/db/compaction_iterator.cc index ac5b70d8d..d81b630f3 100644 --- a/db/compaction_iterator.cc +++ b/db/compaction_iterator.cc @@ -77,6 +77,12 @@ CompactionIterator::CompactionIterator( earliest_snapshot_ = snapshots_->at(0); latest_snapshot_ = snapshots_->back(); } +#ifndef NDEBUG + // findEarliestVisibleSnapshot assumes this ordering. + for (size_t i = 1; i < snapshots_->size(); ++i) { + assert(snapshots_->at(i - 1) <= snapshots_->at(i)); + } +#endif if (compaction_filter_ != nullptr) { if (compaction_filter_->IgnoreSnapshots()) { ignore_snapshots_ = true; @@ -628,18 +634,23 @@ void CompactionIterator::PrepareOutput() { inline SequenceNumber CompactionIterator::findEarliestVisibleSnapshot( SequenceNumber in, SequenceNumber* prev_snapshot) { assert(snapshots_->size()); - SequenceNumber prev = kMaxSequenceNumber; - for (const auto cur : *snapshots_) { - assert(prev == kMaxSequenceNumber || prev <= cur); - if (cur >= in && (snapshot_checker_ == nullptr || - snapshot_checker_->IsInSnapshot(in, cur))) { - *prev_snapshot = prev == kMaxSequenceNumber ? 0 : prev; + auto snapshots_iter = std::lower_bound( + snapshots_->begin(), snapshots_->end(), in); + if (snapshots_iter == snapshots_->begin()) { + *prev_snapshot = 0; + } else { + *prev_snapshot = *std::prev(snapshots_iter); + assert(*prev_snapshot < in); + } + for (; snapshots_iter != snapshots_->end(); ++snapshots_iter) { + auto cur = *snapshots_iter; + assert(in <= cur); + if (snapshot_checker_ == nullptr || + snapshot_checker_->IsInSnapshot(in, cur)) { return cur; } - prev = cur; - assert(prev < kMaxSequenceNumber); + *prev_snapshot = cur; } - *prev_snapshot = prev; return kMaxSequenceNumber; }