2015-09-10 21:35:25 +00:00
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
2016-02-09 23:12:00 +00:00
|
|
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
2015-09-10 21:35:25 +00:00
|
|
|
// This source code is licensed under the BSD-style license found in the
|
|
|
|
// LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
// of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
|
|
|
|
#include "db/compaction_iterator.h"
|
2017-04-27 19:19:55 +00:00
|
|
|
#include "rocksdb/listener.h"
|
2015-10-12 22:06:38 +00:00
|
|
|
#include "table/internal_iterator.h"
|
2015-09-10 21:35:25 +00:00
|
|
|
|
|
|
|
namespace rocksdb {
|
|
|
|
|
2017-04-19 00:12:50 +00:00
|
|
|
#ifndef ROCKSDB_LITE
|
2017-04-18 19:00:36 +00:00
|
|
|
CompactionEventListener::CompactionListenerValueType fromInternalValueType(
|
|
|
|
ValueType vt) {
|
|
|
|
switch (vt) {
|
|
|
|
case kTypeDeletion:
|
|
|
|
return CompactionEventListener::CompactionListenerValueType::kDelete;
|
|
|
|
case kTypeValue:
|
|
|
|
return CompactionEventListener::CompactionListenerValueType::kValue;
|
|
|
|
case kTypeMerge:
|
|
|
|
return CompactionEventListener::CompactionListenerValueType::
|
|
|
|
kMergeOperand;
|
|
|
|
case kTypeSingleDeletion:
|
|
|
|
return CompactionEventListener::CompactionListenerValueType::
|
|
|
|
kSingleDelete;
|
|
|
|
case kTypeRangeDeletion:
|
|
|
|
return CompactionEventListener::CompactionListenerValueType::kRangeDelete;
|
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
return CompactionEventListener::CompactionListenerValueType::kInvalid;
|
|
|
|
}
|
|
|
|
}
|
2017-04-19 00:12:50 +00:00
|
|
|
#endif // ROCKSDB_LITE
|
2017-04-18 19:00:36 +00:00
|
|
|
|
2015-09-10 21:35:25 +00:00
|
|
|
CompactionIterator::CompactionIterator(
|
2015-10-12 22:06:38 +00:00
|
|
|
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
2015-09-10 21:35:25 +00:00
|
|
|
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
2015-12-08 20:25:48 +00:00
|
|
|
SequenceNumber earliest_write_conflict_snapshot, Env* env,
|
Compaction Support for Range Deletion
Summary:
This diff introduces RangeDelAggregator, which takes ownership of iterators
provided to it via AddTombstones(). The tombstones are organized in a two-level
map (snapshot stripe -> begin key -> tombstone). Tombstone creation avoids data
copy by holding Slices returned by the iterator, which remain valid thanks to pinning.
For compaction, we create a hierarchical range tombstone iterator with structure
matching the iterator over compaction input data. An aggregator based on that
iterator is used by CompactionIterator to determine which keys are covered by
range tombstones. In case of merge operand, the same aggregator is used by
MergeHelper. Upon finishing each file in the compaction, relevant range tombstones
are added to the output file's range tombstone metablock and file boundaries are
updated accordingly.
To check whether a key is covered by range tombstone, RangeDelAggregator::ShouldDelete()
considers tombstones in the key's snapshot stripe. When this function is used outside of
compaction, it also checks newer stripes, which can contain covering tombstones. Currently
the intra-stripe check involves a linear scan; however, in the future we plan to collapse ranges
within a stripe such that binary search can be used.
RangeDelAggregator::AddToBuilder() adds all range tombstones in the table's key-range
to a new table's range tombstone meta-block. Since range tombstones may fall in the gap
between files, we may need to extend some files' key-ranges. The strategy is (1) first file
extends as far left as possible and other files do not extend left, (2) all files extend right
until either the start of the next file or the end of the last range tombstone in the gap,
whichever comes first.
One other notable change is adding release/move semantics to ScopedArenaIterator
such that it can be used to transfer ownership of an arena-allocated iterator, similar to
how unique_ptr is used for malloc'd data.
Depends on D61473
Test Plan: compaction_iterator_test, mock_table, end-to-end tests in D63927
Reviewers: sdong, IslamAbdelRahman, wanning, yhchiang, lightmark
Reviewed By: lightmark
Subscribers: andrewkr, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D62205
2016-10-18 19:04:56 +00:00
|
|
|
bool expect_valid_internal_key, RangeDelAggregator* range_del_agg,
|
|
|
|
const Compaction* compaction, const CompactionFilter* compaction_filter,
|
2017-04-18 19:00:36 +00:00
|
|
|
CompactionEventListener* compaction_listener,
|
2017-01-11 23:01:21 +00:00
|
|
|
const std::atomic<bool>* shutting_down)
|
2016-12-01 15:00:17 +00:00
|
|
|
: CompactionIterator(
|
|
|
|
input, cmp, merge_helper, last_sequence, snapshots,
|
|
|
|
earliest_write_conflict_snapshot, env, expect_valid_internal_key,
|
|
|
|
range_del_agg,
|
|
|
|
std::unique_ptr<CompactionProxy>(
|
|
|
|
compaction ? new CompactionProxy(compaction) : nullptr),
|
2017-04-18 19:00:36 +00:00
|
|
|
compaction_filter, compaction_listener, shutting_down) {}
|
2016-12-01 15:00:17 +00:00
|
|
|
|
|
|
|
CompactionIterator::CompactionIterator(
|
|
|
|
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
|
|
|
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
|
|
|
SequenceNumber earliest_write_conflict_snapshot, Env* env,
|
|
|
|
bool expect_valid_internal_key, RangeDelAggregator* range_del_agg,
|
|
|
|
std::unique_ptr<CompactionProxy> compaction,
|
2017-01-11 23:01:21 +00:00
|
|
|
const CompactionFilter* compaction_filter,
|
2017-04-18 19:00:36 +00:00
|
|
|
CompactionEventListener* compaction_listener,
|
2017-01-11 23:01:21 +00:00
|
|
|
const std::atomic<bool>* shutting_down)
|
2015-09-10 21:35:25 +00:00
|
|
|
: input_(input),
|
|
|
|
cmp_(cmp),
|
|
|
|
merge_helper_(merge_helper),
|
|
|
|
snapshots_(snapshots),
|
2015-12-08 20:25:48 +00:00
|
|
|
earliest_write_conflict_snapshot_(earliest_write_conflict_snapshot),
|
2015-09-10 21:35:25 +00:00
|
|
|
env_(env),
|
|
|
|
expect_valid_internal_key_(expect_valid_internal_key),
|
Compaction Support for Range Deletion
Summary:
This diff introduces RangeDelAggregator, which takes ownership of iterators
provided to it via AddTombstones(). The tombstones are organized in a two-level
map (snapshot stripe -> begin key -> tombstone). Tombstone creation avoids data
copy by holding Slices returned by the iterator, which remain valid thanks to pinning.
For compaction, we create a hierarchical range tombstone iterator with structure
matching the iterator over compaction input data. An aggregator based on that
iterator is used by CompactionIterator to determine which keys are covered by
range tombstones. In case of merge operand, the same aggregator is used by
MergeHelper. Upon finishing each file in the compaction, relevant range tombstones
are added to the output file's range tombstone metablock and file boundaries are
updated accordingly.
To check whether a key is covered by range tombstone, RangeDelAggregator::ShouldDelete()
considers tombstones in the key's snapshot stripe. When this function is used outside of
compaction, it also checks newer stripes, which can contain covering tombstones. Currently
the intra-stripe check involves a linear scan; however, in the future we plan to collapse ranges
within a stripe such that binary search can be used.
RangeDelAggregator::AddToBuilder() adds all range tombstones in the table's key-range
to a new table's range tombstone meta-block. Since range tombstones may fall in the gap
between files, we may need to extend some files' key-ranges. The strategy is (1) first file
extends as far left as possible and other files do not extend left, (2) all files extend right
until either the start of the next file or the end of the last range tombstone in the gap,
whichever comes first.
One other notable change is adding release/move semantics to ScopedArenaIterator
such that it can be used to transfer ownership of an arena-allocated iterator, similar to
how unique_ptr is used for malloc'd data.
Depends on D61473
Test Plan: compaction_iterator_test, mock_table, end-to-end tests in D63927
Reviewers: sdong, IslamAbdelRahman, wanning, yhchiang, lightmark
Reviewed By: lightmark
Subscribers: andrewkr, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D62205
2016-10-18 19:04:56 +00:00
|
|
|
range_del_agg_(range_del_agg),
|
2016-12-01 15:00:17 +00:00
|
|
|
compaction_(std::move(compaction)),
|
2015-09-10 21:35:25 +00:00
|
|
|
compaction_filter_(compaction_filter),
|
2017-04-18 19:00:36 +00:00
|
|
|
compaction_listener_(compaction_listener),
|
2017-01-11 23:01:21 +00:00
|
|
|
shutting_down_(shutting_down),
|
2017-04-18 19:00:36 +00:00
|
|
|
ignore_snapshots_(false),
|
2015-09-10 21:35:25 +00:00
|
|
|
merge_out_iter_(merge_helper_) {
|
|
|
|
assert(compaction_filter_ == nullptr || compaction_ != nullptr);
|
|
|
|
bottommost_level_ =
|
|
|
|
compaction_ == nullptr ? false : compaction_->bottommost_level();
|
|
|
|
if (compaction_ != nullptr) {
|
|
|
|
level_ptrs_ = std::vector<size_t>(compaction_->number_levels(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (snapshots_->size() == 0) {
|
|
|
|
// optimize for fast path if there are no snapshots
|
2016-10-13 17:49:06 +00:00
|
|
|
visible_at_tip_ = true;
|
2017-04-21 02:44:55 +00:00
|
|
|
earliest_snapshot_ = kMaxSequenceNumber;
|
2015-09-10 21:35:25 +00:00
|
|
|
latest_snapshot_ = 0;
|
|
|
|
} else {
|
2016-10-13 17:49:06 +00:00
|
|
|
visible_at_tip_ = false;
|
2015-09-10 21:35:25 +00:00
|
|
|
earliest_snapshot_ = snapshots_->at(0);
|
|
|
|
latest_snapshot_ = snapshots_->back();
|
|
|
|
}
|
2017-04-18 19:00:36 +00:00
|
|
|
if (compaction_filter_ != nullptr) {
|
|
|
|
if (compaction_filter_->IgnoreSnapshots()) ignore_snapshots_ = true;
|
2015-11-20 23:57:26 +00:00
|
|
|
} else {
|
|
|
|
ignore_snapshots_ = false;
|
|
|
|
}
|
Introduce FullMergeV2 (eliminate memcpy from merge operators)
Summary:
This diff update the code to pin the merge operator operands while the merge operation is done, so that we can eliminate the memcpy cost, to do that we need a new public API for FullMerge that replace the std::deque<std::string> with std::vector<Slice>
This diff is stacked on top of D56493 and D56511
In this diff we
- Update FullMergeV2 arguments to be encapsulated in MergeOperationInput and MergeOperationOutput which will make it easier to add new arguments in the future
- Replace std::deque<std::string> with std::vector<Slice> to pass operands
- Replace MergeContext std::deque with std::vector (based on a simple benchmark I ran https://gist.github.com/IslamAbdelRahman/78fc86c9ab9f52b1df791e58943fb187)
- Allow FullMergeV2 output to be an existing operand
```
[Everything in Memtable | 10K operands | 10 KB each | 1 operand per key]
DEBUG_LEVEL=0 make db_bench -j64 && ./db_bench --benchmarks="mergerandom,readseq,readseq,readseq,readseq,readseq" --merge_operator="max" --merge_keys=10000 --num=10000 --disable_auto_compactions --value_size=10240 --write_buffer_size=1000000000
[FullMergeV2]
readseq : 0.607 micros/op 1648235 ops/sec; 16121.2 MB/s
readseq : 0.478 micros/op 2091546 ops/sec; 20457.2 MB/s
readseq : 0.252 micros/op 3972081 ops/sec; 38850.5 MB/s
readseq : 0.237 micros/op 4218328 ops/sec; 41259.0 MB/s
readseq : 0.247 micros/op 4043927 ops/sec; 39553.2 MB/s
[master]
readseq : 3.935 micros/op 254140 ops/sec; 2485.7 MB/s
readseq : 3.722 micros/op 268657 ops/sec; 2627.7 MB/s
readseq : 3.149 micros/op 317605 ops/sec; 3106.5 MB/s
readseq : 3.125 micros/op 320024 ops/sec; 3130.1 MB/s
readseq : 4.075 micros/op 245374 ops/sec; 2400.0 MB/s
```
```
[Everything in Memtable | 10K operands | 10 KB each | 10 operand per key]
DEBUG_LEVEL=0 make db_bench -j64 && ./db_bench --benchmarks="mergerandom,readseq,readseq,readseq,readseq,readseq" --merge_operator="max" --merge_keys=1000 --num=10000 --disable_auto_compactions --value_size=10240 --write_buffer_size=1000000000
[FullMergeV2]
readseq : 3.472 micros/op 288018 ops/sec; 2817.1 MB/s
readseq : 2.304 micros/op 434027 ops/sec; 4245.2 MB/s
readseq : 1.163 micros/op 859845 ops/sec; 8410.0 MB/s
readseq : 1.192 micros/op 838926 ops/sec; 8205.4 MB/s
readseq : 1.250 micros/op 800000 ops/sec; 7824.7 MB/s
[master]
readseq : 24.025 micros/op 41623 ops/sec; 407.1 MB/s
readseq : 18.489 micros/op 54086 ops/sec; 529.0 MB/s
readseq : 18.693 micros/op 53495 ops/sec; 523.2 MB/s
readseq : 23.621 micros/op 42335 ops/sec; 414.1 MB/s
readseq : 18.775 micros/op 53262 ops/sec; 521.0 MB/s
```
```
[Everything in Block cache | 10K operands | 10 KB each | 1 operand per key]
[FullMergeV2]
$ DEBUG_LEVEL=0 make db_bench -j64 && ./db_bench --benchmarks="readseq,readseq,readseq,readseq,readseq" --merge_operator="max" --num=100000 --db="/dev/shm/merge-random-10K-10KB" --cache_size=1000000000 --use_existing_db --disable_auto_compactions
readseq : 14.741 micros/op 67837 ops/sec; 663.5 MB/s
readseq : 1.029 micros/op 971446 ops/sec; 9501.6 MB/s
readseq : 0.974 micros/op 1026229 ops/sec; 10037.4 MB/s
readseq : 0.965 micros/op 1036080 ops/sec; 10133.8 MB/s
readseq : 0.943 micros/op 1060657 ops/sec; 10374.2 MB/s
[master]
readseq : 16.735 micros/op 59755 ops/sec; 584.5 MB/s
readseq : 3.029 micros/op 330151 ops/sec; 3229.2 MB/s
readseq : 3.136 micros/op 318883 ops/sec; 3119.0 MB/s
readseq : 3.065 micros/op 326245 ops/sec; 3191.0 MB/s
readseq : 3.014 micros/op 331813 ops/sec; 3245.4 MB/s
```
```
[Everything in Block cache | 10K operands | 10 KB each | 10 operand per key]
DEBUG_LEVEL=0 make db_bench -j64 && ./db_bench --benchmarks="readseq,readseq,readseq,readseq,readseq" --merge_operator="max" --num=100000 --db="/dev/shm/merge-random-10-operands-10K-10KB" --cache_size=1000000000 --use_existing_db --disable_auto_compactions
[FullMergeV2]
readseq : 24.325 micros/op 41109 ops/sec; 402.1 MB/s
readseq : 1.470 micros/op 680272 ops/sec; 6653.7 MB/s
readseq : 1.231 micros/op 812347 ops/sec; 7945.5 MB/s
readseq : 1.091 micros/op 916590 ops/sec; 8965.1 MB/s
readseq : 1.109 micros/op 901713 ops/sec; 8819.6 MB/s
[master]
readseq : 27.257 micros/op 36687 ops/sec; 358.8 MB/s
readseq : 4.443 micros/op 225073 ops/sec; 2201.4 MB/s
readseq : 5.830 micros/op 171526 ops/sec; 1677.7 MB/s
readseq : 4.173 micros/op 239635 ops/sec; 2343.8 MB/s
readseq : 4.150 micros/op 240963 ops/sec; 2356.8 MB/s
```
Test Plan: COMPILE_WITH_ASAN=1 make check -j64
Reviewers: yhchiang, andrewkr, sdong
Reviewed By: sdong
Subscribers: lovro, andrewkr, dhruba
Differential Revision: https://reviews.facebook.net/D57075
2016-07-20 16:49:03 +00:00
|
|
|
input_->SetPinnedItersMgr(&pinned_iters_mgr_);
|
|
|
|
}
|
|
|
|
|
|
|
|
CompactionIterator::~CompactionIterator() {
|
|
|
|
// input_ Iteartor lifetime is longer than pinned_iters_mgr_ lifetime
|
|
|
|
input_->SetPinnedItersMgr(nullptr);
|
2015-09-10 21:35:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CompactionIterator::ResetRecordCounts() {
|
|
|
|
iter_stats_.num_record_drop_user = 0;
|
|
|
|
iter_stats_.num_record_drop_hidden = 0;
|
|
|
|
iter_stats_.num_record_drop_obsolete = 0;
|
2016-11-28 19:44:40 +00:00
|
|
|
iter_stats_.num_record_drop_range_del = 0;
|
|
|
|
iter_stats_.num_range_del_drop_obsolete = 0;
|
2015-09-10 21:35:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CompactionIterator::SeekToFirst() {
|
|
|
|
NextFromInput();
|
|
|
|
PrepareOutput();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CompactionIterator::Next() {
|
|
|
|
// If there is a merge output, return it before continuing to process the
|
|
|
|
// input.
|
|
|
|
if (merge_out_iter_.Valid()) {
|
|
|
|
merge_out_iter_.Next();
|
|
|
|
|
|
|
|
// Check if we returned all records of the merge output.
|
|
|
|
if (merge_out_iter_.Valid()) {
|
|
|
|
key_ = merge_out_iter_.key();
|
|
|
|
value_ = merge_out_iter_.value();
|
|
|
|
bool valid_key __attribute__((__unused__)) =
|
|
|
|
ParseInternalKey(key_, &ikey_);
|
|
|
|
// MergeUntil stops when it encounters a corrupt key and does not
|
|
|
|
// include them in the result, so we expect the keys here to be valid.
|
|
|
|
assert(valid_key);
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
// Keep current_key_ in sync.
|
|
|
|
current_key_.UpdateInternalKey(ikey_.sequence, ikey_.type);
|
2017-04-04 21:17:16 +00:00
|
|
|
key_ = current_key_.GetInternalKey();
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
ikey_.user_key = current_key_.GetUserKey();
|
2015-09-10 21:35:25 +00:00
|
|
|
valid_ = true;
|
|
|
|
} else {
|
Introduce FullMergeV2 (eliminate memcpy from merge operators)
Summary:
This diff update the code to pin the merge operator operands while the merge operation is done, so that we can eliminate the memcpy cost, to do that we need a new public API for FullMerge that replace the std::deque<std::string> with std::vector<Slice>
This diff is stacked on top of D56493 and D56511
In this diff we
- Update FullMergeV2 arguments to be encapsulated in MergeOperationInput and MergeOperationOutput which will make it easier to add new arguments in the future
- Replace std::deque<std::string> with std::vector<Slice> to pass operands
- Replace MergeContext std::deque with std::vector (based on a simple benchmark I ran https://gist.github.com/IslamAbdelRahman/78fc86c9ab9f52b1df791e58943fb187)
- Allow FullMergeV2 output to be an existing operand
```
[Everything in Memtable | 10K operands | 10 KB each | 1 operand per key]
DEBUG_LEVEL=0 make db_bench -j64 && ./db_bench --benchmarks="mergerandom,readseq,readseq,readseq,readseq,readseq" --merge_operator="max" --merge_keys=10000 --num=10000 --disable_auto_compactions --value_size=10240 --write_buffer_size=1000000000
[FullMergeV2]
readseq : 0.607 micros/op 1648235 ops/sec; 16121.2 MB/s
readseq : 0.478 micros/op 2091546 ops/sec; 20457.2 MB/s
readseq : 0.252 micros/op 3972081 ops/sec; 38850.5 MB/s
readseq : 0.237 micros/op 4218328 ops/sec; 41259.0 MB/s
readseq : 0.247 micros/op 4043927 ops/sec; 39553.2 MB/s
[master]
readseq : 3.935 micros/op 254140 ops/sec; 2485.7 MB/s
readseq : 3.722 micros/op 268657 ops/sec; 2627.7 MB/s
readseq : 3.149 micros/op 317605 ops/sec; 3106.5 MB/s
readseq : 3.125 micros/op 320024 ops/sec; 3130.1 MB/s
readseq : 4.075 micros/op 245374 ops/sec; 2400.0 MB/s
```
```
[Everything in Memtable | 10K operands | 10 KB each | 10 operand per key]
DEBUG_LEVEL=0 make db_bench -j64 && ./db_bench --benchmarks="mergerandom,readseq,readseq,readseq,readseq,readseq" --merge_operator="max" --merge_keys=1000 --num=10000 --disable_auto_compactions --value_size=10240 --write_buffer_size=1000000000
[FullMergeV2]
readseq : 3.472 micros/op 288018 ops/sec; 2817.1 MB/s
readseq : 2.304 micros/op 434027 ops/sec; 4245.2 MB/s
readseq : 1.163 micros/op 859845 ops/sec; 8410.0 MB/s
readseq : 1.192 micros/op 838926 ops/sec; 8205.4 MB/s
readseq : 1.250 micros/op 800000 ops/sec; 7824.7 MB/s
[master]
readseq : 24.025 micros/op 41623 ops/sec; 407.1 MB/s
readseq : 18.489 micros/op 54086 ops/sec; 529.0 MB/s
readseq : 18.693 micros/op 53495 ops/sec; 523.2 MB/s
readseq : 23.621 micros/op 42335 ops/sec; 414.1 MB/s
readseq : 18.775 micros/op 53262 ops/sec; 521.0 MB/s
```
```
[Everything in Block cache | 10K operands | 10 KB each | 1 operand per key]
[FullMergeV2]
$ DEBUG_LEVEL=0 make db_bench -j64 && ./db_bench --benchmarks="readseq,readseq,readseq,readseq,readseq" --merge_operator="max" --num=100000 --db="/dev/shm/merge-random-10K-10KB" --cache_size=1000000000 --use_existing_db --disable_auto_compactions
readseq : 14.741 micros/op 67837 ops/sec; 663.5 MB/s
readseq : 1.029 micros/op 971446 ops/sec; 9501.6 MB/s
readseq : 0.974 micros/op 1026229 ops/sec; 10037.4 MB/s
readseq : 0.965 micros/op 1036080 ops/sec; 10133.8 MB/s
readseq : 0.943 micros/op 1060657 ops/sec; 10374.2 MB/s
[master]
readseq : 16.735 micros/op 59755 ops/sec; 584.5 MB/s
readseq : 3.029 micros/op 330151 ops/sec; 3229.2 MB/s
readseq : 3.136 micros/op 318883 ops/sec; 3119.0 MB/s
readseq : 3.065 micros/op 326245 ops/sec; 3191.0 MB/s
readseq : 3.014 micros/op 331813 ops/sec; 3245.4 MB/s
```
```
[Everything in Block cache | 10K operands | 10 KB each | 10 operand per key]
DEBUG_LEVEL=0 make db_bench -j64 && ./db_bench --benchmarks="readseq,readseq,readseq,readseq,readseq" --merge_operator="max" --num=100000 --db="/dev/shm/merge-random-10-operands-10K-10KB" --cache_size=1000000000 --use_existing_db --disable_auto_compactions
[FullMergeV2]
readseq : 24.325 micros/op 41109 ops/sec; 402.1 MB/s
readseq : 1.470 micros/op 680272 ops/sec; 6653.7 MB/s
readseq : 1.231 micros/op 812347 ops/sec; 7945.5 MB/s
readseq : 1.091 micros/op 916590 ops/sec; 8965.1 MB/s
readseq : 1.109 micros/op 901713 ops/sec; 8819.6 MB/s
[master]
readseq : 27.257 micros/op 36687 ops/sec; 358.8 MB/s
readseq : 4.443 micros/op 225073 ops/sec; 2201.4 MB/s
readseq : 5.830 micros/op 171526 ops/sec; 1677.7 MB/s
readseq : 4.173 micros/op 239635 ops/sec; 2343.8 MB/s
readseq : 4.150 micros/op 240963 ops/sec; 2356.8 MB/s
```
Test Plan: COMPILE_WITH_ASAN=1 make check -j64
Reviewers: yhchiang, andrewkr, sdong
Reviewed By: sdong
Subscribers: lovro, andrewkr, dhruba
Differential Revision: https://reviews.facebook.net/D57075
2016-07-20 16:49:03 +00:00
|
|
|
// We consumed all pinned merge operands, release pinned iterators
|
2016-08-11 18:54:17 +00:00
|
|
|
pinned_iters_mgr_.ReleasePinnedData();
|
2015-09-10 21:35:25 +00:00
|
|
|
// MergeHelper moves the iterator to the first record after the merged
|
|
|
|
// records, so even though we reached the end of the merge output, we do
|
|
|
|
// not want to advance the iterator.
|
|
|
|
NextFromInput();
|
|
|
|
}
|
|
|
|
} else {
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
// Only advance the input iterator if there is no merge output and the
|
|
|
|
// iterator is not already at the next record.
|
|
|
|
if (!at_next_) {
|
|
|
|
input_->Next();
|
|
|
|
}
|
2015-09-10 21:35:25 +00:00
|
|
|
NextFromInput();
|
|
|
|
}
|
|
|
|
|
2015-11-05 04:52:22 +00:00
|
|
|
if (valid_) {
|
2016-12-01 15:00:17 +00:00
|
|
|
// Record that we've outputted a record for the current key.
|
2015-11-05 04:52:22 +00:00
|
|
|
has_outputted_key_ = true;
|
|
|
|
}
|
|
|
|
|
2015-09-10 21:35:25 +00:00
|
|
|
PrepareOutput();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CompactionIterator::NextFromInput() {
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
at_next_ = false;
|
2015-09-10 21:35:25 +00:00
|
|
|
valid_ = false;
|
|
|
|
|
2017-01-11 23:01:21 +00:00
|
|
|
while (!valid_ && input_->Valid() && !IsShuttingDown()) {
|
2015-09-10 21:35:25 +00:00
|
|
|
key_ = input_->key();
|
|
|
|
value_ = input_->value();
|
|
|
|
iter_stats_.num_input_records++;
|
|
|
|
|
|
|
|
if (!ParseInternalKey(key_, &ikey_)) {
|
|
|
|
// If `expect_valid_internal_key_` is false, return the corrupted key
|
|
|
|
// and let the caller decide what to do with it.
|
2015-09-10 22:16:32 +00:00
|
|
|
// TODO(noetzli): We should have a more elegant solution for this.
|
|
|
|
if (expect_valid_internal_key_) {
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
assert(!"Corrupted internal key not expected.");
|
|
|
|
status_ = Status::Corruption("Corrupted internal key not expected.");
|
2015-09-10 22:16:32 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-04-04 21:17:16 +00:00
|
|
|
key_ = current_key_.SetInternalKey(key_);
|
2015-09-10 21:35:25 +00:00
|
|
|
has_current_user_key_ = false;
|
|
|
|
current_user_key_sequence_ = kMaxSequenceNumber;
|
|
|
|
current_user_key_snapshot_ = 0;
|
|
|
|
iter_stats_.num_input_corrupt_records++;
|
|
|
|
valid_ = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update input statistics
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
if (ikey_.type == kTypeDeletion || ikey_.type == kTypeSingleDeletion) {
|
2015-09-10 21:35:25 +00:00
|
|
|
iter_stats_.num_input_deletion_records++;
|
|
|
|
}
|
|
|
|
iter_stats_.total_input_raw_key_bytes += key_.size();
|
|
|
|
iter_stats_.total_input_raw_value_bytes += value_.size();
|
|
|
|
|
2016-12-01 15:00:17 +00:00
|
|
|
// If need_skip is true, we should seek the input iterator
|
|
|
|
// to internal key skip_until and continue from there.
|
|
|
|
bool need_skip = false;
|
|
|
|
// Points either into compaction_filter_skip_until_ or into
|
|
|
|
// merge_helper_->compaction_filter_skip_until_.
|
|
|
|
Slice skip_until;
|
|
|
|
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
// Check whether the user key changed. After this if statement current_key_
|
|
|
|
// is a copy of the current input key (maybe converted to a delete by the
|
|
|
|
// compaction filter). ikey_.user_key is pointing to the copy.
|
2015-09-10 21:35:25 +00:00
|
|
|
if (!has_current_user_key_ ||
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
!cmp_->Equal(ikey_.user_key, current_user_key_)) {
|
2015-09-10 21:35:25 +00:00
|
|
|
// First occurrence of this user key
|
Compaction Support for Range Deletion
Summary:
This diff introduces RangeDelAggregator, which takes ownership of iterators
provided to it via AddTombstones(). The tombstones are organized in a two-level
map (snapshot stripe -> begin key -> tombstone). Tombstone creation avoids data
copy by holding Slices returned by the iterator, which remain valid thanks to pinning.
For compaction, we create a hierarchical range tombstone iterator with structure
matching the iterator over compaction input data. An aggregator based on that
iterator is used by CompactionIterator to determine which keys are covered by
range tombstones. In case of merge operand, the same aggregator is used by
MergeHelper. Upon finishing each file in the compaction, relevant range tombstones
are added to the output file's range tombstone metablock and file boundaries are
updated accordingly.
To check whether a key is covered by range tombstone, RangeDelAggregator::ShouldDelete()
considers tombstones in the key's snapshot stripe. When this function is used outside of
compaction, it also checks newer stripes, which can contain covering tombstones. Currently
the intra-stripe check involves a linear scan; however, in the future we plan to collapse ranges
within a stripe such that binary search can be used.
RangeDelAggregator::AddToBuilder() adds all range tombstones in the table's key-range
to a new table's range tombstone meta-block. Since range tombstones may fall in the gap
between files, we may need to extend some files' key-ranges. The strategy is (1) first file
extends as far left as possible and other files do not extend left, (2) all files extend right
until either the start of the next file or the end of the last range tombstone in the gap,
whichever comes first.
One other notable change is adding release/move semantics to ScopedArenaIterator
such that it can be used to transfer ownership of an arena-allocated iterator, similar to
how unique_ptr is used for malloc'd data.
Depends on D61473
Test Plan: compaction_iterator_test, mock_table, end-to-end tests in D63927
Reviewers: sdong, IslamAbdelRahman, wanning, yhchiang, lightmark
Reviewed By: lightmark
Subscribers: andrewkr, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D62205
2016-10-18 19:04:56 +00:00
|
|
|
// Copy key for output
|
2017-04-04 21:17:16 +00:00
|
|
|
key_ = current_key_.SetInternalKey(key_, &ikey_);
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
current_user_key_ = ikey_.user_key;
|
2015-09-10 21:35:25 +00:00
|
|
|
has_current_user_key_ = true;
|
2015-11-05 04:52:22 +00:00
|
|
|
has_outputted_key_ = false;
|
2015-09-10 21:35:25 +00:00
|
|
|
current_user_key_sequence_ = kMaxSequenceNumber;
|
|
|
|
current_user_key_snapshot_ = 0;
|
2015-11-05 04:52:22 +00:00
|
|
|
|
2017-04-19 00:12:50 +00:00
|
|
|
#ifndef ROCKSDB_LITE
|
2017-04-18 19:00:36 +00:00
|
|
|
if (compaction_listener_) {
|
|
|
|
compaction_listener_->OnCompaction(compaction_->level(), ikey_.user_key,
|
|
|
|
fromInternalValueType(ikey_.type),
|
|
|
|
value_, ikey_.sequence, true);
|
|
|
|
}
|
2017-04-19 00:12:50 +00:00
|
|
|
#endif // ROCKSDB_LITE
|
2017-04-18 19:00:36 +00:00
|
|
|
|
2015-09-10 21:35:25 +00:00
|
|
|
// apply the compaction filter to the first occurrence of the user key
|
|
|
|
if (compaction_filter_ != nullptr && ikey_.type == kTypeValue &&
|
2015-11-20 23:57:26 +00:00
|
|
|
(visible_at_tip_ || ikey_.sequence > latest_snapshot_ ||
|
|
|
|
ignore_snapshots_)) {
|
2015-09-10 21:35:25 +00:00
|
|
|
// If the user has specified a compaction filter and the sequence
|
|
|
|
// number is greater than any external snapshot, then invoke the
|
|
|
|
// filter. If the return value of the compaction filter is true,
|
|
|
|
// replace the entry with a deletion marker.
|
2016-12-01 15:00:17 +00:00
|
|
|
CompactionFilter::Decision filter;
|
2015-09-10 21:35:25 +00:00
|
|
|
compaction_filter_value_.clear();
|
2016-12-01 15:00:17 +00:00
|
|
|
compaction_filter_skip_until_.Clear();
|
2015-09-10 21:35:25 +00:00
|
|
|
{
|
|
|
|
StopWatchNano timer(env_, true);
|
2016-12-01 15:00:17 +00:00
|
|
|
filter = compaction_filter_->FilterV2(
|
|
|
|
compaction_->level(), ikey_.user_key,
|
|
|
|
CompactionFilter::ValueType::kValue, value_,
|
|
|
|
&compaction_filter_value_, compaction_filter_skip_until_.rep());
|
2015-09-10 21:35:25 +00:00
|
|
|
iter_stats_.total_filter_time +=
|
|
|
|
env_ != nullptr ? timer.ElapsedNanos() : 0;
|
|
|
|
}
|
2016-12-01 15:00:17 +00:00
|
|
|
|
|
|
|
if (filter == CompactionFilter::Decision::kRemoveAndSkipUntil &&
|
|
|
|
cmp_->Compare(*compaction_filter_skip_until_.rep(),
|
|
|
|
ikey_.user_key) <= 0) {
|
|
|
|
// Can't skip to a key smaller than the current one.
|
|
|
|
// Keep the key as per FilterV2 documentation.
|
|
|
|
filter = CompactionFilter::Decision::kKeep;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (filter == CompactionFilter::Decision::kRemove) {
|
2017-01-11 23:01:21 +00:00
|
|
|
// convert the current key to a delete; key_ is pointing into
|
|
|
|
// current_key_ at this point, so updating current_key_ updates key()
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
ikey_.type = kTypeDeletion;
|
|
|
|
current_key_.UpdateInternalKey(ikey_.sequence, kTypeDeletion);
|
2015-09-10 21:35:25 +00:00
|
|
|
// no value associated with delete
|
|
|
|
value_.clear();
|
|
|
|
iter_stats_.num_record_drop_user++;
|
2016-12-01 15:00:17 +00:00
|
|
|
} else if (filter == CompactionFilter::Decision::kChangeValue) {
|
2015-09-10 21:35:25 +00:00
|
|
|
value_ = compaction_filter_value_;
|
2016-12-01 15:00:17 +00:00
|
|
|
} else if (filter == CompactionFilter::Decision::kRemoveAndSkipUntil) {
|
|
|
|
need_skip = true;
|
|
|
|
compaction_filter_skip_until_.ConvertFromUserKey(kMaxSequenceNumber,
|
|
|
|
kValueTypeForSeek);
|
|
|
|
skip_until = compaction_filter_skip_until_.Encode();
|
2015-09-10 21:35:25 +00:00
|
|
|
}
|
|
|
|
}
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
} else {
|
2017-04-19 00:12:50 +00:00
|
|
|
#ifndef ROCKSDB_LITE
|
2017-04-18 19:00:36 +00:00
|
|
|
if (compaction_listener_) {
|
|
|
|
compaction_listener_->OnCompaction(compaction_->level(), ikey_.user_key,
|
|
|
|
fromInternalValueType(ikey_.type),
|
|
|
|
value_, ikey_.sequence, false);
|
|
|
|
}
|
2017-04-19 00:12:50 +00:00
|
|
|
#endif // ROCKSDB_LITE
|
2017-04-18 19:00:36 +00:00
|
|
|
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
// Update the current key to reflect the new sequence number/type without
|
|
|
|
// copying the user key.
|
2015-11-20 23:57:26 +00:00
|
|
|
// TODO(rven): Compaction filter does not process keys in this path
|
|
|
|
// Need to have the compaction filter process multiple versions
|
|
|
|
// if we have versions on both sides of a snapshot
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
current_key_.UpdateInternalKey(ikey_.sequence, ikey_.type);
|
2017-04-04 21:17:16 +00:00
|
|
|
key_ = current_key_.GetInternalKey();
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
ikey_.user_key = current_key_.GetUserKey();
|
2015-09-10 21:35:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// If there are no snapshots, then this kv affect visibility at tip.
|
|
|
|
// Otherwise, search though all existing snapshots to find the earliest
|
|
|
|
// snapshot that is affected by this kv.
|
|
|
|
SequenceNumber last_sequence __attribute__((__unused__)) =
|
|
|
|
current_user_key_sequence_;
|
|
|
|
current_user_key_sequence_ = ikey_.sequence;
|
|
|
|
SequenceNumber last_snapshot = current_user_key_snapshot_;
|
|
|
|
SequenceNumber prev_snapshot = 0; // 0 means no previous snapshot
|
|
|
|
current_user_key_snapshot_ =
|
2016-10-13 17:49:06 +00:00
|
|
|
visible_at_tip_
|
|
|
|
? earliest_snapshot_
|
|
|
|
: findEarliestVisibleSnapshot(ikey_.sequence, &prev_snapshot);
|
2015-09-10 21:35:25 +00:00
|
|
|
|
2016-12-01 15:00:17 +00:00
|
|
|
if (need_skip) {
|
|
|
|
// This case is handled below.
|
|
|
|
} else if (clear_and_output_next_key_) {
|
2015-11-05 04:52:22 +00:00
|
|
|
// In the previous iteration we encountered a single delete that we could
|
|
|
|
// not compact out. We will keep this Put, but can drop it's data.
|
|
|
|
// (See Optimization 3, below.)
|
|
|
|
assert(ikey_.type == kTypeValue);
|
|
|
|
assert(current_user_key_snapshot_ == last_snapshot);
|
|
|
|
|
|
|
|
value_.clear();
|
|
|
|
valid_ = true;
|
|
|
|
clear_and_output_next_key_ = false;
|
|
|
|
} else if (ikey_.type == kTypeSingleDeletion) {
|
|
|
|
// We can compact out a SingleDelete if:
|
|
|
|
// 1) We encounter the corresponding PUT -OR- we know that this key
|
|
|
|
// doesn't appear past this output level
|
|
|
|
// =AND=
|
|
|
|
// 2) We've already returned a record in this snapshot -OR-
|
|
|
|
// there are no earlier earliest_write_conflict_snapshot.
|
|
|
|
//
|
|
|
|
// Rule 1 is needed for SingleDelete correctness. Rule 2 is needed to
|
|
|
|
// allow Transactions to do write-conflict checking (if we compacted away
|
|
|
|
// all keys, then we wouldn't know that a write happened in this
|
|
|
|
// snapshot). If there is no earlier snapshot, then we know that there
|
|
|
|
// are no active transactions that need to know about any writes.
|
|
|
|
//
|
|
|
|
// Optimization 3:
|
|
|
|
// If we encounter a SingleDelete followed by a PUT and Rule 2 is NOT
|
|
|
|
// true, then we must output a SingleDelete. In this case, we will decide
|
|
|
|
// to also output the PUT. While we are compacting less by outputting the
|
|
|
|
// PUT now, hopefully this will lead to better compaction in the future
|
|
|
|
// when Rule 2 is later true (Ie, We are hoping we can later compact out
|
|
|
|
// both the SingleDelete and the Put, while we couldn't if we only
|
|
|
|
// outputted the SingleDelete now).
|
|
|
|
// In this case, we can save space by removing the PUT's value as it will
|
|
|
|
// never be read.
|
|
|
|
//
|
|
|
|
// Deletes and Merges are not supported on the same key that has a
|
|
|
|
// SingleDelete as it is not possible to correctly do any partial
|
|
|
|
// compaction of such a combination of operations. The result of mixing
|
|
|
|
// those operations for a given key is documented as being undefined. So
|
|
|
|
// we can choose how to handle such a combinations of operations. We will
|
|
|
|
// try to compact out as much as we can in these cases.
|
2016-08-16 15:21:43 +00:00
|
|
|
// We will report counts on these anomalous cases.
|
2015-11-05 04:52:22 +00:00
|
|
|
|
|
|
|
// The easiest way to process a SingleDelete during iteration is to peek
|
|
|
|
// ahead at the next key.
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
ParsedInternalKey next_ikey;
|
|
|
|
input_->Next();
|
|
|
|
|
2015-11-05 04:52:22 +00:00
|
|
|
// Check whether the next key exists, is not corrupt, and is the same key
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
// as the single delete.
|
|
|
|
if (input_->Valid() && ParseInternalKey(input_->key(), &next_ikey) &&
|
|
|
|
cmp_->Equal(ikey_.user_key, next_ikey.user_key)) {
|
2015-11-05 04:52:22 +00:00
|
|
|
// Check whether the next key belongs to the same snapshot as the
|
|
|
|
// SingleDelete.
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
if (prev_snapshot == 0 || next_ikey.sequence > prev_snapshot) {
|
2015-11-05 04:52:22 +00:00
|
|
|
if (next_ikey.type == kTypeSingleDeletion) {
|
|
|
|
// We encountered two SingleDeletes in a row. This could be due to
|
|
|
|
// unexpected user input.
|
|
|
|
// Skip the first SingleDelete and let the next iteration decide how
|
|
|
|
// to handle the second SingleDelete
|
|
|
|
|
|
|
|
// First SingleDelete has been skipped since we already called
|
|
|
|
// input_->Next().
|
|
|
|
++iter_stats_.num_record_drop_obsolete;
|
2016-08-16 15:21:43 +00:00
|
|
|
++iter_stats_.num_single_del_mismatch;
|
2015-11-05 04:52:22 +00:00
|
|
|
} else if ((ikey_.sequence <= earliest_write_conflict_snapshot_) ||
|
|
|
|
has_outputted_key_) {
|
|
|
|
// Found a matching value, we can drop the single delete and the
|
|
|
|
// value. It is safe to drop both records since we've already
|
|
|
|
// outputted a key in this snapshot, or there is no earlier
|
|
|
|
// snapshot (Rule 2 above).
|
|
|
|
|
|
|
|
// Note: it doesn't matter whether the second key is a Put or if it
|
|
|
|
// is an unexpected Merge or Delete. We will compact it out
|
2016-08-16 15:21:43 +00:00
|
|
|
// either way. We will maintain counts of how many mismatches
|
|
|
|
// happened
|
|
|
|
if (next_ikey.type != kTypeValue) {
|
|
|
|
++iter_stats_.num_single_del_mismatch;
|
|
|
|
}
|
|
|
|
|
2015-11-05 04:52:22 +00:00
|
|
|
++iter_stats_.num_record_drop_hidden;
|
|
|
|
++iter_stats_.num_record_drop_obsolete;
|
|
|
|
// Already called input_->Next() once. Call it a second time to
|
|
|
|
// skip past the second key.
|
|
|
|
input_->Next();
|
|
|
|
} else {
|
|
|
|
// Found a matching value, but we cannot drop both keys since
|
|
|
|
// there is an earlier snapshot and we need to leave behind a record
|
|
|
|
// to know that a write happened in this snapshot (Rule 2 above).
|
|
|
|
// Clear the value and output the SingleDelete. (The value will be
|
|
|
|
// outputted on the next iteration.)
|
|
|
|
|
|
|
|
// Setting valid_ to true will output the current SingleDelete
|
|
|
|
valid_ = true;
|
|
|
|
|
|
|
|
// Set up the Put to be outputted in the next iteration.
|
|
|
|
// (Optimization 3).
|
|
|
|
clear_and_output_next_key_ = true;
|
|
|
|
}
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
} else {
|
|
|
|
// We hit the next snapshot without hitting a put, so the iterator
|
|
|
|
// returns the single delete.
|
|
|
|
valid_ = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// We are at the end of the input, could not parse the next key, or hit
|
Compaction Support for Range Deletion
Summary:
This diff introduces RangeDelAggregator, which takes ownership of iterators
provided to it via AddTombstones(). The tombstones are organized in a two-level
map (snapshot stripe -> begin key -> tombstone). Tombstone creation avoids data
copy by holding Slices returned by the iterator, which remain valid thanks to pinning.
For compaction, we create a hierarchical range tombstone iterator with structure
matching the iterator over compaction input data. An aggregator based on that
iterator is used by CompactionIterator to determine which keys are covered by
range tombstones. In case of merge operand, the same aggregator is used by
MergeHelper. Upon finishing each file in the compaction, relevant range tombstones
are added to the output file's range tombstone metablock and file boundaries are
updated accordingly.
To check whether a key is covered by range tombstone, RangeDelAggregator::ShouldDelete()
considers tombstones in the key's snapshot stripe. When this function is used outside of
compaction, it also checks newer stripes, which can contain covering tombstones. Currently
the intra-stripe check involves a linear scan; however, in the future we plan to collapse ranges
within a stripe such that binary search can be used.
RangeDelAggregator::AddToBuilder() adds all range tombstones in the table's key-range
to a new table's range tombstone meta-block. Since range tombstones may fall in the gap
between files, we may need to extend some files' key-ranges. The strategy is (1) first file
extends as far left as possible and other files do not extend left, (2) all files extend right
until either the start of the next file or the end of the last range tombstone in the gap,
whichever comes first.
One other notable change is adding release/move semantics to ScopedArenaIterator
such that it can be used to transfer ownership of an arena-allocated iterator, similar to
how unique_ptr is used for malloc'd data.
Depends on D61473
Test Plan: compaction_iterator_test, mock_table, end-to-end tests in D63927
Reviewers: sdong, IslamAbdelRahman, wanning, yhchiang, lightmark
Reviewed By: lightmark
Subscribers: andrewkr, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D62205
2016-10-18 19:04:56 +00:00
|
|
|
// a different key. The iterator returns the single delete if the key
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
// possibly exists beyond the current output level. We set
|
|
|
|
// has_current_user_key to false so that if the iterator is at the next
|
|
|
|
// key, we do not compare it again against the previous key at the next
|
|
|
|
// iteration. If the next key is corrupt, we return before the
|
|
|
|
// comparison, so the value of has_current_user_key does not matter.
|
|
|
|
has_current_user_key_ = false;
|
2015-11-05 04:52:22 +00:00
|
|
|
if (compaction_ != nullptr && ikey_.sequence <= earliest_snapshot_ &&
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
compaction_->KeyNotExistsBeyondOutputLevel(ikey_.user_key,
|
|
|
|
&level_ptrs_)) {
|
2015-11-05 04:52:22 +00:00
|
|
|
// Key doesn't exist outside of this range.
|
|
|
|
// Can compact out this SingleDelete.
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
++iter_stats_.num_record_drop_obsolete;
|
2016-08-16 15:21:43 +00:00
|
|
|
++iter_stats_.num_single_del_fallthru;
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
} else {
|
2015-11-05 04:52:22 +00:00
|
|
|
// Output SingleDelete
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
valid_ = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (valid_) {
|
|
|
|
at_next_ = true;
|
|
|
|
}
|
|
|
|
} else if (last_snapshot == current_user_key_snapshot_) {
|
2015-09-10 21:35:25 +00:00
|
|
|
// If the earliest snapshot is which this key is visible in
|
|
|
|
// is the same as the visibility of a previous instance of the
|
|
|
|
// same key, then this kv is not visible in any snapshot.
|
|
|
|
// Hidden by an newer entry for same user key
|
2017-04-18 19:00:36 +00:00
|
|
|
// TODO(noetzli): why not > ?
|
Use SST files for Transaction conflict detection
Summary:
Currently, transactions can fail even if there is no actual write conflict. This is due to relying on only the memtables to check for write-conflicts. Users have to tune memtable settings to try to avoid this, but it's hard to figure out exactly how to tune these settings.
With this diff, TransactionDB will use both memtables and SST files to determine if there are any write conflicts. This relies on the fact that BlockBasedTable stores sequence numbers for all writes that happen after any open snapshot. Also, D50295 is needed to prevent SingleDelete from disappearing writes (the TODOs in this test code will be fixed once the other diff is approved and merged).
Note that Optimistic transactions will still rely on tuning memtable settings as we do not want to read from SST while on the write thread. Also, memtable settings can still be used to reduce how often TransactionDB needs to read SST files.
Test Plan: unit tests, db bench
Reviewers: rven, yhchiang, kradhakrishnan, IslamAbdelRahman, sdong
Reviewed By: sdong
Subscribers: dhruba, leveldb, yoshinorim
Differential Revision: https://reviews.facebook.net/D50475
2015-10-15 23:37:15 +00:00
|
|
|
//
|
|
|
|
// Note: Dropping this key will not affect TransactionDB write-conflict
|
|
|
|
// checking since there has already been a record returned for this key
|
|
|
|
// in this snapshot.
|
2015-09-10 21:35:25 +00:00
|
|
|
assert(last_sequence >= current_user_key_sequence_);
|
|
|
|
++iter_stats_.num_record_drop_hidden; // (A)
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
input_->Next();
|
2015-09-10 21:35:25 +00:00
|
|
|
} else if (compaction_ != nullptr && ikey_.type == kTypeDeletion &&
|
|
|
|
ikey_.sequence <= earliest_snapshot_ &&
|
|
|
|
compaction_->KeyNotExistsBeyondOutputLevel(ikey_.user_key,
|
|
|
|
&level_ptrs_)) {
|
|
|
|
// TODO(noetzli): This is the only place where we use compaction_
|
|
|
|
// (besides the constructor). We should probably get rid of this
|
|
|
|
// dependency and find a way to do similar filtering during flushes.
|
|
|
|
//
|
|
|
|
// For this user key:
|
|
|
|
// (1) there is no data in higher levels
|
|
|
|
// (2) data in lower levels will have larger sequence numbers
|
|
|
|
// (3) data in layers that are being compacted here and have
|
|
|
|
// smaller sequence numbers will be dropped in the next
|
|
|
|
// few iterations of this loop (by rule (A) above).
|
|
|
|
// Therefore this deletion marker is obsolete and can be dropped.
|
Use SST files for Transaction conflict detection
Summary:
Currently, transactions can fail even if there is no actual write conflict. This is due to relying on only the memtables to check for write-conflicts. Users have to tune memtable settings to try to avoid this, but it's hard to figure out exactly how to tune these settings.
With this diff, TransactionDB will use both memtables and SST files to determine if there are any write conflicts. This relies on the fact that BlockBasedTable stores sequence numbers for all writes that happen after any open snapshot. Also, D50295 is needed to prevent SingleDelete from disappearing writes (the TODOs in this test code will be fixed once the other diff is approved and merged).
Note that Optimistic transactions will still rely on tuning memtable settings as we do not want to read from SST while on the write thread. Also, memtable settings can still be used to reduce how often TransactionDB needs to read SST files.
Test Plan: unit tests, db bench
Reviewers: rven, yhchiang, kradhakrishnan, IslamAbdelRahman, sdong
Reviewed By: sdong
Subscribers: dhruba, leveldb, yoshinorim
Differential Revision: https://reviews.facebook.net/D50475
2015-10-15 23:37:15 +00:00
|
|
|
//
|
|
|
|
// Note: Dropping this Delete will not affect TransactionDB
|
|
|
|
// write-conflict checking since it is earlier than any snapshot.
|
2015-09-10 21:35:25 +00:00
|
|
|
++iter_stats_.num_record_drop_obsolete;
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
input_->Next();
|
2015-09-10 21:35:25 +00:00
|
|
|
} else if (ikey_.type == kTypeMerge) {
|
|
|
|
if (!merge_helper_->HasOperator()) {
|
|
|
|
status_ = Status::InvalidArgument(
|
|
|
|
"merge_operator is not properly initialized.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
Introduce FullMergeV2 (eliminate memcpy from merge operators)
Summary:
This diff update the code to pin the merge operator operands while the merge operation is done, so that we can eliminate the memcpy cost, to do that we need a new public API for FullMerge that replace the std::deque<std::string> with std::vector<Slice>
This diff is stacked on top of D56493 and D56511
In this diff we
- Update FullMergeV2 arguments to be encapsulated in MergeOperationInput and MergeOperationOutput which will make it easier to add new arguments in the future
- Replace std::deque<std::string> with std::vector<Slice> to pass operands
- Replace MergeContext std::deque with std::vector (based on a simple benchmark I ran https://gist.github.com/IslamAbdelRahman/78fc86c9ab9f52b1df791e58943fb187)
- Allow FullMergeV2 output to be an existing operand
```
[Everything in Memtable | 10K operands | 10 KB each | 1 operand per key]
DEBUG_LEVEL=0 make db_bench -j64 && ./db_bench --benchmarks="mergerandom,readseq,readseq,readseq,readseq,readseq" --merge_operator="max" --merge_keys=10000 --num=10000 --disable_auto_compactions --value_size=10240 --write_buffer_size=1000000000
[FullMergeV2]
readseq : 0.607 micros/op 1648235 ops/sec; 16121.2 MB/s
readseq : 0.478 micros/op 2091546 ops/sec; 20457.2 MB/s
readseq : 0.252 micros/op 3972081 ops/sec; 38850.5 MB/s
readseq : 0.237 micros/op 4218328 ops/sec; 41259.0 MB/s
readseq : 0.247 micros/op 4043927 ops/sec; 39553.2 MB/s
[master]
readseq : 3.935 micros/op 254140 ops/sec; 2485.7 MB/s
readseq : 3.722 micros/op 268657 ops/sec; 2627.7 MB/s
readseq : 3.149 micros/op 317605 ops/sec; 3106.5 MB/s
readseq : 3.125 micros/op 320024 ops/sec; 3130.1 MB/s
readseq : 4.075 micros/op 245374 ops/sec; 2400.0 MB/s
```
```
[Everything in Memtable | 10K operands | 10 KB each | 10 operand per key]
DEBUG_LEVEL=0 make db_bench -j64 && ./db_bench --benchmarks="mergerandom,readseq,readseq,readseq,readseq,readseq" --merge_operator="max" --merge_keys=1000 --num=10000 --disable_auto_compactions --value_size=10240 --write_buffer_size=1000000000
[FullMergeV2]
readseq : 3.472 micros/op 288018 ops/sec; 2817.1 MB/s
readseq : 2.304 micros/op 434027 ops/sec; 4245.2 MB/s
readseq : 1.163 micros/op 859845 ops/sec; 8410.0 MB/s
readseq : 1.192 micros/op 838926 ops/sec; 8205.4 MB/s
readseq : 1.250 micros/op 800000 ops/sec; 7824.7 MB/s
[master]
readseq : 24.025 micros/op 41623 ops/sec; 407.1 MB/s
readseq : 18.489 micros/op 54086 ops/sec; 529.0 MB/s
readseq : 18.693 micros/op 53495 ops/sec; 523.2 MB/s
readseq : 23.621 micros/op 42335 ops/sec; 414.1 MB/s
readseq : 18.775 micros/op 53262 ops/sec; 521.0 MB/s
```
```
[Everything in Block cache | 10K operands | 10 KB each | 1 operand per key]
[FullMergeV2]
$ DEBUG_LEVEL=0 make db_bench -j64 && ./db_bench --benchmarks="readseq,readseq,readseq,readseq,readseq" --merge_operator="max" --num=100000 --db="/dev/shm/merge-random-10K-10KB" --cache_size=1000000000 --use_existing_db --disable_auto_compactions
readseq : 14.741 micros/op 67837 ops/sec; 663.5 MB/s
readseq : 1.029 micros/op 971446 ops/sec; 9501.6 MB/s
readseq : 0.974 micros/op 1026229 ops/sec; 10037.4 MB/s
readseq : 0.965 micros/op 1036080 ops/sec; 10133.8 MB/s
readseq : 0.943 micros/op 1060657 ops/sec; 10374.2 MB/s
[master]
readseq : 16.735 micros/op 59755 ops/sec; 584.5 MB/s
readseq : 3.029 micros/op 330151 ops/sec; 3229.2 MB/s
readseq : 3.136 micros/op 318883 ops/sec; 3119.0 MB/s
readseq : 3.065 micros/op 326245 ops/sec; 3191.0 MB/s
readseq : 3.014 micros/op 331813 ops/sec; 3245.4 MB/s
```
```
[Everything in Block cache | 10K operands | 10 KB each | 10 operand per key]
DEBUG_LEVEL=0 make db_bench -j64 && ./db_bench --benchmarks="readseq,readseq,readseq,readseq,readseq" --merge_operator="max" --num=100000 --db="/dev/shm/merge-random-10-operands-10K-10KB" --cache_size=1000000000 --use_existing_db --disable_auto_compactions
[FullMergeV2]
readseq : 24.325 micros/op 41109 ops/sec; 402.1 MB/s
readseq : 1.470 micros/op 680272 ops/sec; 6653.7 MB/s
readseq : 1.231 micros/op 812347 ops/sec; 7945.5 MB/s
readseq : 1.091 micros/op 916590 ops/sec; 8965.1 MB/s
readseq : 1.109 micros/op 901713 ops/sec; 8819.6 MB/s
[master]
readseq : 27.257 micros/op 36687 ops/sec; 358.8 MB/s
readseq : 4.443 micros/op 225073 ops/sec; 2201.4 MB/s
readseq : 5.830 micros/op 171526 ops/sec; 1677.7 MB/s
readseq : 4.173 micros/op 239635 ops/sec; 2343.8 MB/s
readseq : 4.150 micros/op 240963 ops/sec; 2356.8 MB/s
```
Test Plan: COMPILE_WITH_ASAN=1 make check -j64
Reviewers: yhchiang, andrewkr, sdong
Reviewed By: sdong
Subscribers: lovro, andrewkr, dhruba
Differential Revision: https://reviews.facebook.net/D57075
2016-07-20 16:49:03 +00:00
|
|
|
pinned_iters_mgr_.StartPinning();
|
2015-09-10 21:35:25 +00:00
|
|
|
// We know the merge type entry is not hidden, otherwise we would
|
|
|
|
// have hit (A)
|
|
|
|
// We encapsulate the merge related state machine in a different
|
|
|
|
// object to minimize change to the existing flow.
|
2017-01-11 23:01:21 +00:00
|
|
|
Status s = merge_helper_->MergeUntil(input_, range_del_agg_,
|
|
|
|
prev_snapshot, bottommost_level_);
|
2015-09-10 21:35:25 +00:00
|
|
|
merge_out_iter_.SeekToFirst();
|
|
|
|
|
2017-01-11 23:01:21 +00:00
|
|
|
if (!s.ok() && !s.IsMergeInProgress()) {
|
|
|
|
status_ = s;
|
|
|
|
return;
|
|
|
|
} else if (merge_out_iter_.Valid()) {
|
Compaction filter on merge operands
Summary:
Since Andres' internship is over, I took over https://reviews.facebook.net/D42555 and rebased and simplified it a bit.
The behavior in this diff is a bit simpler than in D42555:
* only merge operators are passed through FilterMergeValue(). If fitler function returns true, the merge operator is ignored
* compaction filter is *not* called on: 1) results of merge operations and 2) base values that are getting merged with merge operands (the second case was also true in previous diff)
Do we also need a compaction filter to get called on merge results?
Test Plan: make && make check
Reviewers: lovro, tnovak, rven, yhchiang, sdong
Reviewed By: sdong
Subscribers: noetzli, kolmike, leveldb, dhruba, sdong
Differential Revision: https://reviews.facebook.net/D47847
2015-10-07 16:30:03 +00:00
|
|
|
// NOTE: key, value, and ikey_ refer to old entries.
|
|
|
|
// These will be correctly set below.
|
|
|
|
key_ = merge_out_iter_.key();
|
|
|
|
value_ = merge_out_iter_.value();
|
|
|
|
bool valid_key __attribute__((__unused__)) =
|
|
|
|
ParseInternalKey(key_, &ikey_);
|
|
|
|
// MergeUntil stops when it encounters a corrupt key and does not
|
|
|
|
// include them in the result, so we expect the keys here to valid.
|
|
|
|
assert(valid_key);
|
|
|
|
// Keep current_key_ in sync.
|
|
|
|
current_key_.UpdateInternalKey(ikey_.sequence, ikey_.type);
|
2017-04-04 21:17:16 +00:00
|
|
|
key_ = current_key_.GetInternalKey();
|
Compaction filter on merge operands
Summary:
Since Andres' internship is over, I took over https://reviews.facebook.net/D42555 and rebased and simplified it a bit.
The behavior in this diff is a bit simpler than in D42555:
* only merge operators are passed through FilterMergeValue(). If fitler function returns true, the merge operator is ignored
* compaction filter is *not* called on: 1) results of merge operations and 2) base values that are getting merged with merge operands (the second case was also true in previous diff)
Do we also need a compaction filter to get called on merge results?
Test Plan: make && make check
Reviewers: lovro, tnovak, rven, yhchiang, sdong
Reviewed By: sdong
Subscribers: noetzli, kolmike, leveldb, dhruba, sdong
Differential Revision: https://reviews.facebook.net/D47847
2015-10-07 16:30:03 +00:00
|
|
|
ikey_.user_key = current_key_.GetUserKey();
|
|
|
|
valid_ = true;
|
|
|
|
} else {
|
|
|
|
// all merge operands were filtered out. reset the user key, since the
|
|
|
|
// batch consumed by the merge operator should not shadow any keys
|
|
|
|
// coming after the merges
|
|
|
|
has_current_user_key_ = false;
|
2016-08-11 18:54:17 +00:00
|
|
|
pinned_iters_mgr_.ReleasePinnedData();
|
2016-12-05 23:07:01 +00:00
|
|
|
|
|
|
|
if (merge_helper_->FilteredUntil(&skip_until)) {
|
|
|
|
need_skip = true;
|
|
|
|
}
|
Compaction filter on merge operands
Summary:
Since Andres' internship is over, I took over https://reviews.facebook.net/D42555 and rebased and simplified it a bit.
The behavior in this diff is a bit simpler than in D42555:
* only merge operators are passed through FilterMergeValue(). If fitler function returns true, the merge operator is ignored
* compaction filter is *not* called on: 1) results of merge operations and 2) base values that are getting merged with merge operands (the second case was also true in previous diff)
Do we also need a compaction filter to get called on merge results?
Test Plan: make && make check
Reviewers: lovro, tnovak, rven, yhchiang, sdong
Reviewed By: sdong
Subscribers: noetzli, kolmike, leveldb, dhruba, sdong
Differential Revision: https://reviews.facebook.net/D47847
2015-10-07 16:30:03 +00:00
|
|
|
}
|
2015-09-10 21:35:25 +00:00
|
|
|
} else {
|
Compaction Support for Range Deletion
Summary:
This diff introduces RangeDelAggregator, which takes ownership of iterators
provided to it via AddTombstones(). The tombstones are organized in a two-level
map (snapshot stripe -> begin key -> tombstone). Tombstone creation avoids data
copy by holding Slices returned by the iterator, which remain valid thanks to pinning.
For compaction, we create a hierarchical range tombstone iterator with structure
matching the iterator over compaction input data. An aggregator based on that
iterator is used by CompactionIterator to determine which keys are covered by
range tombstones. In case of merge operand, the same aggregator is used by
MergeHelper. Upon finishing each file in the compaction, relevant range tombstones
are added to the output file's range tombstone metablock and file boundaries are
updated accordingly.
To check whether a key is covered by range tombstone, RangeDelAggregator::ShouldDelete()
considers tombstones in the key's snapshot stripe. When this function is used outside of
compaction, it also checks newer stripes, which can contain covering tombstones. Currently
the intra-stripe check involves a linear scan; however, in the future we plan to collapse ranges
within a stripe such that binary search can be used.
RangeDelAggregator::AddToBuilder() adds all range tombstones in the table's key-range
to a new table's range tombstone meta-block. Since range tombstones may fall in the gap
between files, we may need to extend some files' key-ranges. The strategy is (1) first file
extends as far left as possible and other files do not extend left, (2) all files extend right
until either the start of the next file or the end of the last range tombstone in the gap,
whichever comes first.
One other notable change is adding release/move semantics to ScopedArenaIterator
such that it can be used to transfer ownership of an arena-allocated iterator, similar to
how unique_ptr is used for malloc'd data.
Depends on D61473
Test Plan: compaction_iterator_test, mock_table, end-to-end tests in D63927
Reviewers: sdong, IslamAbdelRahman, wanning, yhchiang, lightmark
Reviewed By: lightmark
Subscribers: andrewkr, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D62205
2016-10-18 19:04:56 +00:00
|
|
|
// 1. new user key -OR-
|
|
|
|
// 2. different snapshot stripe
|
Maintain position in range deletions map
Summary:
When deletion-collapsing mode is enabled (i.e., for DBIter/CompactionIterator), we maintain position in the tombstone maps across calls to ShouldDelete(). Since iterators often access keys sequentially (or reverse-sequentially), scanning forward/backward from the last position can be faster than binary-searching the map for every key.
- When Next() is invoked on an iterator, we use kForwardTraversal to scan forwards, if needed, until arriving at the range deletion containing the next key.
- Similarly for Prev(), we use kBackwardTraversal to scan backwards in the range deletion map.
- When the iterator seeks, we use kBinarySearch for repositioning
- After tombstones are added or before the first ShouldDelete() invocation, the current position is set to invalid, which forces kBinarySearch to be used.
- Non-iterator users (i.e., Get()) use kFullScan, which has the same behavior as before---scan the whole map for every key passed to ShouldDelete().
Closes https://github.com/facebook/rocksdb/pull/1701
Differential Revision: D4350318
Pulled By: ajkr
fbshipit-source-id: 5129b76
2017-01-05 18:22:46 +00:00
|
|
|
bool should_delete = range_del_agg_->ShouldDelete(
|
|
|
|
key_, RangeDelAggregator::RangePositioningMode::kForwardTraversal);
|
Compaction Support for Range Deletion
Summary:
This diff introduces RangeDelAggregator, which takes ownership of iterators
provided to it via AddTombstones(). The tombstones are organized in a two-level
map (snapshot stripe -> begin key -> tombstone). Tombstone creation avoids data
copy by holding Slices returned by the iterator, which remain valid thanks to pinning.
For compaction, we create a hierarchical range tombstone iterator with structure
matching the iterator over compaction input data. An aggregator based on that
iterator is used by CompactionIterator to determine which keys are covered by
range tombstones. In case of merge operand, the same aggregator is used by
MergeHelper. Upon finishing each file in the compaction, relevant range tombstones
are added to the output file's range tombstone metablock and file boundaries are
updated accordingly.
To check whether a key is covered by range tombstone, RangeDelAggregator::ShouldDelete()
considers tombstones in the key's snapshot stripe. When this function is used outside of
compaction, it also checks newer stripes, which can contain covering tombstones. Currently
the intra-stripe check involves a linear scan; however, in the future we plan to collapse ranges
within a stripe such that binary search can be used.
RangeDelAggregator::AddToBuilder() adds all range tombstones in the table's key-range
to a new table's range tombstone meta-block. Since range tombstones may fall in the gap
between files, we may need to extend some files' key-ranges. The strategy is (1) first file
extends as far left as possible and other files do not extend left, (2) all files extend right
until either the start of the next file or the end of the last range tombstone in the gap,
whichever comes first.
One other notable change is adding release/move semantics to ScopedArenaIterator
such that it can be used to transfer ownership of an arena-allocated iterator, similar to
how unique_ptr is used for malloc'd data.
Depends on D61473
Test Plan: compaction_iterator_test, mock_table, end-to-end tests in D63927
Reviewers: sdong, IslamAbdelRahman, wanning, yhchiang, lightmark
Reviewed By: lightmark
Subscribers: andrewkr, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D62205
2016-10-18 19:04:56 +00:00
|
|
|
if (should_delete) {
|
2016-11-28 19:44:40 +00:00
|
|
|
++iter_stats_.num_record_drop_hidden;
|
|
|
|
++iter_stats_.num_record_drop_range_del;
|
Compaction Support for Range Deletion
Summary:
This diff introduces RangeDelAggregator, which takes ownership of iterators
provided to it via AddTombstones(). The tombstones are organized in a two-level
map (snapshot stripe -> begin key -> tombstone). Tombstone creation avoids data
copy by holding Slices returned by the iterator, which remain valid thanks to pinning.
For compaction, we create a hierarchical range tombstone iterator with structure
matching the iterator over compaction input data. An aggregator based on that
iterator is used by CompactionIterator to determine which keys are covered by
range tombstones. In case of merge operand, the same aggregator is used by
MergeHelper. Upon finishing each file in the compaction, relevant range tombstones
are added to the output file's range tombstone metablock and file boundaries are
updated accordingly.
To check whether a key is covered by range tombstone, RangeDelAggregator::ShouldDelete()
considers tombstones in the key's snapshot stripe. When this function is used outside of
compaction, it also checks newer stripes, which can contain covering tombstones. Currently
the intra-stripe check involves a linear scan; however, in the future we plan to collapse ranges
within a stripe such that binary search can be used.
RangeDelAggregator::AddToBuilder() adds all range tombstones in the table's key-range
to a new table's range tombstone meta-block. Since range tombstones may fall in the gap
between files, we may need to extend some files' key-ranges. The strategy is (1) first file
extends as far left as possible and other files do not extend left, (2) all files extend right
until either the start of the next file or the end of the last range tombstone in the gap,
whichever comes first.
One other notable change is adding release/move semantics to ScopedArenaIterator
such that it can be used to transfer ownership of an arena-allocated iterator, similar to
how unique_ptr is used for malloc'd data.
Depends on D61473
Test Plan: compaction_iterator_test, mock_table, end-to-end tests in D63927
Reviewers: sdong, IslamAbdelRahman, wanning, yhchiang, lightmark
Reviewed By: lightmark
Subscribers: andrewkr, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D62205
2016-10-18 19:04:56 +00:00
|
|
|
input_->Next();
|
|
|
|
} else {
|
|
|
|
valid_ = true;
|
|
|
|
}
|
2015-09-10 21:35:25 +00:00
|
|
|
}
|
2016-12-01 15:00:17 +00:00
|
|
|
|
|
|
|
if (need_skip) {
|
|
|
|
input_->Seek(skip_until);
|
|
|
|
}
|
2015-09-10 21:35:25 +00:00
|
|
|
}
|
2017-01-11 23:01:21 +00:00
|
|
|
|
|
|
|
if (!valid_ && IsShuttingDown()) {
|
|
|
|
status_ = Status::ShutdownInProgress();
|
|
|
|
}
|
2015-09-10 21:35:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CompactionIterator::PrepareOutput() {
|
|
|
|
// Zeroing out the sequence number leads to better compression.
|
|
|
|
// If this is the bottommost level (no files in lower levels)
|
|
|
|
// and the earliest snapshot is larger than this seqno
|
2015-12-10 01:28:46 +00:00
|
|
|
// and the userkey differs from the last userkey in compaction
|
2015-09-10 21:35:25 +00:00
|
|
|
// then we can squash the seqno to zero.
|
Use SST files for Transaction conflict detection
Summary:
Currently, transactions can fail even if there is no actual write conflict. This is due to relying on only the memtables to check for write-conflicts. Users have to tune memtable settings to try to avoid this, but it's hard to figure out exactly how to tune these settings.
With this diff, TransactionDB will use both memtables and SST files to determine if there are any write conflicts. This relies on the fact that BlockBasedTable stores sequence numbers for all writes that happen after any open snapshot. Also, D50295 is needed to prevent SingleDelete from disappearing writes (the TODOs in this test code will be fixed once the other diff is approved and merged).
Note that Optimistic transactions will still rely on tuning memtable settings as we do not want to read from SST while on the write thread. Also, memtable settings can still be used to reduce how often TransactionDB needs to read SST files.
Test Plan: unit tests, db bench
Reviewers: rven, yhchiang, kradhakrishnan, IslamAbdelRahman, sdong
Reviewed By: sdong
Subscribers: dhruba, leveldb, yoshinorim
Differential Revision: https://reviews.facebook.net/D50475
2015-10-15 23:37:15 +00:00
|
|
|
|
|
|
|
// This is safe for TransactionDB write-conflict checking since transactions
|
|
|
|
// only care about sequence number larger than any active snapshots.
|
2017-04-21 02:44:55 +00:00
|
|
|
if (bottommost_level_ && valid_ && ikey_.sequence <= earliest_snapshot_ &&
|
2015-12-10 01:28:46 +00:00
|
|
|
ikey_.type != kTypeMerge &&
|
|
|
|
!cmp_->Equal(compaction_->GetLargestUserKey(), ikey_.user_key)) {
|
Support for SingleDelete()
Summary:
This patch fixes #7460559. It introduces SingleDelete as a new database
operation. This operation can be used to delete keys that were never
overwritten (no put following another put of the same key). If an overwritten
key is single deleted the behavior is undefined. Single deletion of a
non-existent key has no effect but multiple consecutive single deletions are
not allowed (see limitations).
In contrast to the conventional Delete() operation, the deletion entry is
removed along with the value when the two are lined up in a compaction. Note:
The semantics are similar to @igor's prototype that allowed to have this
behavior on the granularity of a column family (
https://reviews.facebook.net/D42093 ). This new patch, however, is more
aggressive when it comes to removing tombstones: It removes the SingleDelete
together with the value whenever there is no snapshot between them while the
older patch only did this when the sequence number of the deletion was older
than the earliest snapshot.
Most of the complex additions are in the Compaction Iterator, all other changes
should be relatively straightforward. The patch also includes basic support for
single deletions in db_stress and db_bench.
Limitations:
- Not compatible with cuckoo hash tables
- Single deletions cannot be used in combination with merges and normal
deletions on the same key (other keys are not affected by this)
- Consecutive single deletions are currently not allowed (and older version of
this patch supported this so it could be resurrected if needed)
Test Plan: make all check
Reviewers: yhchiang, sdong, rven, anthony, yoshinorim, igor
Reviewed By: igor
Subscribers: maykov, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D43179
2015-09-17 18:42:56 +00:00
|
|
|
assert(ikey_.type != kTypeDeletion && ikey_.type != kTypeSingleDeletion);
|
|
|
|
ikey_.sequence = 0;
|
|
|
|
current_key_.UpdateInternalKey(0, ikey_.type);
|
2015-09-10 21:35:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline SequenceNumber CompactionIterator::findEarliestVisibleSnapshot(
|
|
|
|
SequenceNumber in, SequenceNumber* prev_snapshot) {
|
|
|
|
assert(snapshots_->size());
|
2016-04-12 17:35:15 +00:00
|
|
|
SequenceNumber prev __attribute__((__unused__)) = kMaxSequenceNumber;
|
2015-09-10 21:35:25 +00:00
|
|
|
for (const auto cur : *snapshots_) {
|
2016-04-12 17:35:15 +00:00
|
|
|
assert(prev == kMaxSequenceNumber || prev <= cur);
|
2015-09-10 21:35:25 +00:00
|
|
|
if (cur >= in) {
|
2016-04-12 17:35:15 +00:00
|
|
|
*prev_snapshot = prev == kMaxSequenceNumber ? 0 : prev;
|
2015-09-10 21:35:25 +00:00
|
|
|
return cur;
|
|
|
|
}
|
|
|
|
prev = cur;
|
2016-04-12 17:35:15 +00:00
|
|
|
assert(prev < kMaxSequenceNumber);
|
2015-09-10 21:35:25 +00:00
|
|
|
}
|
|
|
|
*prev_snapshot = prev;
|
|
|
|
return kMaxSequenceNumber;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace rocksdb
|