diff --git a/db_stress_tool/db_stress_common.h b/db_stress_tool/db_stress_common.h index 12be9fbc5fd..d14464b829b 100644 --- a/db_stress_tool/db_stress_common.h +++ b/db_stress_tool/db_stress_common.h @@ -209,8 +209,11 @@ DECLARE_int32(ingest_external_file_one_in); DECLARE_int32(ingest_external_file_width); DECLARE_int32(compact_files_one_in); DECLARE_int32(compact_range_one_in); +DECLARE_int32(promote_l0_one_in); DECLARE_int32(mark_for_compaction_one_file_in); DECLARE_int32(flush_one_in); +DECLARE_int32(key_may_exist_one_in); +DECLARE_int32(reset_stats_one_in); DECLARE_int32(pause_background_one_in); DECLARE_int32(disable_file_deletions_one_in); DECLARE_int32(disable_manual_compaction_one_in); diff --git a/db_stress_tool/db_stress_gflags.cc b/db_stress_tool/db_stress_gflags.cc index b037b8d97ae..245bb0938f4 100644 --- a/db_stress_tool/db_stress_gflags.cc +++ b/db_stress_tool/db_stress_gflags.cc @@ -756,6 +756,10 @@ DEFINE_int32(compact_range_one_in, 0, "If non-zero, then CompactRange() will be called once for every N " "operations on average. 0 indicates CompactRange() is disabled."); +DEFINE_int32(promote_l0_one_in, 0, + "If non-zero, then PromoteL0() will be called once for every N " + "operations on average. 0 indicates PromoteL0() is disabled."); + DEFINE_int32(mark_for_compaction_one_file_in, 0, "A `TablePropertiesCollectorFactory` will be registered, which " "creates a `TablePropertiesCollector` with `NeedCompact()` " @@ -766,6 +770,14 @@ DEFINE_int32(flush_one_in, 0, "If non-zero, then Flush() will be called once for every N ops " "on average. 0 indicates calls to Flush() are disabled."); +DEFINE_int32(key_may_exist_one_in, 0, + "If non-zero, then KeyMayExist() will be called " + "once for every N ops on average. 0 disables."); + +DEFINE_int32(reset_stats_one_in, 0, + "If non-zero, then ResetStats() will be called " + "once for every N ops on average. 0 disables."); + DEFINE_int32(pause_background_one_in, 0, "If non-zero, then PauseBackgroundWork()+Continue will be called " "once for every N ops on average. 0 disables."); diff --git a/db_stress_tool/db_stress_test_base.cc b/db_stress_tool/db_stress_test_base.cc index 8e8d37ca772..0621033cd97 100644 --- a/db_stress_tool/db_stress_test_base.cc +++ b/db_stress_tool/db_stress_test_base.cc @@ -1001,6 +1001,10 @@ void StressTest::OperateDb(ThreadState* thread) { } } + if (thread->rand.OneInOpt(FLAGS_promote_l0_one_in)) { + TestPromoteL0(thread, column_family); + } + std::vector rand_column_families = GenerateColumnFamilies(FLAGS_column_families, rand_column_family); @@ -1041,6 +1045,11 @@ void StressTest::OperateDb(ThreadState* thread) { ProcessStatus(shared, "VerifyGetCurrentWalFile", status); } + if (thread->rand.OneInOpt(FLAGS_reset_stats_one_in)) { + Status status = TestResetStats(); + ProcessStatus(shared, "ResetStats", status); + } + if (thread->rand.OneInOpt(FLAGS_pause_background_one_in)) { Status status = TestPauseBackground(thread); ProcessStatus(shared, "Pause/ContinueBackgroundWork", status); @@ -1135,6 +1144,10 @@ void StressTest::OperateDb(ThreadState* thread) { read_opts.timestamp = &read_ts; } + if (thread->rand.OneInOpt(FLAGS_key_may_exist_one_in)) { + TestKeyMayExist(thread, read_opts, rand_column_families, rand_keys); + } + int prob_op = thread->rand.Uniform(100); // Reset this in case we pick something other than a read op. We don't // want to use a stale value when deciding at the beginning of the loop @@ -2428,6 +2441,29 @@ void StressTest::TestCompactFiles(ThreadState* thread, } } +void StressTest::TestPromoteL0(ThreadState* thread, + ColumnFamilyHandle* column_family) { + int target_level = thread->rand.Next() % options_.num_levels; + Status s = db_->PromoteL0(column_family, target_level); + if (!s.ok()) { + // The second error occurs when another concurrent PromoteL0() moving the + // same files finishes first which is an allowed behavior + bool non_ok_status_allowed = + s.IsInvalidArgument() || + (s.IsCorruption() && + s.ToString().find("VersionBuilder: Cannot delete table file") != + std::string::npos && + s.ToString().find("since it is on level") != std::string::npos); + fprintf(non_ok_status_allowed ? stdout : stderr, + "Unable to perform PromoteL0(): %s under specified " + "target_level: %d.\n", + s.ToString().c_str(), target_level); + if (!non_ok_status_allowed) { + thread->shared->SafeTerminate(); + } + } +} + Status StressTest::TestFlush(const std::vector& rand_column_families) { FlushOptions flush_opts; if (FLAGS_atomic_flush) { @@ -2439,6 +2475,8 @@ Status StressTest::TestFlush(const std::vector& rand_column_families) { return db_->Flush(flush_opts, cfhs); } +Status StressTest::TestResetStats() { return db_->ResetStats(); } + Status StressTest::TestPauseBackground(ThreadState* thread) { Status status = db_->PauseBackgroundWork(); if (!status.ok()) { diff --git a/db_stress_tool/db_stress_test_base.h b/db_stress_tool/db_stress_test_base.h index 3532f40e571..9d88a05c85e 100644 --- a/db_stress_tool/db_stress_test_base.h +++ b/db_stress_tool/db_stress_test_base.h @@ -89,6 +89,10 @@ class StressTest { return {rand_key}; } + virtual void TestKeyMayExist(ThreadState*, const ReadOptions&, + const std::vector&, + const std::vector&) {} + virtual Status TestGet(ThreadState* thread, const ReadOptions& read_opts, const std::vector& rand_column_families, const std::vector& rand_keys) = 0; @@ -136,6 +140,9 @@ class StressTest { const Slice& start_key, ColumnFamilyHandle* column_family); + virtual void TestPromoteL0(ThreadState* thread, + ColumnFamilyHandle* column_family); + // Calculate a hash value for all keys in range [start_key, end_key] // at a certain snapshot. uint32_t GetRangeHash(ThreadState* thread, const Snapshot* snapshot, @@ -204,6 +211,8 @@ class StressTest { Status TestFlush(const std::vector& rand_column_families); + Status TestResetStats(); + Status TestPauseBackground(ThreadState* thread); Status TestDisableFileDeletions(ThreadState* thread); diff --git a/db_stress_tool/no_batched_ops_stress.cc b/db_stress_tool/no_batched_ops_stress.cc index 4099d312391..5ec026f2419 100644 --- a/db_stress_tool/no_batched_ops_stress.cc +++ b/db_stress_tool/no_batched_ops_stress.cc @@ -462,6 +462,44 @@ class NonBatchedOpsStressTest : public StressTest { bool IsStateTracked() const override { return true; } + void TestKeyMayExist(ThreadState* thread, const ReadOptions& read_opts, + const std::vector& rand_column_families, + const std::vector& rand_keys) override { + auto cfh = column_families_[rand_column_families[0]]; + std::string key_str = Key(rand_keys[0]); + Slice key = key_str; + std::string ignore; + ReadOptions read_opts_copy = read_opts; + + std::string read_ts_str; + Slice read_ts_slice; + if (FLAGS_user_timestamp_size > 0) { + read_ts_str = GetNowNanos(); + read_ts_slice = read_ts_str; + read_opts_copy.timestamp = &read_ts_slice; + } + bool read_older_ts = MaybeUseOlderTimestampForPointLookup( + thread, read_ts_str, read_ts_slice, read_opts_copy); + + const ExpectedValue pre_read_expected_value = + thread->shared->Get(rand_column_families[0], rand_keys[0]); + bool key_may_exist = db_->KeyMayExist(read_opts_copy, cfh, key, &ignore); + const ExpectedValue post_read_expected_value = + thread->shared->Get(rand_column_families[0], rand_keys[0]); + + if (!key_may_exist && !FLAGS_skip_verifydb && !read_older_ts) { + if (ExpectedValueHelper::MustHaveExisted(pre_read_expected_value, + post_read_expected_value)) { + thread->shared->SetVerificationFailure(); + fprintf(stderr, + "error : inconsistent values for key %s: expected state has " + "the key, TestKeyMayExist() returns false indicating the key " + "must not exist.\n", + key.ToString(true).c_str()); + } + } + } + Status TestGet(ThreadState* thread, const ReadOptions& read_opts, const std::vector& rand_column_families, const std::vector& rand_keys) override { diff --git a/include/rocksdb/db.h b/include/rocksdb/db.h index f05c4994042..f97ea7c0f2c 100644 --- a/include/rocksdb/db.h +++ b/include/rocksdb/db.h @@ -2009,6 +2009,8 @@ class DB { return Status::NotSupported("SuggestCompactRange() is not implemented."); } + // Trivially move L0 files to target level. Should not be called with another + // PromoteL0() concurrently virtual Status PromoteL0(ColumnFamilyHandle* /*column_family*/, int /*target_level*/) { return Status::NotSupported("PromoteL0() is not implemented."); diff --git a/tools/db_crashtest.py b/tools/db_crashtest.py index 67d4214f28a..365ff87550b 100644 --- a/tools/db_crashtest.py +++ b/tools/db_crashtest.py @@ -67,7 +67,9 @@ "clear_column_family_one_in": 0, "compact_files_one_in": lambda: random.choice([1000, 1000000]), "compact_range_one_in": lambda: random.choice([1000, 1000000]), + "promote_l0_one_in": lambda: random.choice([1000, 1000000]), "compaction_pri": random.randint(0, 4), + "key_may_exist_one_in": lambda: random.choice([100, 100000]), "data_block_index_type": lambda: random.choice([0, 1]), "delpercent": 4, "delrangepercent": 1, @@ -116,6 +118,7 @@ "optimize_filters_for_memory": lambda: random.randint(0, 1), "partition_filters": lambda: random.randint(0, 1), "partition_pinning": lambda: random.randint(0, 3), + "reset_stats_one_in": lambda: random.choice([10000, 1000000]), "pause_background_one_in": lambda: random.choice([10000, 1000000]), "disable_file_deletions_one_in": lambda: random.choice([10000, 1000000]), "disable_manual_compaction_one_in": lambda: random.choice([10000, 1000000]),