@nxtedition/rocksdb 7.0.25 → 7.0.28

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 (120) hide show
  1. package/binding.cc +9 -2
  2. package/chained-batch.js +1 -1
  3. package/deps/rocksdb/rocksdb/CMakeLists.txt +3 -0
  4. package/deps/rocksdb/rocksdb/Makefile +3 -0
  5. package/deps/rocksdb/rocksdb/TARGETS +10 -0
  6. package/deps/rocksdb/rocksdb/cache/cache_bench_tool.cc +17 -7
  7. package/deps/rocksdb/rocksdb/cache/cache_entry_roles.cc +2 -0
  8. package/deps/rocksdb/rocksdb/cache/cache_reservation_manager.cc +1 -0
  9. package/deps/rocksdb/rocksdb/cache/charged_cache.cc +117 -0
  10. package/deps/rocksdb/rocksdb/cache/charged_cache.h +121 -0
  11. package/deps/rocksdb/rocksdb/cache/clock_cache.cc +270 -180
  12. package/deps/rocksdb/rocksdb/cache/clock_cache.h +412 -124
  13. package/deps/rocksdb/rocksdb/cache/fast_lru_cache.cc +1 -0
  14. package/deps/rocksdb/rocksdb/cache/lru_cache.cc +1 -1
  15. package/deps/rocksdb/rocksdb/cache/lru_cache.h +2 -2
  16. package/deps/rocksdb/rocksdb/cache/lru_cache_test.cc +2 -2
  17. package/deps/rocksdb/rocksdb/cache/sharded_cache.h +1 -1
  18. package/deps/rocksdb/rocksdb/db/blob/blob_file_builder.cc +71 -9
  19. package/deps/rocksdb/rocksdb/db/blob/blob_file_builder.h +11 -2
  20. package/deps/rocksdb/rocksdb/db/blob/blob_file_builder_test.cc +21 -14
  21. package/deps/rocksdb/rocksdb/db/blob/blob_source.cc +68 -7
  22. package/deps/rocksdb/rocksdb/db/blob/blob_source.h +16 -0
  23. package/deps/rocksdb/rocksdb/db/blob/blob_source_test.cc +519 -12
  24. package/deps/rocksdb/rocksdb/db/blob/db_blob_basic_test.cc +120 -0
  25. package/deps/rocksdb/rocksdb/db/builder.cc +15 -5
  26. package/deps/rocksdb/rocksdb/db/builder.h +3 -0
  27. package/deps/rocksdb/rocksdb/db/c.cc +18 -0
  28. package/deps/rocksdb/rocksdb/db/c_test.c +18 -0
  29. package/deps/rocksdb/rocksdb/db/column_family.h +2 -0
  30. package/deps/rocksdb/rocksdb/db/compaction/clipping_iterator.h +3 -2
  31. package/deps/rocksdb/rocksdb/db/compaction/compaction.cc +9 -4
  32. package/deps/rocksdb/rocksdb/db/compaction/compaction_iterator.cc +15 -10
  33. package/deps/rocksdb/rocksdb/db/compaction/compaction_iterator.h +36 -34
  34. package/deps/rocksdb/rocksdb/db/compaction/compaction_job.cc +50 -13
  35. package/deps/rocksdb/rocksdb/db/compaction/compaction_job.h +12 -0
  36. package/deps/rocksdb/rocksdb/db/compaction/compaction_outputs.cc +8 -1
  37. package/deps/rocksdb/rocksdb/db/compaction/compaction_outputs.h +2 -1
  38. package/deps/rocksdb/rocksdb/db/compaction/tiered_compaction_test.cc +13 -17
  39. package/deps/rocksdb/rocksdb/db/db_basic_test.cc +26 -9
  40. package/deps/rocksdb/rocksdb/db/db_compaction_test.cc +0 -11
  41. package/deps/rocksdb/rocksdb/db/db_impl/db_impl.cc +93 -0
  42. package/deps/rocksdb/rocksdb/db/db_impl/db_impl.h +16 -1
  43. package/deps/rocksdb/rocksdb/db/db_impl/db_impl_compaction_flush.cc +3 -8
  44. package/deps/rocksdb/rocksdb/db/db_impl/db_impl_debug.cc +8 -1
  45. package/deps/rocksdb/rocksdb/db/db_impl/db_impl_open.cc +17 -5
  46. package/deps/rocksdb/rocksdb/db/db_test.cc +0 -3
  47. package/deps/rocksdb/rocksdb/db/db_test2.cc +39 -12
  48. package/deps/rocksdb/rocksdb/db/db_test_util.cc +9 -0
  49. package/deps/rocksdb/rocksdb/db/db_test_util.h +2 -0
  50. package/deps/rocksdb/rocksdb/db/dbformat.cc +0 -38
  51. package/deps/rocksdb/rocksdb/db/dbformat.h +14 -13
  52. package/deps/rocksdb/rocksdb/db/dbformat_test.cc +5 -2
  53. package/deps/rocksdb/rocksdb/db/event_helpers.cc +13 -1
  54. package/deps/rocksdb/rocksdb/db/external_sst_file_basic_test.cc +0 -10
  55. package/deps/rocksdb/rocksdb/db/flush_job.cc +19 -15
  56. package/deps/rocksdb/rocksdb/db/flush_job.h +7 -0
  57. package/deps/rocksdb/rocksdb/db/flush_job_test.cc +21 -15
  58. package/deps/rocksdb/rocksdb/db/forward_iterator.h +4 -3
  59. package/deps/rocksdb/rocksdb/db/memtable_list.cc +9 -0
  60. package/deps/rocksdb/rocksdb/db/memtable_list.h +5 -0
  61. package/deps/rocksdb/rocksdb/db/periodic_work_scheduler.cc +53 -12
  62. package/deps/rocksdb/rocksdb/db/periodic_work_scheduler.h +14 -2
  63. package/deps/rocksdb/rocksdb/db/periodic_work_scheduler_test.cc +10 -10
  64. package/deps/rocksdb/rocksdb/db/repair.cc +8 -6
  65. package/deps/rocksdb/rocksdb/db/seqno_time_test.cc +890 -0
  66. package/deps/rocksdb/rocksdb/db/seqno_to_time_mapping.cc +324 -0
  67. package/deps/rocksdb/rocksdb/db/seqno_to_time_mapping.h +186 -0
  68. package/deps/rocksdb/rocksdb/db_stress_tool/db_stress_common.h +2 -0
  69. package/deps/rocksdb/rocksdb/db_stress_tool/db_stress_gflags.cc +13 -4
  70. package/deps/rocksdb/rocksdb/db_stress_tool/db_stress_test_base.cc +23 -2
  71. package/deps/rocksdb/rocksdb/env/env_test.cc +74 -1
  72. package/deps/rocksdb/rocksdb/env/io_posix.cc +11 -8
  73. package/deps/rocksdb/rocksdb/include/rocksdb/advanced_options.h +28 -0
  74. package/deps/rocksdb/rocksdb/include/rocksdb/c.h +14 -1
  75. package/deps/rocksdb/rocksdb/include/rocksdb/cache.h +4 -4
  76. package/deps/rocksdb/rocksdb/include/rocksdb/comparator.h +30 -23
  77. package/deps/rocksdb/rocksdb/include/rocksdb/db.h +1 -1
  78. package/deps/rocksdb/rocksdb/include/rocksdb/rate_limiter.h +3 -13
  79. package/deps/rocksdb/rocksdb/include/rocksdb/table_properties.h +5 -0
  80. package/deps/rocksdb/rocksdb/include/rocksdb/utilities/debug.h +1 -2
  81. package/deps/rocksdb/rocksdb/include/rocksdb/utilities/ldb_cmd.h +1 -0
  82. package/deps/rocksdb/rocksdb/include/rocksdb/version.h +1 -1
  83. package/deps/rocksdb/rocksdb/monitoring/stats_history_test.cc +26 -26
  84. package/deps/rocksdb/rocksdb/options/cf_options.cc +14 -1
  85. package/deps/rocksdb/rocksdb/options/cf_options.h +5 -0
  86. package/deps/rocksdb/rocksdb/options/customizable_test.cc +0 -56
  87. package/deps/rocksdb/rocksdb/options/db_options.cc +4 -5
  88. package/deps/rocksdb/rocksdb/options/options.cc +11 -1
  89. package/deps/rocksdb/rocksdb/options/options_helper.cc +8 -0
  90. package/deps/rocksdb/rocksdb/options/options_helper.h +4 -0
  91. package/deps/rocksdb/rocksdb/options/options_settable_test.cc +4 -0
  92. package/deps/rocksdb/rocksdb/options/options_test.cc +4 -0
  93. package/deps/rocksdb/rocksdb/src.mk +3 -0
  94. package/deps/rocksdb/rocksdb/table/block_based/block_based_table_builder.cc +6 -1
  95. package/deps/rocksdb/rocksdb/table/block_based/block_based_table_builder.h +4 -0
  96. package/deps/rocksdb/rocksdb/table/block_based/block_based_table_factory.cc +36 -3
  97. package/deps/rocksdb/rocksdb/table/block_based/index_builder.cc +36 -1
  98. package/deps/rocksdb/rocksdb/table/block_based/index_builder.h +14 -3
  99. package/deps/rocksdb/rocksdb/table/internal_iterator.h +1 -1
  100. package/deps/rocksdb/rocksdb/table/meta_blocks.cc +6 -0
  101. package/deps/rocksdb/rocksdb/table/plain/plain_table_builder.cc +5 -0
  102. package/deps/rocksdb/rocksdb/table/plain/plain_table_builder.h +3 -0
  103. package/deps/rocksdb/rocksdb/table/sst_file_writer.cc +10 -7
  104. package/deps/rocksdb/rocksdb/table/table_builder.h +7 -3
  105. package/deps/rocksdb/rocksdb/table/table_properties.cc +9 -0
  106. package/deps/rocksdb/rocksdb/test_util/mock_time_env.h +3 -2
  107. package/deps/rocksdb/rocksdb/tools/db_bench_tool.cc +58 -30
  108. package/deps/rocksdb/rocksdb/tools/db_bench_tool_test.cc +1 -0
  109. package/deps/rocksdb/rocksdb/tools/ldb_cmd.cc +20 -0
  110. package/deps/rocksdb/rocksdb/util/rate_limiter.cc +29 -154
  111. package/deps/rocksdb/rocksdb/util/rate_limiter.h +16 -34
  112. package/deps/rocksdb/rocksdb/util/rate_limiter_test.cc +0 -92
  113. package/deps/rocksdb/rocksdb/util/timer.h +6 -0
  114. package/deps/rocksdb/rocksdb/util/vector_iterator.h +4 -3
  115. package/deps/rocksdb/rocksdb/utilities/backup/backup_engine.cc +4 -45
  116. package/deps/rocksdb/rocksdb/utilities/debug.cc +40 -0
  117. package/deps/rocksdb/rocksdb.gyp +2 -0
  118. package/package.json +1 -1
  119. package/prebuilds/darwin-arm64/node.napi.node +0 -0
  120. package/{deps/rocksdb/rocksdb/prebuilds → prebuilds}/linux-x64/node.napi.node +0 -0
@@ -0,0 +1,890 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ //
3
+ // This source code is licensed under both the GPLv2 (found in the
4
+ // COPYING file in the root directory) and Apache 2.0 License
5
+ // (found in the LICENSE.Apache file in the root directory).
6
+
7
+ #include "db/db_test_util.h"
8
+ #include "db/periodic_work_scheduler.h"
9
+ #include "db/seqno_to_time_mapping.h"
10
+ #include "port/stack_trace.h"
11
+ #include "rocksdb/iostats_context.h"
12
+ #include "test_util/mock_time_env.h"
13
+
14
+ #ifndef ROCKSDB_LITE
15
+
16
+ namespace ROCKSDB_NAMESPACE {
17
+
18
+ class SeqnoTimeTest : public DBTestBase {
19
+ public:
20
+ SeqnoTimeTest() : DBTestBase("seqno_time_test", /*env_do_fsync=*/false) {
21
+ mock_clock_ = std::make_shared<MockSystemClock>(env_->GetSystemClock());
22
+ mock_env_ = std::make_unique<CompositeEnvWrapper>(env_, mock_clock_);
23
+ }
24
+
25
+ protected:
26
+ std::unique_ptr<Env> mock_env_;
27
+ std::shared_ptr<MockSystemClock> mock_clock_;
28
+
29
+ void SetUp() override {
30
+ mock_clock_->InstallTimedWaitFixCallback();
31
+ SyncPoint::GetInstance()->SetCallBack(
32
+ "DBImpl::StartPeriodicWorkScheduler:Init", [&](void* arg) {
33
+ auto* periodic_work_scheduler_ptr =
34
+ reinterpret_cast<PeriodicWorkScheduler**>(arg);
35
+ *periodic_work_scheduler_ptr =
36
+ PeriodicWorkTestScheduler::Default(mock_clock_);
37
+ });
38
+ }
39
+
40
+ // make sure the file is not in cache, otherwise it won't have IO info
41
+ void AssertKetTemperature(int key_id, Temperature expected_temperature) {
42
+ get_iostats_context()->Reset();
43
+ IOStatsContext* iostats = get_iostats_context();
44
+ std::string result = Get(Key(key_id));
45
+ ASSERT_FALSE(result.empty());
46
+ ASSERT_GT(iostats->bytes_read, 0);
47
+ switch (expected_temperature) {
48
+ case Temperature::kUnknown:
49
+ ASSERT_EQ(iostats->file_io_stats_by_temperature.cold_file_read_count,
50
+ 0);
51
+ ASSERT_EQ(iostats->file_io_stats_by_temperature.cold_file_bytes_read,
52
+ 0);
53
+ break;
54
+ case Temperature::kCold:
55
+ ASSERT_GT(iostats->file_io_stats_by_temperature.cold_file_read_count,
56
+ 0);
57
+ ASSERT_GT(iostats->file_io_stats_by_temperature.cold_file_bytes_read,
58
+ 0);
59
+ break;
60
+ default:
61
+ // the test only support kCold now for the bottommost temperature
62
+ FAIL();
63
+ }
64
+ }
65
+ };
66
+
67
+ TEST_F(SeqnoTimeTest, TemperatureBasicUniversal) {
68
+ const int kNumTrigger = 4;
69
+ const int kNumLevels = 7;
70
+ const int kNumKeys = 100;
71
+ const int kKeyPerSec = 10;
72
+
73
+ Options options = CurrentOptions();
74
+ options.compaction_style = kCompactionStyleUniversal;
75
+ options.preclude_last_level_data_seconds = 10000;
76
+ options.env = mock_env_.get();
77
+ options.bottommost_temperature = Temperature::kCold;
78
+ options.num_levels = kNumLevels;
79
+ DestroyAndReopen(options);
80
+
81
+ // pass some time first, otherwise the first a few keys write time are going
82
+ // to be zero, and internally zero has special meaning: kUnknownSeqnoTime
83
+ dbfull()->TEST_WaitForPeridicWorkerRun(
84
+ [&] { mock_clock_->MockSleepForSeconds(static_cast<int>(kKeyPerSec)); });
85
+
86
+ int sst_num = 0;
87
+ // Write files that are overlap and enough to trigger compaction
88
+ for (; sst_num < kNumTrigger; sst_num++) {
89
+ for (int i = 0; i < kNumKeys; i++) {
90
+ ASSERT_OK(Put(Key(sst_num * (kNumKeys - 1) + i), "value"));
91
+ dbfull()->TEST_WaitForPeridicWorkerRun([&] {
92
+ mock_clock_->MockSleepForSeconds(static_cast<int>(kKeyPerSec));
93
+ });
94
+ }
95
+ ASSERT_OK(Flush());
96
+ }
97
+ ASSERT_OK(dbfull()->WaitForCompact(true));
98
+
99
+ // All data is hot, only output to penultimate level
100
+ ASSERT_EQ("0,0,0,0,0,1", FilesPerLevel());
101
+ ASSERT_GT(GetSstSizeHelper(Temperature::kUnknown), 0);
102
+ ASSERT_EQ(GetSstSizeHelper(Temperature::kCold), 0);
103
+
104
+ // read a random key, which should be hot (kUnknown)
105
+ AssertKetTemperature(20, Temperature::kUnknown);
106
+
107
+ // Write more data, but still all hot until the 10th SST, as:
108
+ // write a key every 10 seconds, 100 keys per SST, each SST takes 1000 seconds
109
+ // The preclude_last_level_data_seconds is 10k
110
+ for (; sst_num < kNumTrigger * 2; sst_num++) {
111
+ for (int i = 0; i < kNumKeys; i++) {
112
+ ASSERT_OK(Put(Key(sst_num * (kNumKeys - 1) + i), "value"));
113
+ dbfull()->TEST_WaitForPeridicWorkerRun([&] {
114
+ mock_clock_->MockSleepForSeconds(static_cast<int>(kKeyPerSec));
115
+ });
116
+ }
117
+ ASSERT_OK(Flush());
118
+ ASSERT_OK(dbfull()->WaitForCompact(true));
119
+ ASSERT_GT(GetSstSizeHelper(Temperature::kUnknown), 0);
120
+ ASSERT_EQ(GetSstSizeHelper(Temperature::kCold), 0);
121
+ }
122
+
123
+ // Now we have both hot data and cold data
124
+ for (; sst_num < kNumTrigger * 3; sst_num++) {
125
+ for (int i = 0; i < kNumKeys; i++) {
126
+ ASSERT_OK(Put(Key(sst_num * (kNumKeys - 1) + i), "value"));
127
+ dbfull()->TEST_WaitForPeridicWorkerRun([&] {
128
+ mock_clock_->MockSleepForSeconds(static_cast<int>(kKeyPerSec));
129
+ });
130
+ }
131
+ ASSERT_OK(Flush());
132
+ ASSERT_OK(dbfull()->WaitForCompact(true));
133
+ }
134
+
135
+ CompactRangeOptions cro;
136
+ cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
137
+ ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
138
+ uint64_t hot_data_size = GetSstSizeHelper(Temperature::kUnknown);
139
+ uint64_t cold_data_size = GetSstSizeHelper(Temperature::kCold);
140
+ ASSERT_GT(hot_data_size, 0);
141
+ ASSERT_GT(cold_data_size, 0);
142
+ // the first a few key should be cold
143
+ AssertKetTemperature(20, Temperature::kCold);
144
+
145
+ // Wait some time, each time after compaction, the cold data size is
146
+ // increasing and hot data size is decreasing
147
+ for (int i = 0; i < 30; i++) {
148
+ dbfull()->TEST_WaitForPeridicWorkerRun([&] {
149
+ mock_clock_->MockSleepForSeconds(static_cast<int>(20 * kKeyPerSec));
150
+ });
151
+ ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
152
+ uint64_t pre_hot = hot_data_size;
153
+ uint64_t pre_cold = cold_data_size;
154
+ hot_data_size = GetSstSizeHelper(Temperature::kUnknown);
155
+ cold_data_size = GetSstSizeHelper(Temperature::kCold);
156
+ ASSERT_LT(hot_data_size, pre_hot);
157
+ ASSERT_GT(cold_data_size, pre_cold);
158
+
159
+ // the hot/cold data cut off range should be between i * 20 + 200 -> 250
160
+ AssertKetTemperature(i * 20 + 250, Temperature::kUnknown);
161
+ AssertKetTemperature(i * 20 + 200, Temperature::kCold);
162
+ }
163
+
164
+ // Wait again, all data should be cold after that
165
+ for (int i = 0; i < 5; i++) {
166
+ dbfull()->TEST_WaitForPeridicWorkerRun(
167
+ [&] { mock_clock_->MockSleepForSeconds(static_cast<int>(1000)); });
168
+ ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
169
+ }
170
+
171
+ ASSERT_EQ(GetSstSizeHelper(Temperature::kUnknown), 0);
172
+ ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
173
+
174
+ // any random data should be cold
175
+ AssertKetTemperature(1000, Temperature::kCold);
176
+
177
+ // close explicitly, because the env is local variable which will be released
178
+ // first.
179
+ Close();
180
+ }
181
+
182
+ TEST_F(SeqnoTimeTest, TemperatureBasicLevel) {
183
+ const int kNumLevels = 7;
184
+ const int kNumKeys = 100;
185
+
186
+ Options options = CurrentOptions();
187
+ options.preclude_last_level_data_seconds = 10000;
188
+ options.env = mock_env_.get();
189
+ options.bottommost_temperature = Temperature::kCold;
190
+ options.num_levels = kNumLevels;
191
+ options.level_compaction_dynamic_level_bytes = true;
192
+ // TODO(zjay): for level compaction, auto-compaction may stuck in deadloop, if
193
+ // the penultimate level score > 1, but the hot is not cold enough to compact
194
+ // to last level, which will keep triggering compaction.
195
+ options.disable_auto_compactions = true;
196
+ DestroyAndReopen(options);
197
+
198
+ // pass some time first, otherwise the first a few keys write time are going
199
+ // to be zero, and internally zero has special meaning: kUnknownSeqnoTime
200
+ dbfull()->TEST_WaitForPeridicWorkerRun(
201
+ [&] { mock_clock_->MockSleepForSeconds(static_cast<int>(10)); });
202
+
203
+ int sst_num = 0;
204
+ // Write files that are overlap
205
+ for (; sst_num < 4; sst_num++) {
206
+ for (int i = 0; i < kNumKeys; i++) {
207
+ ASSERT_OK(Put(Key(sst_num * (kNumKeys - 1) + i), "value"));
208
+ dbfull()->TEST_WaitForPeridicWorkerRun(
209
+ [&] { mock_clock_->MockSleepForSeconds(static_cast<int>(10)); });
210
+ }
211
+ ASSERT_OK(Flush());
212
+ }
213
+
214
+ CompactRangeOptions cro;
215
+ cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
216
+ ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
217
+
218
+ // All data is hot, only output to penultimate level
219
+ ASSERT_EQ("0,0,0,0,0,1", FilesPerLevel());
220
+ ASSERT_GT(GetSstSizeHelper(Temperature::kUnknown), 0);
221
+ ASSERT_EQ(GetSstSizeHelper(Temperature::kCold), 0);
222
+
223
+ // read a random key, which should be hot (kUnknown)
224
+ AssertKetTemperature(20, Temperature::kUnknown);
225
+
226
+ // Adding more data to have mixed hot and cold data
227
+ for (; sst_num < 14; sst_num++) {
228
+ for (int i = 0; i < kNumKeys; i++) {
229
+ ASSERT_OK(Put(Key(sst_num * (kNumKeys - 1) + i), "value"));
230
+ dbfull()->TEST_WaitForPeridicWorkerRun(
231
+ [&] { mock_clock_->MockSleepForSeconds(static_cast<int>(10)); });
232
+ }
233
+ ASSERT_OK(Flush());
234
+ }
235
+ // TODO(zjay): all data become cold because of level 5 (penultimate level) is
236
+ // the bottommost level, which converts the data to cold. PerKeyPlacement is
237
+ // for the last level (level 6). Will be fixed by change the
238
+ // bottommost_temperature to the last_level_temperature
239
+ ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
240
+ ASSERT_EQ(GetSstSizeHelper(Temperature::kUnknown), 0);
241
+ ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
242
+
243
+ // Compact the files to the last level which should split the hot/cold data
244
+ MoveFilesToLevel(6);
245
+ uint64_t hot_data_size = GetSstSizeHelper(Temperature::kUnknown);
246
+ uint64_t cold_data_size = GetSstSizeHelper(Temperature::kCold);
247
+ ASSERT_GT(hot_data_size, 0);
248
+ ASSERT_GT(cold_data_size, 0);
249
+ // the first a few key should be cold
250
+ AssertKetTemperature(20, Temperature::kCold);
251
+
252
+ // Wait some time, each it wait, the cold data is increasing and hot data is
253
+ // decreasing
254
+ for (int i = 0; i < 30; i++) {
255
+ dbfull()->TEST_WaitForPeridicWorkerRun(
256
+ [&] { mock_clock_->MockSleepForSeconds(static_cast<int>(200)); });
257
+ ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
258
+ uint64_t pre_hot = hot_data_size;
259
+ uint64_t pre_cold = cold_data_size;
260
+ hot_data_size = GetSstSizeHelper(Temperature::kUnknown);
261
+ cold_data_size = GetSstSizeHelper(Temperature::kCold);
262
+ ASSERT_LT(hot_data_size, pre_hot);
263
+ ASSERT_GT(cold_data_size, pre_cold);
264
+
265
+ // the hot/cold cut_off key should be around i * 20 + 400 -> 450
266
+ AssertKetTemperature(i * 20 + 450, Temperature::kUnknown);
267
+ AssertKetTemperature(i * 20 + 400, Temperature::kCold);
268
+ }
269
+
270
+ // Wait again, all data should be cold after that
271
+ for (int i = 0; i < 5; i++) {
272
+ dbfull()->TEST_WaitForPeridicWorkerRun(
273
+ [&] { mock_clock_->MockSleepForSeconds(static_cast<int>(1000)); });
274
+ ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
275
+ }
276
+
277
+ ASSERT_EQ(GetSstSizeHelper(Temperature::kUnknown), 0);
278
+ ASSERT_GT(GetSstSizeHelper(Temperature::kCold), 0);
279
+
280
+ // any random data should be cold
281
+ AssertKetTemperature(1000, Temperature::kCold);
282
+
283
+ Close();
284
+ }
285
+
286
+ TEST_F(SeqnoTimeTest, BasicSeqnoToTimeMapping) {
287
+ Options options = CurrentOptions();
288
+ options.preclude_last_level_data_seconds = 10000;
289
+ options.env = mock_env_.get();
290
+ options.disable_auto_compactions = true;
291
+ DestroyAndReopen(options);
292
+
293
+ std::set<uint64_t> checked_file_nums;
294
+ SequenceNumber start_seq = dbfull()->GetLatestSequenceNumber();
295
+ // Write a key every 10 seconds
296
+ for (int i = 0; i < 200; i++) {
297
+ ASSERT_OK(Put(Key(i), "value"));
298
+ dbfull()->TEST_WaitForPeridicWorkerRun(
299
+ [&] { mock_clock_->MockSleepForSeconds(static_cast<int>(10)); });
300
+ }
301
+ ASSERT_OK(Flush());
302
+ TablePropertiesCollection tables_props;
303
+ ASSERT_OK(dbfull()->GetPropertiesOfAllTables(&tables_props));
304
+ ASSERT_EQ(tables_props.size(), 1);
305
+ auto it = tables_props.begin();
306
+ SeqnoToTimeMapping tp_mapping;
307
+ ASSERT_OK(tp_mapping.Add(it->second->seqno_to_time_mapping));
308
+ ASSERT_OK(tp_mapping.Sort());
309
+ ASSERT_FALSE(tp_mapping.Empty());
310
+ auto seqs = tp_mapping.TEST_GetInternalMapping();
311
+ ASSERT_GE(seqs.size(), 19);
312
+ ASSERT_LE(seqs.size(), 21);
313
+ SequenceNumber seq_end = dbfull()->GetLatestSequenceNumber();
314
+ for (auto i = start_seq; i < start_seq + 10; i++) {
315
+ ASSERT_LE(tp_mapping.GetOldestApproximateTime(i), (i + 1) * 10);
316
+ }
317
+ start_seq += 10;
318
+ for (auto i = start_seq; i < seq_end; i++) {
319
+ // The result is within the range
320
+ ASSERT_GE(tp_mapping.GetOldestApproximateTime(i), (i - 10) * 10);
321
+ ASSERT_LE(tp_mapping.GetOldestApproximateTime(i), (i + 10) * 10);
322
+ }
323
+ checked_file_nums.insert(it->second->orig_file_number);
324
+ start_seq = seq_end;
325
+
326
+ // Write a key every 1 seconds
327
+ for (int i = 0; i < 200; i++) {
328
+ ASSERT_OK(Put(Key(i + 190), "value"));
329
+ dbfull()->TEST_WaitForPeridicWorkerRun(
330
+ [&] { mock_clock_->MockSleepForSeconds(static_cast<int>(1)); });
331
+ }
332
+ seq_end = dbfull()->GetLatestSequenceNumber();
333
+ ASSERT_OK(Flush());
334
+ tables_props.clear();
335
+ ASSERT_OK(dbfull()->GetPropertiesOfAllTables(&tables_props));
336
+ ASSERT_EQ(tables_props.size(), 2);
337
+ it = tables_props.begin();
338
+ while (it != tables_props.end()) {
339
+ if (!checked_file_nums.count(it->second->orig_file_number)) {
340
+ break;
341
+ }
342
+ it++;
343
+ }
344
+ ASSERT_TRUE(it != tables_props.end());
345
+
346
+ tp_mapping.Clear();
347
+ ASSERT_OK(tp_mapping.Add(it->second->seqno_to_time_mapping));
348
+ ASSERT_OK(tp_mapping.Sort());
349
+ seqs = tp_mapping.TEST_GetInternalMapping();
350
+ // There only a few time sample
351
+ ASSERT_GE(seqs.size(), 1);
352
+ ASSERT_LE(seqs.size(), 3);
353
+ for (auto i = start_seq; i < seq_end; i++) {
354
+ // The result is not very accurate, as there is more data write within small
355
+ // range of time
356
+ ASSERT_GE(tp_mapping.GetOldestApproximateTime(i), (i - start_seq) + 1000);
357
+ ASSERT_LE(tp_mapping.GetOldestApproximateTime(i), (i - start_seq) + 3000);
358
+ }
359
+ checked_file_nums.insert(it->second->orig_file_number);
360
+ start_seq = seq_end;
361
+
362
+ // Write a key every 200 seconds
363
+ for (int i = 0; i < 200; i++) {
364
+ ASSERT_OK(Put(Key(i + 380), "value"));
365
+ dbfull()->TEST_WaitForPeridicWorkerRun(
366
+ [&] { mock_clock_->MockSleepForSeconds(static_cast<int>(200)); });
367
+ }
368
+ seq_end = dbfull()->GetLatestSequenceNumber();
369
+ ASSERT_OK(Flush());
370
+ tables_props.clear();
371
+ ASSERT_OK(dbfull()->GetPropertiesOfAllTables(&tables_props));
372
+ ASSERT_EQ(tables_props.size(), 3);
373
+ it = tables_props.begin();
374
+ while (it != tables_props.end()) {
375
+ if (!checked_file_nums.count(it->second->orig_file_number)) {
376
+ break;
377
+ }
378
+ it++;
379
+ }
380
+ ASSERT_TRUE(it != tables_props.end());
381
+
382
+ tp_mapping.Clear();
383
+ ASSERT_OK(tp_mapping.Add(it->second->seqno_to_time_mapping));
384
+ ASSERT_OK(tp_mapping.Sort());
385
+ seqs = tp_mapping.TEST_GetInternalMapping();
386
+ // The sequence number -> time entries should be maxed
387
+ ASSERT_GE(seqs.size(), 99);
388
+ ASSERT_LE(seqs.size(), 101);
389
+ for (auto i = start_seq; i < seq_end - 99; i++) {
390
+ // likely the first 100 entries reports 0
391
+ ASSERT_LE(tp_mapping.GetOldestApproximateTime(i), (i - start_seq) + 3000);
392
+ }
393
+ start_seq += 101;
394
+
395
+ for (auto i = start_seq; i < seq_end; i++) {
396
+ ASSERT_GE(tp_mapping.GetOldestApproximateTime(i),
397
+ (i - start_seq) * 200 + 22200);
398
+ ASSERT_LE(tp_mapping.GetOldestApproximateTime(i),
399
+ (i - start_seq) * 200 + 22600);
400
+ }
401
+ checked_file_nums.insert(it->second->orig_file_number);
402
+ start_seq = seq_end;
403
+
404
+ // Write a key every 100 seconds
405
+ for (int i = 0; i < 200; i++) {
406
+ ASSERT_OK(Put(Key(i + 570), "value"));
407
+ dbfull()->TEST_WaitForPeridicWorkerRun(
408
+ [&] { mock_clock_->MockSleepForSeconds(static_cast<int>(100)); });
409
+ }
410
+ seq_end = dbfull()->GetLatestSequenceNumber();
411
+ ASSERT_OK(Flush());
412
+ tables_props.clear();
413
+ ASSERT_OK(dbfull()->GetPropertiesOfAllTables(&tables_props));
414
+ ASSERT_EQ(tables_props.size(), 4);
415
+ it = tables_props.begin();
416
+ while (it != tables_props.end()) {
417
+ if (!checked_file_nums.count(it->second->orig_file_number)) {
418
+ break;
419
+ }
420
+ it++;
421
+ }
422
+ ASSERT_TRUE(it != tables_props.end());
423
+ tp_mapping.Clear();
424
+ ASSERT_OK(tp_mapping.Add(it->second->seqno_to_time_mapping));
425
+ ASSERT_OK(tp_mapping.Sort());
426
+ seqs = tp_mapping.TEST_GetInternalMapping();
427
+ ASSERT_GE(seqs.size(), 99);
428
+ ASSERT_LE(seqs.size(), 101);
429
+
430
+ checked_file_nums.insert(it->second->orig_file_number);
431
+
432
+ // re-enable compaction
433
+ ASSERT_OK(dbfull()->SetOptions({
434
+ {"disable_auto_compactions", "false"},
435
+ }));
436
+
437
+ ASSERT_OK(dbfull()->TEST_WaitForCompact());
438
+
439
+ tables_props.clear();
440
+ ASSERT_OK(dbfull()->GetPropertiesOfAllTables(&tables_props));
441
+ ASSERT_GE(tables_props.size(), 1);
442
+ it = tables_props.begin();
443
+ while (it != tables_props.end()) {
444
+ if (!checked_file_nums.count(it->second->orig_file_number)) {
445
+ break;
446
+ }
447
+ it++;
448
+ }
449
+ ASSERT_TRUE(it != tables_props.end());
450
+ tp_mapping.Clear();
451
+ ASSERT_OK(tp_mapping.Add(it->second->seqno_to_time_mapping));
452
+ ASSERT_OK(tp_mapping.Sort());
453
+ seqs = tp_mapping.TEST_GetInternalMapping();
454
+ ASSERT_GE(seqs.size(), 99);
455
+ ASSERT_LE(seqs.size(), 101);
456
+ for (auto i = start_seq; i < seq_end - 99; i++) {
457
+ // likely the first 100 entries reports 0
458
+ ASSERT_LE(tp_mapping.GetOldestApproximateTime(i), (i - start_seq) + 3000);
459
+ }
460
+ start_seq += 101;
461
+
462
+ for (auto i = start_seq; i < seq_end; i++) {
463
+ ASSERT_GE(tp_mapping.GetOldestApproximateTime(i),
464
+ (i - start_seq) * 100 + 52200);
465
+ ASSERT_LE(tp_mapping.GetOldestApproximateTime(i),
466
+ (i - start_seq) * 100 + 52400);
467
+ }
468
+ ASSERT_OK(db_->Close());
469
+ }
470
+
471
+ // TODO(zjay): Disabled, until New CF bug with preclude_last_level_data_seconds
472
+ // is fixed
473
+ TEST_F(SeqnoTimeTest, DISABLED_MultiCFs) {
474
+ Options options = CurrentOptions();
475
+ options.preclude_last_level_data_seconds = 0;
476
+ options.env = mock_env_.get();
477
+ options.stats_dump_period_sec = 0;
478
+ options.stats_persist_period_sec = 0;
479
+ ReopenWithColumnFamilies({"default"}, options);
480
+
481
+ auto scheduler = dbfull()->TEST_GetPeriodicWorkScheduler();
482
+ ASSERT_FALSE(scheduler->TEST_HasValidTask(
483
+ dbfull(), PeriodicWorkTaskNames::kRecordSeqnoTime));
484
+
485
+ // Write some data and increase the current time
486
+ for (int i = 0; i < 200; i++) {
487
+ ASSERT_OK(Put(Key(i), "value"));
488
+ dbfull()->TEST_WaitForPeridicWorkerRun(
489
+ [&] { mock_clock_->MockSleepForSeconds(static_cast<int>(100)); });
490
+ }
491
+ ASSERT_OK(Flush());
492
+ TablePropertiesCollection tables_props;
493
+ ASSERT_OK(dbfull()->GetPropertiesOfAllTables(&tables_props));
494
+ ASSERT_EQ(tables_props.size(), 1);
495
+ auto it = tables_props.begin();
496
+ ASSERT_TRUE(it->second->seqno_to_time_mapping.empty());
497
+
498
+ ASSERT_TRUE(dbfull()->TEST_GetSeqnoToTimeMapping().Empty());
499
+
500
+ Options options_1 = options;
501
+ options_1.preclude_last_level_data_seconds = 10000; // 10k
502
+ CreateColumnFamilies({"one"}, options_1);
503
+ ASSERT_TRUE(scheduler->TEST_HasValidTask(
504
+ dbfull(), PeriodicWorkTaskNames::kRecordSeqnoTime));
505
+
506
+ // Write some data to the default CF (without preclude_last_level feature)
507
+ for (int i = 0; i < 200; i++) {
508
+ ASSERT_OK(Put(Key(i), "value"));
509
+ dbfull()->TEST_WaitForPeridicWorkerRun(
510
+ [&] { mock_clock_->MockSleepForSeconds(static_cast<int>(100)); });
511
+ }
512
+ ASSERT_OK(Flush());
513
+
514
+ // in memory mapping won't increase because CFs with preclude_last_level
515
+ // feature doesn't have memtable
516
+ auto queue = dbfull()->TEST_GetSeqnoToTimeMapping().TEST_GetInternalMapping();
517
+ ASSERT_LT(queue.size(), 5);
518
+
519
+ // Write some data to the CF one
520
+ for (int i = 0; i < 20; i++) {
521
+ ASSERT_OK(Put(1, Key(i), "value"));
522
+ dbfull()->TEST_WaitForPeridicWorkerRun(
523
+ [&] { mock_clock_->MockSleepForSeconds(static_cast<int>(10)); });
524
+ }
525
+ ASSERT_OK(Flush(1));
526
+ tables_props.clear();
527
+ ASSERT_OK(dbfull()->GetPropertiesOfAllTables(handles_[1], &tables_props));
528
+ ASSERT_EQ(tables_props.size(), 1);
529
+ it = tables_props.begin();
530
+ SeqnoToTimeMapping tp_mapping;
531
+ ASSERT_OK(tp_mapping.Add(it->second->seqno_to_time_mapping));
532
+ ASSERT_OK(tp_mapping.Sort());
533
+ ASSERT_FALSE(tp_mapping.Empty());
534
+ auto seqs = tp_mapping.TEST_GetInternalMapping();
535
+ ASSERT_GE(seqs.size(), 1);
536
+ ASSERT_LE(seqs.size(), 3);
537
+
538
+ // Create one more CF with larger preclude_last_level time
539
+ Options options_2 = options;
540
+ options_2.preclude_last_level_data_seconds = 1000000; // 1m
541
+ CreateColumnFamilies({"two"}, options_2);
542
+
543
+ // Add more data to CF "two" to fill the in memory mapping
544
+ for (int i = 0; i < 2000; i++) {
545
+ ASSERT_OK(Put(2, Key(i), "value"));
546
+ dbfull()->TEST_WaitForPeridicWorkerRun(
547
+ [&] { mock_clock_->MockSleepForSeconds(static_cast<int>(100)); });
548
+ }
549
+ seqs = dbfull()->TEST_GetSeqnoToTimeMapping().TEST_GetInternalMapping();
550
+ ASSERT_GE(seqs.size(), 1000 - 1);
551
+ ASSERT_LE(seqs.size(), 1000 + 1);
552
+
553
+ ASSERT_OK(Flush(2));
554
+ tables_props.clear();
555
+ ASSERT_OK(dbfull()->GetPropertiesOfAllTables(handles_[2], &tables_props));
556
+ ASSERT_EQ(tables_props.size(), 1);
557
+ it = tables_props.begin();
558
+ tp_mapping.Clear();
559
+ ASSERT_OK(tp_mapping.Add(it->second->seqno_to_time_mapping));
560
+ ASSERT_OK(tp_mapping.Sort());
561
+ seqs = tp_mapping.TEST_GetInternalMapping();
562
+ // the max encoded entries is 100
563
+ ASSERT_GE(seqs.size(), 100 - 1);
564
+ ASSERT_LE(seqs.size(), 100 + 1);
565
+
566
+ // Write some data to default CF, as all memtable with preclude_last_level
567
+ // enabled have flushed, the in-memory seqno->time mapping should be cleared
568
+ for (int i = 0; i < 10; i++) {
569
+ ASSERT_OK(Put(0, Key(i), "value"));
570
+ dbfull()->TEST_WaitForPeridicWorkerRun(
571
+ [&] { mock_clock_->MockSleepForSeconds(static_cast<int>(100)); });
572
+ }
573
+ seqs = dbfull()->TEST_GetSeqnoToTimeMapping().TEST_GetInternalMapping();
574
+ ASSERT_LE(seqs.size(), 5);
575
+ ASSERT_OK(Flush(0));
576
+
577
+ // trigger compaction for CF "two" and make sure the compaction output has
578
+ // seqno_to_time_mapping
579
+ for (int j = 0; j < 3; j++) {
580
+ for (int i = 0; i < 200; i++) {
581
+ ASSERT_OK(Put(2, Key(i), "value"));
582
+ dbfull()->TEST_WaitForPeridicWorkerRun(
583
+ [&] { mock_clock_->MockSleepForSeconds(static_cast<int>(100)); });
584
+ }
585
+ ASSERT_OK(Flush(2));
586
+ }
587
+ ASSERT_OK(dbfull()->TEST_WaitForCompact());
588
+ tables_props.clear();
589
+ ASSERT_OK(dbfull()->GetPropertiesOfAllTables(handles_[2], &tables_props));
590
+ ASSERT_EQ(tables_props.size(), 1);
591
+ it = tables_props.begin();
592
+ tp_mapping.Clear();
593
+ ASSERT_OK(tp_mapping.Add(it->second->seqno_to_time_mapping));
594
+ ASSERT_OK(tp_mapping.Sort());
595
+ seqs = tp_mapping.TEST_GetInternalMapping();
596
+ ASSERT_GE(seqs.size(), 99);
597
+ ASSERT_LE(seqs.size(), 101);
598
+
599
+ for (int j = 0; j < 2; j++) {
600
+ for (int i = 0; i < 200; i++) {
601
+ ASSERT_OK(Put(0, Key(i), "value"));
602
+ dbfull()->TEST_WaitForPeridicWorkerRun(
603
+ [&] { mock_clock_->MockSleepForSeconds(static_cast<int>(100)); });
604
+ }
605
+ ASSERT_OK(Flush(0));
606
+ }
607
+ ASSERT_OK(dbfull()->TEST_WaitForCompact());
608
+ tables_props.clear();
609
+ ASSERT_OK(dbfull()->GetPropertiesOfAllTables(handles_[0], &tables_props));
610
+ ASSERT_EQ(tables_props.size(), 1);
611
+ it = tables_props.begin();
612
+ ASSERT_TRUE(it->second->seqno_to_time_mapping.empty());
613
+
614
+ // Write some data to CF "two", but don't flush to accumulate
615
+ for (int i = 0; i < 1000; i++) {
616
+ ASSERT_OK(Put(2, Key(i), "value"));
617
+ dbfull()->TEST_WaitForPeridicWorkerRun(
618
+ [&] { mock_clock_->MockSleepForSeconds(static_cast<int>(100)); });
619
+ }
620
+ ASSERT_GE(
621
+ dbfull()->TEST_GetSeqnoToTimeMapping().TEST_GetInternalMapping().size(),
622
+ 500);
623
+ // After dropping CF "one", the in-memory mapping will be change to only
624
+ // follow CF "two" options.
625
+ ASSERT_OK(db_->DropColumnFamily(handles_[1]));
626
+ ASSERT_LE(
627
+ dbfull()->TEST_GetSeqnoToTimeMapping().TEST_GetInternalMapping().size(),
628
+ 100 + 5);
629
+
630
+ // After dropping CF "two", the in-memory mapping is also clear.
631
+ ASSERT_OK(db_->DropColumnFamily(handles_[2]));
632
+ ASSERT_EQ(
633
+ dbfull()->TEST_GetSeqnoToTimeMapping().TEST_GetInternalMapping().size(),
634
+ 0);
635
+
636
+ // And the timer worker is stopped
637
+ ASSERT_FALSE(scheduler->TEST_HasValidTask(
638
+ dbfull(), PeriodicWorkTaskNames::kRecordSeqnoTime));
639
+ Close();
640
+ }
641
+
642
+ TEST_F(SeqnoTimeTest, MultiInstancesBasic) {
643
+ const int kInstanceNum = 2;
644
+
645
+ Options options = CurrentOptions();
646
+ options.preclude_last_level_data_seconds = 10000;
647
+ options.env = mock_env_.get();
648
+ options.stats_dump_period_sec = 0;
649
+ options.stats_persist_period_sec = 0;
650
+
651
+ auto dbs = std::vector<DB*>(kInstanceNum);
652
+ for (int i = 0; i < kInstanceNum; i++) {
653
+ ASSERT_OK(
654
+ DB::Open(options, test::PerThreadDBPath(std::to_string(i)), &(dbs[i])));
655
+ }
656
+
657
+ // Make sure the second instance has the worker enabled
658
+ auto dbi = static_cast_with_check<DBImpl>(dbs[1]);
659
+ WriteOptions wo;
660
+ for (int i = 0; i < 200; i++) {
661
+ ASSERT_OK(dbi->Put(wo, Key(i), "value"));
662
+ dbfull()->TEST_WaitForPeridicWorkerRun(
663
+ [&] { mock_clock_->MockSleepForSeconds(static_cast<int>(100)); });
664
+ }
665
+ SeqnoToTimeMapping seqno_to_time_mapping = dbi->TEST_GetSeqnoToTimeMapping();
666
+ ASSERT_GT(seqno_to_time_mapping.Size(), 10);
667
+
668
+ for (int i = 0; i < kInstanceNum; i++) {
669
+ ASSERT_OK(dbs[i]->Close());
670
+ delete dbs[i];
671
+ }
672
+ }
673
+
674
+ TEST_F(SeqnoTimeTest, SeqnoToTimeMappingUniversal) {
675
+ Options options = CurrentOptions();
676
+ options.compaction_style = kCompactionStyleUniversal;
677
+ options.preclude_last_level_data_seconds = 10000;
678
+ options.env = mock_env_.get();
679
+
680
+ DestroyAndReopen(options);
681
+
682
+ for (int j = 0; j < 3; j++) {
683
+ for (int i = 0; i < 100; i++) {
684
+ ASSERT_OK(Put(Key(i), "value"));
685
+ dbfull()->TEST_WaitForPeridicWorkerRun(
686
+ [&] { mock_clock_->MockSleepForSeconds(static_cast<int>(10)); });
687
+ }
688
+ ASSERT_OK(Flush());
689
+ }
690
+ TablePropertiesCollection tables_props;
691
+ ASSERT_OK(dbfull()->GetPropertiesOfAllTables(&tables_props));
692
+ ASSERT_EQ(tables_props.size(), 3);
693
+ for (const auto& props : tables_props) {
694
+ ASSERT_FALSE(props.second->seqno_to_time_mapping.empty());
695
+ SeqnoToTimeMapping tp_mapping;
696
+ ASSERT_OK(tp_mapping.Add(props.second->seqno_to_time_mapping));
697
+ ASSERT_OK(tp_mapping.Sort());
698
+ ASSERT_FALSE(tp_mapping.Empty());
699
+ auto seqs = tp_mapping.TEST_GetInternalMapping();
700
+ ASSERT_GE(seqs.size(), 10 - 1);
701
+ ASSERT_LE(seqs.size(), 10 + 1);
702
+ }
703
+
704
+ // Trigger a compaction
705
+ for (int i = 0; i < 100; i++) {
706
+ ASSERT_OK(Put(Key(i), "value"));
707
+ dbfull()->TEST_WaitForPeridicWorkerRun(
708
+ [&] { mock_clock_->MockSleepForSeconds(static_cast<int>(10)); });
709
+ }
710
+ ASSERT_OK(Flush());
711
+ ASSERT_OK(dbfull()->TEST_WaitForCompact());
712
+ tables_props.clear();
713
+ ASSERT_OK(dbfull()->GetPropertiesOfAllTables(&tables_props));
714
+ ASSERT_EQ(tables_props.size(), 1);
715
+
716
+ auto it = tables_props.begin();
717
+ SeqnoToTimeMapping tp_mapping;
718
+ ASSERT_FALSE(it->second->seqno_to_time_mapping.empty());
719
+ ASSERT_OK(tp_mapping.Add(it->second->seqno_to_time_mapping));
720
+ Close();
721
+ }
722
+
723
+ TEST_F(SeqnoTimeTest, MappingAppend) {
724
+ SeqnoToTimeMapping test(/*max_time_duration=*/100, /*max_capacity=*/10);
725
+
726
+ // ignore seqno == 0, as it may mean the seqno is zeroed out
727
+ ASSERT_FALSE(test.Append(0, 9));
728
+
729
+ ASSERT_TRUE(test.Append(3, 10));
730
+ auto size = test.Size();
731
+ // normal add
732
+ ASSERT_TRUE(test.Append(10, 11));
733
+ size++;
734
+ ASSERT_EQ(size, test.Size());
735
+
736
+ // Append unsorted
737
+ ASSERT_FALSE(test.Append(8, 12));
738
+ ASSERT_EQ(size, test.Size());
739
+
740
+ // Append with the same seqno, newer time will be accepted
741
+ ASSERT_TRUE(test.Append(10, 12));
742
+ ASSERT_EQ(size, test.Size());
743
+ // older time will be ignored
744
+ ASSERT_FALSE(test.Append(10, 9));
745
+ ASSERT_EQ(size, test.Size());
746
+
747
+ // new seqno with old time will be ignored
748
+ ASSERT_FALSE(test.Append(12, 8));
749
+ ASSERT_EQ(size, test.Size());
750
+ }
751
+
752
+ TEST_F(SeqnoTimeTest, GetOldestApproximateTime) {
753
+ SeqnoToTimeMapping test(/*max_time_duration=*/100, /*max_capacity=*/10);
754
+
755
+ ASSERT_EQ(test.GetOldestApproximateTime(10), kUnknownSeqnoTime);
756
+
757
+ test.Append(3, 10);
758
+
759
+ ASSERT_EQ(test.GetOldestApproximateTime(2), kUnknownSeqnoTime);
760
+ ASSERT_EQ(test.GetOldestApproximateTime(3), 10);
761
+ ASSERT_EQ(test.GetOldestApproximateTime(10), 10);
762
+
763
+ test.Append(10, 100);
764
+
765
+ test.Append(100, 1000);
766
+ ASSERT_EQ(test.GetOldestApproximateTime(10), 100);
767
+ ASSERT_EQ(test.GetOldestApproximateTime(40), 100);
768
+ ASSERT_EQ(test.GetOldestApproximateTime(111), 1000);
769
+ }
770
+
771
+ TEST_F(SeqnoTimeTest, Sort) {
772
+ SeqnoToTimeMapping test;
773
+
774
+ // single entry
775
+ test.Add(10, 11);
776
+ ASSERT_OK(test.Sort());
777
+ ASSERT_EQ(test.Size(), 1);
778
+
779
+ // duplicate, should be removed by sort
780
+ test.Add(10, 11);
781
+ // same seqno, but older time, should be removed
782
+ test.Add(10, 9);
783
+
784
+ // unuseful ones, should be removed by sort
785
+ test.Add(11, 9);
786
+ test.Add(9, 8);
787
+
788
+ // Good ones
789
+ test.Add(1, 10);
790
+ test.Add(100, 100);
791
+
792
+ ASSERT_OK(test.Sort());
793
+
794
+ auto seqs = test.TEST_GetInternalMapping();
795
+
796
+ std::deque<SeqnoToTimeMapping::SeqnoTimePair> expected;
797
+ expected.emplace_back(1, 10);
798
+ expected.emplace_back(10, 11);
799
+ expected.emplace_back(100, 100);
800
+
801
+ ASSERT_EQ(expected, seqs);
802
+ }
803
+
804
+ TEST_F(SeqnoTimeTest, EncodeDecodeBasic) {
805
+ SeqnoToTimeMapping test(0, 1000);
806
+
807
+ std::string output;
808
+ test.Encode(output, 0, 1000, 100);
809
+ ASSERT_TRUE(output.empty());
810
+
811
+ for (int i = 1; i <= 1000; i++) {
812
+ ASSERT_TRUE(test.Append(i, i * 10));
813
+ }
814
+ test.Encode(output, 0, 1000, 100);
815
+
816
+ ASSERT_FALSE(output.empty());
817
+
818
+ SeqnoToTimeMapping decoded;
819
+ ASSERT_OK(decoded.Add(output));
820
+ ASSERT_OK(decoded.Sort());
821
+ ASSERT_EQ(decoded.Size(), SeqnoToTimeMapping::kMaxSeqnoTimePairsPerSST);
822
+ ASSERT_EQ(test.Size(), 1000);
823
+
824
+ for (SequenceNumber seq = 0; seq <= 1000; seq++) {
825
+ // test has the more accurate time mapping, encode only pick
826
+ // kMaxSeqnoTimePairsPerSST number of entries, which is less accurate
827
+ uint64_t target_time = test.GetOldestApproximateTime(seq);
828
+ ASSERT_GE(decoded.GetOldestApproximateTime(seq),
829
+ target_time < 200 ? 0 : target_time - 200);
830
+ ASSERT_LE(decoded.GetOldestApproximateTime(seq), target_time);
831
+ }
832
+ }
833
+
834
+ TEST_F(SeqnoTimeTest, EncodeDecodePerferNewTime) {
835
+ SeqnoToTimeMapping test(0, 10);
836
+
837
+ test.Append(1, 10);
838
+ test.Append(5, 17);
839
+ test.Append(6, 25);
840
+ test.Append(8, 30);
841
+
842
+ std::string output;
843
+ test.Encode(output, 1, 10, 0, 3);
844
+
845
+ SeqnoToTimeMapping decoded;
846
+ ASSERT_OK(decoded.Add(output));
847
+ ASSERT_OK(decoded.Sort());
848
+
849
+ ASSERT_EQ(decoded.Size(), 3);
850
+
851
+ auto seqs = decoded.TEST_GetInternalMapping();
852
+ std::deque<SeqnoToTimeMapping::SeqnoTimePair> expected;
853
+ expected.emplace_back(1, 10);
854
+ expected.emplace_back(6, 25);
855
+ expected.emplace_back(8, 30);
856
+ ASSERT_EQ(expected, seqs);
857
+
858
+ // Add a few large time number
859
+ test.Append(10, 100);
860
+ test.Append(13, 200);
861
+ test.Append(16, 300);
862
+
863
+ output.clear();
864
+ test.Encode(output, 1, 20, 0, 4);
865
+ decoded.Clear();
866
+ ASSERT_OK(decoded.Add(output));
867
+ ASSERT_OK(decoded.Sort());
868
+ ASSERT_EQ(decoded.Size(), 4);
869
+
870
+ expected.clear();
871
+ expected.emplace_back(1, 10);
872
+ // entry #6, #8 are skipped as they are too close to #1.
873
+ // entry #100 is also within skip range, but if it's skipped, there not enough
874
+ // number to fill 4 entries, so select it.
875
+ expected.emplace_back(10, 100);
876
+ expected.emplace_back(13, 200);
877
+ expected.emplace_back(16, 300);
878
+ seqs = decoded.TEST_GetInternalMapping();
879
+ ASSERT_EQ(expected, seqs);
880
+ }
881
+
882
+ } // namespace ROCKSDB_NAMESPACE
883
+
884
+ #endif // ROCKSDB_LITE
885
+
886
+ int main(int argc, char** argv) {
887
+ ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
888
+ ::testing::InitGoogleTest(&argc, argv);
889
+ return RUN_ALL_TESTS();
890
+ }