mirror of
https://github.com/facebook/rocksdb.git
synced 2026-07-07 14:47:40 +08:00
Compare commits
11 Commits
f2c0eb41ef
...
v7.10.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 3258b5c3e2 | |||
| 3a04cd558e | |||
| 0ffa8db9b1 | |||
| 1185bb75ca | |||
| d57ec3f896 | |||
| 8a354a1197 | |||
| fcb0580b08 | |||
| 06765b5131 | |||
| fa13962e0c | |||
| e5dcebf756 | |||
| ab389242fb |
+12
-1
@@ -1,5 +1,16 @@
|
|||||||
# Rocksdb Change Log
|
# Rocksdb Change Log
|
||||||
## Unreleased
|
## 7.10.2 (02/10/2023)
|
||||||
|
### Bug Fixes
|
||||||
|
* Fixed a bug in DB open/recovery from a compressed WAL that was caused due to incorrect handling of certain record fragments with the same offset within a WAL block.
|
||||||
|
|
||||||
|
## 7.10.1 (02/01/2023)
|
||||||
|
### Bug Fixes
|
||||||
|
* Fixed a data race on `ColumnFamilyData::flush_reason` caused by concurrent flushes.
|
||||||
|
* Fixed `DisableManualCompaction()` and `CompactRangeOptions::canceled` to cancel compactions even when they are waiting on conflicting compactions to finish
|
||||||
|
* Fixed a bug in which a successful `GetMergeOperands()` could transiently return `Status::MergeInProgress()`
|
||||||
|
* Return the correct error (Status::NotSupported()) to MultiGet caller when ReadOptions::async_io flag is true and IO uring is not enabled. Previously, Status::Corruption() was being returned when the actual failure was lack of async IO support.
|
||||||
|
|
||||||
|
## 7.10.0 (01/23/2023)
|
||||||
### Behavior changes
|
### Behavior changes
|
||||||
* Make best-efforts recovery verify SST unique ID before Version construction (#10962)
|
* Make best-efforts recovery verify SST unique ID before Version construction (#10962)
|
||||||
* Introduce `epoch_number` and sort L0 files by `epoch_number` instead of `largest_seqno`. `epoch_number` represents the order of a file being flushed or ingested/imported. Compaction output file will be assigned with the minimum `epoch_number` among input files'. For L0, larger `epoch_number` indicates newer L0 file.
|
* Introduce `epoch_number` and sort L0 files by `epoch_number` instead of `largest_seqno`. `epoch_number` represents the order of a file being flushed or ingested/imported. Compaction output file will be assigned with the minimum `epoch_number` among input files'. For L0, larger `epoch_number` indicates newer L0 file.
|
||||||
|
|||||||
@@ -557,7 +557,6 @@ ColumnFamilyData::ColumnFamilyData(
|
|||||||
next_(nullptr),
|
next_(nullptr),
|
||||||
prev_(nullptr),
|
prev_(nullptr),
|
||||||
log_number_(0),
|
log_number_(0),
|
||||||
flush_reason_(FlushReason::kOthers),
|
|
||||||
column_family_set_(column_family_set),
|
column_family_set_(column_family_set),
|
||||||
queued_for_flush_(false),
|
queued_for_flush_(false),
|
||||||
queued_for_compaction_(false),
|
queued_for_compaction_(false),
|
||||||
|
|||||||
@@ -310,10 +310,6 @@ class ColumnFamilyData {
|
|||||||
void SetLogNumber(uint64_t log_number) { log_number_ = log_number; }
|
void SetLogNumber(uint64_t log_number) { log_number_ = log_number; }
|
||||||
uint64_t GetLogNumber() const { return log_number_; }
|
uint64_t GetLogNumber() const { return log_number_; }
|
||||||
|
|
||||||
void SetFlushReason(FlushReason flush_reason) {
|
|
||||||
flush_reason_ = flush_reason;
|
|
||||||
}
|
|
||||||
FlushReason GetFlushReason() const { return flush_reason_; }
|
|
||||||
// thread-safe
|
// thread-safe
|
||||||
const FileOptions* soptions() const;
|
const FileOptions* soptions() const;
|
||||||
const ImmutableOptions* ioptions() const { return &ioptions_; }
|
const ImmutableOptions* ioptions() const { return &ioptions_; }
|
||||||
@@ -616,8 +612,6 @@ class ColumnFamilyData {
|
|||||||
// recovered from
|
// recovered from
|
||||||
uint64_t log_number_;
|
uint64_t log_number_;
|
||||||
|
|
||||||
std::atomic<FlushReason> flush_reason_;
|
|
||||||
|
|
||||||
// An object that keeps all the compaction stats
|
// An object that keeps all the compaction stats
|
||||||
// and picks the next compaction
|
// and picks the next compaction
|
||||||
std::unique_ptr<CompactionPicker> compaction_picker_;
|
std::unique_ptr<CompactionPicker> compaction_picker_;
|
||||||
|
|||||||
+88
-2
@@ -32,6 +32,9 @@
|
|||||||
|
|
||||||
namespace ROCKSDB_NAMESPACE {
|
namespace ROCKSDB_NAMESPACE {
|
||||||
|
|
||||||
|
static bool enable_io_uring = true;
|
||||||
|
extern "C" bool RocksDbIOUringEnable() { return enable_io_uring; }
|
||||||
|
|
||||||
class DBBasicTest : public DBTestBase {
|
class DBBasicTest : public DBTestBase {
|
||||||
public:
|
public:
|
||||||
DBBasicTest() : DBTestBase("db_basic_test", /*env_do_fsync=*/false) {}
|
DBBasicTest() : DBTestBase("db_basic_test", /*env_do_fsync=*/false) {}
|
||||||
@@ -2162,6 +2165,7 @@ class DBMultiGetAsyncIOTest : public DBBasicTest,
|
|||||||
options_.disable_auto_compactions = true;
|
options_.disable_auto_compactions = true;
|
||||||
options_.statistics = statistics_;
|
options_.statistics = statistics_;
|
||||||
options_.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
options_.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||||
|
options_.env = Env::Default();
|
||||||
Reopen(options_);
|
Reopen(options_);
|
||||||
int num_keys = 0;
|
int num_keys = 0;
|
||||||
|
|
||||||
@@ -2228,6 +2232,20 @@ class DBMultiGetAsyncIOTest : public DBBasicTest,
|
|||||||
const std::shared_ptr<Statistics>& statistics() { return statistics_; }
|
const std::shared_ptr<Statistics>& statistics() { return statistics_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void PrepareDBForTest() {
|
||||||
|
#ifdef ROCKSDB_IOURING_PRESENT
|
||||||
|
Reopen(options_);
|
||||||
|
#else // ROCKSDB_IOURING_PRESENT
|
||||||
|
// Warm up the block cache so we don't need to use the IO uring
|
||||||
|
Iterator* iter = dbfull()->NewIterator(ReadOptions());
|
||||||
|
for (iter->SeekToFirst(); iter->Valid() && iter->status().ok();
|
||||||
|
iter->Next())
|
||||||
|
;
|
||||||
|
EXPECT_OK(iter->status());
|
||||||
|
delete iter;
|
||||||
|
#endif // ROCKSDB_IOURING_PRESENT
|
||||||
|
}
|
||||||
|
|
||||||
void ReopenDB() { Reopen(options_); }
|
void ReopenDB() { Reopen(options_); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -2242,6 +2260,8 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL0) {
|
|||||||
std::vector<PinnableSlice> values(key_strs.size());
|
std::vector<PinnableSlice> values(key_strs.size());
|
||||||
std::vector<Status> statuses(key_strs.size());
|
std::vector<Status> statuses(key_strs.size());
|
||||||
|
|
||||||
|
PrepareDBForTest();
|
||||||
|
|
||||||
ReadOptions ro;
|
ReadOptions ro;
|
||||||
ro.async_io = true;
|
ro.async_io = true;
|
||||||
ro.optimize_multiget_for_io = GetParam();
|
ro.optimize_multiget_for_io = GetParam();
|
||||||
@@ -2260,6 +2280,7 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL0) {
|
|||||||
statistics()->histogramData(MULTIGET_IO_BATCH_SIZE, &multiget_io_batch_size);
|
statistics()->histogramData(MULTIGET_IO_BATCH_SIZE, &multiget_io_batch_size);
|
||||||
|
|
||||||
// With async IO, lookups will happen in parallel for each key
|
// With async IO, lookups will happen in parallel for each key
|
||||||
|
#ifdef ROCKSDB_IOURING_PRESENT
|
||||||
if (GetParam()) {
|
if (GetParam()) {
|
||||||
ASSERT_EQ(multiget_io_batch_size.count, 1);
|
ASSERT_EQ(multiget_io_batch_size.count, 1);
|
||||||
ASSERT_EQ(multiget_io_batch_size.max, 3);
|
ASSERT_EQ(multiget_io_batch_size.max, 3);
|
||||||
@@ -2269,6 +2290,11 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL0) {
|
|||||||
// L0 file
|
// L0 file
|
||||||
ASSERT_EQ(multiget_io_batch_size.count, 3);
|
ASSERT_EQ(multiget_io_batch_size.count, 3);
|
||||||
}
|
}
|
||||||
|
#else // ROCKSDB_IOURING_PRESENT
|
||||||
|
if (GetParam()) {
|
||||||
|
ASSERT_EQ(statistics()->getTickerCount(MULTIGET_COROUTINE_COUNT), 3);
|
||||||
|
}
|
||||||
|
#endif // ROCKSDB_IOURING_PRESENT
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(DBMultiGetAsyncIOTest, GetFromL1) {
|
TEST_P(DBMultiGetAsyncIOTest, GetFromL1) {
|
||||||
@@ -2286,6 +2312,8 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1) {
|
|||||||
values.resize(keys.size());
|
values.resize(keys.size());
|
||||||
statuses.resize(keys.size());
|
statuses.resize(keys.size());
|
||||||
|
|
||||||
|
PrepareDBForTest();
|
||||||
|
|
||||||
ReadOptions ro;
|
ReadOptions ro;
|
||||||
ro.async_io = true;
|
ro.async_io = true;
|
||||||
ro.optimize_multiget_for_io = GetParam();
|
ro.optimize_multiget_for_io = GetParam();
|
||||||
@@ -2299,6 +2327,7 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1) {
|
|||||||
ASSERT_EQ(values[1], "val_l1_" + std::to_string(54));
|
ASSERT_EQ(values[1], "val_l1_" + std::to_string(54));
|
||||||
ASSERT_EQ(values[2], "val_l1_" + std::to_string(102));
|
ASSERT_EQ(values[2], "val_l1_" + std::to_string(102));
|
||||||
|
|
||||||
|
#ifdef ROCKSDB_IOURING_PRESENT
|
||||||
HistogramData multiget_io_batch_size;
|
HistogramData multiget_io_batch_size;
|
||||||
|
|
||||||
statistics()->histogramData(MULTIGET_IO_BATCH_SIZE, &multiget_io_batch_size);
|
statistics()->histogramData(MULTIGET_IO_BATCH_SIZE, &multiget_io_batch_size);
|
||||||
@@ -2306,9 +2335,11 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1) {
|
|||||||
// A batch of 3 async IOs is expected, one for each overlapping file in L1
|
// A batch of 3 async IOs is expected, one for each overlapping file in L1
|
||||||
ASSERT_EQ(multiget_io_batch_size.count, 1);
|
ASSERT_EQ(multiget_io_batch_size.count, 1);
|
||||||
ASSERT_EQ(multiget_io_batch_size.max, 3);
|
ASSERT_EQ(multiget_io_batch_size.max, 3);
|
||||||
|
#endif // ROCKSDB_IOURING_PRESENT
|
||||||
ASSERT_EQ(statistics()->getTickerCount(MULTIGET_COROUTINE_COUNT), 3);
|
ASSERT_EQ(statistics()->getTickerCount(MULTIGET_COROUTINE_COUNT), 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ROCKSDB_IOURING_PRESENT
|
||||||
TEST_P(DBMultiGetAsyncIOTest, GetFromL1Error) {
|
TEST_P(DBMultiGetAsyncIOTest, GetFromL1Error) {
|
||||||
std::vector<std::string> key_strs;
|
std::vector<std::string> key_strs;
|
||||||
std::vector<Slice> keys;
|
std::vector<Slice> keys;
|
||||||
@@ -2324,9 +2355,9 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1Error) {
|
|||||||
values.resize(keys.size());
|
values.resize(keys.size());
|
||||||
statuses.resize(keys.size());
|
statuses.resize(keys.size());
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
SyncPoint::GetInstance()->SetCallBack(
|
SyncPoint::GetInstance()->SetCallBack(
|
||||||
"TableCache::GetTableReader:BeforeOpenFile", [&](void* status) {
|
"TableCache::GetTableReader:BeforeOpenFile", [&](void* status) {
|
||||||
static int count = 0;
|
|
||||||
count++;
|
count++;
|
||||||
// Fail the last table reader open, which is the 6th SST file
|
// Fail the last table reader open, which is the 6th SST file
|
||||||
// since 3 overlapping L0 files + 3 L1 files containing the keys
|
// since 3 overlapping L0 files + 3 L1 files containing the keys
|
||||||
@@ -2349,7 +2380,7 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1Error) {
|
|||||||
});
|
});
|
||||||
SyncPoint::GetInstance()->EnableProcessing();
|
SyncPoint::GetInstance()->EnableProcessing();
|
||||||
|
|
||||||
ReopenDB();
|
PrepareDBForTest();
|
||||||
|
|
||||||
ReadOptions ro;
|
ReadOptions ro;
|
||||||
ro.async_io = true;
|
ro.async_io = true;
|
||||||
@@ -2371,6 +2402,7 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1Error) {
|
|||||||
ASSERT_EQ(multiget_io_batch_size.max, 2);
|
ASSERT_EQ(multiget_io_batch_size.max, 2);
|
||||||
ASSERT_EQ(statistics()->getTickerCount(MULTIGET_COROUTINE_COUNT), 2);
|
ASSERT_EQ(statistics()->getTickerCount(MULTIGET_COROUTINE_COUNT), 2);
|
||||||
}
|
}
|
||||||
|
#endif // ROCKSDB_IOURING_PRESENT
|
||||||
|
|
||||||
TEST_P(DBMultiGetAsyncIOTest, LastKeyInFile) {
|
TEST_P(DBMultiGetAsyncIOTest, LastKeyInFile) {
|
||||||
std::vector<std::string> key_strs;
|
std::vector<std::string> key_strs;
|
||||||
@@ -2388,6 +2420,8 @@ TEST_P(DBMultiGetAsyncIOTest, LastKeyInFile) {
|
|||||||
values.resize(keys.size());
|
values.resize(keys.size());
|
||||||
statuses.resize(keys.size());
|
statuses.resize(keys.size());
|
||||||
|
|
||||||
|
PrepareDBForTest();
|
||||||
|
|
||||||
ReadOptions ro;
|
ReadOptions ro;
|
||||||
ro.async_io = true;
|
ro.async_io = true;
|
||||||
ro.optimize_multiget_for_io = GetParam();
|
ro.optimize_multiget_for_io = GetParam();
|
||||||
@@ -2401,6 +2435,7 @@ TEST_P(DBMultiGetAsyncIOTest, LastKeyInFile) {
|
|||||||
ASSERT_EQ(values[1], "val_l1_" + std::to_string(54));
|
ASSERT_EQ(values[1], "val_l1_" + std::to_string(54));
|
||||||
ASSERT_EQ(values[2], "val_l1_" + std::to_string(102));
|
ASSERT_EQ(values[2], "val_l1_" + std::to_string(102));
|
||||||
|
|
||||||
|
#ifdef ROCKSDB_IOURING_PRESENT
|
||||||
HistogramData multiget_io_batch_size;
|
HistogramData multiget_io_batch_size;
|
||||||
|
|
||||||
statistics()->histogramData(MULTIGET_IO_BATCH_SIZE, &multiget_io_batch_size);
|
statistics()->histogramData(MULTIGET_IO_BATCH_SIZE, &multiget_io_batch_size);
|
||||||
@@ -2411,6 +2446,7 @@ TEST_P(DBMultiGetAsyncIOTest, LastKeyInFile) {
|
|||||||
// will lookup 2 files in parallel and issue 2 async reads
|
// will lookup 2 files in parallel and issue 2 async reads
|
||||||
ASSERT_EQ(multiget_io_batch_size.count, 2);
|
ASSERT_EQ(multiget_io_batch_size.count, 2);
|
||||||
ASSERT_EQ(multiget_io_batch_size.max, 2);
|
ASSERT_EQ(multiget_io_batch_size.max, 2);
|
||||||
|
#endif // ROCKSDB_IOURING_PRESENT
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(DBMultiGetAsyncIOTest, GetFromL1AndL2) {
|
TEST_P(DBMultiGetAsyncIOTest, GetFromL1AndL2) {
|
||||||
@@ -2429,6 +2465,8 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1AndL2) {
|
|||||||
values.resize(keys.size());
|
values.resize(keys.size());
|
||||||
statuses.resize(keys.size());
|
statuses.resize(keys.size());
|
||||||
|
|
||||||
|
PrepareDBForTest();
|
||||||
|
|
||||||
ReadOptions ro;
|
ReadOptions ro;
|
||||||
ro.async_io = true;
|
ro.async_io = true;
|
||||||
ro.optimize_multiget_for_io = GetParam();
|
ro.optimize_multiget_for_io = GetParam();
|
||||||
@@ -2442,6 +2480,7 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1AndL2) {
|
|||||||
ASSERT_EQ(values[1], "val_l2_" + std::to_string(56));
|
ASSERT_EQ(values[1], "val_l2_" + std::to_string(56));
|
||||||
ASSERT_EQ(values[2], "val_l1_" + std::to_string(102));
|
ASSERT_EQ(values[2], "val_l1_" + std::to_string(102));
|
||||||
|
|
||||||
|
#ifdef ROCKSDB_IOURING_PRESENT
|
||||||
HistogramData multiget_io_batch_size;
|
HistogramData multiget_io_batch_size;
|
||||||
|
|
||||||
statistics()->histogramData(MULTIGET_IO_BATCH_SIZE, &multiget_io_batch_size);
|
statistics()->histogramData(MULTIGET_IO_BATCH_SIZE, &multiget_io_batch_size);
|
||||||
@@ -2451,6 +2490,7 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1AndL2) {
|
|||||||
// Otherwise, the L2 lookup will happen after L1.
|
// Otherwise, the L2 lookup will happen after L1.
|
||||||
ASSERT_EQ(multiget_io_batch_size.count, GetParam() ? 1 : 2);
|
ASSERT_EQ(multiget_io_batch_size.count, GetParam() ? 1 : 2);
|
||||||
ASSERT_EQ(multiget_io_batch_size.max, GetParam() ? 3 : 2);
|
ASSERT_EQ(multiget_io_batch_size.max, GetParam() ? 3 : 2);
|
||||||
|
#endif // ROCKSDB_IOURING_PRESENT
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(DBMultiGetAsyncIOTest, GetFromL2WithRangeOverlapL0L1) {
|
TEST_P(DBMultiGetAsyncIOTest, GetFromL2WithRangeOverlapL0L1) {
|
||||||
@@ -2467,6 +2507,8 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL2WithRangeOverlapL0L1) {
|
|||||||
values.resize(keys.size());
|
values.resize(keys.size());
|
||||||
statuses.resize(keys.size());
|
statuses.resize(keys.size());
|
||||||
|
|
||||||
|
PrepareDBForTest();
|
||||||
|
|
||||||
ReadOptions ro;
|
ReadOptions ro;
|
||||||
ro.async_io = true;
|
ro.async_io = true;
|
||||||
ro.optimize_multiget_for_io = GetParam();
|
ro.optimize_multiget_for_io = GetParam();
|
||||||
@@ -2482,6 +2524,7 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL2WithRangeOverlapL0L1) {
|
|||||||
ASSERT_EQ(statistics()->getTickerCount(MULTIGET_COROUTINE_COUNT), 2);
|
ASSERT_EQ(statistics()->getTickerCount(MULTIGET_COROUTINE_COUNT), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ROCKSDB_IOURING_PRESENT
|
||||||
TEST_P(DBMultiGetAsyncIOTest, GetFromL2WithRangeDelInL1) {
|
TEST_P(DBMultiGetAsyncIOTest, GetFromL2WithRangeDelInL1) {
|
||||||
std::vector<std::string> key_strs;
|
std::vector<std::string> key_strs;
|
||||||
std::vector<Slice> keys;
|
std::vector<Slice> keys;
|
||||||
@@ -2496,6 +2539,8 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL2WithRangeDelInL1) {
|
|||||||
values.resize(keys.size());
|
values.resize(keys.size());
|
||||||
statuses.resize(keys.size());
|
statuses.resize(keys.size());
|
||||||
|
|
||||||
|
PrepareDBForTest();
|
||||||
|
|
||||||
ReadOptions ro;
|
ReadOptions ro;
|
||||||
ro.async_io = true;
|
ro.async_io = true;
|
||||||
ro.optimize_multiget_for_io = GetParam();
|
ro.optimize_multiget_for_io = GetParam();
|
||||||
@@ -2525,6 +2570,8 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1AndL2WithRangeDelInL1) {
|
|||||||
values.resize(keys.size());
|
values.resize(keys.size());
|
||||||
statuses.resize(keys.size());
|
statuses.resize(keys.size());
|
||||||
|
|
||||||
|
PrepareDBForTest();
|
||||||
|
|
||||||
ReadOptions ro;
|
ReadOptions ro;
|
||||||
ro.async_io = true;
|
ro.async_io = true;
|
||||||
ro.optimize_multiget_for_io = GetParam();
|
ro.optimize_multiget_for_io = GetParam();
|
||||||
@@ -2539,6 +2586,45 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1AndL2WithRangeDelInL1) {
|
|||||||
// Bloom filters in L0/L1 will avoid the coroutine calls in those levels
|
// Bloom filters in L0/L1 will avoid the coroutine calls in those levels
|
||||||
ASSERT_EQ(statistics()->getTickerCount(MULTIGET_COROUTINE_COUNT), 3);
|
ASSERT_EQ(statistics()->getTickerCount(MULTIGET_COROUTINE_COUNT), 3);
|
||||||
}
|
}
|
||||||
|
#endif // ROCKSDB_IOURING_PRESENT
|
||||||
|
|
||||||
|
TEST_P(DBMultiGetAsyncIOTest, GetNoIOUring) {
|
||||||
|
std::vector<std::string> key_strs;
|
||||||
|
std::vector<Slice> keys;
|
||||||
|
std::vector<PinnableSlice> values;
|
||||||
|
std::vector<Status> statuses;
|
||||||
|
|
||||||
|
key_strs.push_back(Key(33));
|
||||||
|
key_strs.push_back(Key(54));
|
||||||
|
key_strs.push_back(Key(102));
|
||||||
|
keys.push_back(key_strs[0]);
|
||||||
|
keys.push_back(key_strs[1]);
|
||||||
|
keys.push_back(key_strs[2]);
|
||||||
|
values.resize(keys.size());
|
||||||
|
statuses.resize(keys.size());
|
||||||
|
|
||||||
|
enable_io_uring = false;
|
||||||
|
ReopenDB();
|
||||||
|
|
||||||
|
ReadOptions ro;
|
||||||
|
ro.async_io = true;
|
||||||
|
ro.optimize_multiget_for_io = GetParam();
|
||||||
|
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||||
|
keys.data(), values.data(), statuses.data());
|
||||||
|
ASSERT_EQ(values.size(), 3);
|
||||||
|
ASSERT_EQ(statuses[0], Status::NotSupported());
|
||||||
|
ASSERT_EQ(statuses[1], Status::NotSupported());
|
||||||
|
ASSERT_EQ(statuses[2], Status::NotSupported());
|
||||||
|
|
||||||
|
HistogramData multiget_io_batch_size;
|
||||||
|
|
||||||
|
statistics()->histogramData(MULTIGET_IO_BATCH_SIZE, &multiget_io_batch_size);
|
||||||
|
|
||||||
|
// A batch of 3 async IOs is expected, one for each overlapping file in L1
|
||||||
|
ASSERT_EQ(multiget_io_batch_size.count, 1);
|
||||||
|
ASSERT_EQ(multiget_io_batch_size.max, 3);
|
||||||
|
ASSERT_EQ(statistics()->getTickerCount(MULTIGET_COROUTINE_COUNT), 3);
|
||||||
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(DBMultiGetAsyncIOTest, DBMultiGetAsyncIOTest,
|
INSTANTIATE_TEST_CASE_P(DBMultiGetAsyncIOTest, DBMultiGetAsyncIOTest,
|
||||||
testing::Bool());
|
testing::Bool());
|
||||||
|
|||||||
@@ -3585,6 +3585,59 @@ TEST_P(DBCompactionTestWithParam, FullCompactionInBottomPriThreadPool) {
|
|||||||
Env::Default()->SetBackgroundThreads(0, Env::Priority::BOTTOM);
|
Env::Default()->SetBackgroundThreads(0, Env::Priority::BOTTOM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DBCompactionTest, CancelCompactionWaitingOnConflict) {
|
||||||
|
// This test verifies cancellation of a compaction waiting to be scheduled due
|
||||||
|
// to conflict with a running compaction.
|
||||||
|
//
|
||||||
|
// A `CompactRange()` in universal compacts all files, waiting for files to
|
||||||
|
// become available if they are locked for another compaction. This test
|
||||||
|
// triggers an automatic compaction that blocks a `CompactRange()`, and
|
||||||
|
// verifies that `DisableManualCompaction()` can successfully cancel the
|
||||||
|
// `CompactRange()` without waiting for the automatic compaction to finish.
|
||||||
|
const int kNumSortedRuns = 4;
|
||||||
|
|
||||||
|
Options options = CurrentOptions();
|
||||||
|
options.compaction_style = kCompactionStyleUniversal;
|
||||||
|
options.level0_file_num_compaction_trigger = kNumSortedRuns;
|
||||||
|
options.memtable_factory.reset(
|
||||||
|
test::NewSpecialSkipListFactory(KNumKeysByGenerateNewFile - 1));
|
||||||
|
Reopen(options);
|
||||||
|
|
||||||
|
test::SleepingBackgroundTask auto_compaction_sleeping_task;
|
||||||
|
// Block automatic compaction when it runs in the callback
|
||||||
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||||
|
"CompactionJob::Run():Start",
|
||||||
|
[&](void* /*arg*/) { auto_compaction_sleeping_task.DoSleep(); });
|
||||||
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||||
|
|
||||||
|
// Fill overlapping files in L0 to trigger an automatic compaction
|
||||||
|
Random rnd(301);
|
||||||
|
for (int i = 0; i < kNumSortedRuns; ++i) {
|
||||||
|
int key_idx = 0;
|
||||||
|
GenerateNewFile(&rnd, &key_idx, true /* nowait */);
|
||||||
|
}
|
||||||
|
auto_compaction_sleeping_task.WaitUntilSleeping();
|
||||||
|
|
||||||
|
// Make sure the manual compaction has seen the conflict before being canceled
|
||||||
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||||
|
{{"ColumnFamilyData::CompactRange:Return",
|
||||||
|
"DBCompactionTest::CancelCompactionWaitingOnConflict:"
|
||||||
|
"PreDisableManualCompaction"}});
|
||||||
|
auto manual_compaction_thread = port::Thread([this]() {
|
||||||
|
ASSERT_TRUE(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr)
|
||||||
|
.IsIncomplete());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cancel it. Thread should be joinable, i.e., manual compaction was unblocked
|
||||||
|
// despite finding a conflict with an automatic compaction that is still
|
||||||
|
// running
|
||||||
|
TEST_SYNC_POINT(
|
||||||
|
"DBCompactionTest::CancelCompactionWaitingOnConflict:"
|
||||||
|
"PreDisableManualCompaction");
|
||||||
|
db_->DisableManualCompaction();
|
||||||
|
manual_compaction_thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(DBCompactionTest, OptimizedDeletionObsoleting) {
|
TEST_F(DBCompactionTest, OptimizedDeletionObsoleting) {
|
||||||
// Deletions can be dropped when compacted to non-last level if they fall
|
// Deletions can be dropped when compacted to non-last level if they fall
|
||||||
// outside the lower-level files' key-ranges.
|
// outside the lower-level files' key-ranges.
|
||||||
|
|||||||
+65
-4
@@ -746,6 +746,64 @@ class TestFlushListener : public EventListener {
|
|||||||
};
|
};
|
||||||
#endif // !ROCKSDB_LITE
|
#endif // !ROCKSDB_LITE
|
||||||
|
|
||||||
|
// RocksDB lite does not support GetLiveFiles()
|
||||||
|
#ifndef ROCKSDB_LITE
|
||||||
|
TEST_F(DBFlushTest, FixFlushReasonRaceFromConcurrentFlushes) {
|
||||||
|
Options options = CurrentOptions();
|
||||||
|
options.atomic_flush = true;
|
||||||
|
options.disable_auto_compactions = true;
|
||||||
|
CreateAndReopenWithCF({"cf1"}, options);
|
||||||
|
|
||||||
|
for (int idx = 0; idx < 1; ++idx) {
|
||||||
|
ASSERT_OK(Put(0, Key(idx), std::string(1, 'v')));
|
||||||
|
ASSERT_OK(Put(1, Key(idx), std::string(1, 'v')));
|
||||||
|
}
|
||||||
|
|
||||||
|
// To coerce a manual flush happenning in the middle of GetLiveFiles's flush,
|
||||||
|
// we need to pause background flush thread and enable it later.
|
||||||
|
std::shared_ptr<test::SleepingBackgroundTask> sleeping_task =
|
||||||
|
std::make_shared<test::SleepingBackgroundTask>();
|
||||||
|
env_->SetBackgroundThreads(1, Env::HIGH);
|
||||||
|
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
|
||||||
|
sleeping_task.get(), Env::Priority::HIGH);
|
||||||
|
sleeping_task->WaitUntilSleeping();
|
||||||
|
|
||||||
|
// Coerce a manual flush happenning in the middle of GetLiveFiles's flush
|
||||||
|
bool get_live_files_paused_at_sync_point = false;
|
||||||
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||||
|
"DBImpl::AtomicFlushMemTables:AfterScheduleFlush", [&](void* /* arg */) {
|
||||||
|
if (get_live_files_paused_at_sync_point) {
|
||||||
|
// To prevent non-GetLiveFiles() flush from pausing at this sync point
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
get_live_files_paused_at_sync_point = true;
|
||||||
|
|
||||||
|
FlushOptions fo;
|
||||||
|
fo.wait = false;
|
||||||
|
fo.allow_write_stall = true;
|
||||||
|
ASSERT_OK(dbfull()->Flush(fo));
|
||||||
|
|
||||||
|
// Resume background flush thread so GetLiveFiles() can finish
|
||||||
|
sleeping_task->WakeUp();
|
||||||
|
sleeping_task->WaitUntilDone();
|
||||||
|
});
|
||||||
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||||
|
|
||||||
|
std::vector<std::string> files;
|
||||||
|
uint64_t manifest_file_size;
|
||||||
|
// Before the fix, a race condition on default cf's flush reason due to
|
||||||
|
// concurrent GetLiveFiles's flush and manual flush will fail
|
||||||
|
// an internal assertion.
|
||||||
|
// After the fix, such race condition is fixed and there is no assertion
|
||||||
|
// failure.
|
||||||
|
ASSERT_OK(db_->GetLiveFiles(files, &manifest_file_size, /*flush*/ true));
|
||||||
|
ASSERT_TRUE(get_live_files_paused_at_sync_point);
|
||||||
|
|
||||||
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||||
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||||
|
}
|
||||||
|
#endif // !ROCKSDB_LITE
|
||||||
|
|
||||||
TEST_F(DBFlushTest, MemPurgeBasic) {
|
TEST_F(DBFlushTest, MemPurgeBasic) {
|
||||||
Options options = CurrentOptions();
|
Options options = CurrentOptions();
|
||||||
|
|
||||||
@@ -2440,7 +2498,9 @@ TEST_P(DBAtomicFlushTest, ManualFlushUnder2PC) {
|
|||||||
options.atomic_flush = GetParam();
|
options.atomic_flush = GetParam();
|
||||||
// 64MB so that memtable flush won't be trigger by the small writes.
|
// 64MB so that memtable flush won't be trigger by the small writes.
|
||||||
options.write_buffer_size = (static_cast<size_t>(64) << 20);
|
options.write_buffer_size = (static_cast<size_t>(64) << 20);
|
||||||
|
auto flush_listener = std::make_shared<FlushCounterListener>();
|
||||||
|
flush_listener->expected_flush_reason = FlushReason::kManualFlush;
|
||||||
|
options.listeners.push_back(flush_listener);
|
||||||
// Destroy the DB to recreate as a TransactionDB.
|
// Destroy the DB to recreate as a TransactionDB.
|
||||||
Close();
|
Close();
|
||||||
Destroy(options, true);
|
Destroy(options, true);
|
||||||
@@ -2507,7 +2567,6 @@ TEST_P(DBAtomicFlushTest, ManualFlushUnder2PC) {
|
|||||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[i]);
|
auto cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[i]);
|
||||||
ASSERT_EQ(0, cfh->cfd()->imm()->NumNotFlushed());
|
ASSERT_EQ(0, cfh->cfd()->imm()->NumNotFlushed());
|
||||||
ASSERT_TRUE(cfh->cfd()->mem()->IsEmpty());
|
ASSERT_TRUE(cfh->cfd()->mem()->IsEmpty());
|
||||||
ASSERT_EQ(cfh->cfd()->GetFlushReason(), FlushReason::kManualFlush);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The recovered min log number with prepared data should be non-zero.
|
// The recovered min log number with prepared data should be non-zero.
|
||||||
@@ -2520,13 +2579,15 @@ TEST_P(DBAtomicFlushTest, ManualFlushUnder2PC) {
|
|||||||
ASSERT_TRUE(db_impl->allow_2pc());
|
ASSERT_TRUE(db_impl->allow_2pc());
|
||||||
ASSERT_NE(db_impl->MinLogNumberToKeep(), 0);
|
ASSERT_NE(db_impl->MinLogNumberToKeep(), 0);
|
||||||
}
|
}
|
||||||
#endif // ROCKSDB_LITE
|
|
||||||
|
|
||||||
TEST_P(DBAtomicFlushTest, ManualAtomicFlush) {
|
TEST_P(DBAtomicFlushTest, ManualAtomicFlush) {
|
||||||
Options options = CurrentOptions();
|
Options options = CurrentOptions();
|
||||||
options.create_if_missing = true;
|
options.create_if_missing = true;
|
||||||
options.atomic_flush = GetParam();
|
options.atomic_flush = GetParam();
|
||||||
options.write_buffer_size = (static_cast<size_t>(64) << 20);
|
options.write_buffer_size = (static_cast<size_t>(64) << 20);
|
||||||
|
auto flush_listener = std::make_shared<FlushCounterListener>();
|
||||||
|
flush_listener->expected_flush_reason = FlushReason::kManualFlush;
|
||||||
|
options.listeners.push_back(flush_listener);
|
||||||
|
|
||||||
CreateAndReopenWithCF({"pikachu", "eevee"}, options);
|
CreateAndReopenWithCF({"pikachu", "eevee"}, options);
|
||||||
size_t num_cfs = handles_.size();
|
size_t num_cfs = handles_.size();
|
||||||
@@ -2551,11 +2612,11 @@ TEST_P(DBAtomicFlushTest, ManualAtomicFlush) {
|
|||||||
|
|
||||||
for (size_t i = 0; i != num_cfs; ++i) {
|
for (size_t i = 0; i != num_cfs; ++i) {
|
||||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[i]);
|
auto cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[i]);
|
||||||
ASSERT_EQ(cfh->cfd()->GetFlushReason(), FlushReason::kManualFlush);
|
|
||||||
ASSERT_EQ(0, cfh->cfd()->imm()->NumNotFlushed());
|
ASSERT_EQ(0, cfh->cfd()->imm()->NumNotFlushed());
|
||||||
ASSERT_TRUE(cfh->cfd()->mem()->IsEmpty());
|
ASSERT_TRUE(cfh->cfd()->mem()->IsEmpty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
|
||||||
TEST_P(DBAtomicFlushTest, PrecomputeMinLogNumberToKeepNon2PC) {
|
TEST_P(DBAtomicFlushTest, PrecomputeMinLogNumberToKeepNon2PC) {
|
||||||
Options options = CurrentOptions();
|
Options options = CurrentOptions();
|
||||||
|
|||||||
@@ -604,7 +604,7 @@ Status DBImpl::CloseHelper() {
|
|||||||
|
|
||||||
while (!flush_queue_.empty()) {
|
while (!flush_queue_.empty()) {
|
||||||
const FlushRequest& flush_req = PopFirstFromFlushQueue();
|
const FlushRequest& flush_req = PopFirstFromFlushQueue();
|
||||||
for (const auto& iter : flush_req) {
|
for (const auto& iter : flush_req.cfd_to_max_mem_id_to_persist) {
|
||||||
iter.first->UnrefAndTryDelete();
|
iter.first->UnrefAndTryDelete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-15
@@ -16,6 +16,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -1383,7 +1384,7 @@ class DBImpl : public DB {
|
|||||||
|
|
||||||
void NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
void NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
||||||
const MutableCFOptions& mutable_cf_options,
|
const MutableCFOptions& mutable_cf_options,
|
||||||
int job_id);
|
int job_id, FlushReason flush_reason);
|
||||||
|
|
||||||
void NotifyOnFlushCompleted(
|
void NotifyOnFlushCompleted(
|
||||||
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options,
|
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options,
|
||||||
@@ -1675,12 +1676,17 @@ class DBImpl : public DB {
|
|||||||
// Argument required by background flush thread.
|
// Argument required by background flush thread.
|
||||||
struct BGFlushArg {
|
struct BGFlushArg {
|
||||||
BGFlushArg()
|
BGFlushArg()
|
||||||
: cfd_(nullptr), max_memtable_id_(0), superversion_context_(nullptr) {}
|
: cfd_(nullptr),
|
||||||
|
max_memtable_id_(0),
|
||||||
|
superversion_context_(nullptr),
|
||||||
|
flush_reason_(FlushReason::kOthers) {}
|
||||||
BGFlushArg(ColumnFamilyData* cfd, uint64_t max_memtable_id,
|
BGFlushArg(ColumnFamilyData* cfd, uint64_t max_memtable_id,
|
||||||
SuperVersionContext* superversion_context)
|
SuperVersionContext* superversion_context,
|
||||||
|
FlushReason flush_reason)
|
||||||
: cfd_(cfd),
|
: cfd_(cfd),
|
||||||
max_memtable_id_(max_memtable_id),
|
max_memtable_id_(max_memtable_id),
|
||||||
superversion_context_(superversion_context) {}
|
superversion_context_(superversion_context),
|
||||||
|
flush_reason_(flush_reason) {}
|
||||||
|
|
||||||
// Column family to flush.
|
// Column family to flush.
|
||||||
ColumnFamilyData* cfd_;
|
ColumnFamilyData* cfd_;
|
||||||
@@ -1691,6 +1697,7 @@ class DBImpl : public DB {
|
|||||||
// installs a new superversion for the column family. This operation
|
// installs a new superversion for the column family. This operation
|
||||||
// requires a SuperVersionContext object (currently embedded in JobContext).
|
// requires a SuperVersionContext object (currently embedded in JobContext).
|
||||||
SuperVersionContext* superversion_context_;
|
SuperVersionContext* superversion_context_;
|
||||||
|
FlushReason flush_reason_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Argument passed to flush thread.
|
// Argument passed to flush thread.
|
||||||
@@ -1819,7 +1826,7 @@ class DBImpl : public DB {
|
|||||||
// installs a new super version for the column family.
|
// installs a new super version for the column family.
|
||||||
Status FlushMemTableToOutputFile(
|
Status FlushMemTableToOutputFile(
|
||||||
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options,
|
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options,
|
||||||
bool* madeProgress, JobContext* job_context,
|
bool* madeProgress, JobContext* job_context, FlushReason flush_reason,
|
||||||
SuperVersionContext* superversion_context,
|
SuperVersionContext* superversion_context,
|
||||||
std::vector<SequenceNumber>& snapshot_seqs,
|
std::vector<SequenceNumber>& snapshot_seqs,
|
||||||
SequenceNumber earliest_write_conflict_snapshot,
|
SequenceNumber earliest_write_conflict_snapshot,
|
||||||
@@ -1865,7 +1872,8 @@ class DBImpl : public DB {
|
|||||||
|
|
||||||
// num_bytes: for slowdown case, delay time is calculated based on
|
// num_bytes: for slowdown case, delay time is calculated based on
|
||||||
// `num_bytes` going through.
|
// `num_bytes` going through.
|
||||||
Status DelayWrite(uint64_t num_bytes, const WriteOptions& write_options);
|
Status DelayWrite(uint64_t num_bytes, WriteThread& write_thread,
|
||||||
|
const WriteOptions& write_options);
|
||||||
|
|
||||||
// Begin stalling of writes when memory usage increases beyond a certain
|
// Begin stalling of writes when memory usage increases beyond a certain
|
||||||
// threshold.
|
// threshold.
|
||||||
@@ -2029,18 +2037,22 @@ class DBImpl : public DB {
|
|||||||
|
|
||||||
void MaybeScheduleFlushOrCompaction();
|
void MaybeScheduleFlushOrCompaction();
|
||||||
|
|
||||||
// A flush request specifies the column families to flush as well as the
|
struct FlushRequest {
|
||||||
// largest memtable id to persist for each column family. Once all the
|
FlushReason flush_reason;
|
||||||
// memtables whose IDs are smaller than or equal to this per-column-family
|
// A map from column family to flush to largest memtable id to persist for
|
||||||
// specified value, this flush request is considered to have completed its
|
// each column family. Once all the memtables whose IDs are smaller than or
|
||||||
// work of flushing this column family. After completing the work for all
|
// equal to this per-column-family specified value, this flush request is
|
||||||
// column families in this request, this flush is considered complete.
|
// considered to have completed its work of flushing this column family.
|
||||||
using FlushRequest = std::vector<std::pair<ColumnFamilyData*, uint64_t>>;
|
// After completing the work for all column families in this request, this
|
||||||
|
// flush is considered complete.
|
||||||
|
std::unordered_map<ColumnFamilyData*, uint64_t>
|
||||||
|
cfd_to_max_mem_id_to_persist;
|
||||||
|
};
|
||||||
|
|
||||||
void GenerateFlushRequest(const autovector<ColumnFamilyData*>& cfds,
|
void GenerateFlushRequest(const autovector<ColumnFamilyData*>& cfds,
|
||||||
FlushRequest* req);
|
FlushReason flush_reason, FlushRequest* req);
|
||||||
|
|
||||||
void SchedulePendingFlush(const FlushRequest& req, FlushReason flush_reason);
|
void SchedulePendingFlush(const FlushRequest& req);
|
||||||
|
|
||||||
void SchedulePendingCompaction(ColumnFamilyData* cfd);
|
void SchedulePendingCompaction(ColumnFamilyData* cfd);
|
||||||
void SchedulePendingPurge(std::string fname, std::string dir_to_sync,
|
void SchedulePendingPurge(std::string fname, std::string dir_to_sync,
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ IOStatus DBImpl::SyncClosedLogs(JobContext* job_context,
|
|||||||
|
|
||||||
Status DBImpl::FlushMemTableToOutputFile(
|
Status DBImpl::FlushMemTableToOutputFile(
|
||||||
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options,
|
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options,
|
||||||
bool* made_progress, JobContext* job_context,
|
bool* made_progress, JobContext* job_context, FlushReason flush_reason,
|
||||||
SuperVersionContext* superversion_context,
|
SuperVersionContext* superversion_context,
|
||||||
std::vector<SequenceNumber>& snapshot_seqs,
|
std::vector<SequenceNumber>& snapshot_seqs,
|
||||||
SequenceNumber earliest_write_conflict_snapshot,
|
SequenceNumber earliest_write_conflict_snapshot,
|
||||||
@@ -215,7 +215,8 @@ Status DBImpl::FlushMemTableToOutputFile(
|
|||||||
dbname_, cfd, immutable_db_options_, mutable_cf_options, max_memtable_id,
|
dbname_, cfd, immutable_db_options_, mutable_cf_options, max_memtable_id,
|
||||||
file_options_for_compaction_, versions_.get(), &mutex_, &shutting_down_,
|
file_options_for_compaction_, versions_.get(), &mutex_, &shutting_down_,
|
||||||
snapshot_seqs, earliest_write_conflict_snapshot, snapshot_checker,
|
snapshot_seqs, earliest_write_conflict_snapshot, snapshot_checker,
|
||||||
job_context, log_buffer, directories_.GetDbDir(), GetDataDir(cfd, 0U),
|
job_context, flush_reason, log_buffer, directories_.GetDbDir(),
|
||||||
|
GetDataDir(cfd, 0U),
|
||||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options), stats_,
|
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options), stats_,
|
||||||
&event_logger_, mutable_cf_options.report_bg_io_stats,
|
&event_logger_, mutable_cf_options.report_bg_io_stats,
|
||||||
true /* sync_output_directory */, true /* write_manifest */, thread_pri,
|
true /* sync_output_directory */, true /* write_manifest */, thread_pri,
|
||||||
@@ -260,7 +261,8 @@ Status DBImpl::FlushMemTableToOutputFile(
|
|||||||
|
|
||||||
#ifndef ROCKSDB_LITE
|
#ifndef ROCKSDB_LITE
|
||||||
// may temporarily unlock and lock the mutex.
|
// may temporarily unlock and lock the mutex.
|
||||||
NotifyOnFlushBegin(cfd, &file_meta, mutable_cf_options, job_context->job_id);
|
NotifyOnFlushBegin(cfd, &file_meta, mutable_cf_options, job_context->job_id,
|
||||||
|
flush_reason);
|
||||||
#endif // ROCKSDB_LITE
|
#endif // ROCKSDB_LITE
|
||||||
|
|
||||||
bool switched_to_mempurge = false;
|
bool switched_to_mempurge = false;
|
||||||
@@ -390,8 +392,9 @@ Status DBImpl::FlushMemTablesToOutputFiles(
|
|||||||
MutableCFOptions mutable_cf_options_copy = *cfd->GetLatestMutableCFOptions();
|
MutableCFOptions mutable_cf_options_copy = *cfd->GetLatestMutableCFOptions();
|
||||||
SuperVersionContext* superversion_context =
|
SuperVersionContext* superversion_context =
|
||||||
bg_flush_arg.superversion_context_;
|
bg_flush_arg.superversion_context_;
|
||||||
|
FlushReason flush_reason = bg_flush_arg.flush_reason_;
|
||||||
Status s = FlushMemTableToOutputFile(
|
Status s = FlushMemTableToOutputFile(
|
||||||
cfd, mutable_cf_options_copy, made_progress, job_context,
|
cfd, mutable_cf_options_copy, made_progress, job_context, flush_reason,
|
||||||
superversion_context, snapshot_seqs, earliest_write_conflict_snapshot,
|
superversion_context, snapshot_seqs, earliest_write_conflict_snapshot,
|
||||||
snapshot_checker, log_buffer, thread_pri);
|
snapshot_checker, log_buffer, thread_pri);
|
||||||
return s;
|
return s;
|
||||||
@@ -420,7 +423,9 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
|||||||
for (const auto cfd : cfds) {
|
for (const auto cfd : cfds) {
|
||||||
assert(cfd->imm()->NumNotFlushed() != 0);
|
assert(cfd->imm()->NumNotFlushed() != 0);
|
||||||
assert(cfd->imm()->IsFlushPending());
|
assert(cfd->imm()->IsFlushPending());
|
||||||
assert(cfd->GetFlushReason() == cfds[0]->GetFlushReason());
|
}
|
||||||
|
for (const auto bg_flush_arg : bg_flush_args) {
|
||||||
|
assert(bg_flush_arg.flush_reason_ == bg_flush_args[0].flush_reason_);
|
||||||
}
|
}
|
||||||
#endif /* !NDEBUG */
|
#endif /* !NDEBUG */
|
||||||
|
|
||||||
@@ -459,13 +464,15 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
|||||||
all_mutable_cf_options.emplace_back(*cfd->GetLatestMutableCFOptions());
|
all_mutable_cf_options.emplace_back(*cfd->GetLatestMutableCFOptions());
|
||||||
const MutableCFOptions& mutable_cf_options = all_mutable_cf_options.back();
|
const MutableCFOptions& mutable_cf_options = all_mutable_cf_options.back();
|
||||||
uint64_t max_memtable_id = bg_flush_args[i].max_memtable_id_;
|
uint64_t max_memtable_id = bg_flush_args[i].max_memtable_id_;
|
||||||
|
FlushReason flush_reason = bg_flush_args[i].flush_reason_;
|
||||||
jobs.emplace_back(new FlushJob(
|
jobs.emplace_back(new FlushJob(
|
||||||
dbname_, cfd, immutable_db_options_, mutable_cf_options,
|
dbname_, cfd, immutable_db_options_, mutable_cf_options,
|
||||||
max_memtable_id, file_options_for_compaction_, versions_.get(), &mutex_,
|
max_memtable_id, file_options_for_compaction_, versions_.get(), &mutex_,
|
||||||
&shutting_down_, snapshot_seqs, earliest_write_conflict_snapshot,
|
&shutting_down_, snapshot_seqs, earliest_write_conflict_snapshot,
|
||||||
snapshot_checker, job_context, log_buffer, directories_.GetDbDir(),
|
snapshot_checker, job_context, flush_reason, log_buffer,
|
||||||
data_dir, GetCompressionFlush(*cfd->ioptions(), mutable_cf_options),
|
directories_.GetDbDir(), data_dir,
|
||||||
stats_, &event_logger_, mutable_cf_options.report_bg_io_stats,
|
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options), stats_,
|
||||||
|
&event_logger_, mutable_cf_options.report_bg_io_stats,
|
||||||
false /* sync_output_directory */, false /* write_manifest */,
|
false /* sync_output_directory */, false /* write_manifest */,
|
||||||
thread_pri, io_tracer_, seqno_time_mapping_, db_id_, db_session_id_,
|
thread_pri, io_tracer_, seqno_time_mapping_, db_id_, db_session_id_,
|
||||||
cfd->GetFullHistoryTsLow(), &blob_callback_));
|
cfd->GetFullHistoryTsLow(), &blob_callback_));
|
||||||
@@ -483,8 +490,9 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
|||||||
for (int i = 0; i != num_cfs; ++i) {
|
for (int i = 0; i != num_cfs; ++i) {
|
||||||
const MutableCFOptions& mutable_cf_options = all_mutable_cf_options.at(i);
|
const MutableCFOptions& mutable_cf_options = all_mutable_cf_options.at(i);
|
||||||
// may temporarily unlock and lock the mutex.
|
// may temporarily unlock and lock the mutex.
|
||||||
|
FlushReason flush_reason = bg_flush_args[i].flush_reason_;
|
||||||
NotifyOnFlushBegin(cfds[i], &file_meta[i], mutable_cf_options,
|
NotifyOnFlushBegin(cfds[i], &file_meta[i], mutable_cf_options,
|
||||||
job_context->job_id);
|
job_context->job_id, flush_reason);
|
||||||
}
|
}
|
||||||
#endif /* !ROCKSDB_LITE */
|
#endif /* !ROCKSDB_LITE */
|
||||||
|
|
||||||
@@ -642,8 +650,9 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
|||||||
|
|
||||||
bool resuming_from_bg_err =
|
bool resuming_from_bg_err =
|
||||||
error_handler_.IsDBStopped() ||
|
error_handler_.IsDBStopped() ||
|
||||||
(cfds[0]->GetFlushReason() == FlushReason::kErrorRecovery ||
|
(bg_flush_args[0].flush_reason_ == FlushReason::kErrorRecovery ||
|
||||||
cfds[0]->GetFlushReason() == FlushReason::kErrorRecoveryRetryFlush);
|
bg_flush_args[0].flush_reason_ ==
|
||||||
|
FlushReason::kErrorRecoveryRetryFlush);
|
||||||
while ((!resuming_from_bg_err || error_handler_.GetRecoveryError().ok())) {
|
while ((!resuming_from_bg_err || error_handler_.GetRecoveryError().ok())) {
|
||||||
std::pair<Status, bool> res = wait_to_install_func();
|
std::pair<Status, bool> res = wait_to_install_func();
|
||||||
|
|
||||||
@@ -660,8 +669,9 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
|||||||
|
|
||||||
resuming_from_bg_err =
|
resuming_from_bg_err =
|
||||||
error_handler_.IsDBStopped() ||
|
error_handler_.IsDBStopped() ||
|
||||||
(cfds[0]->GetFlushReason() == FlushReason::kErrorRecovery ||
|
(bg_flush_args[0].flush_reason_ == FlushReason::kErrorRecovery ||
|
||||||
cfds[0]->GetFlushReason() == FlushReason::kErrorRecoveryRetryFlush);
|
bg_flush_args[0].flush_reason_ ==
|
||||||
|
FlushReason::kErrorRecoveryRetryFlush);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!resuming_from_bg_err) {
|
if (!resuming_from_bg_err) {
|
||||||
@@ -816,7 +826,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
|||||||
|
|
||||||
void DBImpl::NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
void DBImpl::NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
||||||
const MutableCFOptions& mutable_cf_options,
|
const MutableCFOptions& mutable_cf_options,
|
||||||
int job_id) {
|
int job_id, FlushReason flush_reason) {
|
||||||
#ifndef ROCKSDB_LITE
|
#ifndef ROCKSDB_LITE
|
||||||
if (immutable_db_options_.listeners.size() == 0U) {
|
if (immutable_db_options_.listeners.size() == 0U) {
|
||||||
return;
|
return;
|
||||||
@@ -849,7 +859,7 @@ void DBImpl::NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
|||||||
info.triggered_writes_stop = triggered_writes_stop;
|
info.triggered_writes_stop = triggered_writes_stop;
|
||||||
info.smallest_seqno = file_meta->fd.smallest_seqno;
|
info.smallest_seqno = file_meta->fd.smallest_seqno;
|
||||||
info.largest_seqno = file_meta->fd.largest_seqno;
|
info.largest_seqno = file_meta->fd.largest_seqno;
|
||||||
info.flush_reason = cfd->GetFlushReason();
|
info.flush_reason = flush_reason;
|
||||||
for (auto listener : immutable_db_options_.listeners) {
|
for (auto listener : immutable_db_options_.listeners) {
|
||||||
listener->OnFlushBegin(this, info);
|
listener->OnFlushBegin(this, info);
|
||||||
}
|
}
|
||||||
@@ -862,6 +872,7 @@ void DBImpl::NotifyOnFlushBegin(ColumnFamilyData* cfd, FileMetaData* file_meta,
|
|||||||
(void)file_meta;
|
(void)file_meta;
|
||||||
(void)mutable_cf_options;
|
(void)mutable_cf_options;
|
||||||
(void)job_id;
|
(void)job_id;
|
||||||
|
(void)flush_reason;
|
||||||
#endif // ROCKSDB_LITE
|
#endif // ROCKSDB_LITE
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2023,8 +2034,19 @@ Status DBImpl::RunManualCompaction(
|
|||||||
manual.begin, manual.end, &manual.manual_end, &manual_conflict,
|
manual.begin, manual.end, &manual.manual_end, &manual_conflict,
|
||||||
max_file_num_to_ignore, trim_ts)) == nullptr &&
|
max_file_num_to_ignore, trim_ts)) == nullptr &&
|
||||||
manual_conflict))) {
|
manual_conflict))) {
|
||||||
// Running either this or some other manual compaction
|
if (!scheduled) {
|
||||||
bg_cv_.Wait();
|
// There is a conflicting compaction
|
||||||
|
if (manual_compaction_paused_ > 0 || manual.canceled == true) {
|
||||||
|
// Stop waiting since it was canceled. Pretend the error came from
|
||||||
|
// compaction so the below cleanup/error handling code can process it.
|
||||||
|
manual.done = true;
|
||||||
|
manual.status =
|
||||||
|
Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!manual.done) {
|
||||||
|
bg_cv_.Wait();
|
||||||
|
}
|
||||||
if (manual_compaction_paused_ > 0 && scheduled && !unscheduled) {
|
if (manual_compaction_paused_ > 0 && scheduled && !unscheduled) {
|
||||||
assert(thread_pool_priority != Env::Priority::TOTAL);
|
assert(thread_pool_priority != Env::Priority::TOTAL);
|
||||||
// unschedule all manual compactions
|
// unschedule all manual compactions
|
||||||
@@ -2102,16 +2124,17 @@ Status DBImpl::RunManualCompaction(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DBImpl::GenerateFlushRequest(const autovector<ColumnFamilyData*>& cfds,
|
void DBImpl::GenerateFlushRequest(const autovector<ColumnFamilyData*>& cfds,
|
||||||
FlushRequest* req) {
|
FlushReason flush_reason, FlushRequest* req) {
|
||||||
assert(req != nullptr);
|
assert(req != nullptr);
|
||||||
req->reserve(cfds.size());
|
req->flush_reason = flush_reason;
|
||||||
|
req->cfd_to_max_mem_id_to_persist.reserve(cfds.size());
|
||||||
for (const auto cfd : cfds) {
|
for (const auto cfd : cfds) {
|
||||||
if (nullptr == cfd) {
|
if (nullptr == cfd) {
|
||||||
// cfd may be null, see DBImpl::ScheduleFlushes
|
// cfd may be null, see DBImpl::ScheduleFlushes
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
uint64_t max_memtable_id = cfd->imm()->GetLatestMemTableID();
|
uint64_t max_memtable_id = cfd->imm()->GetLatestMemTableID();
|
||||||
req->emplace_back(cfd, max_memtable_id);
|
req->cfd_to_max_mem_id_to_persist.emplace(cfd, max_memtable_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2169,7 +2192,7 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
|||||||
if (s.ok()) {
|
if (s.ok()) {
|
||||||
if (cfd->imm()->NumNotFlushed() != 0 || !cfd->mem()->IsEmpty() ||
|
if (cfd->imm()->NumNotFlushed() != 0 || !cfd->mem()->IsEmpty() ||
|
||||||
!cached_recoverable_state_empty_.load()) {
|
!cached_recoverable_state_empty_.load()) {
|
||||||
FlushRequest req{{cfd, flush_memtable_id}};
|
FlushRequest req{flush_reason, {{cfd, flush_memtable_id}}};
|
||||||
flush_reqs.emplace_back(std::move(req));
|
flush_reqs.emplace_back(std::move(req));
|
||||||
memtable_ids_to_wait.emplace_back(cfd->imm()->GetLatestMemTableID());
|
memtable_ids_to_wait.emplace_back(cfd->imm()->GetLatestMemTableID());
|
||||||
}
|
}
|
||||||
@@ -2197,7 +2220,7 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
|||||||
"to avoid holding old logs",
|
"to avoid holding old logs",
|
||||||
cfd->GetName().c_str());
|
cfd->GetName().c_str());
|
||||||
s = SwitchMemtable(cfd_stats, &context);
|
s = SwitchMemtable(cfd_stats, &context);
|
||||||
FlushRequest req{{cfd_stats, flush_memtable_id}};
|
FlushRequest req{flush_reason, {{cfd_stats, flush_memtable_id}}};
|
||||||
flush_reqs.emplace_back(std::move(req));
|
flush_reqs.emplace_back(std::move(req));
|
||||||
memtable_ids_to_wait.emplace_back(
|
memtable_ids_to_wait.emplace_back(
|
||||||
cfd_stats->imm()->GetLatestMemTableID());
|
cfd_stats->imm()->GetLatestMemTableID());
|
||||||
@@ -2208,8 +2231,9 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
|||||||
|
|
||||||
if (s.ok() && !flush_reqs.empty()) {
|
if (s.ok() && !flush_reqs.empty()) {
|
||||||
for (const auto& req : flush_reqs) {
|
for (const auto& req : flush_reqs) {
|
||||||
assert(req.size() == 1);
|
assert(req.cfd_to_max_mem_id_to_persist.size() == 1);
|
||||||
ColumnFamilyData* loop_cfd = req[0].first;
|
ColumnFamilyData* loop_cfd =
|
||||||
|
req.cfd_to_max_mem_id_to_persist.begin()->first;
|
||||||
loop_cfd->imm()->FlushRequested();
|
loop_cfd->imm()->FlushRequested();
|
||||||
}
|
}
|
||||||
// If the caller wants to wait for this flush to complete, it indicates
|
// If the caller wants to wait for this flush to complete, it indicates
|
||||||
@@ -2218,13 +2242,14 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
|||||||
// Therefore, we increase the cfd's ref count.
|
// Therefore, we increase the cfd's ref count.
|
||||||
if (flush_options.wait) {
|
if (flush_options.wait) {
|
||||||
for (const auto& req : flush_reqs) {
|
for (const auto& req : flush_reqs) {
|
||||||
assert(req.size() == 1);
|
assert(req.cfd_to_max_mem_id_to_persist.size() == 1);
|
||||||
ColumnFamilyData* loop_cfd = req[0].first;
|
ColumnFamilyData* loop_cfd =
|
||||||
|
req.cfd_to_max_mem_id_to_persist.begin()->first;
|
||||||
loop_cfd->Ref();
|
loop_cfd->Ref();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const auto& req : flush_reqs) {
|
for (const auto& req : flush_reqs) {
|
||||||
SchedulePendingFlush(req, flush_reason);
|
SchedulePendingFlush(req);
|
||||||
}
|
}
|
||||||
MaybeScheduleFlushOrCompaction();
|
MaybeScheduleFlushOrCompaction();
|
||||||
}
|
}
|
||||||
@@ -2243,8 +2268,8 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
|||||||
autovector<const uint64_t*> flush_memtable_ids;
|
autovector<const uint64_t*> flush_memtable_ids;
|
||||||
assert(flush_reqs.size() == memtable_ids_to_wait.size());
|
assert(flush_reqs.size() == memtable_ids_to_wait.size());
|
||||||
for (size_t i = 0; i < flush_reqs.size(); ++i) {
|
for (size_t i = 0; i < flush_reqs.size(); ++i) {
|
||||||
assert(flush_reqs[i].size() == 1);
|
assert(flush_reqs[i].cfd_to_max_mem_id_to_persist.size() == 1);
|
||||||
cfds.push_back(flush_reqs[i][0].first);
|
cfds.push_back(flush_reqs[i].cfd_to_max_mem_id_to_persist.begin()->first);
|
||||||
flush_memtable_ids.push_back(&(memtable_ids_to_wait[i]));
|
flush_memtable_ids.push_back(&(memtable_ids_to_wait[i]));
|
||||||
}
|
}
|
||||||
s = WaitForFlushMemTables(
|
s = WaitForFlushMemTables(
|
||||||
@@ -2341,8 +2366,8 @@ Status DBImpl::AtomicFlushMemTables(
|
|||||||
cfd->Ref();
|
cfd->Ref();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GenerateFlushRequest(cfds, &flush_req);
|
GenerateFlushRequest(cfds, flush_reason, &flush_req);
|
||||||
SchedulePendingFlush(flush_req, flush_reason);
|
SchedulePendingFlush(flush_req);
|
||||||
MaybeScheduleFlushOrCompaction();
|
MaybeScheduleFlushOrCompaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2357,7 +2382,7 @@ Status DBImpl::AtomicFlushMemTables(
|
|||||||
TEST_SYNC_POINT("DBImpl::AtomicFlushMemTables:BeforeWaitForBgFlush");
|
TEST_SYNC_POINT("DBImpl::AtomicFlushMemTables:BeforeWaitForBgFlush");
|
||||||
if (s.ok() && flush_options.wait) {
|
if (s.ok() && flush_options.wait) {
|
||||||
autovector<const uint64_t*> flush_memtable_ids;
|
autovector<const uint64_t*> flush_memtable_ids;
|
||||||
for (auto& iter : flush_req) {
|
for (auto& iter : flush_req.cfd_to_max_mem_id_to_persist) {
|
||||||
flush_memtable_ids.push_back(&(iter.second));
|
flush_memtable_ids.push_back(&(iter.second));
|
||||||
}
|
}
|
||||||
s = WaitForFlushMemTables(
|
s = WaitForFlushMemTables(
|
||||||
@@ -2704,9 +2729,9 @@ DBImpl::FlushRequest DBImpl::PopFirstFromFlushQueue() {
|
|||||||
FlushRequest flush_req = flush_queue_.front();
|
FlushRequest flush_req = flush_queue_.front();
|
||||||
flush_queue_.pop_front();
|
flush_queue_.pop_front();
|
||||||
if (!immutable_db_options_.atomic_flush) {
|
if (!immutable_db_options_.atomic_flush) {
|
||||||
assert(flush_req.size() == 1);
|
assert(flush_req.cfd_to_max_mem_id_to_persist.size() == 1);
|
||||||
}
|
}
|
||||||
for (const auto& elem : flush_req) {
|
for (const auto& elem : flush_req.cfd_to_max_mem_id_to_persist) {
|
||||||
if (!immutable_db_options_.atomic_flush) {
|
if (!immutable_db_options_.atomic_flush) {
|
||||||
ColumnFamilyData* cfd = elem.first;
|
ColumnFamilyData* cfd = elem.first;
|
||||||
assert(cfd);
|
assert(cfd);
|
||||||
@@ -2714,7 +2739,6 @@ DBImpl::FlushRequest DBImpl::PopFirstFromFlushQueue() {
|
|||||||
cfd->set_queued_for_flush(false);
|
cfd->set_queued_for_flush(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: need to unset flush reason?
|
|
||||||
return flush_req;
|
return flush_req;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2744,31 +2768,29 @@ ColumnFamilyData* DBImpl::PickCompactionFromQueue(
|
|||||||
return cfd;
|
return cfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DBImpl::SchedulePendingFlush(const FlushRequest& flush_req,
|
void DBImpl::SchedulePendingFlush(const FlushRequest& flush_req) {
|
||||||
FlushReason flush_reason) {
|
|
||||||
mutex_.AssertHeld();
|
mutex_.AssertHeld();
|
||||||
if (flush_req.empty()) {
|
if (flush_req.cfd_to_max_mem_id_to_persist.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!immutable_db_options_.atomic_flush) {
|
if (!immutable_db_options_.atomic_flush) {
|
||||||
// For the non-atomic flush case, we never schedule multiple column
|
// For the non-atomic flush case, we never schedule multiple column
|
||||||
// families in the same flush request.
|
// families in the same flush request.
|
||||||
assert(flush_req.size() == 1);
|
assert(flush_req.cfd_to_max_mem_id_to_persist.size() == 1);
|
||||||
ColumnFamilyData* cfd = flush_req[0].first;
|
ColumnFamilyData* cfd =
|
||||||
|
flush_req.cfd_to_max_mem_id_to_persist.begin()->first;
|
||||||
assert(cfd);
|
assert(cfd);
|
||||||
|
|
||||||
if (!cfd->queued_for_flush() && cfd->imm()->IsFlushPending()) {
|
if (!cfd->queued_for_flush() && cfd->imm()->IsFlushPending()) {
|
||||||
cfd->Ref();
|
cfd->Ref();
|
||||||
cfd->set_queued_for_flush(true);
|
cfd->set_queued_for_flush(true);
|
||||||
cfd->SetFlushReason(flush_reason);
|
|
||||||
++unscheduled_flushes_;
|
++unscheduled_flushes_;
|
||||||
flush_queue_.push_back(flush_req);
|
flush_queue_.push_back(flush_req);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (auto& iter : flush_req) {
|
for (auto& iter : flush_req.cfd_to_max_mem_id_to_persist) {
|
||||||
ColumnFamilyData* cfd = iter.first;
|
ColumnFamilyData* cfd = iter.first;
|
||||||
cfd->Ref();
|
cfd->Ref();
|
||||||
cfd->SetFlushReason(flush_reason);
|
|
||||||
}
|
}
|
||||||
++unscheduled_flushes_;
|
++unscheduled_flushes_;
|
||||||
flush_queue_.push_back(flush_req);
|
flush_queue_.push_back(flush_req);
|
||||||
@@ -2900,10 +2922,12 @@ Status DBImpl::BackgroundFlush(bool* made_progress, JobContext* job_context,
|
|||||||
while (!flush_queue_.empty()) {
|
while (!flush_queue_.empty()) {
|
||||||
// This cfd is already referenced
|
// This cfd is already referenced
|
||||||
const FlushRequest& flush_req = PopFirstFromFlushQueue();
|
const FlushRequest& flush_req = PopFirstFromFlushQueue();
|
||||||
|
FlushReason flush_reason = flush_req.flush_reason;
|
||||||
superversion_contexts.clear();
|
superversion_contexts.clear();
|
||||||
superversion_contexts.reserve(flush_req.size());
|
superversion_contexts.reserve(
|
||||||
|
flush_req.cfd_to_max_mem_id_to_persist.size());
|
||||||
|
|
||||||
for (const auto& iter : flush_req) {
|
for (const auto& iter : flush_req.cfd_to_max_mem_id_to_persist) {
|
||||||
ColumnFamilyData* cfd = iter.first;
|
ColumnFamilyData* cfd = iter.first;
|
||||||
if (cfd->GetMempurgeUsed()) {
|
if (cfd->GetMempurgeUsed()) {
|
||||||
// If imm() contains silent memtables (e.g.: because
|
// If imm() contains silent memtables (e.g.: because
|
||||||
@@ -2919,7 +2943,7 @@ Status DBImpl::BackgroundFlush(bool* made_progress, JobContext* job_context,
|
|||||||
}
|
}
|
||||||
superversion_contexts.emplace_back(SuperVersionContext(true));
|
superversion_contexts.emplace_back(SuperVersionContext(true));
|
||||||
bg_flush_args.emplace_back(cfd, iter.second,
|
bg_flush_args.emplace_back(cfd, iter.second,
|
||||||
&(superversion_contexts.back()));
|
&(superversion_contexts.back()), flush_reason);
|
||||||
}
|
}
|
||||||
if (!bg_flush_args.empty()) {
|
if (!bg_flush_args.empty()) {
|
||||||
break;
|
break;
|
||||||
@@ -2943,9 +2967,14 @@ Status DBImpl::BackgroundFlush(bool* made_progress, JobContext* job_context,
|
|||||||
status = FlushMemTablesToOutputFiles(bg_flush_args, made_progress,
|
status = FlushMemTablesToOutputFiles(bg_flush_args, made_progress,
|
||||||
job_context, log_buffer, thread_pri);
|
job_context, log_buffer, thread_pri);
|
||||||
TEST_SYNC_POINT("DBImpl::BackgroundFlush:BeforeFlush");
|
TEST_SYNC_POINT("DBImpl::BackgroundFlush:BeforeFlush");
|
||||||
// All the CFDs in the FlushReq must have the same flush reason, so just
|
// All the CFD/bg_flush_arg in the FlushReq must have the same flush reason, so
|
||||||
// grab the first one
|
// just grab the first one
|
||||||
*reason = bg_flush_args[0].cfd_->GetFlushReason();
|
#ifndef NDEBUG
|
||||||
|
for (const auto bg_flush_arg : bg_flush_args) {
|
||||||
|
assert(bg_flush_arg.flush_reason_ == bg_flush_args[0].flush_reason_);
|
||||||
|
}
|
||||||
|
#endif /* !NDEBUG */
|
||||||
|
*reason = bg_flush_args[0].flush_reason_;
|
||||||
for (auto& arg : bg_flush_args) {
|
for (auto& arg : bg_flush_args) {
|
||||||
ColumnFamilyData* cfd = arg.cfd_;
|
ColumnFamilyData* cfd = arg.cfd_;
|
||||||
if (cfd->UnrefAndTryDelete()) {
|
if (cfd->UnrefAndTryDelete()) {
|
||||||
|
|||||||
+34
-24
@@ -926,7 +926,8 @@ Status DBImpl::WriteImplWALOnly(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
InstrumentedMutexLock lock(&mutex_);
|
InstrumentedMutexLock lock(&mutex_);
|
||||||
Status status = DelayWrite(/*num_bytes=*/0ull, write_options);
|
Status status =
|
||||||
|
DelayWrite(/*num_bytes=*/0ull, *write_thread, write_options);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
WriteThread::WriteGroup write_group;
|
WriteThread::WriteGroup write_group;
|
||||||
write_thread->EnterAsBatchGroupLeader(&w, &write_group);
|
write_thread->EnterAsBatchGroupLeader(&w, &write_group);
|
||||||
@@ -1201,7 +1202,7 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
|
|||||||
// might happen for smaller writes but larger writes can go through.
|
// might happen for smaller writes but larger writes can go through.
|
||||||
// Can optimize it if it is an issue.
|
// Can optimize it if it is an issue.
|
||||||
InstrumentedMutexLock l(&mutex_);
|
InstrumentedMutexLock l(&mutex_);
|
||||||
status = DelayWrite(last_batch_group_size_, write_options);
|
status = DelayWrite(last_batch_group_size_, write_thread_, write_options);
|
||||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1653,14 +1654,14 @@ Status DBImpl::SwitchWAL(WriteContext* write_context) {
|
|||||||
cfd->imm()->FlushRequested();
|
cfd->imm()->FlushRequested();
|
||||||
if (!immutable_db_options_.atomic_flush) {
|
if (!immutable_db_options_.atomic_flush) {
|
||||||
FlushRequest flush_req;
|
FlushRequest flush_req;
|
||||||
GenerateFlushRequest({cfd}, &flush_req);
|
GenerateFlushRequest({cfd}, FlushReason::kWalFull, &flush_req);
|
||||||
SchedulePendingFlush(flush_req, FlushReason::kWalFull);
|
SchedulePendingFlush(flush_req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (immutable_db_options_.atomic_flush) {
|
if (immutable_db_options_.atomic_flush) {
|
||||||
FlushRequest flush_req;
|
FlushRequest flush_req;
|
||||||
GenerateFlushRequest(cfds, &flush_req);
|
GenerateFlushRequest(cfds, FlushReason::kWalFull, &flush_req);
|
||||||
SchedulePendingFlush(flush_req, FlushReason::kWalFull);
|
SchedulePendingFlush(flush_req);
|
||||||
}
|
}
|
||||||
MaybeScheduleFlushOrCompaction();
|
MaybeScheduleFlushOrCompaction();
|
||||||
}
|
}
|
||||||
@@ -1744,14 +1745,15 @@ Status DBImpl::HandleWriteBufferManagerFlush(WriteContext* write_context) {
|
|||||||
cfd->imm()->FlushRequested();
|
cfd->imm()->FlushRequested();
|
||||||
if (!immutable_db_options_.atomic_flush) {
|
if (!immutable_db_options_.atomic_flush) {
|
||||||
FlushRequest flush_req;
|
FlushRequest flush_req;
|
||||||
GenerateFlushRequest({cfd}, &flush_req);
|
GenerateFlushRequest({cfd}, FlushReason::kWriteBufferManager,
|
||||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferManager);
|
&flush_req);
|
||||||
|
SchedulePendingFlush(flush_req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (immutable_db_options_.atomic_flush) {
|
if (immutable_db_options_.atomic_flush) {
|
||||||
FlushRequest flush_req;
|
FlushRequest flush_req;
|
||||||
GenerateFlushRequest(cfds, &flush_req);
|
GenerateFlushRequest(cfds, FlushReason::kWriteBufferManager, &flush_req);
|
||||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferManager);
|
SchedulePendingFlush(flush_req);
|
||||||
}
|
}
|
||||||
MaybeScheduleFlushOrCompaction();
|
MaybeScheduleFlushOrCompaction();
|
||||||
}
|
}
|
||||||
@@ -1768,8 +1770,8 @@ uint64_t DBImpl::GetMaxTotalWalSize() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// REQUIRES: mutex_ is held
|
// REQUIRES: mutex_ is held
|
||||||
// REQUIRES: this thread is currently at the front of the writer queue
|
// REQUIRES: this thread is currently at the leader for write_thread
|
||||||
Status DBImpl::DelayWrite(uint64_t num_bytes,
|
Status DBImpl::DelayWrite(uint64_t num_bytes, WriteThread& write_thread,
|
||||||
const WriteOptions& write_options) {
|
const WriteOptions& write_options) {
|
||||||
mutex_.AssertHeld();
|
mutex_.AssertHeld();
|
||||||
uint64_t time_delayed = 0;
|
uint64_t time_delayed = 0;
|
||||||
@@ -1777,8 +1779,16 @@ Status DBImpl::DelayWrite(uint64_t num_bytes,
|
|||||||
{
|
{
|
||||||
StopWatch sw(immutable_db_options_.clock, stats_, WRITE_STALL,
|
StopWatch sw(immutable_db_options_.clock, stats_, WRITE_STALL,
|
||||||
&time_delayed);
|
&time_delayed);
|
||||||
uint64_t delay =
|
// To avoid parallel timed delays (bad throttling), only support them
|
||||||
write_controller_.GetDelay(immutable_db_options_.clock, num_bytes);
|
// on the primary write queue.
|
||||||
|
uint64_t delay;
|
||||||
|
if (&write_thread == &write_thread_) {
|
||||||
|
delay =
|
||||||
|
write_controller_.GetDelay(immutable_db_options_.clock, num_bytes);
|
||||||
|
} else {
|
||||||
|
assert(num_bytes == 0);
|
||||||
|
delay = 0;
|
||||||
|
}
|
||||||
TEST_SYNC_POINT("DBImpl::DelayWrite:Start");
|
TEST_SYNC_POINT("DBImpl::DelayWrite:Start");
|
||||||
if (delay > 0) {
|
if (delay > 0) {
|
||||||
if (write_options.no_slowdown) {
|
if (write_options.no_slowdown) {
|
||||||
@@ -1786,9 +1796,9 @@ Status DBImpl::DelayWrite(uint64_t num_bytes,
|
|||||||
}
|
}
|
||||||
TEST_SYNC_POINT("DBImpl::DelayWrite:Sleep");
|
TEST_SYNC_POINT("DBImpl::DelayWrite:Sleep");
|
||||||
|
|
||||||
// Notify write_thread_ about the stall so it can setup a barrier and
|
// Notify write_thread about the stall so it can setup a barrier and
|
||||||
// fail any pending writers with no_slowdown
|
// fail any pending writers with no_slowdown
|
||||||
write_thread_.BeginWriteStall();
|
write_thread.BeginWriteStall();
|
||||||
mutex_.Unlock();
|
mutex_.Unlock();
|
||||||
TEST_SYNC_POINT("DBImpl::DelayWrite:BeginWriteStallDone");
|
TEST_SYNC_POINT("DBImpl::DelayWrite:BeginWriteStallDone");
|
||||||
// We will delay the write until we have slept for `delay` microseconds
|
// We will delay the write until we have slept for `delay` microseconds
|
||||||
@@ -1808,7 +1818,7 @@ Status DBImpl::DelayWrite(uint64_t num_bytes,
|
|||||||
immutable_db_options_.clock->SleepForMicroseconds(kDelayInterval);
|
immutable_db_options_.clock->SleepForMicroseconds(kDelayInterval);
|
||||||
}
|
}
|
||||||
mutex_.Lock();
|
mutex_.Lock();
|
||||||
write_thread_.EndWriteStall();
|
write_thread.EndWriteStall();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't wait if there's a background error, even if its a soft error. We
|
// Don't wait if there's a background error, even if its a soft error. We
|
||||||
@@ -1822,12 +1832,12 @@ Status DBImpl::DelayWrite(uint64_t num_bytes,
|
|||||||
}
|
}
|
||||||
delayed = true;
|
delayed = true;
|
||||||
|
|
||||||
// Notify write_thread_ about the stall so it can setup a barrier and
|
// Notify write_thread about the stall so it can setup a barrier and
|
||||||
// fail any pending writers with no_slowdown
|
// fail any pending writers with no_slowdown
|
||||||
write_thread_.BeginWriteStall();
|
write_thread.BeginWriteStall();
|
||||||
TEST_SYNC_POINT("DBImpl::DelayWrite:Wait");
|
TEST_SYNC_POINT("DBImpl::DelayWrite:Wait");
|
||||||
bg_cv_.Wait();
|
bg_cv_.Wait();
|
||||||
write_thread_.EndWriteStall();
|
write_thread.EndWriteStall();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(!delayed || !write_options.no_slowdown);
|
assert(!delayed || !write_options.no_slowdown);
|
||||||
@@ -2008,13 +2018,13 @@ Status DBImpl::ScheduleFlushes(WriteContext* context) {
|
|||||||
if (immutable_db_options_.atomic_flush) {
|
if (immutable_db_options_.atomic_flush) {
|
||||||
AssignAtomicFlushSeq(cfds);
|
AssignAtomicFlushSeq(cfds);
|
||||||
FlushRequest flush_req;
|
FlushRequest flush_req;
|
||||||
GenerateFlushRequest(cfds, &flush_req);
|
GenerateFlushRequest(cfds, FlushReason::kWriteBufferFull, &flush_req);
|
||||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferFull);
|
SchedulePendingFlush(flush_req);
|
||||||
} else {
|
} else {
|
||||||
for (auto* cfd : cfds) {
|
for (auto* cfd : cfds) {
|
||||||
FlushRequest flush_req;
|
FlushRequest flush_req;
|
||||||
GenerateFlushRequest({cfd}, &flush_req);
|
GenerateFlushRequest({cfd}, FlushReason::kWriteBufferFull, &flush_req);
|
||||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferFull);
|
SchedulePendingFlush(flush_req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MaybeScheduleFlushOrCompaction();
|
MaybeScheduleFlushOrCompaction();
|
||||||
|
|||||||
@@ -439,6 +439,48 @@ TEST_F(DBMergeOperandTest, GetMergeOperandsLargeResultOptimization) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DBMergeOperandTest, GetMergeOperandsBaseDeletionInImmMem) {
|
||||||
|
// In this test, "k1" has a MERGE in a mutable memtable on top of a base
|
||||||
|
// DELETE in an immutable memtable.
|
||||||
|
Options opts = CurrentOptions();
|
||||||
|
opts.max_write_buffer_number = 10;
|
||||||
|
opts.min_write_buffer_number_to_merge = 10;
|
||||||
|
opts.merge_operator = MergeOperators::CreateDeprecatedPutOperator();
|
||||||
|
Reopen(opts);
|
||||||
|
|
||||||
|
ASSERT_OK(Put("k1", "val"));
|
||||||
|
ASSERT_OK(Flush());
|
||||||
|
|
||||||
|
ASSERT_OK(Put("k0", "val"));
|
||||||
|
ASSERT_OK(Delete("k1"));
|
||||||
|
ASSERT_OK(Put("k2", "val"));
|
||||||
|
ASSERT_OK(dbfull()->TEST_SwitchMemtable());
|
||||||
|
ASSERT_OK(Merge("k1", "val"));
|
||||||
|
|
||||||
|
{
|
||||||
|
std::vector<PinnableSlice> values(2);
|
||||||
|
|
||||||
|
GetMergeOperandsOptions merge_operands_info;
|
||||||
|
merge_operands_info.expected_max_number_of_operands =
|
||||||
|
static_cast<int>(values.size());
|
||||||
|
|
||||||
|
std::string key = "k1", from_db;
|
||||||
|
int number_of_operands = 0;
|
||||||
|
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||||
|
key, values.data(), &merge_operands_info,
|
||||||
|
&number_of_operands));
|
||||||
|
ASSERT_EQ(1, number_of_operands);
|
||||||
|
from_db = values[0].ToString();
|
||||||
|
ASSERT_EQ("val", from_db);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::string val;
|
||||||
|
ASSERT_OK(db_->Get(ReadOptions(), "k1", &val));
|
||||||
|
ASSERT_EQ("val", val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ROCKSDB_NAMESPACE
|
} // namespace ROCKSDB_NAMESPACE
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
|
|||||||
+6
-6
@@ -91,7 +91,7 @@ FlushJob::FlushJob(
|
|||||||
std::vector<SequenceNumber> existing_snapshots,
|
std::vector<SequenceNumber> existing_snapshots,
|
||||||
SequenceNumber earliest_write_conflict_snapshot,
|
SequenceNumber earliest_write_conflict_snapshot,
|
||||||
SnapshotChecker* snapshot_checker, JobContext* job_context,
|
SnapshotChecker* snapshot_checker, JobContext* job_context,
|
||||||
LogBuffer* log_buffer, FSDirectory* db_directory,
|
FlushReason flush_reason, LogBuffer* log_buffer, FSDirectory* db_directory,
|
||||||
FSDirectory* output_file_directory, CompressionType output_compression,
|
FSDirectory* output_file_directory, CompressionType output_compression,
|
||||||
Statistics* stats, EventLogger* event_logger, bool measure_io_stats,
|
Statistics* stats, EventLogger* event_logger, bool measure_io_stats,
|
||||||
const bool sync_output_directory, const bool write_manifest,
|
const bool sync_output_directory, const bool write_manifest,
|
||||||
@@ -114,6 +114,7 @@ FlushJob::FlushJob(
|
|||||||
earliest_write_conflict_snapshot_(earliest_write_conflict_snapshot),
|
earliest_write_conflict_snapshot_(earliest_write_conflict_snapshot),
|
||||||
snapshot_checker_(snapshot_checker),
|
snapshot_checker_(snapshot_checker),
|
||||||
job_context_(job_context),
|
job_context_(job_context),
|
||||||
|
flush_reason_(flush_reason),
|
||||||
log_buffer_(log_buffer),
|
log_buffer_(log_buffer),
|
||||||
db_directory_(db_directory),
|
db_directory_(db_directory),
|
||||||
output_file_directory_(output_file_directory),
|
output_file_directory_(output_file_directory),
|
||||||
@@ -245,9 +246,8 @@ Status FlushJob::Run(LogsWithPrepTracker* prep_tracker, FileMetaData* file_meta,
|
|||||||
}
|
}
|
||||||
Status mempurge_s = Status::NotFound("No MemPurge.");
|
Status mempurge_s = Status::NotFound("No MemPurge.");
|
||||||
if ((mempurge_threshold > 0.0) &&
|
if ((mempurge_threshold > 0.0) &&
|
||||||
(cfd_->GetFlushReason() == FlushReason::kWriteBufferFull) &&
|
(flush_reason_ == FlushReason::kWriteBufferFull) && (!mems_.empty()) &&
|
||||||
(!mems_.empty()) && MemPurgeDecider(mempurge_threshold) &&
|
MemPurgeDecider(mempurge_threshold) && !(db_options_.atomic_flush)) {
|
||||||
!(db_options_.atomic_flush)) {
|
|
||||||
cfd_->SetMempurgeUsed();
|
cfd_->SetMempurgeUsed();
|
||||||
mempurge_s = MemPurge();
|
mempurge_s = MemPurge();
|
||||||
if (!mempurge_s.ok()) {
|
if (!mempurge_s.ok()) {
|
||||||
@@ -878,7 +878,7 @@ Status FlushJob::WriteLevel0Table() {
|
|||||||
<< total_num_deletes << "total_data_size"
|
<< total_num_deletes << "total_data_size"
|
||||||
<< total_data_size << "memory_usage"
|
<< total_data_size << "memory_usage"
|
||||||
<< total_memory_usage << "flush_reason"
|
<< total_memory_usage << "flush_reason"
|
||||||
<< GetFlushReasonString(cfd_->GetFlushReason());
|
<< GetFlushReasonString(flush_reason_);
|
||||||
|
|
||||||
{
|
{
|
||||||
ScopedArenaIterator iter(
|
ScopedArenaIterator iter(
|
||||||
@@ -1076,7 +1076,7 @@ std::unique_ptr<FlushJobInfo> FlushJob::GetFlushJobInfo() const {
|
|||||||
info->smallest_seqno = meta_.fd.smallest_seqno;
|
info->smallest_seqno = meta_.fd.smallest_seqno;
|
||||||
info->largest_seqno = meta_.fd.largest_seqno;
|
info->largest_seqno = meta_.fd.largest_seqno;
|
||||||
info->table_properties = table_properties_;
|
info->table_properties = table_properties_;
|
||||||
info->flush_reason = cfd_->GetFlushReason();
|
info->flush_reason = flush_reason_;
|
||||||
info->blob_compression_type = mutable_cf_options_.blob_compression_type;
|
info->blob_compression_type = mutable_cf_options_.blob_compression_type;
|
||||||
|
|
||||||
// Update BlobFilesInfo.
|
// Update BlobFilesInfo.
|
||||||
|
|||||||
+3
-2
@@ -67,8 +67,8 @@ class FlushJob {
|
|||||||
std::vector<SequenceNumber> existing_snapshots,
|
std::vector<SequenceNumber> existing_snapshots,
|
||||||
SequenceNumber earliest_write_conflict_snapshot,
|
SequenceNumber earliest_write_conflict_snapshot,
|
||||||
SnapshotChecker* snapshot_checker, JobContext* job_context,
|
SnapshotChecker* snapshot_checker, JobContext* job_context,
|
||||||
LogBuffer* log_buffer, FSDirectory* db_directory,
|
FlushReason flush_reason, LogBuffer* log_buffer,
|
||||||
FSDirectory* output_file_directory,
|
FSDirectory* db_directory, FSDirectory* output_file_directory,
|
||||||
CompressionType output_compression, Statistics* stats,
|
CompressionType output_compression, Statistics* stats,
|
||||||
EventLogger* event_logger, bool measure_io_stats,
|
EventLogger* event_logger, bool measure_io_stats,
|
||||||
const bool sync_output_directory, const bool write_manifest,
|
const bool sync_output_directory, const bool write_manifest,
|
||||||
@@ -150,6 +150,7 @@ class FlushJob {
|
|||||||
SequenceNumber earliest_write_conflict_snapshot_;
|
SequenceNumber earliest_write_conflict_snapshot_;
|
||||||
SnapshotChecker* snapshot_checker_;
|
SnapshotChecker* snapshot_checker_;
|
||||||
JobContext* job_context_;
|
JobContext* job_context_;
|
||||||
|
FlushReason flush_reason_;
|
||||||
LogBuffer* log_buffer_;
|
LogBuffer* log_buffer_;
|
||||||
FSDirectory* db_directory_;
|
FSDirectory* db_directory_;
|
||||||
FSDirectory* output_file_directory_;
|
FSDirectory* output_file_directory_;
|
||||||
|
|||||||
+29
-29
@@ -164,15 +164,15 @@ TEST_F(FlushJobTest, Empty) {
|
|||||||
auto cfd = versions_->GetColumnFamilySet()->GetDefault();
|
auto cfd = versions_->GetColumnFamilySet()->GetDefault();
|
||||||
EventLogger event_logger(db_options_.info_log.get());
|
EventLogger event_logger(db_options_.info_log.get());
|
||||||
SnapshotChecker* snapshot_checker = nullptr; // not relavant
|
SnapshotChecker* snapshot_checker = nullptr; // not relavant
|
||||||
FlushJob flush_job(dbname_, versions_->GetColumnFamilySet()->GetDefault(),
|
FlushJob flush_job(
|
||||||
db_options_, *cfd->GetLatestMutableCFOptions(),
|
dbname_, versions_->GetColumnFamilySet()->GetDefault(), db_options_,
|
||||||
std::numeric_limits<uint64_t>::max() /* memtable_id */,
|
*cfd->GetLatestMutableCFOptions(),
|
||||||
env_options_, versions_.get(), &mutex_, &shutting_down_,
|
std::numeric_limits<uint64_t>::max() /* memtable_id */, env_options_,
|
||||||
{}, kMaxSequenceNumber, snapshot_checker, &job_context,
|
versions_.get(), &mutex_, &shutting_down_, {}, kMaxSequenceNumber,
|
||||||
nullptr, nullptr, nullptr, kNoCompression, nullptr,
|
snapshot_checker, &job_context, FlushReason::kTest, nullptr, nullptr,
|
||||||
&event_logger, false, true /* sync_output_directory */,
|
nullptr, kNoCompression, nullptr, &event_logger, false,
|
||||||
true /* write_manifest */, Env::Priority::USER,
|
true /* sync_output_directory */, true /* write_manifest */,
|
||||||
nullptr /*IOTracer*/, empty_seqno_to_time_mapping_);
|
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_);
|
||||||
{
|
{
|
||||||
InstrumentedMutexLock l(&mutex_);
|
InstrumentedMutexLock l(&mutex_);
|
||||||
flush_job.PickMemTable();
|
flush_job.PickMemTable();
|
||||||
@@ -255,9 +255,9 @@ TEST_F(FlushJobTest, NonEmpty) {
|
|||||||
*cfd->GetLatestMutableCFOptions(),
|
*cfd->GetLatestMutableCFOptions(),
|
||||||
std::numeric_limits<uint64_t>::max() /* memtable_id */, env_options_,
|
std::numeric_limits<uint64_t>::max() /* memtable_id */, env_options_,
|
||||||
versions_.get(), &mutex_, &shutting_down_, {}, kMaxSequenceNumber,
|
versions_.get(), &mutex_, &shutting_down_, {}, kMaxSequenceNumber,
|
||||||
snapshot_checker, &job_context, nullptr, nullptr, nullptr, kNoCompression,
|
snapshot_checker, &job_context, FlushReason::kTest, nullptr, nullptr,
|
||||||
db_options_.statistics.get(), &event_logger, true,
|
nullptr, kNoCompression, db_options_.statistics.get(), &event_logger,
|
||||||
true /* sync_output_directory */, true /* write_manifest */,
|
true, true /* sync_output_directory */, true /* write_manifest */,
|
||||||
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_);
|
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_);
|
||||||
|
|
||||||
HistogramData hist;
|
HistogramData hist;
|
||||||
@@ -318,9 +318,9 @@ TEST_F(FlushJobTest, FlushMemTablesSingleColumnFamily) {
|
|||||||
dbname_, versions_->GetColumnFamilySet()->GetDefault(), db_options_,
|
dbname_, versions_->GetColumnFamilySet()->GetDefault(), db_options_,
|
||||||
*cfd->GetLatestMutableCFOptions(), flush_memtable_id, env_options_,
|
*cfd->GetLatestMutableCFOptions(), flush_memtable_id, env_options_,
|
||||||
versions_.get(), &mutex_, &shutting_down_, {}, kMaxSequenceNumber,
|
versions_.get(), &mutex_, &shutting_down_, {}, kMaxSequenceNumber,
|
||||||
snapshot_checker, &job_context, nullptr, nullptr, nullptr, kNoCompression,
|
snapshot_checker, &job_context, FlushReason::kTest, nullptr, nullptr,
|
||||||
db_options_.statistics.get(), &event_logger, true,
|
nullptr, kNoCompression, db_options_.statistics.get(), &event_logger,
|
||||||
true /* sync_output_directory */, true /* write_manifest */,
|
true, true /* sync_output_directory */, true /* write_manifest */,
|
||||||
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_);
|
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_);
|
||||||
HistogramData hist;
|
HistogramData hist;
|
||||||
FileMetaData file_meta;
|
FileMetaData file_meta;
|
||||||
@@ -391,8 +391,8 @@ TEST_F(FlushJobTest, FlushMemtablesMultipleColumnFamilies) {
|
|||||||
dbname_, cfd, db_options_, *cfd->GetLatestMutableCFOptions(),
|
dbname_, cfd, db_options_, *cfd->GetLatestMutableCFOptions(),
|
||||||
memtable_ids[k], env_options_, versions_.get(), &mutex_,
|
memtable_ids[k], env_options_, versions_.get(), &mutex_,
|
||||||
&shutting_down_, snapshot_seqs, kMaxSequenceNumber, snapshot_checker,
|
&shutting_down_, snapshot_seqs, kMaxSequenceNumber, snapshot_checker,
|
||||||
&job_context, nullptr, nullptr, nullptr, kNoCompression,
|
&job_context, FlushReason::kTest, nullptr, nullptr, nullptr,
|
||||||
db_options_.statistics.get(), &event_logger, true,
|
kNoCompression, db_options_.statistics.get(), &event_logger, true,
|
||||||
false /* sync_output_directory */, false /* write_manifest */,
|
false /* sync_output_directory */, false /* write_manifest */,
|
||||||
Env::Priority::USER, nullptr /*IOTracer*/,
|
Env::Priority::USER, nullptr /*IOTracer*/,
|
||||||
empty_seqno_to_time_mapping_));
|
empty_seqno_to_time_mapping_));
|
||||||
@@ -520,9 +520,9 @@ TEST_F(FlushJobTest, Snapshots) {
|
|||||||
*cfd->GetLatestMutableCFOptions(),
|
*cfd->GetLatestMutableCFOptions(),
|
||||||
std::numeric_limits<uint64_t>::max() /* memtable_id */, env_options_,
|
std::numeric_limits<uint64_t>::max() /* memtable_id */, env_options_,
|
||||||
versions_.get(), &mutex_, &shutting_down_, snapshots, kMaxSequenceNumber,
|
versions_.get(), &mutex_, &shutting_down_, snapshots, kMaxSequenceNumber,
|
||||||
snapshot_checker, &job_context, nullptr, nullptr, nullptr, kNoCompression,
|
snapshot_checker, &job_context, FlushReason::kTest, nullptr, nullptr,
|
||||||
db_options_.statistics.get(), &event_logger, true,
|
nullptr, kNoCompression, db_options_.statistics.get(), &event_logger,
|
||||||
true /* sync_output_directory */, true /* write_manifest */,
|
true, true /* sync_output_directory */, true /* write_manifest */,
|
||||||
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_);
|
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_);
|
||||||
mutex_.Lock();
|
mutex_.Lock();
|
||||||
flush_job.PickMemTable();
|
flush_job.PickMemTable();
|
||||||
@@ -576,9 +576,9 @@ TEST_F(FlushJobTest, GetRateLimiterPriorityForWrite) {
|
|||||||
dbname_, versions_->GetColumnFamilySet()->GetDefault(), db_options_,
|
dbname_, versions_->GetColumnFamilySet()->GetDefault(), db_options_,
|
||||||
*cfd->GetLatestMutableCFOptions(), flush_memtable_id, env_options_,
|
*cfd->GetLatestMutableCFOptions(), flush_memtable_id, env_options_,
|
||||||
versions_.get(), &mutex_, &shutting_down_, {}, kMaxSequenceNumber,
|
versions_.get(), &mutex_, &shutting_down_, {}, kMaxSequenceNumber,
|
||||||
snapshot_checker, &job_context, nullptr, nullptr, nullptr, kNoCompression,
|
snapshot_checker, &job_context, FlushReason::kTest, nullptr, nullptr,
|
||||||
db_options_.statistics.get(), &event_logger, true,
|
nullptr, kNoCompression, db_options_.statistics.get(), &event_logger,
|
||||||
true /* sync_output_directory */, true /* write_manifest */,
|
true, true /* sync_output_directory */, true /* write_manifest */,
|
||||||
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_);
|
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_);
|
||||||
|
|
||||||
// When the state from WriteController is normal.
|
// When the state from WriteController is normal.
|
||||||
@@ -656,9 +656,9 @@ TEST_F(FlushJobTimestampTest, AllKeysExpired) {
|
|||||||
dbname_, cfd, db_options_, *cfd->GetLatestMutableCFOptions(),
|
dbname_, cfd, db_options_, *cfd->GetLatestMutableCFOptions(),
|
||||||
std::numeric_limits<uint64_t>::max() /* memtable_id */, env_options_,
|
std::numeric_limits<uint64_t>::max() /* memtable_id */, env_options_,
|
||||||
versions_.get(), &mutex_, &shutting_down_, snapshots, kMaxSequenceNumber,
|
versions_.get(), &mutex_, &shutting_down_, snapshots, kMaxSequenceNumber,
|
||||||
snapshot_checker, &job_context, nullptr, nullptr, nullptr, kNoCompression,
|
snapshot_checker, &job_context, FlushReason::kTest, nullptr, nullptr,
|
||||||
db_options_.statistics.get(), &event_logger, true,
|
nullptr, kNoCompression, db_options_.statistics.get(), &event_logger,
|
||||||
true /* sync_output_directory */, true /* write_manifest */,
|
true, true /* sync_output_directory */, true /* write_manifest */,
|
||||||
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_,
|
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_,
|
||||||
/*db_id=*/"",
|
/*db_id=*/"",
|
||||||
/*db_session_id=*/"", full_history_ts_low);
|
/*db_session_id=*/"", full_history_ts_low);
|
||||||
@@ -709,9 +709,9 @@ TEST_F(FlushJobTimestampTest, NoKeyExpired) {
|
|||||||
dbname_, cfd, db_options_, *cfd->GetLatestMutableCFOptions(),
|
dbname_, cfd, db_options_, *cfd->GetLatestMutableCFOptions(),
|
||||||
std::numeric_limits<uint64_t>::max() /* memtable_id */, env_options_,
|
std::numeric_limits<uint64_t>::max() /* memtable_id */, env_options_,
|
||||||
versions_.get(), &mutex_, &shutting_down_, snapshots, kMaxSequenceNumber,
|
versions_.get(), &mutex_, &shutting_down_, snapshots, kMaxSequenceNumber,
|
||||||
snapshot_checker, &job_context, nullptr, nullptr, nullptr, kNoCompression,
|
snapshot_checker, &job_context, FlushReason::kTest, nullptr, nullptr,
|
||||||
db_options_.statistics.get(), &event_logger, true,
|
nullptr, kNoCompression, db_options_.statistics.get(), &event_logger,
|
||||||
true /* sync_output_directory */, true /* write_manifest */,
|
true, true /* sync_output_directory */, true /* write_manifest */,
|
||||||
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_,
|
Env::Priority::USER, nullptr /*IOTracer*/, empty_seqno_to_time_mapping_,
|
||||||
/*db_id=*/"",
|
/*db_id=*/"",
|
||||||
/*db_session_id=*/"", full_history_ts_low);
|
/*db_session_id=*/"", full_history_ts_low);
|
||||||
|
|||||||
+8
-6
@@ -515,10 +515,11 @@ unsigned int Reader::ReadPhysicalRecord(Slice* result, size_t* drop_size,
|
|||||||
|
|
||||||
size_t uncompressed_size = 0;
|
size_t uncompressed_size = 0;
|
||||||
int remaining = 0;
|
int remaining = 0;
|
||||||
|
const char* input = header + header_size;
|
||||||
do {
|
do {
|
||||||
remaining = uncompress_->Uncompress(header + header_size, length,
|
remaining = uncompress_->Uncompress(
|
||||||
uncompressed_buffer_.get(),
|
input, length, uncompressed_buffer_.get(), &uncompressed_size);
|
||||||
&uncompressed_size);
|
input = nullptr;
|
||||||
if (remaining < 0) {
|
if (remaining < 0) {
|
||||||
buffer_.clear();
|
buffer_.clear();
|
||||||
return kBadRecord;
|
return kBadRecord;
|
||||||
@@ -830,10 +831,11 @@ bool FragmentBufferedReader::TryReadFragment(
|
|||||||
uncompressed_record_.clear();
|
uncompressed_record_.clear();
|
||||||
size_t uncompressed_size = 0;
|
size_t uncompressed_size = 0;
|
||||||
int remaining = 0;
|
int remaining = 0;
|
||||||
|
const char* input = header + header_size;
|
||||||
do {
|
do {
|
||||||
remaining = uncompress_->Uncompress(header + header_size, length,
|
remaining = uncompress_->Uncompress(
|
||||||
uncompressed_buffer_.get(),
|
input, length, uncompressed_buffer_.get(), &uncompressed_size);
|
||||||
&uncompressed_size);
|
input = nullptr;
|
||||||
if (remaining < 0) {
|
if (remaining < 0) {
|
||||||
buffer_.clear();
|
buffer_.clear();
|
||||||
*fragment_type_or_err = kBadRecord;
|
*fragment_type_or_err = kBadRecord;
|
||||||
|
|||||||
+35
-2
@@ -979,6 +979,38 @@ TEST_P(CompressionLogTest, Fragmentation) {
|
|||||||
ASSERT_EQ("EOF", Read());
|
ASSERT_EQ("EOF", Read());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_P(CompressionLogTest, AlignedFragmentation) {
|
||||||
|
CompressionType compression_type = std::get<2>(GetParam());
|
||||||
|
if (!StreamingCompressionTypeSupported(compression_type)) {
|
||||||
|
ROCKSDB_GTEST_SKIP("Test requires support for compression type");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ASSERT_OK(SetupTestEnv());
|
||||||
|
Random rnd(301);
|
||||||
|
int num_filler_records = 0;
|
||||||
|
// Keep writing small records until the next record will be aligned at the
|
||||||
|
// beginning of the block.
|
||||||
|
while ((WrittenBytes() & (kBlockSize - 1)) >= kHeaderSize) {
|
||||||
|
char entry = 'a';
|
||||||
|
ASSERT_OK(writer_->AddRecord(Slice(&entry, 1)));
|
||||||
|
num_filler_records++;
|
||||||
|
}
|
||||||
|
const std::vector<std::string> wal_entries = {
|
||||||
|
rnd.RandomBinaryString(3 * kBlockSize),
|
||||||
|
};
|
||||||
|
for (const std::string& wal_entry : wal_entries) {
|
||||||
|
Write(wal_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < num_filler_records; ++i) {
|
||||||
|
ASSERT_EQ("a", Read());
|
||||||
|
}
|
||||||
|
for (const std::string& wal_entry : wal_entries) {
|
||||||
|
ASSERT_EQ(wal_entry, Read());
|
||||||
|
}
|
||||||
|
ASSERT_EQ("EOF", Read());
|
||||||
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(
|
INSTANTIATE_TEST_CASE_P(
|
||||||
Compression, CompressionLogTest,
|
Compression, CompressionLogTest,
|
||||||
::testing::Combine(::testing::Values(0, 1), ::testing::Bool(),
|
::testing::Combine(::testing::Values(0, 1), ::testing::Bool(),
|
||||||
@@ -1026,10 +1058,11 @@ TEST_P(StreamingCompressionTest, Basic) {
|
|||||||
for (int i = 0; i < (int)compressed_buffers.size(); i++) {
|
for (int i = 0; i < (int)compressed_buffers.size(); i++) {
|
||||||
// Call uncompress till either the entire input is consumed or the output
|
// Call uncompress till either the entire input is consumed or the output
|
||||||
// buffer size is equal to the allocated output buffer size.
|
// buffer size is equal to the allocated output buffer size.
|
||||||
|
const char* input = compressed_buffers[i].c_str();
|
||||||
do {
|
do {
|
||||||
ret_val = uncompress->Uncompress(compressed_buffers[i].c_str(),
|
ret_val = uncompress->Uncompress(input, compressed_buffers[i].size(),
|
||||||
compressed_buffers[i].size(),
|
|
||||||
uncompressed_output_buffer, &output_pos);
|
uncompressed_output_buffer, &output_pos);
|
||||||
|
input = nullptr;
|
||||||
if (output_pos > 0) {
|
if (output_pos > 0) {
|
||||||
std::string uncompressed_fragment;
|
std::string uncompressed_fragment;
|
||||||
uncompressed_fragment.assign(uncompressed_output_buffer, output_pos);
|
uncompressed_fragment.assign(uncompressed_output_buffer, output_pos);
|
||||||
|
|||||||
@@ -1207,6 +1207,11 @@ static bool SaveValue(void* arg, const char* entry) {
|
|||||||
s->columns->SetPlainValue(result);
|
s->columns->SetPlainValue(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// We have found a final value (a base deletion) and have newer
|
||||||
|
// merge operands that we do not intend to merge. Nothing remains
|
||||||
|
// to be done so assign status to OK.
|
||||||
|
*(s->status) = Status::OK();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
*(s->status) = Status::NotFound();
|
*(s->status) = Status::NotFound();
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
// minor or major version number planned for release.
|
// minor or major version number planned for release.
|
||||||
#define ROCKSDB_MAJOR 7
|
#define ROCKSDB_MAJOR 7
|
||||||
#define ROCKSDB_MINOR 10
|
#define ROCKSDB_MINOR 10
|
||||||
#define ROCKSDB_PATCH 0
|
#define ROCKSDB_PATCH 2
|
||||||
|
|
||||||
// Do not use these. We made the mistake of declaring macros starting with
|
// Do not use these. We made the mistake of declaring macros starting with
|
||||||
// double underscore. Now we have to live with our choice. We'll deprecate these
|
// double underscore. Now we have to live with our choice. We'll deprecate these
|
||||||
|
|||||||
+20
-12
@@ -20,17 +20,20 @@ bool AsyncFileReader::MultiReadAsyncImpl(ReadAwaiter* awaiter) {
|
|||||||
awaiter->io_handle_.resize(awaiter->num_reqs_);
|
awaiter->io_handle_.resize(awaiter->num_reqs_);
|
||||||
awaiter->del_fn_.resize(awaiter->num_reqs_);
|
awaiter->del_fn_.resize(awaiter->num_reqs_);
|
||||||
for (size_t i = 0; i < awaiter->num_reqs_; ++i) {
|
for (size_t i = 0; i < awaiter->num_reqs_; ++i) {
|
||||||
awaiter->file_
|
IOStatus s = awaiter->file_->ReadAsync(
|
||||||
->ReadAsync(
|
awaiter->read_reqs_[i], awaiter->opts_,
|
||||||
awaiter->read_reqs_[i], awaiter->opts_,
|
[](const FSReadRequest& req, void* cb_arg) {
|
||||||
[](const FSReadRequest& req, void* cb_arg) {
|
FSReadRequest* read_req = static_cast<FSReadRequest*>(cb_arg);
|
||||||
FSReadRequest* read_req = static_cast<FSReadRequest*>(cb_arg);
|
read_req->status = req.status;
|
||||||
read_req->status = req.status;
|
read_req->result = req.result;
|
||||||
read_req->result = req.result;
|
},
|
||||||
},
|
&awaiter->read_reqs_[i], &awaiter->io_handle_[i], &awaiter->del_fn_[i],
|
||||||
&awaiter->read_reqs_[i], &awaiter->io_handle_[i],
|
/*aligned_buf=*/nullptr);
|
||||||
&awaiter->del_fn_[i], /*aligned_buf=*/nullptr)
|
if (!s.ok()) {
|
||||||
.PermitUncheckedError();
|
// For any non-ok status, the FileSystem will not call the callback
|
||||||
|
// So let's update the status ourselves
|
||||||
|
awaiter->read_reqs_[i].status = s;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -41,6 +44,7 @@ void AsyncFileReader::Wait() {
|
|||||||
}
|
}
|
||||||
ReadAwaiter* waiter;
|
ReadAwaiter* waiter;
|
||||||
std::vector<void*> io_handles;
|
std::vector<void*> io_handles;
|
||||||
|
IOStatus s;
|
||||||
io_handles.reserve(num_reqs_);
|
io_handles.reserve(num_reqs_);
|
||||||
waiter = head_;
|
waiter = head_;
|
||||||
do {
|
do {
|
||||||
@@ -52,7 +56,7 @@ void AsyncFileReader::Wait() {
|
|||||||
} while (waiter != tail_ && (waiter = waiter->next_));
|
} while (waiter != tail_ && (waiter = waiter->next_));
|
||||||
if (io_handles.size() > 0) {
|
if (io_handles.size() > 0) {
|
||||||
StopWatch sw(SystemClock::Default().get(), stats_, POLL_WAIT_MICROS);
|
StopWatch sw(SystemClock::Default().get(), stats_, POLL_WAIT_MICROS);
|
||||||
fs_->Poll(io_handles, io_handles.size()).PermitUncheckedError();
|
s = fs_->Poll(io_handles, io_handles.size());
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
waiter = head_;
|
waiter = head_;
|
||||||
@@ -62,6 +66,10 @@ void AsyncFileReader::Wait() {
|
|||||||
if (waiter->io_handle_[i] && waiter->del_fn_[i]) {
|
if (waiter->io_handle_[i] && waiter->del_fn_[i]) {
|
||||||
waiter->del_fn_[i](waiter->io_handle_[i]);
|
waiter->del_fn_[i](waiter->io_handle_[i]);
|
||||||
}
|
}
|
||||||
|
if (waiter->read_reqs_[i].status.ok() && !s.ok()) {
|
||||||
|
// Override the request status with the Poll error
|
||||||
|
waiter->read_reqs_[i].status = s;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
waiter->awaiting_coro_.resume();
|
waiter->awaiting_coro_.resume();
|
||||||
} while (waiter != tail_);
|
} while (waiter != tail_);
|
||||||
|
|||||||
+2
-2
@@ -85,14 +85,14 @@ void ZSTDStreamingCompress::Reset() {
|
|||||||
|
|
||||||
int ZSTDStreamingUncompress::Uncompress(const char* input, size_t input_size,
|
int ZSTDStreamingUncompress::Uncompress(const char* input, size_t input_size,
|
||||||
char* output, size_t* output_pos) {
|
char* output, size_t* output_pos) {
|
||||||
assert(input != nullptr && output != nullptr && output_pos != nullptr);
|
assert(output != nullptr && output_pos != nullptr);
|
||||||
*output_pos = 0;
|
*output_pos = 0;
|
||||||
// Don't need to uncompress an empty input
|
// Don't need to uncompress an empty input
|
||||||
if (input_size == 0) {
|
if (input_size == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#ifdef ZSTD_STREAMING
|
#ifdef ZSTD_STREAMING
|
||||||
if (input_buffer_.src != input) {
|
if (input) {
|
||||||
// New input
|
// New input
|
||||||
input_buffer_ = {input, input_size, /*pos=*/0};
|
input_buffer_ = {input, input_size, /*pos=*/0};
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -1711,8 +1711,11 @@ class StreamingUncompress {
|
|||||||
compress_format_version_(compress_format_version),
|
compress_format_version_(compress_format_version),
|
||||||
max_output_len_(max_output_len) {}
|
max_output_len_(max_output_len) {}
|
||||||
virtual ~StreamingUncompress() = default;
|
virtual ~StreamingUncompress() = default;
|
||||||
// uncompress should be called again with the same input if output_size is
|
// Uncompress can be called repeatedly to progressively process the same
|
||||||
// equal to max_output_len or with the next input fragment.
|
// input buffer, or can be called with a new input buffer. When the input
|
||||||
|
// buffer is not fully consumed, the return value is > 0 or output_size
|
||||||
|
// == max_output_len. When calling uncompress to continue processing the
|
||||||
|
// same input buffer, the input argument should be nullptr.
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// input - buffer to uncompress
|
// input - buffer to uncompress
|
||||||
// input_size - size of input buffer
|
// input_size - size of input buffer
|
||||||
|
|||||||
@@ -6589,6 +6589,58 @@ TEST_P(TransactionTest, LockWal) {
|
|||||||
SyncPoint::GetInstance()->DisableProcessing();
|
SyncPoint::GetInstance()->DisableProcessing();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_P(TransactionTest, StallTwoWriteQueues) {
|
||||||
|
// There was a two_write_queues bug in which both write thread leaders (for
|
||||||
|
// each queue) would attempt to own the stopping of writes in the primary
|
||||||
|
// write queue. This nearly worked but could lead to some broken assertions
|
||||||
|
// and a kind of deadlock in the test below. (Would resume if someone
|
||||||
|
// eventually signalled bg_cv_ again.)
|
||||||
|
if (!options.two_write_queues) {
|
||||||
|
ROCKSDB_GTEST_BYPASS("Test only needed with two_write_queues");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop writes
|
||||||
|
ASSERT_OK(db->LockWAL());
|
||||||
|
|
||||||
|
WriteOptions wopts;
|
||||||
|
wopts.sync = true;
|
||||||
|
wopts.disableWAL = false;
|
||||||
|
|
||||||
|
// Create one write thread that blocks in the primary write queue and one
|
||||||
|
// that blocks in the nonmem queue.
|
||||||
|
bool t1_completed = false;
|
||||||
|
bool t2_completed = false;
|
||||||
|
port::Thread t1{[&]() {
|
||||||
|
ASSERT_OK(db->Put(wopts, "x", "y"));
|
||||||
|
t1_completed = true;
|
||||||
|
}};
|
||||||
|
port::Thread t2{[&]() {
|
||||||
|
std::unique_ptr<Transaction> txn0{db->BeginTransaction(wopts, {})};
|
||||||
|
ASSERT_OK(txn0->SetName("xid"));
|
||||||
|
ASSERT_OK(txn0->Prepare()); // nonmem
|
||||||
|
ASSERT_OK(txn0->Commit());
|
||||||
|
t2_completed = true;
|
||||||
|
}};
|
||||||
|
|
||||||
|
// Sleep long enough to that above threads can usually reach a waiting point,
|
||||||
|
// to usually reveal deadlock if the bug is present.
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
// Ensure proper test setup
|
||||||
|
ASSERT_FALSE(t1_completed);
|
||||||
|
ASSERT_FALSE(t2_completed);
|
||||||
|
|
||||||
|
// Resume writes
|
||||||
|
ASSERT_OK(db->UnlockWAL());
|
||||||
|
|
||||||
|
// Wait for writes to finish
|
||||||
|
t1.join();
|
||||||
|
t2.join();
|
||||||
|
// Ensure proper test setup
|
||||||
|
ASSERT_TRUE(t1_completed);
|
||||||
|
ASSERT_TRUE(t2_completed);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ROCKSDB_NAMESPACE
|
} // namespace ROCKSDB_NAMESPACE
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
|
|||||||
Reference in New Issue
Block a user