wal_manager_test.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  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. #ifndef ROCKSDB_LITE
  6. #include <map>
  7. #include <string>
  8. #include "rocksdb/cache.h"
  9. #include "rocksdb/write_batch.h"
  10. #include "rocksdb/write_buffer_manager.h"
  11. #include "db/column_family.h"
  12. #include "db/db_impl/db_impl.h"
  13. #include "db/log_writer.h"
  14. #include "db/version_set.h"
  15. #include "db/wal_manager.h"
  16. #include "env/mock_env.h"
  17. #include "file/writable_file_writer.h"
  18. #include "table/mock_table.h"
  19. #include "test_util/testharness.h"
  20. #include "test_util/testutil.h"
  21. #include "util/string_util.h"
  22. namespace ROCKSDB_NAMESPACE {
  23. // TODO(icanadi) mock out VersionSet
  24. // TODO(icanadi) move other WalManager-specific tests from db_test here
  25. class WalManagerTest : public testing::Test {
  26. public:
  27. WalManagerTest()
  28. : env_(new MockEnv(Env::Default())),
  29. dbname_(test::PerThreadDBPath("wal_manager_test")),
  30. db_options_(),
  31. table_cache_(NewLRUCache(50000, 16)),
  32. write_buffer_manager_(db_options_.db_write_buffer_size),
  33. current_log_number_(0) {
  34. DestroyDB(dbname_, Options());
  35. }
  36. void Init() {
  37. ASSERT_OK(env_->CreateDirIfMissing(dbname_));
  38. ASSERT_OK(env_->CreateDirIfMissing(ArchivalDirectory(dbname_)));
  39. db_options_.db_paths.emplace_back(dbname_,
  40. std::numeric_limits<uint64_t>::max());
  41. db_options_.wal_dir = dbname_;
  42. db_options_.env = env_.get();
  43. fs_.reset(new LegacyFileSystemWrapper(env_.get()));
  44. db_options_.fs = fs_;
  45. versions_.reset(new VersionSet(dbname_, &db_options_, env_options_,
  46. table_cache_.get(), &write_buffer_manager_,
  47. &write_controller_,
  48. /*block_cache_tracer=*/nullptr));
  49. wal_manager_.reset(new WalManager(db_options_, env_options_));
  50. }
  51. void Reopen() {
  52. wal_manager_.reset(new WalManager(db_options_, env_options_));
  53. }
  54. // NOT thread safe
  55. void Put(const std::string& key, const std::string& value) {
  56. assert(current_log_writer_.get() != nullptr);
  57. uint64_t seq = versions_->LastSequence() + 1;
  58. WriteBatch batch;
  59. batch.Put(key, value);
  60. WriteBatchInternal::SetSequence(&batch, seq);
  61. current_log_writer_->AddRecord(WriteBatchInternal::Contents(&batch));
  62. versions_->SetLastAllocatedSequence(seq);
  63. versions_->SetLastPublishedSequence(seq);
  64. versions_->SetLastSequence(seq);
  65. }
  66. // NOT thread safe
  67. void RollTheLog(bool /*archived*/) {
  68. current_log_number_++;
  69. std::string fname = ArchivedLogFileName(dbname_, current_log_number_);
  70. std::unique_ptr<WritableFile> file;
  71. ASSERT_OK(env_->NewWritableFile(fname, &file, env_options_));
  72. std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
  73. NewLegacyWritableFileWrapper(std::move(file)), fname, env_options_));
  74. current_log_writer_.reset(new log::Writer(std::move(file_writer), 0, false));
  75. }
  76. void CreateArchiveLogs(int num_logs, int entries_per_log) {
  77. for (int i = 1; i <= num_logs; ++i) {
  78. RollTheLog(true);
  79. for (int k = 0; k < entries_per_log; ++k) {
  80. Put(ToString(k), std::string(1024, 'a'));
  81. }
  82. }
  83. }
  84. std::unique_ptr<TransactionLogIterator> OpenTransactionLogIter(
  85. const SequenceNumber seq) {
  86. std::unique_ptr<TransactionLogIterator> iter;
  87. Status status = wal_manager_->GetUpdatesSince(
  88. seq, &iter, TransactionLogIterator::ReadOptions(), versions_.get());
  89. EXPECT_OK(status);
  90. return iter;
  91. }
  92. std::unique_ptr<MockEnv> env_;
  93. std::string dbname_;
  94. ImmutableDBOptions db_options_;
  95. WriteController write_controller_;
  96. EnvOptions env_options_;
  97. std::shared_ptr<Cache> table_cache_;
  98. WriteBufferManager write_buffer_manager_;
  99. std::unique_ptr<VersionSet> versions_;
  100. std::unique_ptr<WalManager> wal_manager_;
  101. std::shared_ptr<LegacyFileSystemWrapper> fs_;
  102. std::unique_ptr<log::Writer> current_log_writer_;
  103. uint64_t current_log_number_;
  104. };
  105. TEST_F(WalManagerTest, ReadFirstRecordCache) {
  106. Init();
  107. std::string path = dbname_ + "/000001.log";
  108. std::unique_ptr<WritableFile> file;
  109. ASSERT_OK(env_->NewWritableFile(path, &file, EnvOptions()));
  110. SequenceNumber s;
  111. ASSERT_OK(wal_manager_->TEST_ReadFirstLine(path, 1 /* number */, &s));
  112. ASSERT_EQ(s, 0U);
  113. ASSERT_OK(
  114. wal_manager_->TEST_ReadFirstRecord(kAliveLogFile, 1 /* number */, &s));
  115. ASSERT_EQ(s, 0U);
  116. std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
  117. NewLegacyWritableFileWrapper(std::move(file)), path, EnvOptions()));
  118. log::Writer writer(std::move(file_writer), 1,
  119. db_options_.recycle_log_file_num > 0);
  120. WriteBatch batch;
  121. batch.Put("foo", "bar");
  122. WriteBatchInternal::SetSequence(&batch, 10);
  123. writer.AddRecord(WriteBatchInternal::Contents(&batch));
  124. // TODO(icanadi) move SpecialEnv outside of db_test, so we can reuse it here.
  125. // Waiting for lei to finish with db_test
  126. // env_->count_sequential_reads_ = true;
  127. // sequential_read_counter_ sanity test
  128. // ASSERT_EQ(env_->sequential_read_counter_.Read(), 0);
  129. ASSERT_OK(wal_manager_->TEST_ReadFirstRecord(kAliveLogFile, 1, &s));
  130. ASSERT_EQ(s, 10U);
  131. // did a read
  132. // TODO(icanadi) move SpecialEnv outside of db_test, so we can reuse it here
  133. // ASSERT_EQ(env_->sequential_read_counter_.Read(), 1);
  134. ASSERT_OK(wal_manager_->TEST_ReadFirstRecord(kAliveLogFile, 1, &s));
  135. ASSERT_EQ(s, 10U);
  136. // no new reads since the value is cached
  137. // TODO(icanadi) move SpecialEnv outside of db_test, so we can reuse it here
  138. // ASSERT_EQ(env_->sequential_read_counter_.Read(), 1);
  139. }
  140. namespace {
  141. uint64_t GetLogDirSize(std::string dir_path, Env* env) {
  142. uint64_t dir_size = 0;
  143. std::vector<std::string> files;
  144. env->GetChildren(dir_path, &files);
  145. for (auto& f : files) {
  146. uint64_t number;
  147. FileType type;
  148. if (ParseFileName(f, &number, &type) && type == kLogFile) {
  149. std::string const file_path = dir_path + "/" + f;
  150. uint64_t file_size;
  151. env->GetFileSize(file_path, &file_size);
  152. dir_size += file_size;
  153. }
  154. }
  155. return dir_size;
  156. }
  157. std::vector<std::uint64_t> ListSpecificFiles(
  158. Env* env, const std::string& path, const FileType expected_file_type) {
  159. std::vector<std::string> files;
  160. std::vector<uint64_t> file_numbers;
  161. env->GetChildren(path, &files);
  162. uint64_t number;
  163. FileType type;
  164. for (size_t i = 0; i < files.size(); ++i) {
  165. if (ParseFileName(files[i], &number, &type)) {
  166. if (type == expected_file_type) {
  167. file_numbers.push_back(number);
  168. }
  169. }
  170. }
  171. return file_numbers;
  172. }
  173. int CountRecords(TransactionLogIterator* iter) {
  174. int count = 0;
  175. SequenceNumber lastSequence = 0;
  176. BatchResult res;
  177. while (iter->Valid()) {
  178. res = iter->GetBatch();
  179. EXPECT_TRUE(res.sequence > lastSequence);
  180. ++count;
  181. lastSequence = res.sequence;
  182. EXPECT_OK(iter->status());
  183. iter->Next();
  184. }
  185. return count;
  186. }
  187. } // namespace
  188. TEST_F(WalManagerTest, WALArchivalSizeLimit) {
  189. db_options_.wal_ttl_seconds = 0;
  190. db_options_.wal_size_limit_mb = 1000;
  191. Init();
  192. // TEST : Create WalManager with huge size limit and no ttl.
  193. // Create some archived files and call PurgeObsoleteWALFiles().
  194. // Count the archived log files that survived.
  195. // Assert that all of them did.
  196. // Change size limit. Re-open WalManager.
  197. // Assert that archive is not greater than wal_size_limit_mb after
  198. // PurgeObsoleteWALFiles()
  199. // Set ttl and time_to_check_ to small values. Re-open db.
  200. // Assert that there are no archived logs left.
  201. std::string archive_dir = ArchivalDirectory(dbname_);
  202. CreateArchiveLogs(20, 5000);
  203. std::vector<std::uint64_t> log_files =
  204. ListSpecificFiles(env_.get(), archive_dir, kLogFile);
  205. ASSERT_EQ(log_files.size(), 20U);
  206. db_options_.wal_size_limit_mb = 8;
  207. Reopen();
  208. wal_manager_->PurgeObsoleteWALFiles();
  209. uint64_t archive_size = GetLogDirSize(archive_dir, env_.get());
  210. ASSERT_TRUE(archive_size <= db_options_.wal_size_limit_mb * 1024 * 1024);
  211. db_options_.wal_ttl_seconds = 1;
  212. env_->FakeSleepForMicroseconds(2 * 1000 * 1000);
  213. Reopen();
  214. wal_manager_->PurgeObsoleteWALFiles();
  215. log_files = ListSpecificFiles(env_.get(), archive_dir, kLogFile);
  216. ASSERT_TRUE(log_files.empty());
  217. }
  218. TEST_F(WalManagerTest, WALArchivalTtl) {
  219. db_options_.wal_ttl_seconds = 1000;
  220. Init();
  221. // TEST : Create WalManager with a ttl and no size limit.
  222. // Create some archived log files and call PurgeObsoleteWALFiles().
  223. // Assert that files are not deleted
  224. // Reopen db with small ttl.
  225. // Assert that all archived logs was removed.
  226. std::string archive_dir = ArchivalDirectory(dbname_);
  227. CreateArchiveLogs(20, 5000);
  228. std::vector<uint64_t> log_files =
  229. ListSpecificFiles(env_.get(), archive_dir, kLogFile);
  230. ASSERT_GT(log_files.size(), 0U);
  231. db_options_.wal_ttl_seconds = 1;
  232. env_->FakeSleepForMicroseconds(3 * 1000 * 1000);
  233. Reopen();
  234. wal_manager_->PurgeObsoleteWALFiles();
  235. log_files = ListSpecificFiles(env_.get(), archive_dir, kLogFile);
  236. ASSERT_TRUE(log_files.empty());
  237. }
  238. TEST_F(WalManagerTest, TransactionLogIteratorMoveOverZeroFiles) {
  239. Init();
  240. RollTheLog(false);
  241. Put("key1", std::string(1024, 'a'));
  242. // Create a zero record WAL file.
  243. RollTheLog(false);
  244. RollTheLog(false);
  245. Put("key2", std::string(1024, 'a'));
  246. auto iter = OpenTransactionLogIter(0);
  247. ASSERT_EQ(2, CountRecords(iter.get()));
  248. }
  249. TEST_F(WalManagerTest, TransactionLogIteratorJustEmptyFile) {
  250. Init();
  251. RollTheLog(false);
  252. auto iter = OpenTransactionLogIter(0);
  253. // Check that an empty iterator is returned
  254. ASSERT_TRUE(!iter->Valid());
  255. }
  256. TEST_F(WalManagerTest, TransactionLogIteratorNewFileWhileScanning) {
  257. Init();
  258. CreateArchiveLogs(2, 100);
  259. auto iter = OpenTransactionLogIter(0);
  260. CreateArchiveLogs(1, 100);
  261. int i = 0;
  262. for (; iter->Valid(); iter->Next()) {
  263. i++;
  264. }
  265. ASSERT_EQ(i, 200);
  266. // A new log file was added after the iterator was created.
  267. // TryAgain indicates a new iterator is needed to fetch the new data
  268. ASSERT_TRUE(iter->status().IsTryAgain());
  269. iter = OpenTransactionLogIter(0);
  270. i = 0;
  271. for (; iter->Valid(); iter->Next()) {
  272. i++;
  273. }
  274. ASSERT_EQ(i, 300);
  275. ASSERT_TRUE(iter->status().ok());
  276. }
  277. } // namespace ROCKSDB_NAMESPACE
  278. int main(int argc, char** argv) {
  279. ::testing::InitGoogleTest(&argc, argv);
  280. return RUN_ALL_TESTS();
  281. }
  282. #else
  283. #include <stdio.h>
  284. int main(int /*argc*/, char** /*argv*/) {
  285. fprintf(stderr, "SKIPPED as WalManager is not supported in ROCKSDB_LITE\n");
  286. return 0;
  287. }
  288. #endif // !ROCKSDB_LITE