delete_scheduler_test.cc 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  1. // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. #include <atomic>
  6. #include <cinttypes>
  7. #include <thread>
  8. #include <vector>
  9. #include "file/delete_scheduler.h"
  10. #include "file/sst_file_manager_impl.h"
  11. #include "rocksdb/env.h"
  12. #include "rocksdb/options.h"
  13. #include "test_util/sync_point.h"
  14. #include "test_util/testharness.h"
  15. #include "test_util/testutil.h"
  16. #include "util/string_util.h"
  17. #ifndef ROCKSDB_LITE
  18. namespace ROCKSDB_NAMESPACE {
  19. class DeleteSchedulerTest : public testing::Test {
  20. public:
  21. DeleteSchedulerTest() : env_(Env::Default()) {
  22. const int kNumDataDirs = 3;
  23. dummy_files_dirs_.reserve(kNumDataDirs);
  24. for (size_t i = 0; i < kNumDataDirs; ++i) {
  25. dummy_files_dirs_.emplace_back(
  26. test::PerThreadDBPath(env_, "delete_scheduler_dummy_data_dir") +
  27. ToString(i));
  28. DestroyAndCreateDir(dummy_files_dirs_.back());
  29. }
  30. }
  31. ~DeleteSchedulerTest() override {
  32. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
  33. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({});
  34. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
  35. for (const auto& dummy_files_dir : dummy_files_dirs_) {
  36. test::DestroyDir(env_, dummy_files_dir);
  37. }
  38. }
  39. void DestroyAndCreateDir(const std::string& dir) {
  40. ASSERT_OK(test::DestroyDir(env_, dir));
  41. EXPECT_OK(env_->CreateDir(dir));
  42. }
  43. int CountNormalFiles(size_t dummy_files_dirs_idx = 0) {
  44. std::vector<std::string> files_in_dir;
  45. EXPECT_OK(env_->GetChildren(dummy_files_dirs_[dummy_files_dirs_idx],
  46. &files_in_dir));
  47. int normal_cnt = 0;
  48. for (auto& f : files_in_dir) {
  49. if (!DeleteScheduler::IsTrashFile(f) && f != "." && f != "..") {
  50. normal_cnt++;
  51. }
  52. }
  53. return normal_cnt;
  54. }
  55. int CountTrashFiles(size_t dummy_files_dirs_idx = 0) {
  56. std::vector<std::string> files_in_dir;
  57. EXPECT_OK(env_->GetChildren(dummy_files_dirs_[dummy_files_dirs_idx],
  58. &files_in_dir));
  59. int trash_cnt = 0;
  60. for (auto& f : files_in_dir) {
  61. if (DeleteScheduler::IsTrashFile(f)) {
  62. trash_cnt++;
  63. }
  64. }
  65. return trash_cnt;
  66. }
  67. std::string NewDummyFile(const std::string& file_name, uint64_t size = 1024,
  68. size_t dummy_files_dirs_idx = 0) {
  69. std::string file_path =
  70. dummy_files_dirs_[dummy_files_dirs_idx] + "/" + file_name;
  71. std::unique_ptr<WritableFile> f;
  72. env_->NewWritableFile(file_path, &f, EnvOptions());
  73. std::string data(size, 'A');
  74. EXPECT_OK(f->Append(data));
  75. EXPECT_OK(f->Close());
  76. sst_file_mgr_->OnAddFile(file_path, false);
  77. return file_path;
  78. }
  79. void NewDeleteScheduler() {
  80. // Tests in this file are for DeleteScheduler component and dont create any
  81. // DBs, so we need to set max_trash_db_ratio to 100% (instead of default
  82. // 25%)
  83. std::shared_ptr<FileSystem>
  84. fs(std::make_shared<LegacyFileSystemWrapper>(env_));
  85. sst_file_mgr_.reset(
  86. new SstFileManagerImpl(env_, fs, nullptr, rate_bytes_per_sec_,
  87. /* max_trash_db_ratio= */ 1.1, 128 * 1024));
  88. delete_scheduler_ = sst_file_mgr_->delete_scheduler();
  89. }
  90. Env* env_;
  91. std::vector<std::string> dummy_files_dirs_;
  92. int64_t rate_bytes_per_sec_;
  93. DeleteScheduler* delete_scheduler_;
  94. std::unique_ptr<SstFileManagerImpl> sst_file_mgr_;
  95. };
  96. // Test the basic functionality of DeleteScheduler (Rate Limiting).
  97. // 1- Create 100 dummy files
  98. // 2- Delete the 100 dummy files using DeleteScheduler
  99. // --- Hold DeleteScheduler::BackgroundEmptyTrash ---
  100. // 3- Wait for DeleteScheduler to delete all files in trash
  101. // 4- Verify that BackgroundEmptyTrash used to correct penlties for the files
  102. // 5- Make sure that all created files were completely deleted
  103. TEST_F(DeleteSchedulerTest, BasicRateLimiting) {
  104. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
  105. {"DeleteSchedulerTest::BasicRateLimiting:1",
  106. "DeleteScheduler::BackgroundEmptyTrash"},
  107. });
  108. std::vector<uint64_t> penalties;
  109. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  110. "DeleteScheduler::BackgroundEmptyTrash:Wait",
  111. [&](void* arg) { penalties.push_back(*(static_cast<uint64_t*>(arg))); });
  112. int dir_synced = 0;
  113. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  114. "DeleteScheduler::DeleteTrashFile::AfterSyncDir", [&](void* arg) {
  115. dir_synced++;
  116. std::string* dir = reinterpret_cast<std::string*>(arg);
  117. EXPECT_EQ(dummy_files_dirs_[0], *dir);
  118. });
  119. int num_files = 100; // 100 files
  120. uint64_t file_size = 1024; // every file is 1 kb
  121. std::vector<uint64_t> delete_kbs_per_sec = {512, 200, 100, 50, 25};
  122. for (size_t t = 0; t < delete_kbs_per_sec.size(); t++) {
  123. penalties.clear();
  124. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearTrace();
  125. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
  126. DestroyAndCreateDir(dummy_files_dirs_[0]);
  127. rate_bytes_per_sec_ = delete_kbs_per_sec[t] * 1024;
  128. NewDeleteScheduler();
  129. dir_synced = 0;
  130. // Create 100 dummy files, every file is 1 Kb
  131. std::vector<std::string> generated_files;
  132. for (int i = 0; i < num_files; i++) {
  133. std::string file_name = "file" + ToString(i) + ".data";
  134. generated_files.push_back(NewDummyFile(file_name, file_size));
  135. }
  136. // Delete dummy files and measure time spent to empty trash
  137. for (int i = 0; i < num_files; i++) {
  138. ASSERT_OK(delete_scheduler_->DeleteFile(generated_files[i],
  139. dummy_files_dirs_[0]));
  140. }
  141. ASSERT_EQ(CountNormalFiles(), 0);
  142. uint64_t delete_start_time = env_->NowMicros();
  143. TEST_SYNC_POINT("DeleteSchedulerTest::BasicRateLimiting:1");
  144. delete_scheduler_->WaitForEmptyTrash();
  145. uint64_t time_spent_deleting = env_->NowMicros() - delete_start_time;
  146. auto bg_errors = delete_scheduler_->GetBackgroundErrors();
  147. ASSERT_EQ(bg_errors.size(), 0);
  148. uint64_t total_files_size = 0;
  149. uint64_t expected_penlty = 0;
  150. ASSERT_EQ(penalties.size(), num_files);
  151. for (int i = 0; i < num_files; i++) {
  152. total_files_size += file_size;
  153. expected_penlty = ((total_files_size * 1000000) / rate_bytes_per_sec_);
  154. ASSERT_EQ(expected_penlty, penalties[i]);
  155. }
  156. ASSERT_GT(time_spent_deleting, expected_penlty * 0.9);
  157. ASSERT_EQ(num_files, dir_synced);
  158. ASSERT_EQ(CountTrashFiles(), 0);
  159. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
  160. }
  161. }
  162. TEST_F(DeleteSchedulerTest, MultiDirectoryDeletionsScheduled) {
  163. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
  164. {"DeleteSchedulerTest::MultiDbPathDeletionsScheduled:1",
  165. "DeleteScheduler::BackgroundEmptyTrash"},
  166. });
  167. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
  168. rate_bytes_per_sec_ = 1 << 20; // 1MB
  169. NewDeleteScheduler();
  170. // Generate dummy files in multiple directories
  171. const size_t kNumFiles = dummy_files_dirs_.size();
  172. const size_t kFileSize = 1 << 10; // 1KB
  173. std::vector<std::string> generated_files;
  174. for (size_t i = 0; i < kNumFiles; i++) {
  175. generated_files.push_back(NewDummyFile("file", kFileSize, i));
  176. ASSERT_EQ(1, CountNormalFiles(i));
  177. }
  178. // Mark dummy files as trash
  179. for (size_t i = 0; i < kNumFiles; i++) {
  180. ASSERT_OK(delete_scheduler_->DeleteFile(generated_files[i], ""));
  181. ASSERT_EQ(0, CountNormalFiles(i));
  182. ASSERT_EQ(1, CountTrashFiles(i));
  183. }
  184. TEST_SYNC_POINT("DeleteSchedulerTest::MultiDbPathDeletionsScheduled:1");
  185. delete_scheduler_->WaitForEmptyTrash();
  186. // Verify dummy files eventually got deleted
  187. for (size_t i = 0; i < kNumFiles; i++) {
  188. ASSERT_EQ(0, CountNormalFiles(i));
  189. ASSERT_EQ(0, CountTrashFiles(i));
  190. }
  191. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
  192. }
  193. // Same as the BasicRateLimiting test but delete files in multiple threads.
  194. // 1- Create 100 dummy files
  195. // 2- Delete the 100 dummy files using DeleteScheduler using 10 threads
  196. // --- Hold DeleteScheduler::BackgroundEmptyTrash ---
  197. // 3- Wait for DeleteScheduler to delete all files in queue
  198. // 4- Verify that BackgroundEmptyTrash used to correct penlties for the files
  199. // 5- Make sure that all created files were completely deleted
  200. TEST_F(DeleteSchedulerTest, RateLimitingMultiThreaded) {
  201. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
  202. {"DeleteSchedulerTest::RateLimitingMultiThreaded:1",
  203. "DeleteScheduler::BackgroundEmptyTrash"},
  204. });
  205. std::vector<uint64_t> penalties;
  206. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  207. "DeleteScheduler::BackgroundEmptyTrash:Wait",
  208. [&](void* arg) { penalties.push_back(*(static_cast<uint64_t*>(arg))); });
  209. int thread_cnt = 10;
  210. int num_files = 10; // 10 files per thread
  211. uint64_t file_size = 1024; // every file is 1 kb
  212. std::vector<uint64_t> delete_kbs_per_sec = {512, 200, 100, 50, 25};
  213. for (size_t t = 0; t < delete_kbs_per_sec.size(); t++) {
  214. penalties.clear();
  215. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearTrace();
  216. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
  217. DestroyAndCreateDir(dummy_files_dirs_[0]);
  218. rate_bytes_per_sec_ = delete_kbs_per_sec[t] * 1024;
  219. NewDeleteScheduler();
  220. // Create 100 dummy files, every file is 1 Kb
  221. std::vector<std::string> generated_files;
  222. for (int i = 0; i < num_files * thread_cnt; i++) {
  223. std::string file_name = "file" + ToString(i) + ".data";
  224. generated_files.push_back(NewDummyFile(file_name, file_size));
  225. }
  226. // Delete dummy files using 10 threads and measure time spent to empty trash
  227. std::atomic<int> thread_num(0);
  228. std::vector<port::Thread> threads;
  229. std::function<void()> delete_thread = [&]() {
  230. int idx = thread_num.fetch_add(1);
  231. int range_start = idx * num_files;
  232. int range_end = range_start + num_files;
  233. for (int j = range_start; j < range_end; j++) {
  234. ASSERT_OK(delete_scheduler_->DeleteFile(generated_files[j], ""));
  235. }
  236. };
  237. for (int i = 0; i < thread_cnt; i++) {
  238. threads.emplace_back(delete_thread);
  239. }
  240. for (size_t i = 0; i < threads.size(); i++) {
  241. threads[i].join();
  242. }
  243. uint64_t delete_start_time = env_->NowMicros();
  244. TEST_SYNC_POINT("DeleteSchedulerTest::RateLimitingMultiThreaded:1");
  245. delete_scheduler_->WaitForEmptyTrash();
  246. uint64_t time_spent_deleting = env_->NowMicros() - delete_start_time;
  247. auto bg_errors = delete_scheduler_->GetBackgroundErrors();
  248. ASSERT_EQ(bg_errors.size(), 0);
  249. uint64_t total_files_size = 0;
  250. uint64_t expected_penlty = 0;
  251. ASSERT_EQ(penalties.size(), num_files * thread_cnt);
  252. for (int i = 0; i < num_files * thread_cnt; i++) {
  253. total_files_size += file_size;
  254. expected_penlty = ((total_files_size * 1000000) / rate_bytes_per_sec_);
  255. ASSERT_EQ(expected_penlty, penalties[i]);
  256. }
  257. ASSERT_GT(time_spent_deleting, expected_penlty * 0.9);
  258. ASSERT_EQ(CountNormalFiles(), 0);
  259. ASSERT_EQ(CountTrashFiles(), 0);
  260. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
  261. }
  262. }
  263. // Disable rate limiting by setting rate_bytes_per_sec_ to 0 and make sure
  264. // that when DeleteScheduler delete a file it delete it immediately and dont
  265. // move it to trash
  266. TEST_F(DeleteSchedulerTest, DisableRateLimiting) {
  267. int bg_delete_file = 0;
  268. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  269. "DeleteScheduler::DeleteTrashFile:DeleteFile",
  270. [&](void* /*arg*/) { bg_delete_file++; });
  271. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
  272. rate_bytes_per_sec_ = 0;
  273. NewDeleteScheduler();
  274. for (int i = 0; i < 10; i++) {
  275. // Every file we delete will be deleted immediately
  276. std::string dummy_file = NewDummyFile("dummy.data");
  277. ASSERT_OK(delete_scheduler_->DeleteFile(dummy_file, ""));
  278. ASSERT_TRUE(env_->FileExists(dummy_file).IsNotFound());
  279. ASSERT_EQ(CountNormalFiles(), 0);
  280. ASSERT_EQ(CountTrashFiles(), 0);
  281. }
  282. ASSERT_EQ(bg_delete_file, 0);
  283. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
  284. }
  285. // Testing that moving files to trash with the same name is not a problem
  286. // 1- Create 10 files with the same name "conflict.data"
  287. // 2- Delete the 10 files using DeleteScheduler
  288. // 3- Make sure that trash directory contain 10 files ("conflict.data" x 10)
  289. // --- Hold DeleteScheduler::BackgroundEmptyTrash ---
  290. // 4- Make sure that files are deleted from trash
  291. TEST_F(DeleteSchedulerTest, ConflictNames) {
  292. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
  293. {"DeleteSchedulerTest::ConflictNames:1",
  294. "DeleteScheduler::BackgroundEmptyTrash"},
  295. });
  296. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
  297. rate_bytes_per_sec_ = 1024 * 1024; // 1 Mb/sec
  298. NewDeleteScheduler();
  299. // Create "conflict.data" and move it to trash 10 times
  300. for (int i = 0; i < 10; i++) {
  301. std::string dummy_file = NewDummyFile("conflict.data");
  302. ASSERT_OK(delete_scheduler_->DeleteFile(dummy_file, ""));
  303. }
  304. ASSERT_EQ(CountNormalFiles(), 0);
  305. // 10 files ("conflict.data" x 10) in trash
  306. ASSERT_EQ(CountTrashFiles(), 10);
  307. // Hold BackgroundEmptyTrash
  308. TEST_SYNC_POINT("DeleteSchedulerTest::ConflictNames:1");
  309. delete_scheduler_->WaitForEmptyTrash();
  310. ASSERT_EQ(CountTrashFiles(), 0);
  311. auto bg_errors = delete_scheduler_->GetBackgroundErrors();
  312. ASSERT_EQ(bg_errors.size(), 0);
  313. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
  314. }
  315. // 1- Create 10 dummy files
  316. // 2- Delete the 10 files using DeleteScheduler (move them to trsah)
  317. // 3- Delete the 10 files directly (using env_->DeleteFile)
  318. // --- Hold DeleteScheduler::BackgroundEmptyTrash ---
  319. // 4- Make sure that DeleteScheduler failed to delete the 10 files and
  320. // reported 10 background errors
  321. TEST_F(DeleteSchedulerTest, BackgroundError) {
  322. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
  323. {"DeleteSchedulerTest::BackgroundError:1",
  324. "DeleteScheduler::BackgroundEmptyTrash"},
  325. });
  326. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
  327. rate_bytes_per_sec_ = 1024 * 1024; // 1 Mb/sec
  328. NewDeleteScheduler();
  329. // Generate 10 dummy files and move them to trash
  330. for (int i = 0; i < 10; i++) {
  331. std::string file_name = "data_" + ToString(i) + ".data";
  332. ASSERT_OK(delete_scheduler_->DeleteFile(NewDummyFile(file_name), ""));
  333. }
  334. ASSERT_EQ(CountNormalFiles(), 0);
  335. ASSERT_EQ(CountTrashFiles(), 10);
  336. // Delete 10 files from trash, this will cause background errors in
  337. // BackgroundEmptyTrash since we already deleted the files it was
  338. // goind to delete
  339. for (int i = 0; i < 10; i++) {
  340. std::string file_name = "data_" + ToString(i) + ".data.trash";
  341. ASSERT_OK(env_->DeleteFile(dummy_files_dirs_[0] + "/" + file_name));
  342. }
  343. // Hold BackgroundEmptyTrash
  344. TEST_SYNC_POINT("DeleteSchedulerTest::BackgroundError:1");
  345. delete_scheduler_->WaitForEmptyTrash();
  346. auto bg_errors = delete_scheduler_->GetBackgroundErrors();
  347. ASSERT_EQ(bg_errors.size(), 10);
  348. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
  349. }
  350. // 1- Create 10 dummy files
  351. // 2- Delete 10 dummy files using DeleteScheduler
  352. // 3- Wait for DeleteScheduler to delete all files in queue
  353. // 4- Make sure all files in trash directory were deleted
  354. // 5- Repeat previous steps 5 times
  355. TEST_F(DeleteSchedulerTest, StartBGEmptyTrashMultipleTimes) {
  356. int bg_delete_file = 0;
  357. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  358. "DeleteScheduler::DeleteTrashFile:DeleteFile",
  359. [&](void* /*arg*/) { bg_delete_file++; });
  360. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
  361. rate_bytes_per_sec_ = 1024 * 1024; // 1 MB / sec
  362. NewDeleteScheduler();
  363. // Move files to trash, wait for empty trash, start again
  364. for (int run = 1; run <= 5; run++) {
  365. // Generate 10 dummy files and move them to trash
  366. for (int i = 0; i < 10; i++) {
  367. std::string file_name = "data_" + ToString(i) + ".data";
  368. ASSERT_OK(delete_scheduler_->DeleteFile(NewDummyFile(file_name), ""));
  369. }
  370. ASSERT_EQ(CountNormalFiles(), 0);
  371. delete_scheduler_->WaitForEmptyTrash();
  372. ASSERT_EQ(bg_delete_file, 10 * run);
  373. ASSERT_EQ(CountTrashFiles(), 0);
  374. auto bg_errors = delete_scheduler_->GetBackgroundErrors();
  375. ASSERT_EQ(bg_errors.size(), 0);
  376. }
  377. ASSERT_EQ(bg_delete_file, 50);
  378. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
  379. }
  380. TEST_F(DeleteSchedulerTest, DeletePartialFile) {
  381. int bg_delete_file = 0;
  382. int bg_fsync = 0;
  383. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  384. "DeleteScheduler::DeleteTrashFile:DeleteFile",
  385. [&](void*) { bg_delete_file++; });
  386. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  387. "DeleteScheduler::DeleteTrashFile:Fsync", [&](void*) { bg_fsync++; });
  388. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
  389. rate_bytes_per_sec_ = 1024 * 1024; // 1 MB / sec
  390. NewDeleteScheduler();
  391. // Should delete in 4 batch
  392. ASSERT_OK(
  393. delete_scheduler_->DeleteFile(NewDummyFile("data_1", 500 * 1024), ""));
  394. ASSERT_OK(
  395. delete_scheduler_->DeleteFile(NewDummyFile("data_2", 100 * 1024), ""));
  396. // Should delete in 2 batch
  397. ASSERT_OK(
  398. delete_scheduler_->DeleteFile(NewDummyFile("data_2", 200 * 1024), ""));
  399. delete_scheduler_->WaitForEmptyTrash();
  400. auto bg_errors = delete_scheduler_->GetBackgroundErrors();
  401. ASSERT_EQ(bg_errors.size(), 0);
  402. ASSERT_EQ(7, bg_delete_file);
  403. ASSERT_EQ(4, bg_fsync);
  404. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
  405. }
  406. #ifdef OS_LINUX
  407. TEST_F(DeleteSchedulerTest, NoPartialDeleteWithLink) {
  408. int bg_delete_file = 0;
  409. int bg_fsync = 0;
  410. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  411. "DeleteScheduler::DeleteTrashFile:DeleteFile",
  412. [&](void*) { bg_delete_file++; });
  413. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  414. "DeleteScheduler::DeleteTrashFile:Fsync", [&](void*) { bg_fsync++; });
  415. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
  416. rate_bytes_per_sec_ = 1024 * 1024; // 1 MB / sec
  417. NewDeleteScheduler();
  418. std::string file1 = NewDummyFile("data_1", 500 * 1024);
  419. std::string file2 = NewDummyFile("data_2", 100 * 1024);
  420. ASSERT_OK(env_->LinkFile(file1, dummy_files_dirs_[0] + "/data_1b"));
  421. ASSERT_OK(env_->LinkFile(file2, dummy_files_dirs_[0] + "/data_2b"));
  422. // Should delete in 4 batch if there is no hardlink
  423. ASSERT_OK(delete_scheduler_->DeleteFile(file1, ""));
  424. ASSERT_OK(delete_scheduler_->DeleteFile(file2, ""));
  425. delete_scheduler_->WaitForEmptyTrash();
  426. auto bg_errors = delete_scheduler_->GetBackgroundErrors();
  427. ASSERT_EQ(bg_errors.size(), 0);
  428. ASSERT_EQ(2, bg_delete_file);
  429. ASSERT_EQ(0, bg_fsync);
  430. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
  431. }
  432. #endif
  433. // 1- Create a DeleteScheduler with very slow rate limit (1 Byte / sec)
  434. // 2- Delete 100 files using DeleteScheduler
  435. // 3- Delete the DeleteScheduler (call the destructor while queue is not empty)
  436. // 4- Make sure that not all files were deleted from trash and that
  437. // DeleteScheduler background thread did not delete all files
  438. TEST_F(DeleteSchedulerTest, DestructorWithNonEmptyQueue) {
  439. int bg_delete_file = 0;
  440. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  441. "DeleteScheduler::DeleteTrashFile:DeleteFile",
  442. [&](void* /*arg*/) { bg_delete_file++; });
  443. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
  444. rate_bytes_per_sec_ = 1; // 1 Byte / sec
  445. NewDeleteScheduler();
  446. for (int i = 0; i < 100; i++) {
  447. std::string file_name = "data_" + ToString(i) + ".data";
  448. ASSERT_OK(delete_scheduler_->DeleteFile(NewDummyFile(file_name), ""));
  449. }
  450. // Deleting 100 files will need >28 hours to delete
  451. // we will delete the DeleteScheduler while delete queue is not empty
  452. sst_file_mgr_.reset();
  453. ASSERT_LT(bg_delete_file, 100);
  454. ASSERT_GT(CountTrashFiles(), 0);
  455. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
  456. }
  457. TEST_F(DeleteSchedulerTest, DISABLED_DynamicRateLimiting1) {
  458. std::vector<uint64_t> penalties;
  459. int bg_delete_file = 0;
  460. int fg_delete_file = 0;
  461. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  462. "DeleteScheduler::DeleteTrashFile:DeleteFile",
  463. [&](void* /*arg*/) { bg_delete_file++; });
  464. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  465. "DeleteScheduler::DeleteFile", [&](void* /*arg*/) { fg_delete_file++; });
  466. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  467. "DeleteScheduler::BackgroundEmptyTrash:Wait",
  468. [&](void* arg) { penalties.push_back(*(static_cast<int*>(arg))); });
  469. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
  470. {"DeleteSchedulerTest::DynamicRateLimiting1:1",
  471. "DeleteScheduler::BackgroundEmptyTrash"},
  472. });
  473. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
  474. rate_bytes_per_sec_ = 0; // Disable rate limiting initially
  475. NewDeleteScheduler();
  476. int num_files = 10; // 10 files
  477. uint64_t file_size = 1024; // every file is 1 kb
  478. std::vector<int64_t> delete_kbs_per_sec = {512, 200, 0, 100, 50, -2, 25};
  479. for (size_t t = 0; t < delete_kbs_per_sec.size(); t++) {
  480. penalties.clear();
  481. bg_delete_file = 0;
  482. fg_delete_file = 0;
  483. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearTrace();
  484. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
  485. DestroyAndCreateDir(dummy_files_dirs_[0]);
  486. rate_bytes_per_sec_ = delete_kbs_per_sec[t] * 1024;
  487. delete_scheduler_->SetRateBytesPerSecond(rate_bytes_per_sec_);
  488. // Create 100 dummy files, every file is 1 Kb
  489. std::vector<std::string> generated_files;
  490. for (int i = 0; i < num_files; i++) {
  491. std::string file_name = "file" + ToString(i) + ".data";
  492. generated_files.push_back(NewDummyFile(file_name, file_size));
  493. }
  494. // Delete dummy files and measure time spent to empty trash
  495. for (int i = 0; i < num_files; i++) {
  496. ASSERT_OK(delete_scheduler_->DeleteFile(generated_files[i], ""));
  497. }
  498. ASSERT_EQ(CountNormalFiles(), 0);
  499. if (rate_bytes_per_sec_ > 0) {
  500. uint64_t delete_start_time = env_->NowMicros();
  501. TEST_SYNC_POINT("DeleteSchedulerTest::DynamicRateLimiting1:1");
  502. delete_scheduler_->WaitForEmptyTrash();
  503. uint64_t time_spent_deleting = env_->NowMicros() - delete_start_time;
  504. auto bg_errors = delete_scheduler_->GetBackgroundErrors();
  505. ASSERT_EQ(bg_errors.size(), 0);
  506. uint64_t total_files_size = 0;
  507. uint64_t expected_penlty = 0;
  508. ASSERT_EQ(penalties.size(), num_files);
  509. for (int i = 0; i < num_files; i++) {
  510. total_files_size += file_size;
  511. expected_penlty = ((total_files_size * 1000000) / rate_bytes_per_sec_);
  512. ASSERT_EQ(expected_penlty, penalties[i]);
  513. }
  514. ASSERT_GT(time_spent_deleting, expected_penlty * 0.9);
  515. ASSERT_EQ(bg_delete_file, num_files);
  516. ASSERT_EQ(fg_delete_file, 0);
  517. } else {
  518. ASSERT_EQ(penalties.size(), 0);
  519. ASSERT_EQ(bg_delete_file, 0);
  520. ASSERT_EQ(fg_delete_file, num_files);
  521. }
  522. ASSERT_EQ(CountTrashFiles(), 0);
  523. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
  524. }
  525. }
  526. TEST_F(DeleteSchedulerTest, ImmediateDeleteOn25PercDBSize) {
  527. int bg_delete_file = 0;
  528. int fg_delete_file = 0;
  529. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  530. "DeleteScheduler::DeleteTrashFile:DeleteFile",
  531. [&](void* /*arg*/) { bg_delete_file++; });
  532. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  533. "DeleteScheduler::DeleteFile", [&](void* /*arg*/) { fg_delete_file++; });
  534. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
  535. int num_files = 100; // 100 files
  536. uint64_t file_size = 1024 * 10; // 100 KB as a file size
  537. rate_bytes_per_sec_ = 1; // 1 byte per sec (very slow trash delete)
  538. NewDeleteScheduler();
  539. delete_scheduler_->SetMaxTrashDBRatio(0.25);
  540. std::vector<std::string> generated_files;
  541. for (int i = 0; i < num_files; i++) {
  542. std::string file_name = "file" + ToString(i) + ".data";
  543. generated_files.push_back(NewDummyFile(file_name, file_size));
  544. }
  545. for (std::string& file_name : generated_files) {
  546. delete_scheduler_->DeleteFile(file_name, "");
  547. }
  548. // When we end up with 26 files in trash we will start
  549. // deleting new files immediately
  550. ASSERT_EQ(fg_delete_file, 74);
  551. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
  552. }
  553. TEST_F(DeleteSchedulerTest, IsTrashCheck) {
  554. // Trash files
  555. ASSERT_TRUE(DeleteScheduler::IsTrashFile("x.trash"));
  556. ASSERT_TRUE(DeleteScheduler::IsTrashFile(".trash"));
  557. ASSERT_TRUE(DeleteScheduler::IsTrashFile("abc.sst.trash"));
  558. ASSERT_TRUE(DeleteScheduler::IsTrashFile("/a/b/c/abc..sst.trash"));
  559. ASSERT_TRUE(DeleteScheduler::IsTrashFile("log.trash"));
  560. ASSERT_TRUE(DeleteScheduler::IsTrashFile("^^^^^.log.trash"));
  561. ASSERT_TRUE(DeleteScheduler::IsTrashFile("abc.t.trash"));
  562. // Not trash files
  563. ASSERT_FALSE(DeleteScheduler::IsTrashFile("abc.sst"));
  564. ASSERT_FALSE(DeleteScheduler::IsTrashFile("abc.txt"));
  565. ASSERT_FALSE(DeleteScheduler::IsTrashFile("/a/b/c/abc.sst"));
  566. ASSERT_FALSE(DeleteScheduler::IsTrashFile("/a/b/c/abc.sstrash"));
  567. ASSERT_FALSE(DeleteScheduler::IsTrashFile("^^^^^.trashh"));
  568. ASSERT_FALSE(DeleteScheduler::IsTrashFile("abc.ttrash"));
  569. ASSERT_FALSE(DeleteScheduler::IsTrashFile(".ttrash"));
  570. ASSERT_FALSE(DeleteScheduler::IsTrashFile("abc.trashx"));
  571. }
  572. } // namespace ROCKSDB_NAMESPACE
  573. int main(int argc, char** argv) {
  574. ::testing::InitGoogleTest(&argc, argv);
  575. return RUN_ALL_TESTS();
  576. }
  577. #else
  578. int main(int /*argc*/, char** /*argv*/) {
  579. printf("DeleteScheduler is not supported in ROCKSDB_LITE\n");
  580. return 0;
  581. }
  582. #endif // ROCKSDB_LITE