1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
6 // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
7 // Use of this source code is governed by a BSD-style license that can be
8 // found in the LICENSE file. See the AUTHORS file for names of contributors.
10 // The test uses an array to compare against values written to the database.
11 // Keys written to the array are in 1:1 correspondence to the actual values in
12 // the database according to the formula in the function GenerateValue.
14 // Space is reserved in the array from 0 to FLAGS_max_key and values are
15 // randomly written/deleted/read from those positions. During verification we
16 // compare all the positions in the array. To shorten/elongate the running
17 // time, you could change the settings: FLAGS_max_key, FLAGS_ops_per_thread,
18 // (sometimes also FLAGS_threads).
20 // NOTE that if FLAGS_test_batches_snapshots is set, the test will have
21 // different behavior. See comment of the flag for details.
24 #include "db_stress_tool/db_stress_common.h"
25 #include "db_stress_tool/db_stress_driver.h"
26 #include "rocksdb/convenience.h"
27 #include "utilities/fault_injection_fs.h"
29 namespace ROCKSDB_NAMESPACE
{
31 static std::shared_ptr
<ROCKSDB_NAMESPACE::Env
> env_guard
;
32 static std::shared_ptr
<ROCKSDB_NAMESPACE::DbStressEnvWrapper
> env_wrapper_guard
;
33 static std::shared_ptr
<ROCKSDB_NAMESPACE::DbStressEnvWrapper
>
34 dbsl_env_wrapper_guard
;
35 static std::shared_ptr
<CompositeEnvWrapper
> fault_env_guard
;
38 KeyGenContext key_gen_ctx
;
40 int db_stress_tool(int argc
, char** argv
) {
41 SetUsageMessage(std::string("\nUSAGE:\n") + std::string(argv
[0]) +
43 ParseCommandLineFlags(&argc
, &argv
, true);
45 SanitizeDoubleParam(&FLAGS_bloom_bits
);
46 SanitizeDoubleParam(&FLAGS_memtable_prefix_bloom_size_ratio
);
47 SanitizeDoubleParam(&FLAGS_max_bytes_for_level_multiplier
);
50 if (FLAGS_mock_direct_io
) {
51 SetupSyncPointsToMockDirectIO();
54 if (FLAGS_statistics
) {
55 dbstats
= ROCKSDB_NAMESPACE::CreateDBStatistics();
56 if (FLAGS_test_secondary
) {
57 dbstats_secondaries
= ROCKSDB_NAMESPACE::CreateDBStatistics();
60 compression_type_e
= StringToCompressionType(FLAGS_compression_type
.c_str());
61 bottommost_compression_type_e
=
62 StringToCompressionType(FLAGS_bottommost_compression_type
.c_str());
63 checksum_type_e
= StringToChecksumType(FLAGS_checksum_type
.c_str());
67 int env_opts
= !FLAGS_env_uri
.empty() + !FLAGS_fs_uri
.empty();
69 fprintf(stderr
, "Error: --env_uri and --fs_uri are mutually exclusive\n");
73 Status s
= Env::CreateFromUri(ConfigOptions(), FLAGS_env_uri
, FLAGS_fs_uri
,
74 &raw_env
, &env_guard
);
76 fprintf(stderr
, "Error Creating Env URI: %s: %s\n", FLAGS_env_uri
.c_str(),
77 s
.ToString().c_str());
80 dbsl_env_wrapper_guard
= std::make_shared
<DbStressEnvWrapper
>(raw_env
);
81 db_stress_listener_env
= dbsl_env_wrapper_guard
.get();
83 if (FLAGS_read_fault_one_in
|| FLAGS_sync_fault_injection
||
84 FLAGS_write_fault_one_in
|| FLAGS_open_metadata_write_fault_one_in
||
85 FLAGS_open_write_fault_one_in
|| FLAGS_open_read_fault_one_in
) {
86 FaultInjectionTestFS
* fs
=
87 new FaultInjectionTestFS(raw_env
->GetFileSystem());
88 fault_fs_guard
.reset(fs
);
89 if (FLAGS_write_fault_one_in
) {
90 fault_fs_guard
->SetFilesystemDirectWritable(false);
92 fault_fs_guard
->SetFilesystemDirectWritable(true);
95 std::make_shared
<CompositeEnvWrapper
>(raw_env
, fault_fs_guard
);
96 raw_env
= fault_env_guard
.get();
99 env_wrapper_guard
= std::make_shared
<DbStressEnvWrapper
>(raw_env
);
100 db_stress_env
= env_wrapper_guard
.get();
102 if (FLAGS_write_fault_one_in
) {
103 // In the write injection case, we need to use the FS interface and returns
104 // the IOStatus with different error and flags. Therefore,
105 // DbStressEnvWrapper cannot be used which will swallow the FS
106 // implementations. We should directly use the raw_env which is the
107 // CompositeEnvWrapper of env and fault_fs.
108 db_stress_env
= raw_env
;
111 FLAGS_rep_factory
= StringToRepFactory(FLAGS_memtablerep
.c_str());
113 // The number of background threads should be at least as much the
114 // max number of concurrent compactions.
115 db_stress_env
->SetBackgroundThreads(FLAGS_max_background_compactions
,
116 ROCKSDB_NAMESPACE::Env::Priority::LOW
);
117 db_stress_env
->SetBackgroundThreads(FLAGS_num_bottom_pri_threads
,
118 ROCKSDB_NAMESPACE::Env::Priority::BOTTOM
);
119 if (FLAGS_prefixpercent
> 0 && FLAGS_prefix_size
< 0) {
121 "Error: prefixpercent is non-zero while prefix_size is "
125 if (FLAGS_test_batches_snapshots
&& FLAGS_prefix_size
<= 0) {
127 "Error: please specify prefix_size for "
128 "test_batches_snapshots test!\n");
131 if (FLAGS_memtable_prefix_bloom_size_ratio
> 0.0 && FLAGS_prefix_size
< 0 &&
132 !FLAGS_memtable_whole_key_filtering
) {
134 "Error: please specify positive prefix_size or enable whole key "
135 "filtering in order to use memtable_prefix_bloom_size_ratio\n");
138 if ((FLAGS_readpercent
+ FLAGS_prefixpercent
+ FLAGS_writepercent
+
139 FLAGS_delpercent
+ FLAGS_delrangepercent
+ FLAGS_iterpercent
+
140 FLAGS_customopspercent
) != 100) {
144 "Read(-readpercent=%d)+Prefix(-prefixpercent=%d)+Write(-writepercent=%"
145 "d)+Delete(-delpercent=%d)+DeleteRange(-delrangepercent=%d)"
146 "+Iterate(-iterpercent=%d)+CustomOps(-customopspercent=%d) percents != "
148 FLAGS_readpercent
, FLAGS_prefixpercent
, FLAGS_writepercent
,
149 FLAGS_delpercent
, FLAGS_delrangepercent
, FLAGS_iterpercent
,
150 FLAGS_customopspercent
);
153 if (FLAGS_disable_wal
== 1 && FLAGS_reopen
> 0) {
154 fprintf(stderr
, "Error: Db cannot reopen safely with disable_wal set!\n");
157 if ((unsigned)FLAGS_reopen
>= FLAGS_ops_per_thread
) {
159 "Error: #DB-reopens should be < ops_per_thread\n"
160 "Provided reopens = %d and ops_per_thread = %lu\n",
161 FLAGS_reopen
, (unsigned long)FLAGS_ops_per_thread
);
164 if (FLAGS_test_batches_snapshots
&& FLAGS_delrangepercent
> 0) {
166 "Error: nonzero delrangepercent unsupported in "
167 "test_batches_snapshots mode\n");
170 if (FLAGS_active_width
> FLAGS_max_key
) {
171 fprintf(stderr
, "Error: active_width can be at most max_key\n");
173 } else if (FLAGS_active_width
== 0) {
174 FLAGS_active_width
= FLAGS_max_key
;
176 if (FLAGS_value_size_mult
* kRandomValueMaxFactor
> kValueMaxLen
) {
177 fprintf(stderr
, "Error: value_size_mult can be at most %d\n",
178 kValueMaxLen
/ kRandomValueMaxFactor
);
181 if (FLAGS_use_merge
&& FLAGS_nooverwritepercent
== 100) {
184 "Error: nooverwritepercent must not be 100 when using merge operands");
187 if (FLAGS_ingest_external_file_one_in
> 0 &&
188 FLAGS_nooverwritepercent
== 100) {
191 "Error: nooverwritepercent must not be 100 when using file ingestion");
194 if (FLAGS_clear_column_family_one_in
> 0 && FLAGS_backup_one_in
> 0) {
196 "Error: clear_column_family_one_in must be 0 when using backup\n");
199 if (FLAGS_test_cf_consistency
&& FLAGS_disable_wal
) {
200 FLAGS_atomic_flush
= true;
203 if (FLAGS_read_only
) {
204 if (FLAGS_writepercent
!= 0 || FLAGS_delpercent
!= 0 ||
205 FLAGS_delrangepercent
!= 0) {
206 fprintf(stderr
, "Error: updates are not supported in read only mode\n");
208 } else if (FLAGS_checkpoint_one_in
> 0 &&
209 FLAGS_clear_column_family_one_in
> 0) {
211 "Warn: checkpoint won't be validated since column families may "
216 // Choose a location for the test database if none given with --db=<path>
217 if (FLAGS_db
.empty()) {
218 std::string default_db_path
;
219 db_stress_env
->GetTestDirectory(&default_db_path
);
220 default_db_path
+= "/dbstress";
221 FLAGS_db
= default_db_path
;
224 if ((FLAGS_test_secondary
|| FLAGS_continuous_verification_interval
> 0) &&
225 FLAGS_secondaries_base
.empty()) {
226 std::string default_secondaries_path
;
227 db_stress_env
->GetTestDirectory(&default_secondaries_path
);
228 default_secondaries_path
+= "/dbstress_secondaries";
229 s
= db_stress_env
->CreateDirIfMissing(default_secondaries_path
);
231 fprintf(stderr
, "Failed to create directory %s: %s\n",
232 default_secondaries_path
.c_str(), s
.ToString().c_str());
235 FLAGS_secondaries_base
= default_secondaries_path
;
238 if (FLAGS_best_efforts_recovery
&& !FLAGS_skip_verifydb
&&
239 !FLAGS_disable_wal
) {
241 "With best-efforts recovery, either skip_verifydb or disable_wal "
242 "should be set to true.\n");
245 if (FLAGS_skip_verifydb
) {
246 if (FLAGS_verify_db_one_in
> 0) {
248 "Must set -verify_db_one_in=0 if skip_verifydb is true.\n");
251 if (FLAGS_continuous_verification_interval
> 0) {
253 "Must set -continuous_verification_interval=0 if skip_verifydb "
258 if (FLAGS_enable_compaction_filter
&&
259 (FLAGS_acquire_snapshot_one_in
> 0 || FLAGS_compact_range_one_in
> 0 ||
260 FLAGS_iterpercent
> 0 || FLAGS_test_batches_snapshots
||
261 FLAGS_test_cf_consistency
)) {
264 "Error: acquire_snapshot_one_in, compact_range_one_in, iterpercent, "
265 "test_batches_snapshots must all be 0 when using compaction filter\n");
268 if (FLAGS_test_multi_ops_txns
) {
269 CheckAndSetOptionsForMultiOpsTxnStressTest();
272 if (FLAGS_create_timestamped_snapshot_one_in
> 0) {
273 if (!FLAGS_use_txn
) {
274 fprintf(stderr
, "timestamped snapshot supported only in TransactionDB\n");
276 } else if (FLAGS_txn_write_policy
!= 0) {
278 "timestamped snapshot supported only in write-committed\n");
283 if (FLAGS_preserve_unverified_changes
&& FLAGS_reopen
!= 0) {
285 "Reopen DB is incompatible with preserving unverified changes\n");
289 if (FLAGS_use_txn
&& FLAGS_sync_fault_injection
&&
290 FLAGS_txn_write_policy
!= 0) {
292 "For TransactionDB, correctness testing with unsync data loss is "
293 "currently compatible with only write committed policy\n");
297 if (FLAGS_use_put_entity_one_in
> 0 &&
298 (FLAGS_ingest_external_file_one_in
> 0 || FLAGS_use_merge
||
299 FLAGS_use_full_merge_v1
|| FLAGS_use_txn
|| FLAGS_test_multi_ops_txns
||
300 FLAGS_user_timestamp_size
> 0)) {
302 "PutEntity is currently incompatible with SstFileWriter, Merge,"
303 " transactions, and user-defined timestamps\n");
308 KillPoint
* kp
= KillPoint::GetInstance();
309 kp
->rocksdb_kill_odds
= FLAGS_kill_random_test
;
310 kp
->rocksdb_kill_exclude_prefixes
= SplitString(FLAGS_kill_exclude_prefixes
);
313 unsigned int levels
= FLAGS_max_key_len
;
314 std::vector
<std::string
> weights
;
315 uint64_t scale_factor
= FLAGS_key_window_scale_factor
;
316 key_gen_ctx
.window
= scale_factor
* 100;
317 if (!FLAGS_key_len_percent_dist
.empty()) {
318 weights
= SplitString(FLAGS_key_len_percent_dist
);
319 if (weights
.size() != levels
) {
321 "Number of weights in key_len_dist should be equal to"
326 uint64_t total_weight
= 0;
327 for (std::string
& weight
: weights
) {
328 uint64_t val
= std::stoull(weight
);
329 key_gen_ctx
.weights
.emplace_back(val
* scale_factor
);
332 if (total_weight
!= 100) {
333 fprintf(stderr
, "Sum of all weights in key_len_dist should be 100");
337 uint64_t keys_per_level
= key_gen_ctx
.window
/ levels
;
338 for (unsigned int level
= 0; level
+ 1 < levels
; ++level
) {
339 key_gen_ctx
.weights
.emplace_back(keys_per_level
);
341 key_gen_ctx
.weights
.emplace_back(key_gen_ctx
.window
-
342 keys_per_level
* (levels
- 1));
345 std::unique_ptr
<ROCKSDB_NAMESPACE::StressTest
> stress
;
346 if (FLAGS_test_cf_consistency
) {
347 stress
.reset(CreateCfConsistencyStressTest());
348 } else if (FLAGS_test_batches_snapshots
) {
349 stress
.reset(CreateBatchedOpsStressTest());
350 } else if (FLAGS_test_multi_ops_txns
) {
351 stress
.reset(CreateMultiOpsTxnsStressTest());
353 stress
.reset(CreateNonBatchedOpsStressTest());
355 // Initialize the Zipfian pre-calculated array
356 InitializeHotKeyGenerator(FLAGS_hot_key_alpha
);
357 if (RunStressTest(stress
.get())) {
364 } // namespace ROCKSDB_NAMESPACE