]>
Commit | Line | Data |
---|---|---|
7c673cae | 1 | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
11fdf7f2 TL |
2 | // This source code is licensed under both the GPLv2 (found in the |
3 | // COPYING file in the root directory) and Apache 2.0 License | |
4 | // (found in the LICENSE.Apache file in the root directory). | |
7c673cae FG |
5 | // |
6 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. | |
7 | // Use of this source code is governed by a BSD-style license that can be | |
8 | // found in the LICENSE file. See the AUTHORS file for names of contributors. | |
9 | ||
10 | #include "db/db_test_util.h" | |
f67539c2 | 11 | #include "file/sst_file_manager_impl.h" |
7c673cae FG |
12 | #include "port/port.h" |
13 | #include "port/stack_trace.h" | |
14 | #include "rocksdb/sst_file_manager.h" | |
20effc67 | 15 | #include "util/random.h" |
7c673cae | 16 | |
f67539c2 | 17 | namespace ROCKSDB_NAMESPACE { |
7c673cae FG |
18 | |
19 | class DBSSTTest : public DBTestBase { | |
20 | public: | |
20effc67 | 21 | DBSSTTest() : DBTestBase("/db_sst_test", /*env_do_fsync=*/true) {} |
7c673cae FG |
22 | }; |
23 | ||
11fdf7f2 TL |
24 | #ifndef ROCKSDB_LITE |
25 | // A class which remembers the name of each flushed file. | |
26 | class FlushedFileCollector : public EventListener { | |
27 | public: | |
28 | FlushedFileCollector() {} | |
494da23a | 29 | ~FlushedFileCollector() override {} |
11fdf7f2 | 30 | |
494da23a | 31 | void OnFlushCompleted(DB* /*db*/, const FlushJobInfo& info) override { |
11fdf7f2 TL |
32 | std::lock_guard<std::mutex> lock(mutex_); |
33 | flushed_files_.push_back(info.file_path); | |
34 | } | |
35 | ||
36 | std::vector<std::string> GetFlushedFiles() { | |
37 | std::lock_guard<std::mutex> lock(mutex_); | |
38 | std::vector<std::string> result; | |
39 | for (auto fname : flushed_files_) { | |
40 | result.push_back(fname); | |
41 | } | |
42 | return result; | |
43 | } | |
44 | void ClearFlushedFiles() { | |
45 | std::lock_guard<std::mutex> lock(mutex_); | |
46 | flushed_files_.clear(); | |
47 | } | |
48 | ||
49 | private: | |
50 | std::vector<std::string> flushed_files_; | |
51 | std::mutex mutex_; | |
52 | }; | |
53 | #endif // ROCKSDB_LITE | |
54 | ||
7c673cae FG |
55 | TEST_F(DBSSTTest, DontDeletePendingOutputs) { |
56 | Options options; | |
57 | options.env = env_; | |
58 | options.create_if_missing = true; | |
59 | DestroyAndReopen(options); | |
60 | ||
61 | // Every time we write to a table file, call FOF/POF with full DB scan. This | |
62 | // will make sure our pending_outputs_ protection work correctly | |
63 | std::function<void()> purge_obsolete_files_function = [&]() { | |
64 | JobContext job_context(0); | |
65 | dbfull()->TEST_LockMutex(); | |
66 | dbfull()->FindObsoleteFiles(&job_context, true /*force*/); | |
67 | dbfull()->TEST_UnlockMutex(); | |
68 | dbfull()->PurgeObsoleteFiles(job_context); | |
69 | job_context.Clean(); | |
70 | }; | |
71 | ||
72 | env_->table_write_callback_ = &purge_obsolete_files_function; | |
73 | ||
74 | for (int i = 0; i < 2; ++i) { | |
75 | ASSERT_OK(Put("a", "begin")); | |
76 | ASSERT_OK(Put("z", "end")); | |
77 | ASSERT_OK(Flush()); | |
78 | } | |
79 | ||
80 | // If pending output guard does not work correctly, PurgeObsoleteFiles() will | |
81 | // delete the file that Compaction is trying to create, causing this: error | |
82 | // db/db_test.cc:975: IO error: | |
83 | // /tmp/rocksdbtest-1552237650/db_test/000009.sst: No such file or directory | |
84 | Compact("a", "b"); | |
85 | } | |
86 | ||
87 | // 1 Create some SST files by inserting K-V pairs into DB | |
88 | // 2 Close DB and change suffix from ".sst" to ".ldb" for every other SST file | |
89 | // 3 Open DB and check if all key can be read | |
90 | TEST_F(DBSSTTest, SSTsWithLdbSuffixHandling) { | |
91 | Options options = CurrentOptions(); | |
92 | options.write_buffer_size = 110 << 10; // 110KB | |
93 | options.num_levels = 4; | |
94 | DestroyAndReopen(options); | |
95 | ||
96 | Random rnd(301); | |
97 | int key_id = 0; | |
98 | for (int i = 0; i < 10; ++i) { | |
99 | GenerateNewFile(&rnd, &key_id, false); | |
100 | } | |
101 | Flush(); | |
102 | Close(); | |
103 | int const num_files = GetSstFileCount(dbname_); | |
104 | ASSERT_GT(num_files, 0); | |
105 | ||
f67539c2 TL |
106 | Reopen(options); |
107 | std::vector<std::string> values; | |
108 | values.reserve(key_id); | |
109 | for (int k = 0; k < key_id; ++k) { | |
110 | values.push_back(Get(Key(k))); | |
111 | } | |
112 | Close(); | |
113 | ||
7c673cae | 114 | std::vector<std::string> filenames; |
11fdf7f2 | 115 | GetSstFiles(env_, dbname_, &filenames); |
7c673cae FG |
116 | int num_ldb_files = 0; |
117 | for (size_t i = 0; i < filenames.size(); ++i) { | |
118 | if (i & 1) { | |
119 | continue; | |
120 | } | |
121 | std::string const rdb_name = dbname_ + "/" + filenames[i]; | |
122 | std::string const ldb_name = Rocks2LevelTableFileName(rdb_name); | |
123 | ASSERT_TRUE(env_->RenameFile(rdb_name, ldb_name).ok()); | |
124 | ++num_ldb_files; | |
125 | } | |
126 | ASSERT_GT(num_ldb_files, 0); | |
127 | ASSERT_EQ(num_files, GetSstFileCount(dbname_)); | |
128 | ||
129 | Reopen(options); | |
130 | for (int k = 0; k < key_id; ++k) { | |
f67539c2 | 131 | ASSERT_EQ(values[k], Get(Key(k))); |
7c673cae FG |
132 | } |
133 | Destroy(options); | |
134 | } | |
135 | ||
f67539c2 TL |
136 | // Check that we don't crash when opening DB with |
137 | // DBOptions::skip_checking_sst_file_sizes_on_db_open = true. | |
138 | TEST_F(DBSSTTest, SkipCheckingSSTFileSizesOnDBOpen) { | |
139 | ASSERT_OK(Put("pika", "choo")); | |
140 | ASSERT_OK(Flush()); | |
141 | ||
142 | // Just open the DB with the option set to true and check that we don't crash. | |
143 | Options options; | |
20effc67 | 144 | options.env = env_; |
f67539c2 TL |
145 | options.skip_checking_sst_file_sizes_on_db_open = true; |
146 | Reopen(options); | |
147 | ||
148 | ASSERT_EQ("choo", Get("pika")); | |
149 | } | |
150 | ||
7c673cae FG |
151 | #ifndef ROCKSDB_LITE |
152 | TEST_F(DBSSTTest, DontDeleteMovedFile) { | |
153 | // This test triggers move compaction and verifies that the file is not | |
154 | // deleted when it's part of move compaction | |
155 | Options options = CurrentOptions(); | |
156 | options.env = env_; | |
157 | options.create_if_missing = true; | |
158 | options.max_bytes_for_level_base = 1024 * 1024; // 1 MB | |
159 | options.level0_file_num_compaction_trigger = | |
160 | 2; // trigger compaction when we have 2 files | |
161 | DestroyAndReopen(options); | |
162 | ||
163 | Random rnd(301); | |
164 | // Create two 1MB sst files | |
165 | for (int i = 0; i < 2; ++i) { | |
166 | // Create 1MB sst file | |
167 | for (int j = 0; j < 100; ++j) { | |
20effc67 | 168 | ASSERT_OK(Put(Key(i * 50 + j), rnd.RandomString(10 * 1024))); |
7c673cae FG |
169 | } |
170 | ASSERT_OK(Flush()); | |
171 | } | |
172 | // this should execute both L0->L1 and L1->(move)->L2 compactions | |
173 | dbfull()->TEST_WaitForCompact(); | |
174 | ASSERT_EQ("0,0,1", FilesPerLevel(0)); | |
175 | ||
176 | // If the moved file is actually deleted (the move-safeguard in | |
177 | // ~Version::Version() is not there), we get this failure: | |
178 | // Corruption: Can't access /000009.sst | |
179 | Reopen(options); | |
180 | } | |
181 | ||
182 | // This reproduces a bug where we don't delete a file because when it was | |
183 | // supposed to be deleted, it was blocked by pending_outputs | |
184 | // Consider: | |
185 | // 1. current file_number is 13 | |
186 | // 2. compaction (1) starts, blocks deletion of all files starting with 13 | |
187 | // (pending outputs) | |
188 | // 3. file 13 is created by compaction (2) | |
189 | // 4. file 13 is consumed by compaction (3) and file 15 was created. Since file | |
190 | // 13 has no references, it is put into VersionSet::obsolete_files_ | |
191 | // 5. FindObsoleteFiles() gets file 13 from VersionSet::obsolete_files_. File 13 | |
192 | // is deleted from obsolete_files_ set. | |
193 | // 6. PurgeObsoleteFiles() tries to delete file 13, but this file is blocked by | |
194 | // pending outputs since compaction (1) is still running. It is not deleted and | |
195 | // it is not present in obsolete_files_ anymore. Therefore, we never delete it. | |
196 | TEST_F(DBSSTTest, DeleteObsoleteFilesPendingOutputs) { | |
197 | Options options = CurrentOptions(); | |
198 | options.env = env_; | |
199 | options.write_buffer_size = 2 * 1024 * 1024; // 2 MB | |
200 | options.max_bytes_for_level_base = 1024 * 1024; // 1 MB | |
201 | options.level0_file_num_compaction_trigger = | |
202 | 2; // trigger compaction when we have 2 files | |
203 | options.max_background_flushes = 2; | |
204 | options.max_background_compactions = 2; | |
205 | ||
206 | OnFileDeletionListener* listener = new OnFileDeletionListener(); | |
207 | options.listeners.emplace_back(listener); | |
208 | ||
209 | Reopen(options); | |
210 | ||
211 | Random rnd(301); | |
212 | // Create two 1MB sst files | |
213 | for (int i = 0; i < 2; ++i) { | |
214 | // Create 1MB sst file | |
215 | for (int j = 0; j < 100; ++j) { | |
20effc67 | 216 | ASSERT_OK(Put(Key(i * 50 + j), rnd.RandomString(10 * 1024))); |
7c673cae FG |
217 | } |
218 | ASSERT_OK(Flush()); | |
219 | } | |
220 | // this should execute both L0->L1 and L1->(move)->L2 compactions | |
221 | dbfull()->TEST_WaitForCompact(); | |
222 | ASSERT_EQ("0,0,1", FilesPerLevel(0)); | |
223 | ||
224 | test::SleepingBackgroundTask blocking_thread; | |
225 | port::Mutex mutex_; | |
226 | bool already_blocked(false); | |
227 | ||
228 | // block the flush | |
229 | std::function<void()> block_first_time = [&]() { | |
230 | bool blocking = false; | |
231 | { | |
232 | MutexLock l(&mutex_); | |
233 | if (!already_blocked) { | |
234 | blocking = true; | |
235 | already_blocked = true; | |
236 | } | |
237 | } | |
238 | if (blocking) { | |
239 | blocking_thread.DoSleep(); | |
240 | } | |
241 | }; | |
242 | env_->table_write_callback_ = &block_first_time; | |
243 | // Insert 2.5MB data, which should trigger a flush because we exceed | |
244 | // write_buffer_size. The flush will be blocked with block_first_time | |
245 | // pending_file is protecting all the files created after | |
246 | for (int j = 0; j < 256; ++j) { | |
20effc67 | 247 | ASSERT_OK(Put(Key(j), rnd.RandomString(10 * 1024))); |
7c673cae FG |
248 | } |
249 | blocking_thread.WaitUntilSleeping(); | |
250 | ||
251 | ASSERT_OK(dbfull()->TEST_CompactRange(2, nullptr, nullptr)); | |
252 | ||
253 | ASSERT_EQ("0,0,0,1", FilesPerLevel(0)); | |
254 | std::vector<LiveFileMetaData> metadata; | |
255 | db_->GetLiveFilesMetaData(&metadata); | |
256 | ASSERT_EQ(metadata.size(), 1U); | |
257 | auto file_on_L2 = metadata[0].name; | |
258 | listener->SetExpectedFileName(dbname_ + file_on_L2); | |
259 | ||
260 | ASSERT_OK(dbfull()->TEST_CompactRange(3, nullptr, nullptr, nullptr, | |
261 | true /* disallow trivial move */)); | |
262 | ASSERT_EQ("0,0,0,0,1", FilesPerLevel(0)); | |
263 | ||
264 | // finish the flush! | |
265 | blocking_thread.WakeUp(); | |
266 | blocking_thread.WaitUntilDone(); | |
267 | dbfull()->TEST_WaitForFlushMemTable(); | |
268 | // File just flushed is too big for L0 and L1 so gets moved to L2. | |
269 | dbfull()->TEST_WaitForCompact(); | |
270 | ASSERT_EQ("0,0,1,0,1", FilesPerLevel(0)); | |
271 | ||
272 | metadata.clear(); | |
273 | db_->GetLiveFilesMetaData(&metadata); | |
274 | ASSERT_EQ(metadata.size(), 2U); | |
275 | ||
276 | // This file should have been deleted during last compaction | |
277 | ASSERT_EQ(Status::NotFound(), env_->FileExists(dbname_ + file_on_L2)); | |
278 | listener->VerifyMatchedCount(1); | |
279 | } | |
280 | ||
281 | TEST_F(DBSSTTest, DBWithSstFileManager) { | |
282 | std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_)); | |
283 | auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get()); | |
284 | ||
285 | int files_added = 0; | |
286 | int files_deleted = 0; | |
287 | int files_moved = 0; | |
f67539c2 | 288 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( |
11fdf7f2 | 289 | "SstFileManagerImpl::OnAddFile", [&](void* /*arg*/) { files_added++; }); |
f67539c2 TL |
290 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( |
291 | "SstFileManagerImpl::OnDeleteFile", | |
292 | [&](void* /*arg*/) { files_deleted++; }); | |
293 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( | |
11fdf7f2 | 294 | "SstFileManagerImpl::OnMoveFile", [&](void* /*arg*/) { files_moved++; }); |
f67539c2 | 295 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing(); |
7c673cae FG |
296 | |
297 | Options options = CurrentOptions(); | |
298 | options.sst_file_manager = sst_file_manager; | |
299 | DestroyAndReopen(options); | |
300 | ||
301 | Random rnd(301); | |
302 | for (int i = 0; i < 25; i++) { | |
303 | GenerateNewRandomFile(&rnd); | |
304 | ASSERT_OK(Flush()); | |
305 | dbfull()->TEST_WaitForFlushMemTable(); | |
306 | dbfull()->TEST_WaitForCompact(); | |
307 | // Verify that we are tracking all sst files in dbname_ | |
20effc67 TL |
308 | std::unordered_map<std::string, uint64_t> files_in_db; |
309 | ASSERT_OK(GetAllSSTFiles(&files_in_db)); | |
310 | ASSERT_EQ(sfm->GetTrackedFiles(), files_in_db); | |
7c673cae FG |
311 | } |
312 | ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr)); | |
313 | ||
20effc67 TL |
314 | std::unordered_map<std::string, uint64_t> files_in_db; |
315 | ASSERT_OK(GetAllSSTFiles(&files_in_db)); | |
7c673cae FG |
316 | // Verify that we are tracking all sst files in dbname_ |
317 | ASSERT_EQ(sfm->GetTrackedFiles(), files_in_db); | |
318 | // Verify the total files size | |
319 | uint64_t total_files_size = 0; | |
320 | for (auto& file_to_size : files_in_db) { | |
321 | total_files_size += file_to_size.second; | |
322 | } | |
323 | ASSERT_EQ(sfm->GetTotalSize(), total_files_size); | |
324 | // We flushed at least 25 files | |
325 | ASSERT_GE(files_added, 25); | |
326 | // Compaction must have deleted some files | |
327 | ASSERT_GT(files_deleted, 0); | |
328 | // No files were moved | |
329 | ASSERT_EQ(files_moved, 0); | |
330 | ||
331 | Close(); | |
332 | Reopen(options); | |
333 | ASSERT_EQ(sfm->GetTrackedFiles(), files_in_db); | |
334 | ASSERT_EQ(sfm->GetTotalSize(), total_files_size); | |
335 | ||
336 | // Verify that we track all the files again after the DB is closed and opened | |
337 | Close(); | |
338 | sst_file_manager.reset(NewSstFileManager(env_)); | |
339 | options.sst_file_manager = sst_file_manager; | |
340 | sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get()); | |
341 | ||
342 | Reopen(options); | |
343 | ASSERT_EQ(sfm->GetTrackedFiles(), files_in_db); | |
344 | ASSERT_EQ(sfm->GetTotalSize(), total_files_size); | |
345 | ||
f67539c2 | 346 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing(); |
7c673cae FG |
347 | } |
348 | ||
349 | TEST_F(DBSSTTest, RateLimitedDelete) { | |
350 | Destroy(last_options_); | |
f67539c2 | 351 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({ |
7c673cae FG |
352 | {"DBSSTTest::RateLimitedDelete:1", |
353 | "DeleteScheduler::BackgroundEmptyTrash"}, | |
354 | }); | |
355 | ||
356 | std::vector<uint64_t> penalties; | |
f67539c2 | 357 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( |
7c673cae FG |
358 | "DeleteScheduler::BackgroundEmptyTrash:Wait", |
359 | [&](void* arg) { penalties.push_back(*(static_cast<uint64_t*>(arg))); }); | |
f67539c2 | 360 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( |
7c673cae FG |
361 | "InstrumentedCondVar::TimedWaitInternal", [&](void* arg) { |
362 | // Turn timed wait into a simulated sleep | |
363 | uint64_t* abs_time_us = static_cast<uint64_t*>(arg); | |
20effc67 TL |
364 | uint64_t cur_time = env_->NowMicros(); |
365 | if (*abs_time_us > cur_time) { | |
366 | env_->MockSleepForMicroseconds(*abs_time_us - cur_time); | |
7c673cae FG |
367 | } |
368 | ||
20effc67 TL |
369 | // Plus an additional short, random amount |
370 | env_->MockSleepForMicroseconds(Random::GetTLSInstance()->Uniform(10)); | |
7c673cae | 371 | |
20effc67 TL |
372 | // Set wait until time to before (actual) current time to force not |
373 | // to sleep | |
374 | *abs_time_us = Env::Default()->NowMicros(); | |
7c673cae FG |
375 | }); |
376 | ||
f67539c2 | 377 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing(); |
7c673cae | 378 | |
7c673cae | 379 | Options options = CurrentOptions(); |
20effc67 | 380 | SetTimeElapseOnlySleepOnReopen(&options); |
7c673cae FG |
381 | options.disable_auto_compactions = true; |
382 | options.env = env_; | |
20effc67 | 383 | options.statistics = CreateDBStatistics(); |
7c673cae | 384 | |
7c673cae FG |
385 | int64_t rate_bytes_per_sec = 1024 * 10; // 10 Kbs / Sec |
386 | Status s; | |
387 | options.sst_file_manager.reset( | |
11fdf7f2 | 388 | NewSstFileManager(env_, nullptr, "", 0, false, &s, 0)); |
7c673cae FG |
389 | ASSERT_OK(s); |
390 | options.sst_file_manager->SetDeleteRateBytesPerSecond(rate_bytes_per_sec); | |
391 | auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get()); | |
11fdf7f2 | 392 | sfm->delete_scheduler()->SetMaxTrashDBRatio(1.1); |
7c673cae | 393 | |
494da23a TL |
394 | WriteOptions wo; |
395 | wo.disableWAL = true; | |
7c673cae FG |
396 | ASSERT_OK(TryReopen(options)); |
397 | // Create 4 files in L0 | |
398 | for (char v = 'a'; v <= 'd'; v++) { | |
494da23a TL |
399 | ASSERT_OK(Put("Key2", DummyString(1024, v), wo)); |
400 | ASSERT_OK(Put("Key3", DummyString(1024, v), wo)); | |
401 | ASSERT_OK(Put("Key4", DummyString(1024, v), wo)); | |
402 | ASSERT_OK(Put("Key1", DummyString(1024, v), wo)); | |
403 | ASSERT_OK(Put("Key4", DummyString(1024, v), wo)); | |
7c673cae FG |
404 | ASSERT_OK(Flush()); |
405 | } | |
406 | // We created 4 sst files in L0 | |
407 | ASSERT_EQ("4", FilesPerLevel(0)); | |
408 | ||
409 | std::vector<LiveFileMetaData> metadata; | |
410 | db_->GetLiveFilesMetaData(&metadata); | |
411 | ||
412 | // Compaction will move the 4 files in L0 to trash and create 1 L1 file | |
413 | ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr)); | |
494da23a | 414 | ASSERT_OK(dbfull()->TEST_WaitForCompact(true)); |
7c673cae FG |
415 | ASSERT_EQ("0,1", FilesPerLevel(0)); |
416 | ||
417 | uint64_t delete_start_time = env_->NowMicros(); | |
418 | // Hold BackgroundEmptyTrash | |
419 | TEST_SYNC_POINT("DBSSTTest::RateLimitedDelete:1"); | |
420 | sfm->WaitForEmptyTrash(); | |
421 | uint64_t time_spent_deleting = env_->NowMicros() - delete_start_time; | |
422 | ||
423 | uint64_t total_files_size = 0; | |
424 | uint64_t expected_penlty = 0; | |
425 | ASSERT_EQ(penalties.size(), metadata.size()); | |
426 | for (size_t i = 0; i < metadata.size(); i++) { | |
427 | total_files_size += metadata[i].size; | |
428 | expected_penlty = ((total_files_size * 1000000) / rate_bytes_per_sec); | |
429 | ASSERT_EQ(expected_penlty, penalties[i]); | |
430 | } | |
431 | ASSERT_GT(time_spent_deleting, expected_penlty * 0.9); | |
432 | ASSERT_LT(time_spent_deleting, expected_penlty * 1.1); | |
20effc67 TL |
433 | ASSERT_EQ(4, options.statistics->getAndResetTickerCount(FILES_MARKED_TRASH)); |
434 | ASSERT_EQ( | |
435 | 0, options.statistics->getAndResetTickerCount(FILES_DELETED_IMMEDIATELY)); | |
7c673cae | 436 | |
f67539c2 | 437 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing(); |
7c673cae FG |
438 | } |
439 | ||
494da23a TL |
440 | TEST_F(DBSSTTest, RateLimitedWALDelete) { |
441 | Destroy(last_options_); | |
442 | ||
443 | std::vector<uint64_t> penalties; | |
f67539c2 | 444 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( |
494da23a TL |
445 | "DeleteScheduler::BackgroundEmptyTrash:Wait", |
446 | [&](void* arg) { penalties.push_back(*(static_cast<uint64_t*>(arg))); }); | |
447 | ||
494da23a TL |
448 | Options options = CurrentOptions(); |
449 | options.disable_auto_compactions = true; | |
f67539c2 | 450 | options.compression = kNoCompression; |
494da23a TL |
451 | options.env = env_; |
452 | ||
453 | int64_t rate_bytes_per_sec = 1024 * 10; // 10 Kbs / Sec | |
454 | Status s; | |
455 | options.sst_file_manager.reset( | |
456 | NewSstFileManager(env_, nullptr, "", 0, false, &s, 0)); | |
457 | ASSERT_OK(s); | |
458 | options.sst_file_manager->SetDeleteRateBytesPerSecond(rate_bytes_per_sec); | |
459 | auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get()); | |
f67539c2 | 460 | sfm->delete_scheduler()->SetMaxTrashDBRatio(3.1); |
20effc67 | 461 | SetTimeElapseOnlySleepOnReopen(&options); |
494da23a TL |
462 | |
463 | ASSERT_OK(TryReopen(options)); | |
f67539c2 | 464 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing(); |
494da23a TL |
465 | |
466 | // Create 4 files in L0 | |
467 | for (char v = 'a'; v <= 'd'; v++) { | |
468 | ASSERT_OK(Put("Key2", DummyString(1024, v))); | |
469 | ASSERT_OK(Put("Key3", DummyString(1024, v))); | |
470 | ASSERT_OK(Put("Key4", DummyString(1024, v))); | |
471 | ASSERT_OK(Put("Key1", DummyString(1024, v))); | |
472 | ASSERT_OK(Put("Key4", DummyString(1024, v))); | |
473 | ASSERT_OK(Flush()); | |
474 | } | |
475 | // We created 4 sst files in L0 | |
476 | ASSERT_EQ("4", FilesPerLevel(0)); | |
477 | ||
478 | // Compaction will move the 4 files in L0 to trash and create 1 L1 file | |
f67539c2 TL |
479 | CompactRangeOptions cro; |
480 | cro.bottommost_level_compaction = BottommostLevelCompaction::kForce; | |
481 | ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr)); | |
494da23a TL |
482 | ASSERT_OK(dbfull()->TEST_WaitForCompact(true)); |
483 | ASSERT_EQ("0,1", FilesPerLevel(0)); | |
484 | ||
485 | sfm->WaitForEmptyTrash(); | |
486 | ASSERT_EQ(penalties.size(), 8); | |
487 | ||
f67539c2 TL |
488 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing(); |
489 | } | |
490 | ||
491 | class DBWALTestWithParam | |
492 | : public DBSSTTest, | |
493 | public testing::WithParamInterface<std::tuple<std::string, bool>> { | |
494 | public: | |
495 | DBWALTestWithParam() { | |
496 | wal_dir_ = std::get<0>(GetParam()); | |
497 | wal_dir_same_as_dbname_ = std::get<1>(GetParam()); | |
498 | } | |
499 | ||
500 | std::string wal_dir_; | |
501 | bool wal_dir_same_as_dbname_; | |
502 | }; | |
503 | ||
504 | TEST_P(DBWALTestWithParam, WALTrashCleanupOnOpen) { | |
505 | class MyEnv : public EnvWrapper { | |
506 | public: | |
507 | MyEnv(Env* t) : EnvWrapper(t), fake_log_delete(false) {} | |
508 | ||
509 | Status DeleteFile(const std::string& fname) { | |
510 | if (fname.find(".log.trash") != std::string::npos && fake_log_delete) { | |
511 | return Status::OK(); | |
512 | } | |
513 | ||
514 | return target()->DeleteFile(fname); | |
515 | } | |
516 | ||
517 | void set_fake_log_delete(bool fake) { fake_log_delete = fake; } | |
518 | ||
519 | private: | |
520 | bool fake_log_delete; | |
521 | }; | |
522 | ||
20effc67 | 523 | std::unique_ptr<MyEnv> env(new MyEnv(env_)); |
f67539c2 TL |
524 | Destroy(last_options_); |
525 | ||
526 | env->set_fake_log_delete(true); | |
527 | ||
528 | Options options = CurrentOptions(); | |
529 | options.disable_auto_compactions = true; | |
530 | options.compression = kNoCompression; | |
531 | options.env = env.get(); | |
532 | options.wal_dir = dbname_ + wal_dir_; | |
533 | ||
534 | int64_t rate_bytes_per_sec = 1024 * 10; // 10 Kbs / Sec | |
535 | Status s; | |
536 | options.sst_file_manager.reset( | |
537 | NewSstFileManager(env_, nullptr, "", 0, false, &s, 0)); | |
538 | ASSERT_OK(s); | |
539 | options.sst_file_manager->SetDeleteRateBytesPerSecond(rate_bytes_per_sec); | |
540 | auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get()); | |
541 | sfm->delete_scheduler()->SetMaxTrashDBRatio(3.1); | |
542 | ||
543 | ASSERT_OK(TryReopen(options)); | |
544 | ||
545 | // Create 4 files in L0 | |
546 | for (char v = 'a'; v <= 'd'; v++) { | |
547 | ASSERT_OK(Put("Key2", DummyString(1024, v))); | |
548 | ASSERT_OK(Put("Key3", DummyString(1024, v))); | |
549 | ASSERT_OK(Put("Key4", DummyString(1024, v))); | |
550 | ASSERT_OK(Put("Key1", DummyString(1024, v))); | |
551 | ASSERT_OK(Put("Key4", DummyString(1024, v))); | |
552 | ASSERT_OK(Flush()); | |
553 | } | |
554 | // We created 4 sst files in L0 | |
555 | ASSERT_EQ("4", FilesPerLevel(0)); | |
556 | ||
557 | Close(); | |
558 | ||
559 | options.sst_file_manager.reset(); | |
560 | std::vector<std::string> filenames; | |
561 | int trash_log_count = 0; | |
562 | if (!wal_dir_same_as_dbname_) { | |
563 | // Forcibly create some trash log files | |
564 | std::unique_ptr<WritableFile> result; | |
565 | env->NewWritableFile(options.wal_dir + "/1000.log.trash", &result, | |
566 | EnvOptions()); | |
567 | result.reset(); | |
568 | } | |
569 | env->GetChildren(options.wal_dir, &filenames); | |
570 | for (const std::string& fname : filenames) { | |
571 | if (fname.find(".log.trash") != std::string::npos) { | |
572 | trash_log_count++; | |
573 | } | |
574 | } | |
575 | ASSERT_GE(trash_log_count, 1); | |
576 | ||
577 | env->set_fake_log_delete(false); | |
578 | ASSERT_OK(TryReopen(options)); | |
579 | ||
580 | filenames.clear(); | |
581 | trash_log_count = 0; | |
582 | env->GetChildren(options.wal_dir, &filenames); | |
583 | for (const std::string& fname : filenames) { | |
584 | if (fname.find(".log.trash") != std::string::npos) { | |
585 | trash_log_count++; | |
586 | } | |
587 | } | |
588 | ASSERT_EQ(trash_log_count, 0); | |
589 | Close(); | |
494da23a TL |
590 | } |
591 | ||
f67539c2 TL |
592 | INSTANTIATE_TEST_CASE_P(DBWALTestWithParam, DBWALTestWithParam, |
593 | ::testing::Values(std::make_tuple("", true), | |
594 | std::make_tuple("_wal_dir", false))); | |
595 | ||
11fdf7f2 TL |
596 | TEST_F(DBSSTTest, OpenDBWithExistingTrash) { |
597 | Options options = CurrentOptions(); | |
598 | ||
599 | options.sst_file_manager.reset( | |
600 | NewSstFileManager(env_, nullptr, "", 1024 * 1024 /* 1 MB/sec */)); | |
601 | auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get()); | |
602 | ||
603 | Destroy(last_options_); | |
604 | ||
605 | // Add some trash files to the db directory so the DB can clean them up | |
606 | env_->CreateDirIfMissing(dbname_); | |
607 | ASSERT_OK(WriteStringToFile(env_, "abc", dbname_ + "/" + "001.sst.trash")); | |
608 | ASSERT_OK(WriteStringToFile(env_, "abc", dbname_ + "/" + "002.sst.trash")); | |
609 | ASSERT_OK(WriteStringToFile(env_, "abc", dbname_ + "/" + "003.sst.trash")); | |
610 | ||
611 | // Reopen the DB and verify that it deletes existing trash files | |
612 | ASSERT_OK(TryReopen(options)); | |
613 | sfm->WaitForEmptyTrash(); | |
614 | ASSERT_NOK(env_->FileExists(dbname_ + "/" + "001.sst.trash")); | |
615 | ASSERT_NOK(env_->FileExists(dbname_ + "/" + "002.sst.trash")); | |
616 | ASSERT_NOK(env_->FileExists(dbname_ + "/" + "003.sst.trash")); | |
617 | } | |
618 | ||
619 | ||
7c673cae FG |
620 | // Create a DB with 2 db_paths, and generate multiple files in the 2 |
621 | // db_paths using CompactRangeOptions, make sure that files that were | |
622 | // deleted from first db_path were deleted using DeleteScheduler and | |
623 | // files in the second path were not. | |
624 | TEST_F(DBSSTTest, DeleteSchedulerMultipleDBPaths) { | |
11fdf7f2 | 625 | std::atomic<int> bg_delete_file(0); |
f67539c2 | 626 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( |
7c673cae | 627 | "DeleteScheduler::DeleteTrashFile:DeleteFile", |
11fdf7f2 TL |
628 | [&](void* /*arg*/) { bg_delete_file++; }); |
629 | // The deletion scheduler sometimes skips marking file as trash according to | |
630 | // a heuristic. In that case the deletion will go through the below SyncPoint. | |
f67539c2 TL |
631 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( |
632 | "DeleteScheduler::DeleteFile", [&](void* /*arg*/) { bg_delete_file++; }); | |
7c673cae FG |
633 | |
634 | Options options = CurrentOptions(); | |
635 | options.disable_auto_compactions = true; | |
636 | options.db_paths.emplace_back(dbname_, 1024 * 100); | |
637 | options.db_paths.emplace_back(dbname_ + "_2", 1024 * 100); | |
638 | options.env = env_; | |
639 | ||
7c673cae FG |
640 | int64_t rate_bytes_per_sec = 1024 * 1024; // 1 Mb / Sec |
641 | Status s; | |
11fdf7f2 TL |
642 | options.sst_file_manager.reset( |
643 | NewSstFileManager(env_, nullptr, "", rate_bytes_per_sec, false, &s, | |
644 | /* max_trash_db_ratio= */ 1.1)); | |
645 | ||
7c673cae FG |
646 | ASSERT_OK(s); |
647 | auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get()); | |
648 | ||
649 | DestroyAndReopen(options); | |
f67539c2 | 650 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing(); |
494da23a TL |
651 | |
652 | WriteOptions wo; | |
653 | wo.disableWAL = true; | |
7c673cae FG |
654 | |
655 | // Create 4 files in L0 | |
656 | for (int i = 0; i < 4; i++) { | |
494da23a | 657 | ASSERT_OK(Put("Key" + ToString(i), DummyString(1024, 'A'), wo)); |
7c673cae FG |
658 | ASSERT_OK(Flush()); |
659 | } | |
660 | // We created 4 sst files in L0 | |
661 | ASSERT_EQ("4", FilesPerLevel(0)); | |
662 | // Compaction will delete files from L0 in first db path and generate a new | |
663 | // file in L1 in second db path | |
664 | CompactRangeOptions compact_options; | |
665 | compact_options.target_path_id = 1; | |
666 | Slice begin("Key0"); | |
667 | Slice end("Key3"); | |
668 | ASSERT_OK(db_->CompactRange(compact_options, &begin, &end)); | |
669 | ASSERT_EQ("0,1", FilesPerLevel(0)); | |
670 | ||
671 | // Create 4 files in L0 | |
672 | for (int i = 4; i < 8; i++) { | |
494da23a | 673 | ASSERT_OK(Put("Key" + ToString(i), DummyString(1024, 'B'), wo)); |
7c673cae FG |
674 | ASSERT_OK(Flush()); |
675 | } | |
676 | ASSERT_EQ("4,1", FilesPerLevel(0)); | |
677 | ||
678 | // Compaction will delete files from L0 in first db path and generate a new | |
679 | // file in L1 in second db path | |
680 | begin = "Key4"; | |
681 | end = "Key7"; | |
682 | ASSERT_OK(db_->CompactRange(compact_options, &begin, &end)); | |
683 | ASSERT_EQ("0,2", FilesPerLevel(0)); | |
684 | ||
685 | sfm->WaitForEmptyTrash(); | |
686 | ASSERT_EQ(bg_delete_file, 8); | |
687 | ||
11fdf7f2 TL |
688 | // Compaction will delete both files and regenerate a file in L1 in second |
689 | // db path. The deleted files should still be cleaned up via delete scheduler. | |
7c673cae | 690 | compact_options.bottommost_level_compaction = |
f67539c2 | 691 | BottommostLevelCompaction::kForceOptimized; |
7c673cae FG |
692 | ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr)); |
693 | ASSERT_EQ("0,1", FilesPerLevel(0)); | |
694 | ||
695 | sfm->WaitForEmptyTrash(); | |
11fdf7f2 | 696 | ASSERT_EQ(bg_delete_file, 10); |
7c673cae | 697 | |
f67539c2 | 698 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing(); |
7c673cae FG |
699 | } |
700 | ||
701 | TEST_F(DBSSTTest, DestroyDBWithRateLimitedDelete) { | |
702 | int bg_delete_file = 0; | |
f67539c2 | 703 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( |
7c673cae | 704 | "DeleteScheduler::DeleteTrashFile:DeleteFile", |
11fdf7f2 | 705 | [&](void* /*arg*/) { bg_delete_file++; }); |
f67539c2 | 706 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing(); |
7c673cae | 707 | |
11fdf7f2 | 708 | Status s; |
7c673cae FG |
709 | Options options = CurrentOptions(); |
710 | options.disable_auto_compactions = true; | |
711 | options.env = env_; | |
11fdf7f2 TL |
712 | options.sst_file_manager.reset( |
713 | NewSstFileManager(env_, nullptr, "", 0, false, &s, 0)); | |
714 | ASSERT_OK(s); | |
7c673cae FG |
715 | DestroyAndReopen(options); |
716 | ||
717 | // Create 4 files in L0 | |
718 | for (int i = 0; i < 4; i++) { | |
719 | ASSERT_OK(Put("Key" + ToString(i), DummyString(1024, 'A'))); | |
720 | ASSERT_OK(Flush()); | |
721 | } | |
722 | // We created 4 sst files in L0 | |
723 | ASSERT_EQ("4", FilesPerLevel(0)); | |
724 | ||
725 | // Close DB and destroy it using DeleteScheduler | |
726 | Close(); | |
7c673cae | 727 | |
494da23a TL |
728 | int num_sst_files = 0; |
729 | int num_wal_files = 0; | |
730 | std::vector<std::string> db_files; | |
731 | env_->GetChildren(dbname_, &db_files); | |
732 | for (std::string f : db_files) { | |
733 | if (f.substr(f.find_last_of(".") + 1) == "sst") { | |
734 | num_sst_files++; | |
735 | } else if (f.substr(f.find_last_of(".") + 1) == "log") { | |
736 | num_wal_files++; | |
737 | } | |
738 | } | |
739 | ASSERT_GT(num_sst_files, 0); | |
740 | ASSERT_GT(num_wal_files, 0); | |
741 | ||
7c673cae | 742 | auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get()); |
11fdf7f2 TL |
743 | |
744 | sfm->SetDeleteRateBytesPerSecond(1024 * 1024); | |
745 | sfm->delete_scheduler()->SetMaxTrashDBRatio(1.1); | |
746 | ASSERT_OK(DestroyDB(dbname_, options)); | |
7c673cae | 747 | sfm->WaitForEmptyTrash(); |
494da23a | 748 | ASSERT_EQ(bg_delete_file, num_sst_files + num_wal_files); |
7c673cae FG |
749 | } |
750 | ||
751 | TEST_F(DBSSTTest, DBWithMaxSpaceAllowed) { | |
752 | std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_)); | |
753 | auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get()); | |
754 | ||
755 | Options options = CurrentOptions(); | |
756 | options.sst_file_manager = sst_file_manager; | |
757 | options.disable_auto_compactions = true; | |
758 | DestroyAndReopen(options); | |
759 | ||
760 | Random rnd(301); | |
761 | ||
762 | // Generate a file containing 100 keys. | |
763 | for (int i = 0; i < 100; i++) { | |
20effc67 | 764 | ASSERT_OK(Put(Key(i), rnd.RandomString(50))); |
7c673cae FG |
765 | } |
766 | ASSERT_OK(Flush()); | |
767 | ||
768 | uint64_t first_file_size = 0; | |
20effc67 TL |
769 | std::unordered_map<std::string, uint64_t> files_in_db; |
770 | ASSERT_OK(GetAllSSTFiles(&files_in_db, &first_file_size)); | |
7c673cae FG |
771 | ASSERT_EQ(sfm->GetTotalSize(), first_file_size); |
772 | ||
773 | // Set the maximum allowed space usage to the current total size | |
774 | sfm->SetMaxAllowedSpaceUsage(first_file_size + 1); | |
775 | ||
776 | ASSERT_OK(Put("key1", "val1")); | |
777 | // This flush will cause bg_error_ and will fail | |
778 | ASSERT_NOK(Flush()); | |
779 | } | |
780 | ||
11fdf7f2 TL |
781 | TEST_F(DBSSTTest, CancellingCompactionsWorks) { |
782 | std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_)); | |
783 | auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get()); | |
784 | ||
785 | Options options = CurrentOptions(); | |
786 | options.sst_file_manager = sst_file_manager; | |
787 | options.level0_file_num_compaction_trigger = 2; | |
788 | options.statistics = CreateDBStatistics(); | |
789 | DestroyAndReopen(options); | |
790 | ||
791 | int completed_compactions = 0; | |
f67539c2 | 792 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( |
11fdf7f2 TL |
793 | "DBImpl::BackgroundCompaction():CancelledCompaction", [&](void* /*arg*/) { |
794 | sfm->SetMaxAllowedSpaceUsage(0); | |
795 | ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0); | |
796 | }); | |
f67539c2 | 797 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( |
11fdf7f2 TL |
798 | "DBImpl::BackgroundCompaction:NonTrivial:AfterRun", |
799 | [&](void* /*arg*/) { completed_compactions++; }); | |
f67539c2 | 800 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing(); |
11fdf7f2 TL |
801 | |
802 | Random rnd(301); | |
803 | ||
804 | // Generate a file containing 10 keys. | |
805 | for (int i = 0; i < 10; i++) { | |
20effc67 | 806 | ASSERT_OK(Put(Key(i), rnd.RandomString(50))); |
11fdf7f2 TL |
807 | } |
808 | ASSERT_OK(Flush()); | |
809 | uint64_t total_file_size = 0; | |
20effc67 TL |
810 | std::unordered_map<std::string, uint64_t> files_in_db; |
811 | ASSERT_OK(GetAllSSTFiles(&files_in_db, &total_file_size)); | |
11fdf7f2 TL |
812 | // Set the maximum allowed space usage to the current total size |
813 | sfm->SetMaxAllowedSpaceUsage(2 * total_file_size + 1); | |
814 | ||
815 | // Generate another file to trigger compaction. | |
816 | for (int i = 0; i < 10; i++) { | |
20effc67 | 817 | ASSERT_OK(Put(Key(i), rnd.RandomString(50))); |
11fdf7f2 TL |
818 | } |
819 | ASSERT_OK(Flush()); | |
820 | dbfull()->TEST_WaitForCompact(true); | |
821 | ||
822 | // Because we set a callback in CancelledCompaction, we actually | |
823 | // let the compaction run | |
824 | ASSERT_GT(completed_compactions, 0); | |
825 | ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0); | |
826 | // Make sure the stat is bumped | |
827 | ASSERT_GT(dbfull()->immutable_db_options().statistics.get()->getTickerCount(COMPACTION_CANCELLED), 0); | |
20effc67 TL |
828 | ASSERT_EQ(0, |
829 | dbfull()->immutable_db_options().statistics.get()->getTickerCount( | |
830 | FILES_MARKED_TRASH)); | |
831 | ASSERT_EQ(4, | |
832 | dbfull()->immutable_db_options().statistics.get()->getTickerCount( | |
833 | FILES_DELETED_IMMEDIATELY)); | |
f67539c2 | 834 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing(); |
11fdf7f2 TL |
835 | } |
836 | ||
837 | TEST_F(DBSSTTest, CancellingManualCompactionsWorks) { | |
838 | std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_)); | |
839 | auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get()); | |
840 | ||
841 | Options options = CurrentOptions(); | |
842 | options.sst_file_manager = sst_file_manager; | |
843 | options.statistics = CreateDBStatistics(); | |
844 | ||
845 | FlushedFileCollector* collector = new FlushedFileCollector(); | |
846 | options.listeners.emplace_back(collector); | |
847 | ||
848 | DestroyAndReopen(options); | |
849 | ||
850 | Random rnd(301); | |
851 | ||
852 | // Generate a file containing 10 keys. | |
853 | for (int i = 0; i < 10; i++) { | |
20effc67 | 854 | ASSERT_OK(Put(Key(i), rnd.RandomString(50))); |
11fdf7f2 TL |
855 | } |
856 | ASSERT_OK(Flush()); | |
857 | uint64_t total_file_size = 0; | |
20effc67 TL |
858 | std::unordered_map<std::string, uint64_t> files_in_db; |
859 | ASSERT_OK(GetAllSSTFiles(&files_in_db, &total_file_size)); | |
11fdf7f2 TL |
860 | // Set the maximum allowed space usage to the current total size |
861 | sfm->SetMaxAllowedSpaceUsage(2 * total_file_size + 1); | |
862 | ||
863 | // Generate another file to trigger compaction. | |
864 | for (int i = 0; i < 10; i++) { | |
20effc67 | 865 | ASSERT_OK(Put(Key(i), rnd.RandomString(50))); |
11fdf7f2 TL |
866 | } |
867 | ASSERT_OK(Flush()); | |
868 | ||
869 | // OK, now trigger a manual compaction | |
870 | dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr); | |
871 | ||
872 | // Wait for manual compaction to get scheduled and finish | |
873 | dbfull()->TEST_WaitForCompact(true); | |
874 | ||
875 | ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0); | |
876 | // Make sure the stat is bumped | |
877 | ASSERT_EQ(dbfull()->immutable_db_options().statistics.get()->getTickerCount( | |
878 | COMPACTION_CANCELLED), | |
879 | 1); | |
880 | ||
881 | // Now make sure CompactFiles also gets cancelled | |
882 | auto l0_files = collector->GetFlushedFiles(); | |
f67539c2 | 883 | dbfull()->CompactFiles(ROCKSDB_NAMESPACE::CompactionOptions(), l0_files, 0); |
11fdf7f2 TL |
884 | |
885 | // Wait for manual compaction to get scheduled and finish | |
886 | dbfull()->TEST_WaitForCompact(true); | |
887 | ||
888 | ASSERT_EQ(dbfull()->immutable_db_options().statistics.get()->getTickerCount( | |
889 | COMPACTION_CANCELLED), | |
890 | 2); | |
891 | ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0); | |
892 | ||
893 | // Now let the flush through and make sure GetCompactionsReservedSize | |
894 | // returns to normal | |
895 | sfm->SetMaxAllowedSpaceUsage(0); | |
896 | int completed_compactions = 0; | |
f67539c2 | 897 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( |
11fdf7f2 TL |
898 | "CompactFilesImpl:End", [&](void* /*arg*/) { completed_compactions++; }); |
899 | ||
f67539c2 TL |
900 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing(); |
901 | dbfull()->CompactFiles(ROCKSDB_NAMESPACE::CompactionOptions(), l0_files, 0); | |
11fdf7f2 TL |
902 | dbfull()->TEST_WaitForCompact(true); |
903 | ||
904 | ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0); | |
905 | ASSERT_GT(completed_compactions, 0); | |
906 | ||
f67539c2 | 907 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing(); |
11fdf7f2 TL |
908 | } |
909 | ||
7c673cae FG |
910 | TEST_F(DBSSTTest, DBWithMaxSpaceAllowedRandomized) { |
911 | // This test will set a maximum allowed space for the DB, then it will | |
912 | // keep filling the DB until the limit is reached and bg_error_ is set. | |
913 | // When bg_error_ is set we will verify that the DB size is greater | |
914 | // than the limit. | |
915 | ||
11fdf7f2 TL |
916 | std::vector<int> max_space_limits_mbs = {1, 10}; |
917 | std::atomic<bool> bg_error_set(false); | |
7c673cae | 918 | |
11fdf7f2 TL |
919 | std::atomic<int> reached_max_space_on_flush(0); |
920 | std::atomic<int> reached_max_space_on_compaction(0); | |
f67539c2 | 921 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( |
7c673cae FG |
922 | "DBImpl::FlushMemTableToOutputFile:MaxAllowedSpaceReached", |
923 | [&](void* arg) { | |
924 | Status* bg_error = static_cast<Status*>(arg); | |
925 | bg_error_set = true; | |
7c673cae | 926 | reached_max_space_on_flush++; |
7c673cae FG |
927 | // clear error to ensure compaction callback is called |
928 | *bg_error = Status::OK(); | |
11fdf7f2 TL |
929 | }); |
930 | ||
f67539c2 | 931 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( |
11fdf7f2 TL |
932 | "DBImpl::BackgroundCompaction():CancelledCompaction", [&](void* arg) { |
933 | bool* enough_room = static_cast<bool*>(arg); | |
934 | *enough_room = true; | |
7c673cae FG |
935 | }); |
936 | ||
f67539c2 | 937 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( |
7c673cae | 938 | "CompactionJob::FinishCompactionOutputFile:MaxAllowedSpaceReached", |
11fdf7f2 | 939 | [&](void* /*arg*/) { |
7c673cae | 940 | bg_error_set = true; |
7c673cae FG |
941 | reached_max_space_on_compaction++; |
942 | }); | |
943 | ||
944 | for (auto limit_mb : max_space_limits_mbs) { | |
945 | bg_error_set = false; | |
f67539c2 TL |
946 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearTrace(); |
947 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing(); | |
7c673cae FG |
948 | std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_)); |
949 | auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get()); | |
950 | ||
951 | Options options = CurrentOptions(); | |
952 | options.sst_file_manager = sst_file_manager; | |
953 | options.write_buffer_size = 1024 * 512; // 512 Kb | |
954 | DestroyAndReopen(options); | |
955 | Random rnd(301); | |
956 | ||
957 | sfm->SetMaxAllowedSpaceUsage(limit_mb * 1024 * 1024); | |
958 | ||
11fdf7f2 TL |
959 | // It is easy to detect if the test is stuck in a loop. No need for |
960 | // complex termination logic. | |
7c673cae | 961 | while (true) { |
20effc67 | 962 | auto s = Put(rnd.RandomString(10), rnd.RandomString(50)); |
7c673cae FG |
963 | if (!s.ok()) { |
964 | break; | |
965 | } | |
7c673cae FG |
966 | } |
967 | ASSERT_TRUE(bg_error_set); | |
11fdf7f2 | 968 | uint64_t total_sst_files_size = 0; |
20effc67 TL |
969 | std::unordered_map<std::string, uint64_t> files_in_db; |
970 | ASSERT_OK(GetAllSSTFiles(&files_in_db, &total_sst_files_size)); | |
7c673cae | 971 | ASSERT_GE(total_sst_files_size, limit_mb * 1024 * 1024); |
f67539c2 | 972 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing(); |
7c673cae FG |
973 | } |
974 | ||
975 | ASSERT_GT(reached_max_space_on_flush, 0); | |
976 | ASSERT_GT(reached_max_space_on_compaction, 0); | |
977 | } | |
978 | ||
979 | TEST_F(DBSSTTest, OpenDBWithInfiniteMaxOpenFiles) { | |
980 | // Open DB with infinite max open files | |
981 | // - First iteration use 1 thread to open files | |
982 | // - Second iteration use 5 threads to open files | |
983 | for (int iter = 0; iter < 2; iter++) { | |
984 | Options options; | |
985 | options.create_if_missing = true; | |
986 | options.write_buffer_size = 100000; | |
987 | options.disable_auto_compactions = true; | |
988 | options.max_open_files = -1; | |
989 | if (iter == 0) { | |
990 | options.max_file_opening_threads = 1; | |
991 | } else { | |
992 | options.max_file_opening_threads = 5; | |
993 | } | |
994 | options = CurrentOptions(options); | |
995 | DestroyAndReopen(options); | |
996 | ||
997 | // Create 12 Files in L0 (then move then to L2) | |
998 | for (int i = 0; i < 12; i++) { | |
999 | std::string k = "L2_" + Key(i); | |
1000 | ASSERT_OK(Put(k, k + std::string(1000, 'a'))); | |
1001 | ASSERT_OK(Flush()); | |
1002 | } | |
1003 | CompactRangeOptions compact_options; | |
1004 | compact_options.change_level = true; | |
1005 | compact_options.target_level = 2; | |
1006 | db_->CompactRange(compact_options, nullptr, nullptr); | |
1007 | ||
1008 | // Create 12 Files in L0 | |
1009 | for (int i = 0; i < 12; i++) { | |
1010 | std::string k = "L0_" + Key(i); | |
1011 | ASSERT_OK(Put(k, k + std::string(1000, 'a'))); | |
1012 | ASSERT_OK(Flush()); | |
1013 | } | |
1014 | Close(); | |
1015 | ||
1016 | // Reopening the DB will load all existing files | |
1017 | Reopen(options); | |
1018 | ASSERT_EQ("12,0,12", FilesPerLevel(0)); | |
1019 | std::vector<std::vector<FileMetaData>> files; | |
1020 | dbfull()->TEST_GetFilesMetaData(db_->DefaultColumnFamily(), &files); | |
1021 | ||
1022 | for (const auto& level : files) { | |
1023 | for (const auto& file : level) { | |
1024 | ASSERT_TRUE(file.table_reader_handle != nullptr); | |
1025 | } | |
1026 | } | |
1027 | ||
1028 | for (int i = 0; i < 12; i++) { | |
1029 | ASSERT_EQ(Get("L0_" + Key(i)), "L0_" + Key(i) + std::string(1000, 'a')); | |
1030 | ASSERT_EQ(Get("L2_" + Key(i)), "L2_" + Key(i) + std::string(1000, 'a')); | |
1031 | } | |
1032 | } | |
1033 | } | |
1034 | ||
1035 | TEST_F(DBSSTTest, GetTotalSstFilesSize) { | |
11fdf7f2 TL |
1036 | // We don't propagate oldest-key-time table property on compaction and |
1037 | // just write 0 as default value. This affect the exact table size, since | |
1038 | // we encode table properties as varint64. Force time to be 0 to work around | |
1039 | // it. Should remove the workaround after we propagate the property on | |
1040 | // compaction. | |
20effc67 TL |
1041 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( |
1042 | "FlushJob::WriteLevel0Table:oldest_ancester_time", [&](void* arg) { | |
1043 | uint64_t* current_time = static_cast<uint64_t*>(arg); | |
1044 | *current_time = 0; | |
1045 | }); | |
1046 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing(); | |
11fdf7f2 | 1047 | |
7c673cae FG |
1048 | Options options = CurrentOptions(); |
1049 | options.disable_auto_compactions = true; | |
1050 | options.compression = kNoCompression; | |
1051 | DestroyAndReopen(options); | |
1052 | // Generate 5 files in L0 | |
1053 | for (int i = 0; i < 5; i++) { | |
1054 | for (int j = 0; j < 10; j++) { | |
1055 | std::string val = "val_file_" + ToString(i); | |
1056 | ASSERT_OK(Put(Key(j), val)); | |
1057 | } | |
1058 | Flush(); | |
1059 | } | |
1060 | ASSERT_EQ("5", FilesPerLevel(0)); | |
1061 | ||
1062 | std::vector<LiveFileMetaData> live_files_meta; | |
1063 | dbfull()->GetLiveFilesMetaData(&live_files_meta); | |
1064 | ASSERT_EQ(live_files_meta.size(), 5); | |
1065 | uint64_t single_file_size = live_files_meta[0].size; | |
1066 | ||
1067 | uint64_t live_sst_files_size = 0; | |
1068 | uint64_t total_sst_files_size = 0; | |
1069 | for (const auto& file_meta : live_files_meta) { | |
1070 | live_sst_files_size += file_meta.size; | |
1071 | } | |
1072 | ||
1073 | ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size", | |
1074 | &total_sst_files_size)); | |
1075 | // Live SST files = 5 | |
1076 | // Total SST files = 5 | |
1077 | ASSERT_EQ(live_sst_files_size, 5 * single_file_size); | |
1078 | ASSERT_EQ(total_sst_files_size, 5 * single_file_size); | |
1079 | ||
1080 | // hold current version | |
1081 | std::unique_ptr<Iterator> iter1(dbfull()->NewIterator(ReadOptions())); | |
1082 | ||
1083 | // Compact 5 files into 1 file in L0 | |
1084 | ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr)); | |
1085 | ASSERT_EQ("0,1", FilesPerLevel(0)); | |
1086 | ||
1087 | live_files_meta.clear(); | |
1088 | dbfull()->GetLiveFilesMetaData(&live_files_meta); | |
1089 | ASSERT_EQ(live_files_meta.size(), 1); | |
1090 | ||
1091 | live_sst_files_size = 0; | |
1092 | total_sst_files_size = 0; | |
1093 | for (const auto& file_meta : live_files_meta) { | |
1094 | live_sst_files_size += file_meta.size; | |
1095 | } | |
1096 | ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size", | |
1097 | &total_sst_files_size)); | |
1098 | // Live SST files = 1 (compacted file) | |
1099 | // Total SST files = 6 (5 original files + compacted file) | |
1100 | ASSERT_EQ(live_sst_files_size, 1 * single_file_size); | |
1101 | ASSERT_EQ(total_sst_files_size, 6 * single_file_size); | |
1102 | ||
1103 | // hold current version | |
1104 | std::unique_ptr<Iterator> iter2(dbfull()->NewIterator(ReadOptions())); | |
1105 | ||
1106 | // Delete all keys and compact, this will delete all live files | |
1107 | for (int i = 0; i < 10; i++) { | |
1108 | ASSERT_OK(Delete(Key(i))); | |
1109 | } | |
1110 | Flush(); | |
1111 | ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr)); | |
1112 | ASSERT_EQ("", FilesPerLevel(0)); | |
1113 | ||
1114 | live_files_meta.clear(); | |
1115 | dbfull()->GetLiveFilesMetaData(&live_files_meta); | |
1116 | ASSERT_EQ(live_files_meta.size(), 0); | |
1117 | ||
1118 | ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size", | |
1119 | &total_sst_files_size)); | |
1120 | // Live SST files = 0 | |
1121 | // Total SST files = 6 (5 original files + compacted file) | |
1122 | ASSERT_EQ(total_sst_files_size, 6 * single_file_size); | |
1123 | ||
1124 | iter1.reset(); | |
1125 | ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size", | |
1126 | &total_sst_files_size)); | |
1127 | // Live SST files = 0 | |
1128 | // Total SST files = 1 (compacted file) | |
1129 | ASSERT_EQ(total_sst_files_size, 1 * single_file_size); | |
1130 | ||
1131 | iter2.reset(); | |
1132 | ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size", | |
1133 | &total_sst_files_size)); | |
1134 | // Live SST files = 0 | |
1135 | // Total SST files = 0 | |
1136 | ASSERT_EQ(total_sst_files_size, 0); | |
11fdf7f2 | 1137 | |
20effc67 | 1138 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing(); |
7c673cae FG |
1139 | } |
1140 | ||
1141 | TEST_F(DBSSTTest, GetTotalSstFilesSizeVersionsFilesShared) { | |
1142 | Options options = CurrentOptions(); | |
1143 | options.disable_auto_compactions = true; | |
1144 | options.compression = kNoCompression; | |
1145 | DestroyAndReopen(options); | |
1146 | // Generate 5 files in L0 | |
1147 | for (int i = 0; i < 5; i++) { | |
1148 | ASSERT_OK(Put(Key(i), "val")); | |
1149 | Flush(); | |
1150 | } | |
1151 | ASSERT_EQ("5", FilesPerLevel(0)); | |
1152 | ||
1153 | std::vector<LiveFileMetaData> live_files_meta; | |
1154 | dbfull()->GetLiveFilesMetaData(&live_files_meta); | |
1155 | ASSERT_EQ(live_files_meta.size(), 5); | |
1156 | uint64_t single_file_size = live_files_meta[0].size; | |
1157 | ||
1158 | uint64_t live_sst_files_size = 0; | |
1159 | uint64_t total_sst_files_size = 0; | |
1160 | for (const auto& file_meta : live_files_meta) { | |
1161 | live_sst_files_size += file_meta.size; | |
1162 | } | |
1163 | ||
1164 | ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size", | |
1165 | &total_sst_files_size)); | |
1166 | ||
1167 | // Live SST files = 5 | |
1168 | // Total SST files = 5 | |
1169 | ASSERT_EQ(live_sst_files_size, 5 * single_file_size); | |
1170 | ASSERT_EQ(total_sst_files_size, 5 * single_file_size); | |
1171 | ||
1172 | // hold current version | |
1173 | std::unique_ptr<Iterator> iter1(dbfull()->NewIterator(ReadOptions())); | |
1174 | ||
1175 | // Compaction will do trivial move from L0 to L1 | |
1176 | ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr)); | |
1177 | ASSERT_EQ("0,5", FilesPerLevel(0)); | |
1178 | ||
1179 | live_files_meta.clear(); | |
1180 | dbfull()->GetLiveFilesMetaData(&live_files_meta); | |
1181 | ASSERT_EQ(live_files_meta.size(), 5); | |
1182 | ||
1183 | live_sst_files_size = 0; | |
1184 | total_sst_files_size = 0; | |
1185 | for (const auto& file_meta : live_files_meta) { | |
1186 | live_sst_files_size += file_meta.size; | |
1187 | } | |
1188 | ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size", | |
1189 | &total_sst_files_size)); | |
1190 | // Live SST files = 5 | |
1191 | // Total SST files = 5 (used in 2 version) | |
1192 | ASSERT_EQ(live_sst_files_size, 5 * single_file_size); | |
1193 | ASSERT_EQ(total_sst_files_size, 5 * single_file_size); | |
1194 | ||
1195 | // hold current version | |
1196 | std::unique_ptr<Iterator> iter2(dbfull()->NewIterator(ReadOptions())); | |
1197 | ||
1198 | // Delete all keys and compact, this will delete all live files | |
1199 | for (int i = 0; i < 5; i++) { | |
1200 | ASSERT_OK(Delete(Key(i))); | |
1201 | } | |
1202 | Flush(); | |
1203 | ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr)); | |
1204 | ASSERT_EQ("", FilesPerLevel(0)); | |
1205 | ||
1206 | live_files_meta.clear(); | |
1207 | dbfull()->GetLiveFilesMetaData(&live_files_meta); | |
1208 | ASSERT_EQ(live_files_meta.size(), 0); | |
1209 | ||
1210 | ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size", | |
1211 | &total_sst_files_size)); | |
1212 | // Live SST files = 0 | |
1213 | // Total SST files = 5 (used in 2 version) | |
1214 | ASSERT_EQ(total_sst_files_size, 5 * single_file_size); | |
1215 | ||
1216 | iter1.reset(); | |
1217 | iter2.reset(); | |
1218 | ||
1219 | ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size", | |
1220 | &total_sst_files_size)); | |
1221 | // Live SST files = 0 | |
1222 | // Total SST files = 0 | |
1223 | ASSERT_EQ(total_sst_files_size, 0); | |
1224 | } | |
1225 | ||
1226 | #endif // ROCKSDB_LITE | |
1227 | ||
f67539c2 | 1228 | } // namespace ROCKSDB_NAMESPACE |
7c673cae FG |
1229 | |
1230 | int main(int argc, char** argv) { | |
f67539c2 | 1231 | ROCKSDB_NAMESPACE::port::InstallStackTraceHandler(); |
7c673cae FG |
1232 | ::testing::InitGoogleTest(&argc, argv); |
1233 | return RUN_ALL_TESTS(); | |
1234 | } |