@nxtedition/rocksdb 8.0.0 → 8.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/BUILDING.md +2 -2
  2. package/binding.cc +2 -7
  3. package/deps/rocksdb/rocksdb/CMakeLists.txt +10 -9
  4. package/deps/rocksdb/rocksdb/Makefile +2 -2
  5. package/deps/rocksdb/rocksdb/TARGETS +4 -2
  6. package/deps/rocksdb/rocksdb/cache/cache_bench_tool.cc +0 -5
  7. package/deps/rocksdb/rocksdb/cache/cache_test.cc +8 -29
  8. package/deps/rocksdb/rocksdb/cache/clock_cache.cc +146 -0
  9. package/deps/rocksdb/rocksdb/cache/clock_cache.h +13 -1
  10. package/deps/rocksdb/rocksdb/cache/lru_cache_test.cc +57 -146
  11. package/deps/rocksdb/rocksdb/cache/secondary_cache.cc +32 -0
  12. package/deps/rocksdb/rocksdb/db/blob/blob_counting_iterator.h +11 -0
  13. package/deps/rocksdb/rocksdb/db/column_family.cc +11 -9
  14. package/deps/rocksdb/rocksdb/db/column_family.h +20 -0
  15. package/deps/rocksdb/rocksdb/db/compaction/clipping_iterator.h +5 -0
  16. package/deps/rocksdb/rocksdb/db/compaction/compaction.cc +13 -33
  17. package/deps/rocksdb/rocksdb/db/compaction/compaction.h +5 -0
  18. package/deps/rocksdb/rocksdb/db/compaction/compaction_iterator.cc +27 -8
  19. package/deps/rocksdb/rocksdb/db/compaction/compaction_iterator.h +17 -1
  20. package/deps/rocksdb/rocksdb/db/compaction/compaction_job.cc +2 -1
  21. package/deps/rocksdb/rocksdb/db/compaction/compaction_job.h +4 -2
  22. package/deps/rocksdb/rocksdb/db/compaction/compaction_job_test.cc +8 -6
  23. package/deps/rocksdb/rocksdb/db/compaction/compaction_outputs.cc +65 -7
  24. package/deps/rocksdb/rocksdb/db/compaction/compaction_outputs.h +5 -0
  25. package/deps/rocksdb/rocksdb/db/compaction/compaction_picker.cc +10 -32
  26. package/deps/rocksdb/rocksdb/db/compaction/compaction_picker.h +28 -47
  27. package/deps/rocksdb/rocksdb/db/compaction/compaction_picker_fifo.cc +28 -22
  28. package/deps/rocksdb/rocksdb/db/compaction/compaction_picker_fifo.h +8 -14
  29. package/deps/rocksdb/rocksdb/db/compaction/compaction_picker_level.cc +8 -8
  30. package/deps/rocksdb/rocksdb/db/compaction/compaction_picker_level.h +5 -4
  31. package/deps/rocksdb/rocksdb/db/compaction/compaction_picker_test.cc +170 -140
  32. package/deps/rocksdb/rocksdb/db/compaction/compaction_picker_universal.cc +5 -1
  33. package/deps/rocksdb/rocksdb/db/compaction/compaction_picker_universal.h +5 -4
  34. package/deps/rocksdb/rocksdb/db/compaction/compaction_service_job.cc +8 -2
  35. package/deps/rocksdb/rocksdb/db/compaction/subcompaction_state.h +8 -0
  36. package/deps/rocksdb/rocksdb/db/compaction/tiered_compaction_test.cc +266 -138
  37. package/deps/rocksdb/rocksdb/db/corruption_test.cc +86 -1
  38. package/deps/rocksdb/rocksdb/db/db_basic_test.cc +72 -5
  39. package/deps/rocksdb/rocksdb/db/db_block_cache_test.cc +119 -10
  40. package/deps/rocksdb/rocksdb/db/db_compaction_test.cc +585 -264
  41. package/deps/rocksdb/rocksdb/db/db_impl/db_impl.cc +46 -18
  42. package/deps/rocksdb/rocksdb/db/db_impl/db_impl.h +5 -1
  43. package/deps/rocksdb/rocksdb/db/db_impl/db_impl_compaction_flush.cc +6 -15
  44. package/deps/rocksdb/rocksdb/db/db_impl/db_impl_debug.cc +1 -1
  45. package/deps/rocksdb/rocksdb/db/db_impl/db_impl_experimental.cc +1 -1
  46. package/deps/rocksdb/rocksdb/db/db_impl/db_impl_files.cc +3 -0
  47. package/deps/rocksdb/rocksdb/db/db_impl/db_impl_open.cc +8 -8
  48. package/deps/rocksdb/rocksdb/db/db_impl/db_impl_write.cc +10 -0
  49. package/deps/rocksdb/rocksdb/db/db_iter.cc +57 -36
  50. package/deps/rocksdb/rocksdb/db/db_iter.h +2 -1
  51. package/deps/rocksdb/rocksdb/db/db_range_del_test.cc +250 -2
  52. package/deps/rocksdb/rocksdb/db/db_test.cc +3 -0
  53. package/deps/rocksdb/rocksdb/db/db_test2.cc +307 -8
  54. package/deps/rocksdb/rocksdb/db/db_wal_test.cc +129 -0
  55. package/deps/rocksdb/rocksdb/db/db_with_timestamp_compaction_test.cc +21 -0
  56. package/deps/rocksdb/rocksdb/db/dbformat.cc +25 -0
  57. package/deps/rocksdb/rocksdb/db/dbformat.h +2 -0
  58. package/deps/rocksdb/rocksdb/db/experimental.cc +1 -1
  59. package/deps/rocksdb/rocksdb/db/external_sst_file_ingestion_job.cc +5 -2
  60. package/deps/rocksdb/rocksdb/db/flush_job.cc +5 -2
  61. package/deps/rocksdb/rocksdb/db/history_trimming_iterator.h +4 -0
  62. package/deps/rocksdb/rocksdb/db/import_column_family_job.cc +56 -53
  63. package/deps/rocksdb/rocksdb/db/import_column_family_test.cc +3 -4
  64. package/deps/rocksdb/rocksdb/db/memtable.cc +55 -9
  65. package/deps/rocksdb/rocksdb/db/merge_helper.cc +76 -102
  66. package/deps/rocksdb/rocksdb/db/merge_helper.h +2 -11
  67. package/deps/rocksdb/rocksdb/db/periodic_task_scheduler_test.cc +10 -10
  68. package/deps/rocksdb/rocksdb/db/repair.cc +64 -22
  69. package/deps/rocksdb/rocksdb/db/repair_test.cc +54 -0
  70. package/deps/rocksdb/rocksdb/db/seqno_time_test.cc +26 -26
  71. package/deps/rocksdb/rocksdb/db/table_cache.cc +2 -0
  72. package/deps/rocksdb/rocksdb/db/table_properties_collector.h +3 -1
  73. package/deps/rocksdb/rocksdb/db/version_builder.cc +90 -43
  74. package/deps/rocksdb/rocksdb/db/version_builder.h +20 -0
  75. package/deps/rocksdb/rocksdb/db/version_builder_test.cc +190 -67
  76. package/deps/rocksdb/rocksdb/db/version_edit.cc +15 -1
  77. package/deps/rocksdb/rocksdb/db/version_edit.h +16 -4
  78. package/deps/rocksdb/rocksdb/db/version_edit_handler.cc +41 -11
  79. package/deps/rocksdb/rocksdb/db/version_edit_handler.h +27 -12
  80. package/deps/rocksdb/rocksdb/db/version_edit_test.cc +18 -16
  81. package/deps/rocksdb/rocksdb/db/version_set.cc +219 -38
  82. package/deps/rocksdb/rocksdb/db/version_set.h +34 -4
  83. package/deps/rocksdb/rocksdb/db/version_set_test.cc +45 -25
  84. package/deps/rocksdb/rocksdb/db/wide/db_wide_basic_test.cc +122 -61
  85. package/deps/rocksdb/rocksdb/db/write_thread.cc +5 -2
  86. package/deps/rocksdb/rocksdb/db_stress_tool/db_stress_common.h +0 -1
  87. package/deps/rocksdb/rocksdb/db_stress_tool/db_stress_gflags.cc +0 -4
  88. package/deps/rocksdb/rocksdb/db_stress_tool/db_stress_test_base.cc +12 -17
  89. package/deps/rocksdb/rocksdb/db_stress_tool/no_batched_ops_stress.cc +6 -4
  90. package/deps/rocksdb/rocksdb/file/file_prefetch_buffer.cc +1 -1
  91. package/deps/rocksdb/rocksdb/file/file_prefetch_buffer.h +1 -0
  92. package/deps/rocksdb/rocksdb/file/prefetch_test.cc +0 -48
  93. package/deps/rocksdb/rocksdb/file/random_access_file_reader.cc +8 -0
  94. package/deps/rocksdb/rocksdb/include/rocksdb/cache.h +196 -171
  95. package/deps/rocksdb/rocksdb/include/rocksdb/db.h +6 -0
  96. package/deps/rocksdb/rocksdb/include/rocksdb/metadata.h +9 -3
  97. package/deps/rocksdb/rocksdb/include/rocksdb/options.h +25 -18
  98. package/deps/rocksdb/rocksdb/include/rocksdb/secondary_cache.h +27 -5
  99. package/deps/rocksdb/rocksdb/include/rocksdb/statistics.h +5 -0
  100. package/deps/rocksdb/rocksdb/include/rocksdb/status.h +3 -0
  101. package/deps/rocksdb/rocksdb/include/rocksdb/table.h +3 -0
  102. package/deps/rocksdb/rocksdb/include/rocksdb/version.h +1 -1
  103. package/deps/rocksdb/rocksdb/logging/logging.h +13 -19
  104. package/deps/rocksdb/rocksdb/memory/arena.cc +4 -3
  105. package/deps/rocksdb/rocksdb/memory/arena_test.cc +30 -0
  106. package/deps/rocksdb/rocksdb/monitoring/statistics.cc +3 -1
  107. package/deps/rocksdb/rocksdb/monitoring/stats_history_test.cc +26 -26
  108. package/deps/rocksdb/rocksdb/src.mk +2 -1
  109. package/deps/rocksdb/rocksdb/table/adaptive/adaptive_table_factory.cc +3 -2
  110. package/deps/rocksdb/rocksdb/table/block_based/block_based_table_builder.cc +2 -10
  111. package/deps/rocksdb/rocksdb/table/block_based/block_based_table_reader.cc +12 -29
  112. package/deps/rocksdb/rocksdb/table/block_based/block_based_table_reader_test.cc +1 -1
  113. package/deps/rocksdb/rocksdb/table/block_based/block_like_traits.h +0 -39
  114. package/deps/rocksdb/rocksdb/table/block_based/filter_block_reader_common.cc +0 -1
  115. package/deps/rocksdb/rocksdb/table/block_fetcher_test.cc +3 -3
  116. package/deps/rocksdb/rocksdb/table/compaction_merging_iterator.cc +142 -0
  117. package/deps/rocksdb/rocksdb/table/compaction_merging_iterator.h +241 -0
  118. package/deps/rocksdb/rocksdb/table/format.cc +24 -20
  119. package/deps/rocksdb/rocksdb/table/format.h +5 -2
  120. package/deps/rocksdb/rocksdb/table/get_context.cc +52 -11
  121. package/deps/rocksdb/rocksdb/table/merging_iterator.cc +97 -115
  122. package/deps/rocksdb/rocksdb/table/merging_iterator.h +82 -1
  123. package/deps/rocksdb/rocksdb/table/meta_blocks.cc +2 -2
  124. package/deps/rocksdb/rocksdb/table/sst_file_dumper.cc +1 -1
  125. package/deps/rocksdb/rocksdb/table/table_test.cc +7 -6
  126. package/deps/rocksdb/rocksdb/test_util/testutil.h +10 -0
  127. package/deps/rocksdb/rocksdb/tools/db_bench_tool.cc +0 -6
  128. package/deps/rocksdb/rocksdb/trace_replay/block_cache_tracer.h +2 -2
  129. package/deps/rocksdb/rocksdb/util/bloom_test.cc +1 -1
  130. package/deps/rocksdb/rocksdb/util/crc32c.cc +1 -1
  131. package/deps/rocksdb/rocksdb/util/status.cc +7 -0
  132. package/deps/rocksdb/rocksdb/utilities/backup/backup_engine.cc +5 -0
  133. package/deps/rocksdb/rocksdb/utilities/backup/backup_engine_test.cc +4 -0
  134. package/deps/rocksdb/rocksdb/utilities/cache_dump_load_impl.cc +7 -67
  135. package/deps/rocksdb/rocksdb/utilities/cache_dump_load_impl.h +1 -3
  136. package/deps/rocksdb/rocksdb/utilities/checkpoint/checkpoint_impl.cc +1 -0
  137. package/deps/rocksdb/rocksdb/utilities/transactions/transaction_test.cc +59 -0
  138. package/deps/rocksdb/rocksdb.gyp +2 -1
  139. package/package.json +1 -1
  140. package/prebuilds/darwin-arm64/node.napi.node +0 -0
  141. package/prebuilds/linux-x64/node.napi.node +0 -0
  142. package/deps/rocksdb/rocksdb/cache/fast_lru_cache.cc +0 -580
  143. package/deps/rocksdb/rocksdb/cache/fast_lru_cache.h +0 -476
  144. package/max_rev_operator.h +0 -100
@@ -9,8 +9,10 @@
9
9
 
10
10
  #include <tuple>
11
11
 
12
+ #include "compaction/compaction_picker_universal.h"
12
13
  #include "db/blob/blob_index.h"
13
14
  #include "db/db_test_util.h"
15
+ #include "db/dbformat.h"
14
16
  #include "env/mock_env.h"
15
17
  #include "port/port.h"
16
18
  #include "port/stack_trace.h"
@@ -186,13 +188,6 @@ class RoundRobinSubcompactionsAgainstResources
186
188
  int max_compaction_limits_;
187
189
  };
188
190
 
189
- class DBCompactionTestFIFOCheckConsistencyWithParam
190
- : public DBCompactionTest,
191
- public testing::WithParamInterface<std::string> {
192
- public:
193
- DBCompactionTestFIFOCheckConsistencyWithParam() : DBCompactionTest() {}
194
- };
195
-
196
191
  namespace {
197
192
  class FlushedFileCollector : public EventListener {
198
193
  public:
@@ -6278,327 +6273,653 @@ void IngestOneKeyValue(DBImpl* db, const std::string& key,
6278
6273
  ASSERT_OK(db->IngestExternalFile({info.file_path}, ingest_opt));
6279
6274
  }
6280
6275
 
6281
- TEST_P(DBCompactionTestWithParam,
6282
- FlushAfterIntraL0CompactionCheckConsistencyFail) {
6283
- Options options = CurrentOptions();
6284
- options.force_consistency_checks = true;
6285
- options.compression = kNoCompression;
6286
- options.level0_file_num_compaction_trigger = 5;
6287
- options.max_background_compactions = 2;
6288
- options.max_subcompactions = max_subcompactions_;
6289
- DestroyAndReopen(options);
6276
+ class DBCompactionTestL0FilesMisorderCorruption : public DBCompactionTest {
6277
+ public:
6278
+ DBCompactionTestL0FilesMisorderCorruption() : DBCompactionTest() {}
6279
+ void SetupOptions(const CompactionStyle compaciton_style,
6280
+ const std::string& compaction_path_to_test = "") {
6281
+ options_ = CurrentOptions();
6282
+ options_.create_if_missing = true;
6283
+ options_.compression = kNoCompression;
6284
+
6285
+ options_.force_consistency_checks = true;
6286
+ options_.compaction_style = compaciton_style;
6287
+
6288
+ if (compaciton_style == CompactionStyle::kCompactionStyleLevel) {
6289
+ options_.num_levels = 7;
6290
+ // Level compaction's PickIntraL0Compaction() impl detail requires
6291
+ // `options.level0_file_num_compaction_trigger` to be
6292
+ // at least 2 files less than the actual number of level 0 files
6293
+ // (i.e, 7 by design in this test)
6294
+ options_.level0_file_num_compaction_trigger = 5;
6295
+ options_.max_background_compactions = 2;
6296
+ options_.write_buffer_size = 2 << 20;
6297
+ options_.max_write_buffer_number = 6;
6298
+ } else if (compaciton_style == CompactionStyle::kCompactionStyleUniversal) {
6299
+ // TODO: expand test coverage to num_lvels > 1 for universal compacion,
6300
+ // which requires careful unit test design to compact to level 0 despite
6301
+ // num_levels > 1
6302
+ options_.num_levels = 1;
6303
+ options_.level0_file_num_compaction_trigger = 5;
6304
+
6305
+ CompactionOptionsUniversal universal_options;
6306
+ if (compaction_path_to_test == "PickCompactionToReduceSizeAmp") {
6307
+ universal_options.max_size_amplification_percent = 50;
6308
+ } else if (compaction_path_to_test ==
6309
+ "PickCompactionToReduceSortedRuns") {
6310
+ universal_options.max_size_amplification_percent = 400;
6311
+ } else if (compaction_path_to_test == "PickDeleteTriggeredCompaction") {
6312
+ universal_options.max_size_amplification_percent = 400;
6313
+ universal_options.min_merge_width = 6;
6314
+ }
6315
+ options_.compaction_options_universal = universal_options;
6316
+ } else if (compaciton_style == CompactionStyle::kCompactionStyleFIFO) {
6317
+ options_.max_open_files = -1;
6318
+ options_.num_levels = 1;
6319
+ options_.level0_file_num_compaction_trigger = 3;
6320
+
6321
+ CompactionOptionsFIFO fifo_options;
6322
+ if (compaction_path_to_test == "FindIntraL0Compaction" ||
6323
+ compaction_path_to_test == "CompactRange") {
6324
+ fifo_options.allow_compaction = true;
6325
+ fifo_options.age_for_warm = 0;
6326
+ } else if (compaction_path_to_test == "CompactFile") {
6327
+ fifo_options.allow_compaction = false;
6328
+ fifo_options.age_for_warm = 0;
6329
+ }
6330
+ options_.compaction_options_fifo = fifo_options;
6331
+ }
6290
6332
 
6291
- const size_t kValueSize = 1 << 20;
6292
- Random rnd(301);
6293
- std::atomic<int> pick_intra_l0_count(0);
6294
- std::string value(rnd.RandomString(kValueSize));
6333
+ if (compaction_path_to_test == "CompactFile" ||
6334
+ compaction_path_to_test == "CompactRange") {
6335
+ options_.disable_auto_compactions = true;
6336
+ } else {
6337
+ options_.disable_auto_compactions = false;
6338
+ }
6339
+ }
6295
6340
 
6296
- // The L0->L1 must be picked before we begin ingesting files to trigger
6297
- // intra-L0 compaction, and must not finish until after an intra-L0
6298
- // compaction has been picked.
6299
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
6300
- {{"LevelCompactionPicker::PickCompaction:Return",
6301
- "DBCompactionTestWithParam::"
6302
- "FlushAfterIntraL0CompactionCheckConsistencyFail:L0ToL1Ready"},
6303
- {"LevelCompactionPicker::PickCompactionBySize:0",
6304
- "CompactionJob::Run():Start"}});
6305
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
6306
- "FindIntraL0Compaction",
6307
- [&](void* /*arg*/) { pick_intra_l0_count.fetch_add(1); });
6341
+ void Destroy(const Options& options) {
6342
+ if (snapshot_) {
6343
+ assert(db_);
6344
+ db_->ReleaseSnapshot(snapshot_);
6345
+ snapshot_ = nullptr;
6346
+ }
6347
+ DBTestBase::Destroy(options);
6348
+ }
6308
6349
 
6309
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
6350
+ void Reopen(const Options& options) {
6351
+ DBTestBase::Reopen(options);
6352
+ if (options.compaction_style != CompactionStyle::kCompactionStyleLevel) {
6353
+ // To force assigning the global seqno to ingested file
6354
+ // for our test purpose.
6355
+ assert(snapshot_ == nullptr);
6356
+ snapshot_ = db_->GetSnapshot();
6357
+ }
6358
+ }
6310
6359
 
6311
- // prevents trivial move
6312
- for (int i = 0; i < 10; ++i) {
6313
- ASSERT_OK(Put(Key(i), "")); // prevents trivial move
6360
+ void DestroyAndReopen(Options& options) {
6361
+ Destroy(options);
6362
+ Reopen(options);
6314
6363
  }
6315
- ASSERT_OK(Flush());
6316
- Compact("", Key(99));
6317
- ASSERT_EQ(0, NumTableFilesAtLevel(0));
6318
6364
 
6319
- // Flush 5 L0 sst.
6320
- for (int i = 0; i < 5; ++i) {
6321
- ASSERT_OK(Put(Key(i + 1), value));
6322
- ASSERT_OK(Flush());
6365
+ void PauseCompactionThread() {
6366
+ sleeping_task_.reset(new test::SleepingBackgroundTask());
6367
+ env_->SetBackgroundThreads(1, Env::LOW);
6368
+ env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
6369
+ sleeping_task_.get(), Env::Priority::LOW);
6370
+ sleeping_task_->WaitUntilSleeping();
6323
6371
  }
6324
- ASSERT_EQ(5, NumTableFilesAtLevel(0));
6325
6372
 
6326
- // Put one key, to make smallest log sequence number in this memtable is less
6327
- // than sst which would be ingested in next step.
6328
- ASSERT_OK(Put(Key(0), "a"));
6373
+ void ResumeCompactionThread() {
6374
+ if (sleeping_task_) {
6375
+ sleeping_task_->WakeUp();
6376
+ sleeping_task_->WaitUntilDone();
6377
+ }
6378
+ }
6329
6379
 
6330
- ASSERT_EQ(5, NumTableFilesAtLevel(0));
6331
- TEST_SYNC_POINT(
6332
- "DBCompactionTestWithParam::"
6333
- "FlushAfterIntraL0CompactionCheckConsistencyFail:L0ToL1Ready");
6380
+ void AddFilesMarkedForPeriodicCompaction(const size_t num_files) {
6381
+ assert(options_.compaction_style ==
6382
+ CompactionStyle::kCompactionStyleUniversal);
6383
+ VersionSet* const versions = dbfull()->GetVersionSet();
6384
+ assert(versions);
6385
+ ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
6386
+ assert(cfd);
6387
+ Version* const current = cfd->current();
6388
+ assert(current);
6334
6389
 
6335
- // Ingest 5 L0 sst. And this files would trigger PickIntraL0Compaction.
6336
- for (int i = 5; i < 10; i++) {
6337
- ASSERT_EQ(i, NumTableFilesAtLevel(0));
6338
- IngestOneKeyValue(dbfull(), Key(i), value, options);
6390
+ VersionStorageInfo* const storage_info = current->storage_info();
6391
+ assert(storage_info);
6392
+
6393
+ const std::vector<FileMetaData*> level0_files = storage_info->LevelFiles(0);
6394
+ assert(level0_files.size() == num_files);
6395
+
6396
+ for (FileMetaData* f : level0_files) {
6397
+ storage_info->TEST_AddFileMarkedForPeriodicCompaction(0, f);
6398
+ }
6339
6399
  }
6340
6400
 
6341
- // Put one key, to make biggest log sequence number in this memtable is bigger
6342
- // than sst which would be ingested in next step.
6343
- ASSERT_OK(Put(Key(2), "b"));
6344
- ASSERT_OK(dbfull()->TEST_WaitForCompact());
6345
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
6346
- std::vector<std::vector<FileMetaData>> level_to_files;
6347
- dbfull()->TEST_GetFilesMetaData(dbfull()->DefaultColumnFamily(),
6348
- &level_to_files);
6349
- ASSERT_GT(level_to_files[0].size(), 0);
6350
- ASSERT_GT(pick_intra_l0_count.load(), 0);
6401
+ void AddFilesMarkedForCompaction(const size_t num_files) {
6402
+ assert(options_.compaction_style ==
6403
+ CompactionStyle::kCompactionStyleUniversal);
6404
+ VersionSet* const versions = dbfull()->GetVersionSet();
6405
+ assert(versions);
6406
+ ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
6407
+ assert(cfd);
6408
+ Version* const current = cfd->current();
6409
+ assert(current);
6351
6410
 
6352
- ASSERT_OK(Flush());
6353
- }
6411
+ VersionStorageInfo* const storage_info = current->storage_info();
6412
+ assert(storage_info);
6354
6413
 
6355
- TEST_P(DBCompactionTestWithParam,
6356
- IntraL0CompactionAfterFlushCheckConsistencyFail) {
6357
- Options options = CurrentOptions();
6358
- options.force_consistency_checks = true;
6359
- options.compression = kNoCompression;
6360
- options.level0_file_num_compaction_trigger = 5;
6361
- options.max_background_compactions = 2;
6362
- options.max_subcompactions = max_subcompactions_;
6363
- options.write_buffer_size = 2 << 20;
6364
- options.max_write_buffer_number = 6;
6365
- DestroyAndReopen(options);
6414
+ const std::vector<FileMetaData*> level0_files = storage_info->LevelFiles(0);
6415
+ assert(level0_files.size() == num_files);
6366
6416
 
6367
- const size_t kValueSize = 1 << 20;
6368
- Random rnd(301);
6369
- std::string value(rnd.RandomString(kValueSize));
6370
- std::string value2(rnd.RandomString(kValueSize));
6371
- std::string bigvalue = value + value;
6417
+ for (FileMetaData* f : level0_files) {
6418
+ storage_info->TEST_AddFileMarkedForCompaction(0, f);
6419
+ }
6420
+ }
6421
+
6422
+ void SetupSyncPoints(const std::string& compaction_path_to_test) {
6423
+ compaction_path_sync_point_called_.store(false);
6424
+ if (compaction_path_to_test == "FindIntraL0Compaction" &&
6425
+ options_.compaction_style == CompactionStyle::kCompactionStyleLevel) {
6426
+ ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
6427
+ "PostPickFileToCompact", [&](void* arg) {
6428
+ bool* picked_file_to_compact = (bool*)arg;
6429
+ // To trigger intra-L0 compaction specifically,
6430
+ // we mock PickFileToCompact()'s result to be false
6431
+ *picked_file_to_compact = false;
6432
+ });
6433
+ ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
6434
+ "FindIntraL0Compaction", [&](void* /*arg*/) {
6435
+ compaction_path_sync_point_called_.store(true);
6436
+ });
6437
+
6438
+ } else if (compaction_path_to_test == "PickPeriodicCompaction") {
6439
+ assert(options_.compaction_style ==
6440
+ CompactionStyle::kCompactionStyleUniversal);
6441
+ ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
6442
+ "PostPickPeriodicCompaction", [&](void* compaction_arg) {
6443
+ Compaction* compaction = (Compaction*)compaction_arg;
6444
+ if (compaction != nullptr) {
6445
+ compaction_path_sync_point_called_.store(true);
6446
+ }
6447
+ });
6448
+ } else if (compaction_path_to_test == "PickCompactionToReduceSizeAmp") {
6449
+ assert(options_.compaction_style ==
6450
+ CompactionStyle::kCompactionStyleUniversal);
6451
+ ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
6452
+ "PickCompactionToReduceSizeAmpReturnNonnullptr", [&](void* /*arg*/) {
6453
+ compaction_path_sync_point_called_.store(true);
6454
+ });
6455
+ } else if (compaction_path_to_test == "PickCompactionToReduceSortedRuns") {
6456
+ assert(options_.compaction_style ==
6457
+ CompactionStyle::kCompactionStyleUniversal);
6458
+ ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
6459
+ "PickCompactionToReduceSortedRunsReturnNonnullptr",
6460
+ [&](void* /*arg*/) {
6461
+ compaction_path_sync_point_called_.store(true);
6462
+ });
6463
+ } else if (compaction_path_to_test == "PickDeleteTriggeredCompaction") {
6464
+ assert(options_.compaction_style ==
6465
+ CompactionStyle::kCompactionStyleUniversal);
6466
+ ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
6467
+ "PickDeleteTriggeredCompactionReturnNonnullptr", [&](void* /*arg*/) {
6468
+ compaction_path_sync_point_called_.store(true);
6469
+ });
6470
+ } else if ((compaction_path_to_test == "FindIntraL0Compaction" ||
6471
+ compaction_path_to_test == "CompactRange") &&
6472
+ options_.compaction_style ==
6473
+ CompactionStyle::kCompactionStyleFIFO) {
6474
+ ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
6475
+ "FindIntraL0Compaction", [&](void* /*arg*/) {
6476
+ compaction_path_sync_point_called_.store(true);
6477
+ });
6478
+ }
6479
+
6480
+ ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
6481
+ }
6482
+
6483
+ bool SyncPointsCalled() { return compaction_path_sync_point_called_.load(); }
6484
+
6485
+ void DisableSyncPoints() {
6486
+ ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
6487
+ ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
6488
+ }
6489
+
6490
+ // Return the largest seqno of the latest L0 file based on file number
6491
+ SequenceNumber GetLatestL0FileLargestSeqnoHelper() {
6492
+ VersionSet* const versions = dbfull()->GetVersionSet();
6493
+ assert(versions);
6494
+ ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
6495
+ assert(cfd);
6496
+ Version* const current = cfd->current();
6497
+ assert(current);
6498
+ VersionStorageInfo* const storage_info = current->storage_info();
6499
+ assert(storage_info);
6500
+ const std::vector<FileMetaData*> level0_files = storage_info->LevelFiles(0);
6501
+ assert(level0_files.size() >= 1);
6502
+
6503
+ uint64_t latest_file_num = 0;
6504
+ uint64_t latest_file_largest_seqno = 0;
6505
+ for (FileMetaData* f : level0_files) {
6506
+ if (f->fd.GetNumber() > latest_file_num) {
6507
+ latest_file_num = f->fd.GetNumber();
6508
+ latest_file_largest_seqno = f->fd.largest_seqno;
6509
+ }
6510
+ }
6511
+
6512
+ return latest_file_largest_seqno;
6513
+ }
6372
6514
 
6373
- // prevents trivial move
6515
+ protected:
6516
+ Options options_;
6517
+
6518
+ private:
6519
+ const Snapshot* snapshot_ = nullptr;
6520
+ std::atomic<bool> compaction_path_sync_point_called_;
6521
+ std::shared_ptr<test::SleepingBackgroundTask> sleeping_task_;
6522
+ };
6523
+
6524
+ TEST_F(DBCompactionTestL0FilesMisorderCorruption,
6525
+ FlushAfterIntraL0LevelCompactionWithIngestedFile) {
6526
+ SetupOptions(CompactionStyle::kCompactionStyleLevel, "");
6527
+ DestroyAndReopen(options_);
6528
+ // Prevents trivial move
6374
6529
  for (int i = 0; i < 10; ++i) {
6375
- ASSERT_OK(Put(Key(i), "")); // prevents trivial move
6530
+ ASSERT_OK(Put(Key(i), "")); // Prevents trivial move
6376
6531
  }
6377
6532
  ASSERT_OK(Flush());
6378
6533
  Compact("", Key(99));
6379
6534
  ASSERT_EQ(0, NumTableFilesAtLevel(0));
6380
6535
 
6381
- std::atomic<int> pick_intra_l0_count(0);
6382
- // The L0->L1 must be picked before we begin ingesting files to trigger
6383
- // intra-L0 compaction, and must not finish until after an intra-L0
6384
- // compaction has been picked.
6385
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
6386
- {{"LevelCompactionPicker::PickCompaction:Return",
6387
- "DBCompactionTestWithParam::"
6388
- "IntraL0CompactionAfterFlushCheckConsistencyFail:L0ToL1Ready"},
6389
- {"LevelCompactionPicker::PickCompactionBySize:0",
6390
- "CompactionJob::Run():Start"}});
6391
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
6392
- "FindIntraL0Compaction",
6393
- [&](void* /*arg*/) { pick_intra_l0_count.fetch_add(1); });
6394
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
6395
- // Make 6 L0 sst.
6536
+ // To get accurate NumTableFilesAtLevel(0) when the number reaches
6537
+ // options_.level0_file_num_compaction_trigger
6538
+ PauseCompactionThread();
6539
+
6540
+ // To create below LSM tree
6541
+ // (key:value@n indicates key-value pair has seqno "n", L0 is sorted):
6542
+ //
6543
+ // memtable: m1[ 5:new@12 .. 1:new@8, 0:new@7]
6544
+ // L0: s6[6:new@13], s5[5:old@6] ... s1[1:old@2],s0[0:old@1]
6545
+ //
6546
+ // (1) Make 6 L0 sst (i.e, s0 - s5)
6396
6547
  for (int i = 0; i < 6; ++i) {
6397
6548
  if (i % 2 == 0) {
6398
- IngestOneKeyValue(dbfull(), Key(i), value, options);
6549
+ IngestOneKeyValue(dbfull(), Key(i), "old", options_);
6399
6550
  } else {
6400
- ASSERT_OK(Put(Key(i), value));
6551
+ ASSERT_OK(Put(Key(i), "old"));
6401
6552
  ASSERT_OK(Flush());
6402
6553
  }
6403
6554
  }
6404
-
6405
6555
  ASSERT_EQ(6, NumTableFilesAtLevel(0));
6406
6556
 
6407
- // Stop run flush job
6408
- env_->SetBackgroundThreads(1, Env::HIGH);
6409
- test::SleepingBackgroundTask sleeping_tasks;
6410
- env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_tasks,
6411
- Env::Priority::HIGH);
6412
- sleeping_tasks.WaitUntilSleeping();
6413
-
6414
- // Put many keys to make memtable request to flush
6557
+ // (2) Create m1
6415
6558
  for (int i = 0; i < 6; ++i) {
6416
- ASSERT_OK(Put(Key(i), bigvalue));
6559
+ ASSERT_OK(Put(Key(i), "new"));
6417
6560
  }
6418
-
6419
6561
  ASSERT_EQ(6, NumTableFilesAtLevel(0));
6420
- TEST_SYNC_POINT(
6421
- "DBCompactionTestWithParam::"
6422
- "IntraL0CompactionAfterFlushCheckConsistencyFail:L0ToL1Ready");
6423
- // ingest file to trigger IntraL0Compaction
6424
- for (int i = 6; i < 10; ++i) {
6562
+
6563
+ // (3) Ingest file (i.e, s6) to trigger IntraL0Compaction()
6564
+ for (int i = 6; i < 7; ++i) {
6425
6565
  ASSERT_EQ(i, NumTableFilesAtLevel(0));
6426
- IngestOneKeyValue(dbfull(), Key(i), value2, options);
6566
+ IngestOneKeyValue(dbfull(), Key(i), "new", options_);
6427
6567
  }
6428
6568
 
6429
- // Wake up flush job
6430
- sleeping_tasks.WakeUp();
6431
- sleeping_tasks.WaitUntilDone();
6569
+ SetupSyncPoints("FindIntraL0Compaction");
6570
+ ResumeCompactionThread();
6571
+
6432
6572
  ASSERT_OK(dbfull()->TEST_WaitForCompact());
6433
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
6434
6573
 
6435
- uint64_t error_count = 0;
6436
- db_->GetIntProperty("rocksdb.background-errors", &error_count);
6437
- ASSERT_EQ(error_count, 0);
6438
- ASSERT_GT(pick_intra_l0_count.load(), 0);
6574
+ ASSERT_TRUE(SyncPointsCalled());
6575
+ DisableSyncPoints();
6576
+
6577
+ // After compaction, we have LSM tree:
6578
+ //
6579
+ // memtable: m1[ 5:new@12 .. 1:new@8, 0:new@7]
6580
+ // L0: s7[6:new@13, 5:old@6 .. 0:old@1]
6581
+ ASSERT_EQ(1, NumTableFilesAtLevel(0));
6582
+ SequenceNumber compact_output_file_largest_seqno =
6583
+ GetLatestL0FileLargestSeqnoHelper();
6584
+
6585
+ ASSERT_OK(Flush());
6586
+ // After flush, we have LSM tree:
6587
+ //
6588
+ // L0: s8[5:new@12 .. 0:new@7],s7[6:new@13, 5:old@5 .. 0:old@1]
6589
+ ASSERT_EQ(2, NumTableFilesAtLevel(0));
6590
+ SequenceNumber flushed_file_largest_seqno =
6591
+ GetLatestL0FileLargestSeqnoHelper();
6592
+
6593
+ // To verify there isn't any file misorder leading to returning a old value
6594
+ // of Key(0) - Key(5) , which is caused by flushed table s8 has a
6595
+ // smaller largest seqno than the compaction output file s7's largest seqno
6596
+ // while the flushed table has the newer version of the values than the
6597
+ // compaction output file's.
6598
+ ASSERT_TRUE(flushed_file_largest_seqno < compact_output_file_largest_seqno);
6439
6599
  for (int i = 0; i < 6; ++i) {
6440
- ASSERT_EQ(bigvalue, Get(Key(i)));
6600
+ ASSERT_EQ("new", Get(Key(i)));
6441
6601
  }
6442
- for (int i = 6; i < 10; ++i) {
6443
- ASSERT_EQ(value2, Get(Key(i)));
6602
+ for (int i = 6; i < 7; ++i) {
6603
+ ASSERT_EQ("new", Get(Key(i)));
6444
6604
  }
6445
6605
  }
6446
6606
 
6447
- INSTANTIATE_TEST_CASE_P(DBCompactionTestFIFOCheckConsistencyWithParam,
6448
- DBCompactionTestFIFOCheckConsistencyWithParam,
6449
- ::testing::Values("FindIntraL0Compaction",
6450
- "PickCompactionToWarm",
6451
- "CompactRange", "CompactFile"));
6607
+ TEST_F(DBCompactionTestL0FilesMisorderCorruption,
6608
+ FlushAfterIntraL0UniversalCompactionWithIngestedFile) {
6609
+ for (const std::string compaction_path_to_test :
6610
+ {"PickPeriodicCompaction", "PickCompactionToReduceSizeAmp",
6611
+ "PickCompactionToReduceSortedRuns", "PickDeleteTriggeredCompaction"}) {
6612
+ SetupOptions(CompactionStyle::kCompactionStyleUniversal,
6613
+ compaction_path_to_test);
6614
+ DestroyAndReopen(options_);
6615
+
6616
+ // To get accurate NumTableFilesAtLevel(0) when the number reaches
6617
+ // options_.level0_file_num_compaction_trigger
6618
+ PauseCompactionThread();
6619
+
6620
+ // To create below LSM tree
6621
+ // (key:value@n indicates key-value pair has seqno "n", L0 is sorted):
6622
+ //
6623
+ // memtable: m1 [ k2:new@8, k1:new@7]
6624
+ // L0: s4[k9:dummy@10], s3[k8:dummy@9],
6625
+ // s2[k7:old@6, k6:old@5].. s0[k3:old@2, k1:old@1]
6626
+ //
6627
+ // (1) Create 3 existing SST file (i.e, s0 - s2)
6628
+ ASSERT_OK(Put("k1", "old"));
6629
+ ASSERT_OK(Put("k3", "old"));
6630
+ ASSERT_OK(Flush());
6631
+ ASSERT_EQ(1, NumTableFilesAtLevel(0));
6632
+ ASSERT_OK(Put("k4", "old"));
6633
+ ASSERT_OK(Put("k5", "old"));
6634
+ ASSERT_OK(Flush());
6635
+ ASSERT_EQ(2, NumTableFilesAtLevel(0));
6636
+ ASSERT_OK(Put("k6", "old"));
6637
+ ASSERT_OK(Put("k7", "old"));
6638
+ ASSERT_OK(Flush());
6639
+ ASSERT_EQ(3, NumTableFilesAtLevel(0));
6452
6640
 
6453
- TEST_P(DBCompactionTestFIFOCheckConsistencyWithParam,
6454
- FlushAfterIntraL0CompactionWithIngestedFile) {
6455
- Options options = CurrentOptions();
6456
- options.create_if_missing = true;
6457
- options.compression = kNoCompression;
6641
+ // (2) Create m1. Noted that it contains a overlaped key with s0
6642
+ ASSERT_OK(Put("k1", "new")); // overlapped key
6643
+ ASSERT_OK(Put("k2", "new"));
6458
6644
 
6459
- options.force_consistency_checks = true;
6460
- options.compaction_style = kCompactionStyleFIFO;
6461
- options.max_open_files = -1;
6462
- options.num_levels = 1;
6463
- options.level0_file_num_compaction_trigger = 3;
6645
+ // (3) Ingest two SST files s3, s4
6646
+ IngestOneKeyValue(dbfull(), "k8", "dummy", options_);
6647
+ IngestOneKeyValue(dbfull(), "k9", "dummy", options_);
6648
+ // Up to now, L0 contains s0 - s4
6649
+ ASSERT_EQ(5, NumTableFilesAtLevel(0));
6464
6650
 
6465
- CompactionOptionsFIFO fifo_options;
6466
- const std::string compaction_path_to_test = GetParam();
6467
- if (compaction_path_to_test == "FindIntraL0Compaction") {
6468
- fifo_options.allow_compaction = true;
6469
- fifo_options.age_for_warm = 0;
6470
- } else if (compaction_path_to_test == "PickCompactionToWarm") {
6471
- fifo_options.allow_compaction = false;
6472
- fifo_options.age_for_warm = 2;
6473
- } else if (compaction_path_to_test == "CompactRange") {
6474
- // FIFOCompactionPicker::CompactRange() implementes
6475
- // on top of regular compaction paths. Here we choose
6476
- // to trigger FIFOCompactionPicker::PickCompactionToWarm()
6477
- // for simplicity
6478
- fifo_options.allow_compaction = false;
6479
- fifo_options.age_for_warm = 2;
6480
- options.disable_auto_compactions = true;
6481
- } else if (compaction_path_to_test == "CompactFile") {
6482
- fifo_options.allow_compaction = false;
6483
- fifo_options.age_for_warm = 0;
6484
- options.disable_auto_compactions = true;
6485
- } else {
6486
- assert(false);
6487
- }
6488
- options.compaction_options_fifo = fifo_options;
6651
+ if (compaction_path_to_test == "PickPeriodicCompaction") {
6652
+ AddFilesMarkedForPeriodicCompaction(5);
6653
+ } else if (compaction_path_to_test == "PickDeleteTriggeredCompaction") {
6654
+ AddFilesMarkedForCompaction(5);
6655
+ }
6489
6656
 
6490
- DestroyAndReopen(options);
6657
+ SetupSyncPoints(compaction_path_to_test);
6658
+ ResumeCompactionThread();
6491
6659
 
6492
- // To force assigning the global seqno to ingested file
6493
- // for our test purpose
6494
- const Snapshot* snapshot = db_->GetSnapshot();
6660
+ ASSERT_OK(dbfull()->TEST_WaitForCompact());
6495
6661
 
6496
- std::atomic<bool> compaction_path_sync_point_called(false);
6497
- if (compaction_path_to_test == "FindIntraL0Compaction") {
6498
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
6499
- "FindIntraL0Compaction",
6500
- [&](void* /*arg*/) { compaction_path_sync_point_called.store(true); });
6501
- } else if (compaction_path_to_test == "PickCompactionToWarm" ||
6502
- compaction_path_to_test == "CompactRange") {
6503
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
6504
- "PickCompactionToWarm",
6505
- [&](void* /*arg*/) { compaction_path_sync_point_called.store(true); });
6506
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
6507
- "PickCompactionToWarm::BeforeGetCurrentTime",
6508
- [&fifo_options](void* current_time_arg) -> void {
6509
- // The unit test goes so quickly that there is almost no time
6510
- // elapsed after we ingest a file and before we check whether ingested
6511
- // files can compact to warm.
6512
- // Therefore we need this trick to simulate elapsed
6513
- // time in reality.
6514
- int64_t* current_time = (int64_t*)current_time_arg;
6515
- *current_time = *current_time + fifo_options.age_for_warm + 1;
6516
- });
6517
- } else if (compaction_path_to_test == "CompactFile") {
6518
- // Sync point is not needed in this case
6519
- compaction_path_sync_point_called.store(true);
6520
- }
6662
+ ASSERT_TRUE(SyncPointsCalled())
6663
+ << "failed for compaction path to test: " << compaction_path_to_test;
6664
+ DisableSyncPoints();
6521
6665
 
6522
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
6666
+ // After compaction, we have LSM tree:
6667
+ //
6668
+ // memtable: m1[ k2:new@8, k1:new@7]
6669
+ // L0: s5[k9:dummy@10, k8@dummy@9, k7:old@6 .. k3:old@2, k1:old@1]
6670
+ ASSERT_EQ(1, NumTableFilesAtLevel(0))
6671
+ << "failed for compaction path to test: " << compaction_path_to_test;
6672
+ SequenceNumber compact_output_file_largest_seqno =
6673
+ GetLatestL0FileLargestSeqnoHelper();
6674
+
6675
+ ASSERT_OK(Flush()) << "failed for compaction path to test: "
6676
+ << compaction_path_to_test;
6677
+ // After flush, we have LSM tree:
6678
+ //
6679
+ // L0: s6[k2:new@8, k1:new@7],
6680
+ // s5[k9:dummy@10, k8@dummy@9, k7:old@6 .. k3:old@2, k1:old@1]
6681
+ ASSERT_EQ(2, NumTableFilesAtLevel(0))
6682
+ << "failed for compaction path to test: " << compaction_path_to_test;
6683
+ SequenceNumber flushed_file_largest_seqno =
6684
+ GetLatestL0FileLargestSeqnoHelper();
6685
+
6686
+ // To verify there isn't any file misorder leading to returning a old
6687
+ // value of "k1" , which is caused by flushed table s6 has a
6688
+ // smaller largest seqno than the compaction output file s5's largest seqno
6689
+ // while the flushed table has the newer version of the value
6690
+ // than the compaction output file's.
6691
+ ASSERT_TRUE(flushed_file_largest_seqno < compact_output_file_largest_seqno)
6692
+ << "failed for compaction path to test: " << compaction_path_to_test;
6693
+ EXPECT_EQ(Get("k1"), "new")
6694
+ << "failed for compaction path to test: " << compaction_path_to_test;
6695
+ }
6696
+
6697
+ Destroy(options_);
6698
+ }
6699
+
6700
+ TEST_F(DBCompactionTestL0FilesMisorderCorruption,
6701
+ FlushAfterIntraL0FIFOCompactionWithIngestedFile) {
6702
+ for (const std::string compaction_path_to_test : {"FindIntraL0Compaction"}) {
6703
+ SetupOptions(CompactionStyle::kCompactionStyleFIFO,
6704
+ compaction_path_to_test);
6705
+ DestroyAndReopen(options_);
6706
+
6707
+ // To create below LSM tree
6708
+ // (key:value@n indicates key-value pair has seqno "n", L0 is sorted):
6709
+ //
6710
+ // memtable: m1 [ k2:new@4, k1:new@3]
6711
+ // L0: s2[k5:dummy@6], s1[k4:dummy@5], s0[k3:old@2, k1:old@1]
6712
+ //
6713
+ // (1) Create an existing SST file s0
6714
+ ASSERT_OK(Put("k1", "old"));
6715
+ ASSERT_OK(Put("k3", "old"));
6716
+ ASSERT_OK(Flush());
6717
+ ASSERT_EQ(1, NumTableFilesAtLevel(0));
6718
+
6719
+ // (2) Create memtable m1. Noted that it contains a overlaped key with s0
6720
+ ASSERT_OK(Put("k1", "new")); // overlapped key
6721
+ ASSERT_OK(Put("k2", "new"));
6722
+
6723
+ // To get accurate NumTableFilesAtLevel(0) when the number reaches
6724
+ // options_.level0_file_num_compaction_trigger
6725
+ PauseCompactionThread();
6726
+
6727
+ // (3) Ingest two SST files s1, s2
6728
+ IngestOneKeyValue(dbfull(), "k4", "dummy", options_);
6729
+ IngestOneKeyValue(dbfull(), "k5", "dummy", options_);
6730
+ // Up to now, L0 contains s0, s1, s2
6731
+ ASSERT_EQ(3, NumTableFilesAtLevel(0));
6523
6732
 
6524
- // Create an existing SST file s0 of key range [key1,key4] and seqno range
6525
- // [1,2]
6526
- ASSERT_OK(Put("key1", "seq1"));
6527
- ASSERT_OK(Put("key4", "seq2"));
6733
+ SetupSyncPoints(compaction_path_to_test);
6734
+ ResumeCompactionThread();
6735
+
6736
+ ASSERT_OK(dbfull()->TEST_WaitForCompact());
6737
+
6738
+ ASSERT_TRUE(SyncPointsCalled())
6739
+ << "failed for compaction path to test: " << compaction_path_to_test;
6740
+ DisableSyncPoints();
6741
+ // After compaction, we have LSM tree:
6742
+ //
6743
+ // memtable: m1 [ k2:new@4, k1:new@3]
6744
+ // L0: s3[k5:dummy@6, k4:dummy@5, k3:old@2, k1:old@1]
6745
+ ASSERT_EQ(1, NumTableFilesAtLevel(0))
6746
+ << "failed for compaction path to test: " << compaction_path_to_test;
6747
+ SequenceNumber compact_output_file_largest_seqno =
6748
+ GetLatestL0FileLargestSeqnoHelper();
6749
+
6750
+ ASSERT_OK(Flush()) << "failed for compaction path to test: "
6751
+ << compaction_path_to_test;
6752
+ // After flush, we have LSM tree:
6753
+ //
6754
+ // L0: s4[k2:new@4, k1:new@3], s3[k5:dummy@6, k4:dummy@5, k3:old@2,
6755
+ // k1:old@1]
6756
+ ASSERT_EQ(2, NumTableFilesAtLevel(0))
6757
+ << "failed for compaction path to test: " << compaction_path_to_test;
6758
+ SequenceNumber flushed_file_largest_seqno =
6759
+ GetLatestL0FileLargestSeqnoHelper();
6760
+
6761
+ // To verify there isn't any file misorder leading to returning a old
6762
+ // value of "k1" , which is caused by flushed table s4 has a
6763
+ // smaller largest seqno than the compaction output file s3's largest seqno
6764
+ // while the flushed table has the newer version of the value
6765
+ // than the compaction output file's.
6766
+ ASSERT_TRUE(flushed_file_largest_seqno < compact_output_file_largest_seqno)
6767
+ << "failed for compaction path to test: " << compaction_path_to_test;
6768
+ EXPECT_EQ(Get("k1"), "new")
6769
+ << "failed for compaction path to test: " << compaction_path_to_test;
6770
+ }
6771
+
6772
+ Destroy(options_);
6773
+ }
6774
+
6775
+ class DBCompactionTestL0FilesMisorderCorruptionWithParam
6776
+ : public DBCompactionTestL0FilesMisorderCorruption,
6777
+ public testing::WithParamInterface<CompactionStyle> {
6778
+ public:
6779
+ DBCompactionTestL0FilesMisorderCorruptionWithParam()
6780
+ : DBCompactionTestL0FilesMisorderCorruption() {}
6781
+ };
6782
+
6783
+ // TODO: add `CompactionStyle::kCompactionStyleLevel` to testing parameter,
6784
+ // which requires careful unit test
6785
+ // design for ingesting file to L0 and CompactRange()/CompactFile() to L0
6786
+ INSTANTIATE_TEST_CASE_P(
6787
+ DBCompactionTestL0FilesMisorderCorruptionWithParam,
6788
+ DBCompactionTestL0FilesMisorderCorruptionWithParam,
6789
+ ::testing::Values(CompactionStyle::kCompactionStyleUniversal,
6790
+ CompactionStyle::kCompactionStyleFIFO));
6791
+
6792
+ TEST_P(DBCompactionTestL0FilesMisorderCorruptionWithParam,
6793
+ FlushAfterIntraL0CompactFileWithIngestedFile) {
6794
+ SetupOptions(GetParam(), "CompactFile");
6795
+ DestroyAndReopen(options_);
6796
+
6797
+ // To create below LSM tree
6798
+ // (key:value@n indicates key-value pair has seqno "n", L0 is sorted):
6799
+ //
6800
+ // memtable: m1 [ k2:new@4, k1:new@3]
6801
+ // L0: s2[k5:dummy@6], s1[k4:dummy@5], s0[k3:old@2, k1:old@1]
6802
+ //
6803
+ // (1) Create an existing SST file s0
6804
+ ASSERT_OK(Put("k1", "old"));
6805
+ ASSERT_OK(Put("k3", "old"));
6528
6806
  ASSERT_OK(Flush());
6529
6807
  ASSERT_EQ(1, NumTableFilesAtLevel(0));
6530
6808
 
6531
- // Accumulate entries in a memtable m1 of key range [key1,key2] and seqno
6532
- // range [3,4] Noted that it contains a overlaped key with s0
6533
- ASSERT_OK(Put("key1", "seq3")); // overlapped key
6534
- ASSERT_OK(Put("key2", "seq4"));
6809
+ // (2) Create memtable m1. Noted that it contains a overlaped key with s0
6810
+ ASSERT_OK(Put("k1", "new")); // overlapped key
6811
+ ASSERT_OK(Put("k2", "new"));
6535
6812
 
6536
- ASSERT_TRUE(compaction_path_to_test == "CompactFile" ||
6537
- !compaction_path_sync_point_called.load());
6813
+ // (3) Ingest two SST files s1, s2
6814
+ IngestOneKeyValue(dbfull(), "k4", "dummy", options_);
6815
+ IngestOneKeyValue(dbfull(), "k5", "dummy", options_);
6816
+ // Up to now, L0 contains s0, s1, s2
6817
+ ASSERT_EQ(3, NumTableFilesAtLevel(0));
6538
6818
 
6539
- // Stop background compaction job to obtain accurate
6540
- // `NumTableFilesAtLevel(0)` after file ingestion
6541
- test::SleepingBackgroundTask sleeping_tasks;
6542
- if (!options.disable_auto_compactions) {
6543
- env_->SetBackgroundThreads(1, Env::LOW);
6544
- env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_tasks,
6545
- Env::Priority::LOW);
6546
- sleeping_tasks.WaitUntilSleeping();
6819
+ ColumnFamilyMetaData cf_meta_data;
6820
+ db_->GetColumnFamilyMetaData(&cf_meta_data);
6821
+ ASSERT_EQ(cf_meta_data.levels[0].files.size(), 3);
6822
+ std::vector<std::string> input_files;
6823
+ for (const auto& file : cf_meta_data.levels[0].files) {
6824
+ input_files.push_back(file.name);
6547
6825
  }
6826
+ ASSERT_EQ(input_files.size(), 3);
6827
+
6828
+ Status s = db_->CompactFiles(CompactionOptions(), input_files, 0);
6829
+ // After compaction, we have LSM tree:
6830
+ //
6831
+ // memtable: m1 [ k2:new@4, k1:new@3]
6832
+ // L0: s3[k5:dummy@6, k4:dummy@5, k3:old@2, k1:old@1]
6833
+ ASSERT_OK(s);
6834
+ ASSERT_EQ(1, NumTableFilesAtLevel(0));
6835
+ SequenceNumber compact_output_file_largest_seqno =
6836
+ GetLatestL0FileLargestSeqnoHelper();
6548
6837
 
6549
- // Ingested two SST files, s1 of key range [key5,key5] and seqno range [5,5]
6550
- // and s2 of key range [key6,key6] and seqno range [6,6]
6551
- IngestOneKeyValue(dbfull(), "key5", "seq5", options);
6552
- IngestOneKeyValue(dbfull(), "key6", "seq6", options);
6838
+ ASSERT_OK(Flush());
6839
+ // After flush, we have LSM tree:
6840
+ //
6841
+ // L0: s4[k2:new@4, k1:new@3], s3[k5:dummy@6, k4:dummy@5, k3:old@2,
6842
+ // k1:old@1]
6843
+ ASSERT_EQ(2, NumTableFilesAtLevel(0));
6844
+ SequenceNumber flushed_file_largest_seqno =
6845
+ GetLatestL0FileLargestSeqnoHelper();
6846
+
6847
+ // To verify there isn't any file misorder leading to returning a old value
6848
+ // of "1" , which is caused by flushed table s4 has a smaller
6849
+ // largest seqno than the compaction output file s3's largest seqno while the
6850
+ // flushed table has the newer version of the value than the
6851
+ // compaction output file's.
6852
+ ASSERT_TRUE(flushed_file_largest_seqno < compact_output_file_largest_seqno);
6853
+ EXPECT_EQ(Get("k1"), "new");
6854
+
6855
+ Destroy(options_);
6856
+ }
6857
+
6858
+ TEST_P(DBCompactionTestL0FilesMisorderCorruptionWithParam,
6859
+ FlushAfterIntraL0CompactRangeWithIngestedFile) {
6860
+ SetupOptions(GetParam(), "CompactRange");
6861
+ DestroyAndReopen(options_);
6862
+
6863
+ // To create below LSM tree
6864
+ // (key:value@n indicates key-value pair has seqno "n", L0 is sorted):
6865
+ //
6866
+ // memtable: m1 [ k2:new@4, k1:new@3]
6867
+ // L0: s2[k5:dummy@6], s1[k4:dummy@5], s0[k3:old@2, k1:old@1]
6868
+ //
6869
+ // (1) Create an existing SST file s0
6870
+ ASSERT_OK(Put("k1", "old"));
6871
+ ASSERT_OK(Put("k3", "old"));
6872
+ ASSERT_OK(Flush());
6873
+ ASSERT_EQ(1, NumTableFilesAtLevel(0));
6874
+
6875
+ // (2) Create memtable m1. Noted that it contains a overlaped key with s0
6876
+ ASSERT_OK(Put("k1", "new")); // overlapped key
6877
+ ASSERT_OK(Put("k2", "new"));
6878
+
6879
+ // (3) Ingest two SST files s1, s2
6880
+ IngestOneKeyValue(dbfull(), "k4", "dummy", options_);
6881
+ IngestOneKeyValue(dbfull(), "k5", "dummy", options_);
6553
6882
  // Up to now, L0 contains s0, s1, s2
6554
6883
  ASSERT_EQ(3, NumTableFilesAtLevel(0));
6555
6884
 
6556
- // Resume background compaction job so that Intra level0 compaction can be
6557
- // triggered
6558
- if (!options.disable_auto_compactions) {
6559
- sleeping_tasks.WakeUp();
6560
- sleeping_tasks.WaitUntilDone();
6561
- }
6562
-
6563
- if (compaction_path_to_test == "CompactRange") {
6564
- // `start` and `end` is carefully chosen so that compact range:
6565
- // (1) doesn't overlap with memtable therefore the memtable won't be flushed
6566
- // (2) should target at compacting s0 with s1 and s2
6567
- Slice start("key4"), end("key6");
6568
- ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &start, &end));
6569
- } else if (compaction_path_to_test == "CompactFile") {
6570
- ColumnFamilyMetaData cf_meta_data;
6571
- db_->GetColumnFamilyMetaData(&cf_meta_data);
6572
- assert(cf_meta_data.levels[0].files.size() == 3);
6573
- std::vector<std::string> input_files;
6574
- for (const auto& file : cf_meta_data.levels[0].files) {
6575
- input_files.push_back(file.name);
6576
- }
6577
- Status s = db_->CompactFiles(CompactionOptions(), input_files, 0);
6578
- EXPECT_TRUE(s.IsAborted());
6579
- EXPECT_TRUE(s.ToString().find(
6580
- "has overlapping seqnos with earliest memtable seqnos") !=
6581
- std::string::npos);
6582
- } else {
6583
- ASSERT_OK(dbfull()->TEST_WaitForCompact());
6885
+ if (options_.compaction_style == CompactionStyle::kCompactionStyleFIFO) {
6886
+ SetupSyncPoints("CompactRange");
6584
6887
  }
6585
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
6586
-
6587
- ASSERT_TRUE(compaction_path_to_test == "CompactFile" ||
6588
- compaction_path_sync_point_called.load());
6888
+ // `start` and `end` is carefully chosen so that compact range:
6889
+ // (1) doesn't overlap with memtable therefore the memtable won't be flushed
6890
+ // (2) should target at compacting s0 with s1 and s2
6891
+ Slice start("k3"), end("k5");
6892
+ ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &start, &end));
6893
+ // After compaction, we have LSM tree:
6894
+ //
6895
+ // memtable: m1 [ k2:new@4, k1:new@3]
6896
+ // L0: s3[k5:dummy@6, k4:dummy@5, k3:old@2, k1:old@1]
6897
+ if (options_.compaction_style == CompactionStyle::kCompactionStyleFIFO) {
6898
+ ASSERT_TRUE(SyncPointsCalled());
6899
+ DisableSyncPoints();
6900
+ }
6901
+ ASSERT_EQ(1, NumTableFilesAtLevel(0));
6902
+ SequenceNumber compact_output_file_largest_seqno =
6903
+ GetLatestL0FileLargestSeqnoHelper();
6589
6904
 
6590
- // To verify compaction of s0, s1 and s2 (leading to new SST s4) didn't
6591
- // happen.
6905
+ ASSERT_OK(Flush());
6906
+ // After flush, we have LSM tree:
6592
6907
  //
6593
- // Otherwise, when m1 flushes in the next step and become s3,
6594
- // we will have s3 of seqnos [3, 4], s4 of seqnos [1, 6], which is a
6595
- // corruption because s3 is older than s4 based on largest seqno while s2
6596
- // contains a value of Key(1) newer than the value of Key(1) contained in s4.
6597
- // And in this case, Flush() will return Status::Corruption() caught by
6598
- // `force_consistency_checks=1`
6599
- EXPECT_EQ(3, NumTableFilesAtLevel(0));
6600
- EXPECT_OK(Flush());
6601
- db_->ReleaseSnapshot(snapshot);
6908
+ // L0: s4[k2:new@4, k1:new@3], s3[k5:dummy@6, k4:dummy@5, k3:old@2,
6909
+ // k1:old@1]
6910
+ ASSERT_EQ(2, NumTableFilesAtLevel(0));
6911
+ SequenceNumber flushed_file_largest_seqno =
6912
+ GetLatestL0FileLargestSeqnoHelper();
6913
+
6914
+ // To verify there isn't any file misorder leading to returning a old value
6915
+ // of "k1" , which is caused by flushed table s4 has a smaller
6916
+ // largest seqno than the compaction output file s3's largest seqno while the
6917
+ // flushed table has the newer version of the value than the
6918
+ // compaction output file's.
6919
+ ASSERT_TRUE(flushed_file_largest_seqno < compact_output_file_largest_seqno);
6920
+ EXPECT_EQ(Get("k1"), "new");
6921
+
6922
+ Destroy(options_);
6602
6923
  }
6603
6924
 
6604
6925
  TEST_P(DBCompactionTestWithBottommostParam, SequenceKeysManualCompaction) {