mock_table.cc 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  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 "table/mock_table.h"
  6. #include "db/dbformat.h"
  7. #include "env/composite_env_wrapper.h"
  8. #include "file/random_access_file_reader.h"
  9. #include "port/port.h"
  10. #include "rocksdb/table_properties.h"
  11. #include "table/get_context.h"
  12. #include "util/coding.h"
  13. namespace ROCKSDB_NAMESPACE::mock {
  14. KVVector MakeMockFile(std::initializer_list<KVPair> l) { return KVVector(l); }
  15. void SortKVVector(KVVector* kv_vector, const Comparator* ucmp) {
  16. InternalKeyComparator icmp(ucmp);
  17. std::sort(kv_vector->begin(), kv_vector->end(),
  18. [icmp](KVPair a, KVPair b) -> bool {
  19. return icmp.Compare(a.first, b.first) < 0;
  20. });
  21. }
  22. class MockTableIterator : public InternalIterator {
  23. public:
  24. explicit MockTableIterator(const KVVector& table) : table_(table) {
  25. itr_ = table_.end();
  26. }
  27. bool Valid() const override { return itr_ != table_.end(); }
  28. void SeekToFirst() override { itr_ = table_.begin(); }
  29. void SeekToLast() override {
  30. itr_ = table_.end();
  31. --itr_;
  32. }
  33. void Seek(const Slice& target) override {
  34. KVPair target_pair(target.ToString(), "");
  35. InternalKeyComparator icmp(BytewiseComparator());
  36. itr_ = std::lower_bound(table_.begin(), table_.end(), target_pair,
  37. [icmp](KVPair a, KVPair b) -> bool {
  38. return icmp.Compare(a.first, b.first) < 0;
  39. });
  40. }
  41. void SeekForPrev(const Slice& target) override {
  42. KVPair target_pair(target.ToString(), "");
  43. InternalKeyComparator icmp(BytewiseComparator());
  44. itr_ = std::upper_bound(table_.begin(), table_.end(), target_pair,
  45. [icmp](KVPair a, KVPair b) -> bool {
  46. return icmp.Compare(a.first, b.first) < 0;
  47. });
  48. Prev();
  49. }
  50. void Next() override { ++itr_; }
  51. void Prev() override {
  52. if (itr_ == table_.begin()) {
  53. itr_ = table_.end();
  54. } else {
  55. --itr_;
  56. }
  57. }
  58. Slice key() const override { return Slice(itr_->first); }
  59. Slice value() const override { return Slice(itr_->second); }
  60. Status status() const override { return Status::OK(); }
  61. private:
  62. const KVVector& table_;
  63. KVVector::const_iterator itr_;
  64. };
  65. class MockTableBuilder : public TableBuilder {
  66. public:
  67. MockTableBuilder(uint32_t id, MockTableFileSystem* file_system,
  68. MockTableFactory::MockCorruptionMode corrupt_mode =
  69. MockTableFactory::kCorruptNone,
  70. size_t key_value_size = 1)
  71. : id_(id),
  72. file_system_(file_system),
  73. corrupt_mode_(corrupt_mode),
  74. key_value_size_(key_value_size) {
  75. table_ = MakeMockFile({});
  76. }
  77. // REQUIRES: Either Finish() or Abandon() has been called.
  78. ~MockTableBuilder() = default;
  79. // Add key,value to the table being constructed.
  80. // REQUIRES: key is after any previously added key according to comparator.
  81. // REQUIRES: Finish(), Abandon() have not been called
  82. void Add(const Slice& key, const Slice& value) override {
  83. if (corrupt_mode_ == MockTableFactory::kCorruptValue) {
  84. // Corrupt the value
  85. table_.push_back({key.ToString(), value.ToString() + " "});
  86. corrupt_mode_ = MockTableFactory::kCorruptNone;
  87. } else if (corrupt_mode_ == MockTableFactory::kCorruptKey) {
  88. table_.push_back({key.ToString() + " ", value.ToString()});
  89. corrupt_mode_ = MockTableFactory::kCorruptNone;
  90. } else if (corrupt_mode_ == MockTableFactory::kCorruptReorderKey) {
  91. if (prev_key_.empty()) {
  92. prev_key_ = key.ToString();
  93. prev_value_ = value.ToString();
  94. } else {
  95. table_.push_back({key.ToString(), value.ToString()});
  96. table_.push_back({prev_key_, prev_value_});
  97. corrupt_mode_ = MockTableFactory::kCorruptNone;
  98. }
  99. } else {
  100. table_.push_back({key.ToString(), value.ToString()});
  101. }
  102. }
  103. // Return non-ok iff some error has been detected.
  104. Status status() const override { return Status::OK(); }
  105. // Return non-ok iff some error happens during IO.
  106. IOStatus io_status() const override { return IOStatus::OK(); }
  107. Status Finish() override {
  108. MutexLock lock_guard(&file_system_->mutex);
  109. file_system_->files.insert({id_, table_});
  110. return Status::OK();
  111. }
  112. void Abandon() override {}
  113. uint64_t NumEntries() const override { return table_.size(); }
  114. uint64_t FileSize() const override { return table_.size() * key_value_size_; }
  115. TableProperties GetTableProperties() const override {
  116. return TableProperties();
  117. }
  118. // Get file checksum
  119. std::string GetFileChecksum() const override { return kUnknownFileChecksum; }
  120. // Get file checksum function name
  121. const char* GetFileChecksumFuncName() const override {
  122. return kUnknownFileChecksumFuncName;
  123. }
  124. private:
  125. uint32_t id_;
  126. std::string prev_key_;
  127. std::string prev_value_;
  128. MockTableFileSystem* file_system_;
  129. int corrupt_mode_;
  130. KVVector table_;
  131. size_t key_value_size_;
  132. };
  133. InternalIterator* MockTableReader::NewIterator(
  134. const ReadOptions&, const SliceTransform* /* prefix_extractor */,
  135. Arena* /*arena*/, bool /*skip_filters*/, TableReaderCaller /*caller*/,
  136. size_t /*compaction_readahead_size*/, bool /* allow_unprepared_value */) {
  137. return new MockTableIterator(table_);
  138. }
  139. Status MockTableReader::Get(const ReadOptions&, const Slice& key,
  140. GetContext* get_context,
  141. const SliceTransform* /*prefix_extractor*/,
  142. bool /*skip_filters*/) {
  143. std::unique_ptr<MockTableIterator> iter(new MockTableIterator(table_));
  144. for (iter->Seek(key); iter->Valid(); iter->Next()) {
  145. ParsedInternalKey parsed_key;
  146. Status pik_status =
  147. ParseInternalKey(iter->key(), &parsed_key, true /* log_err_key */);
  148. if (!pik_status.ok()) {
  149. return pik_status;
  150. }
  151. bool dont_care __attribute__((__unused__));
  152. Status read_status;
  153. bool ret = get_context->SaveValue(parsed_key, iter->value(), &dont_care,
  154. &read_status);
  155. if (!read_status.ok()) {
  156. return read_status;
  157. }
  158. if (!ret) {
  159. break;
  160. }
  161. }
  162. return Status::OK();
  163. }
  164. MockTableFactory::MockTableFactory()
  165. : next_id_(1), corrupt_mode_(MockTableFactory::kCorruptNone) {}
  166. Status MockTableFactory::NewTableReader(
  167. const ReadOptions& /*ro*/,
  168. const TableReaderOptions& /*table_reader_options*/,
  169. std::unique_ptr<RandomAccessFileReader>&& file, uint64_t /*file_size*/,
  170. std::unique_ptr<TableReader>* table_reader,
  171. bool /*prefetch_index_and_filter_in_cache*/) const {
  172. uint32_t id;
  173. Status s = GetIDFromFile(file.get(), &id);
  174. if (!s.ok()) {
  175. return s;
  176. }
  177. MutexLock lock_guard(&file_system_.mutex);
  178. auto it = file_system_.files.find(id);
  179. if (it == file_system_.files.end()) {
  180. return Status::IOError("Mock file not found");
  181. }
  182. table_reader->reset(new MockTableReader(it->second));
  183. return Status::OK();
  184. }
  185. TableBuilder* MockTableFactory::NewTableBuilder(
  186. const TableBuilderOptions& /*table_builder_options*/,
  187. WritableFileWriter* file) const {
  188. uint32_t id;
  189. Status s = GetAndWriteNextID(file, &id);
  190. assert(s.ok());
  191. return new MockTableBuilder(id, &file_system_, corrupt_mode_,
  192. key_value_size_);
  193. }
  194. Status MockTableFactory::CreateMockTable(Env* env, const std::string& fname,
  195. KVVector file_contents) {
  196. std::unique_ptr<WritableFileWriter> file_writer;
  197. Status s = WritableFileWriter::Create(env->GetFileSystem(), fname,
  198. FileOptions(), &file_writer, nullptr);
  199. if (!s.ok()) {
  200. return s;
  201. }
  202. uint32_t id;
  203. s = GetAndWriteNextID(file_writer.get(), &id);
  204. if (s.ok()) {
  205. file_system_.files.insert({id, std::move(file_contents)});
  206. }
  207. return s;
  208. }
  209. Status MockTableFactory::GetAndWriteNextID(WritableFileWriter* file,
  210. uint32_t* next_id) const {
  211. *next_id = next_id_.fetch_add(1);
  212. char buf[4];
  213. EncodeFixed32(buf, *next_id);
  214. return file->Append(IOOptions(), Slice(buf, 4));
  215. }
  216. Status MockTableFactory::GetIDFromFile(RandomAccessFileReader* file,
  217. uint32_t* id) const {
  218. char buf[4];
  219. Slice result;
  220. Status s = file->Read(IOOptions(), 0, 4, &result, buf, nullptr);
  221. assert(result.size() == 4);
  222. *id = DecodeFixed32(buf);
  223. return s;
  224. }
  225. void MockTableFactory::AssertSingleFile(const KVVector& file_contents) {
  226. ASSERT_EQ(file_system_.files.size(), 1U);
  227. ASSERT_EQ(file_contents, file_system_.files.begin()->second);
  228. }
  229. void MockTableFactory::AssertLatestFiles(
  230. const std::vector<KVVector>& files_contents) {
  231. ASSERT_GE(file_system_.files.size(), files_contents.size());
  232. auto it = file_system_.files.rbegin();
  233. for (auto expect = files_contents.rbegin(); expect != files_contents.rend();
  234. expect++, it++) {
  235. ASSERT_TRUE(it != file_system_.files.rend());
  236. if (*expect != it->second) {
  237. std::cout << "Wrong content! Content of file, expect:" << std::endl;
  238. for (const auto& kv : *expect) {
  239. ParsedInternalKey ikey;
  240. std::string key, value;
  241. std::tie(key, value) = kv;
  242. ASSERT_OK(ParseInternalKey(Slice(key), &ikey, true /* log_err_key */));
  243. std::cout << ikey.DebugString(true, false) << " -> " << value
  244. << std::endl;
  245. }
  246. std::cout << "actual:" << std::endl;
  247. for (const auto& kv : it->second) {
  248. ParsedInternalKey ikey;
  249. std::string key, value;
  250. std::tie(key, value) = kv;
  251. ASSERT_OK(ParseInternalKey(Slice(key), &ikey, true /* log_err_key */));
  252. std::cout << ikey.DebugString(true, false) << " -> " << value
  253. << std::endl;
  254. }
  255. FAIL();
  256. }
  257. }
  258. }
  259. } // namespace ROCKSDB_NAMESPACE::mock