db_blob_index_test.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  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. //
  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. #include <functional>
  10. #include <string>
  11. #include <utility>
  12. #include <vector>
  13. #include "db/arena_wrapped_db_iter.h"
  14. #include "db/column_family.h"
  15. #include "db/db_iter.h"
  16. #include "db/db_test_util.h"
  17. #include "db/dbformat.h"
  18. #include "db/write_batch_internal.h"
  19. #include "port/port.h"
  20. #include "port/stack_trace.h"
  21. #include "util/string_util.h"
  22. #include "utilities/merge_operators.h"
  23. namespace ROCKSDB_NAMESPACE {
  24. // kTypeBlobIndex is a value type used by BlobDB only. The base rocksdb
  25. // should accept the value type on write, and report not supported value
  26. // for reads, unless caller request for it explicitly. The base rocksdb
  27. // doesn't understand format of actual blob index (the value).
  28. class DBBlobIndexTest : public DBTestBase {
  29. public:
  30. enum Tier {
  31. kMemtable = 0,
  32. kImmutableMemtables = 1,
  33. kL0SstFile = 2,
  34. kLnSstFile = 3,
  35. };
  36. const std::vector<Tier> kAllTiers = {Tier::kMemtable,
  37. Tier::kImmutableMemtables,
  38. Tier::kL0SstFile, Tier::kLnSstFile};
  39. DBBlobIndexTest() : DBTestBase("/db_blob_index_test") {}
  40. ColumnFamilyHandle* cfh() { return dbfull()->DefaultColumnFamily(); }
  41. ColumnFamilyData* cfd() {
  42. return reinterpret_cast<ColumnFamilyHandleImpl*>(cfh())->cfd();
  43. }
  44. Status PutBlobIndex(WriteBatch* batch, const Slice& key,
  45. const Slice& blob_index) {
  46. return WriteBatchInternal::PutBlobIndex(batch, cfd()->GetID(), key,
  47. blob_index);
  48. }
  49. Status Write(WriteBatch* batch) {
  50. return dbfull()->Write(WriteOptions(), batch);
  51. }
  52. std::string GetImpl(const Slice& key, bool* is_blob_index = nullptr,
  53. const Snapshot* snapshot = nullptr) {
  54. ReadOptions read_options;
  55. read_options.snapshot = snapshot;
  56. PinnableSlice value;
  57. DBImpl::GetImplOptions get_impl_options;
  58. get_impl_options.column_family = cfh();
  59. get_impl_options.value = &value;
  60. get_impl_options.is_blob_index = is_blob_index;
  61. auto s = dbfull()->GetImpl(read_options, key, get_impl_options);
  62. if (s.IsNotFound()) {
  63. return "NOT_FOUND";
  64. }
  65. if (s.IsNotSupported()) {
  66. return "NOT_SUPPORTED";
  67. }
  68. if (!s.ok()) {
  69. return s.ToString();
  70. }
  71. return value.ToString();
  72. }
  73. std::string GetBlobIndex(const Slice& key,
  74. const Snapshot* snapshot = nullptr) {
  75. bool is_blob_index = false;
  76. std::string value = GetImpl(key, &is_blob_index, snapshot);
  77. if (!is_blob_index) {
  78. return "NOT_BLOB";
  79. }
  80. return value;
  81. }
  82. ArenaWrappedDBIter* GetBlobIterator() {
  83. return dbfull()->NewIteratorImpl(
  84. ReadOptions(), cfd(), dbfull()->GetLatestSequenceNumber(),
  85. nullptr /*read_callback*/, true /*allow_blob*/);
  86. }
  87. Options GetTestOptions() {
  88. Options options;
  89. options.create_if_missing = true;
  90. options.num_levels = 2;
  91. options.disable_auto_compactions = true;
  92. // Disable auto flushes.
  93. options.max_write_buffer_number = 10;
  94. options.min_write_buffer_number_to_merge = 10;
  95. options.merge_operator = MergeOperators::CreateStringAppendOperator();
  96. return options;
  97. }
  98. void MoveDataTo(Tier tier) {
  99. switch (tier) {
  100. case Tier::kMemtable:
  101. break;
  102. case Tier::kImmutableMemtables:
  103. ASSERT_OK(dbfull()->TEST_SwitchMemtable());
  104. break;
  105. case Tier::kL0SstFile:
  106. ASSERT_OK(Flush());
  107. break;
  108. case Tier::kLnSstFile:
  109. ASSERT_OK(Flush());
  110. ASSERT_OK(Put("a", "dummy"));
  111. ASSERT_OK(Put("z", "dummy"));
  112. ASSERT_OK(Flush());
  113. ASSERT_OK(
  114. dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
  115. #ifndef ROCKSDB_LITE
  116. ASSERT_EQ("0,1", FilesPerLevel());
  117. #endif // !ROCKSDB_LITE
  118. break;
  119. }
  120. }
  121. };
  122. // Should be able to write kTypeBlobIndex to memtables and SST files.
  123. TEST_F(DBBlobIndexTest, Write) {
  124. for (auto tier : kAllTiers) {
  125. DestroyAndReopen(GetTestOptions());
  126. for (int i = 1; i <= 5; i++) {
  127. std::string index = ToString(i);
  128. WriteBatch batch;
  129. ASSERT_OK(PutBlobIndex(&batch, "key" + index, "blob" + index));
  130. ASSERT_OK(Write(&batch));
  131. }
  132. MoveDataTo(tier);
  133. for (int i = 1; i <= 5; i++) {
  134. std::string index = ToString(i);
  135. ASSERT_EQ("blob" + index, GetBlobIndex("key" + index));
  136. }
  137. }
  138. }
  139. // Get should be able to return blob index if is_blob_index is provided,
  140. // otherwise return Status::NotSupported status.
  141. TEST_F(DBBlobIndexTest, Get) {
  142. for (auto tier : kAllTiers) {
  143. DestroyAndReopen(GetTestOptions());
  144. WriteBatch batch;
  145. ASSERT_OK(batch.Put("key", "value"));
  146. ASSERT_OK(PutBlobIndex(&batch, "blob_key", "blob_index"));
  147. ASSERT_OK(Write(&batch));
  148. MoveDataTo(tier);
  149. // Verify normal value
  150. bool is_blob_index = false;
  151. PinnableSlice value;
  152. ASSERT_EQ("value", Get("key"));
  153. ASSERT_EQ("value", GetImpl("key"));
  154. ASSERT_EQ("value", GetImpl("key", &is_blob_index));
  155. ASSERT_FALSE(is_blob_index);
  156. // Verify blob index
  157. ASSERT_TRUE(Get("blob_key", &value).IsNotSupported());
  158. ASSERT_EQ("NOT_SUPPORTED", GetImpl("blob_key"));
  159. ASSERT_EQ("blob_index", GetImpl("blob_key", &is_blob_index));
  160. ASSERT_TRUE(is_blob_index);
  161. }
  162. }
  163. // Get should NOT return Status::NotSupported if blob index is updated with
  164. // a normal value.
  165. TEST_F(DBBlobIndexTest, Updated) {
  166. for (auto tier : kAllTiers) {
  167. DestroyAndReopen(GetTestOptions());
  168. WriteBatch batch;
  169. for (int i = 0; i < 10; i++) {
  170. ASSERT_OK(PutBlobIndex(&batch, "key" + ToString(i), "blob_index"));
  171. }
  172. ASSERT_OK(Write(&batch));
  173. // Avoid blob values from being purged.
  174. const Snapshot* snapshot = dbfull()->GetSnapshot();
  175. ASSERT_OK(Put("key1", "new_value"));
  176. ASSERT_OK(Merge("key2", "a"));
  177. ASSERT_OK(Merge("key2", "b"));
  178. ASSERT_OK(Merge("key2", "c"));
  179. ASSERT_OK(Delete("key3"));
  180. ASSERT_OK(SingleDelete("key4"));
  181. ASSERT_OK(Delete("key5"));
  182. ASSERT_OK(Merge("key5", "a"));
  183. ASSERT_OK(Merge("key5", "b"));
  184. ASSERT_OK(Merge("key5", "c"));
  185. ASSERT_OK(dbfull()->DeleteRange(WriteOptions(), cfh(), "key6", "key9"));
  186. MoveDataTo(tier);
  187. for (int i = 0; i < 10; i++) {
  188. ASSERT_EQ("blob_index", GetBlobIndex("key" + ToString(i), snapshot));
  189. }
  190. ASSERT_EQ("new_value", Get("key1"));
  191. ASSERT_EQ("NOT_SUPPORTED", GetImpl("key2"));
  192. ASSERT_EQ("NOT_FOUND", Get("key3"));
  193. ASSERT_EQ("NOT_FOUND", Get("key4"));
  194. ASSERT_EQ("a,b,c", GetImpl("key5"));
  195. for (int i = 6; i < 9; i++) {
  196. ASSERT_EQ("NOT_FOUND", Get("key" + ToString(i)));
  197. }
  198. ASSERT_EQ("blob_index", GetBlobIndex("key9"));
  199. dbfull()->ReleaseSnapshot(snapshot);
  200. }
  201. }
  202. // Iterator should get blob value if allow_blob flag is set,
  203. // otherwise return Status::NotSupported status.
  204. TEST_F(DBBlobIndexTest, Iterate) {
  205. const std::vector<std::vector<ValueType>> data = {
  206. /*00*/ {kTypeValue},
  207. /*01*/ {kTypeBlobIndex},
  208. /*02*/ {kTypeValue},
  209. /*03*/ {kTypeBlobIndex, kTypeValue},
  210. /*04*/ {kTypeValue},
  211. /*05*/ {kTypeValue, kTypeBlobIndex},
  212. /*06*/ {kTypeValue},
  213. /*07*/ {kTypeDeletion, kTypeBlobIndex},
  214. /*08*/ {kTypeValue},
  215. /*09*/ {kTypeSingleDeletion, kTypeBlobIndex},
  216. /*10*/ {kTypeValue},
  217. /*11*/ {kTypeMerge, kTypeMerge, kTypeMerge, kTypeBlobIndex},
  218. /*12*/ {kTypeValue},
  219. /*13*/
  220. {kTypeMerge, kTypeMerge, kTypeMerge, kTypeDeletion, kTypeBlobIndex},
  221. /*14*/ {kTypeValue},
  222. /*15*/ {kTypeBlobIndex},
  223. /*16*/ {kTypeValue},
  224. };
  225. auto get_key = [](int index) {
  226. char buf[20];
  227. snprintf(buf, sizeof(buf), "%02d", index);
  228. return "key" + std::string(buf);
  229. };
  230. auto get_value = [&](int index, int version) {
  231. return get_key(index) + "_value" + ToString(version);
  232. };
  233. auto check_iterator = [&](Iterator* iterator, Status::Code expected_status,
  234. const Slice& expected_value) {
  235. ASSERT_EQ(expected_status, iterator->status().code());
  236. if (expected_status == Status::kOk) {
  237. ASSERT_TRUE(iterator->Valid());
  238. ASSERT_EQ(expected_value, iterator->value());
  239. } else {
  240. ASSERT_FALSE(iterator->Valid());
  241. }
  242. };
  243. auto create_normal_iterator = [&]() -> Iterator* {
  244. return dbfull()->NewIterator(ReadOptions());
  245. };
  246. auto create_blob_iterator = [&]() -> Iterator* { return GetBlobIterator(); };
  247. auto check_is_blob = [&](bool is_blob) {
  248. return [is_blob](Iterator* iterator) {
  249. ASSERT_EQ(is_blob,
  250. reinterpret_cast<ArenaWrappedDBIter*>(iterator)->IsBlob());
  251. };
  252. };
  253. auto verify = [&](int index, Status::Code expected_status,
  254. const Slice& forward_value, const Slice& backward_value,
  255. std::function<Iterator*()> create_iterator,
  256. std::function<void(Iterator*)> extra_check = nullptr) {
  257. // Seek
  258. auto* iterator = create_iterator();
  259. ASSERT_OK(iterator->Refresh());
  260. iterator->Seek(get_key(index));
  261. check_iterator(iterator, expected_status, forward_value);
  262. if (extra_check) {
  263. extra_check(iterator);
  264. }
  265. delete iterator;
  266. // Next
  267. iterator = create_iterator();
  268. ASSERT_OK(iterator->Refresh());
  269. iterator->Seek(get_key(index - 1));
  270. ASSERT_TRUE(iterator->Valid());
  271. iterator->Next();
  272. check_iterator(iterator, expected_status, forward_value);
  273. if (extra_check) {
  274. extra_check(iterator);
  275. }
  276. delete iterator;
  277. // SeekForPrev
  278. iterator = create_iterator();
  279. ASSERT_OK(iterator->Refresh());
  280. iterator->SeekForPrev(get_key(index));
  281. check_iterator(iterator, expected_status, backward_value);
  282. if (extra_check) {
  283. extra_check(iterator);
  284. }
  285. delete iterator;
  286. // Prev
  287. iterator = create_iterator();
  288. iterator->Seek(get_key(index + 1));
  289. ASSERT_TRUE(iterator->Valid());
  290. iterator->Prev();
  291. check_iterator(iterator, expected_status, backward_value);
  292. if (extra_check) {
  293. extra_check(iterator);
  294. }
  295. delete iterator;
  296. };
  297. for (auto tier : {Tier::kMemtable} /*kAllTiers*/) {
  298. // Avoid values from being purged.
  299. std::vector<const Snapshot*> snapshots;
  300. DestroyAndReopen(GetTestOptions());
  301. // fill data
  302. for (int i = 0; i < static_cast<int>(data.size()); i++) {
  303. for (int j = static_cast<int>(data[i].size()) - 1; j >= 0; j--) {
  304. std::string key = get_key(i);
  305. std::string value = get_value(i, j);
  306. WriteBatch batch;
  307. switch (data[i][j]) {
  308. case kTypeValue:
  309. ASSERT_OK(Put(key, value));
  310. break;
  311. case kTypeDeletion:
  312. ASSERT_OK(Delete(key));
  313. break;
  314. case kTypeSingleDeletion:
  315. ASSERT_OK(SingleDelete(key));
  316. break;
  317. case kTypeMerge:
  318. ASSERT_OK(Merge(key, value));
  319. break;
  320. case kTypeBlobIndex:
  321. ASSERT_OK(PutBlobIndex(&batch, key, value));
  322. ASSERT_OK(Write(&batch));
  323. break;
  324. default:
  325. assert(false);
  326. };
  327. }
  328. snapshots.push_back(dbfull()->GetSnapshot());
  329. }
  330. ASSERT_OK(
  331. dbfull()->DeleteRange(WriteOptions(), cfh(), get_key(15), get_key(16)));
  332. snapshots.push_back(dbfull()->GetSnapshot());
  333. MoveDataTo(tier);
  334. // Normal iterator
  335. verify(1, Status::kNotSupported, "", "", create_normal_iterator);
  336. verify(3, Status::kNotSupported, "", "", create_normal_iterator);
  337. verify(5, Status::kOk, get_value(5, 0), get_value(5, 0),
  338. create_normal_iterator);
  339. verify(7, Status::kOk, get_value(8, 0), get_value(6, 0),
  340. create_normal_iterator);
  341. verify(9, Status::kOk, get_value(10, 0), get_value(8, 0),
  342. create_normal_iterator);
  343. verify(11, Status::kNotSupported, "", "", create_normal_iterator);
  344. verify(13, Status::kOk,
  345. get_value(13, 2) + "," + get_value(13, 1) + "," + get_value(13, 0),
  346. get_value(13, 2) + "," + get_value(13, 1) + "," + get_value(13, 0),
  347. create_normal_iterator);
  348. verify(15, Status::kOk, get_value(16, 0), get_value(14, 0),
  349. create_normal_iterator);
  350. // Iterator with blob support
  351. verify(1, Status::kOk, get_value(1, 0), get_value(1, 0),
  352. create_blob_iterator, check_is_blob(true));
  353. verify(3, Status::kOk, get_value(3, 0), get_value(3, 0),
  354. create_blob_iterator, check_is_blob(true));
  355. verify(5, Status::kOk, get_value(5, 0), get_value(5, 0),
  356. create_blob_iterator, check_is_blob(false));
  357. verify(7, Status::kOk, get_value(8, 0), get_value(6, 0),
  358. create_blob_iterator, check_is_blob(false));
  359. verify(9, Status::kOk, get_value(10, 0), get_value(8, 0),
  360. create_blob_iterator, check_is_blob(false));
  361. verify(11, Status::kNotSupported, "", "", create_blob_iterator);
  362. verify(13, Status::kOk,
  363. get_value(13, 2) + "," + get_value(13, 1) + "," + get_value(13, 0),
  364. get_value(13, 2) + "," + get_value(13, 1) + "," + get_value(13, 0),
  365. create_blob_iterator, check_is_blob(false));
  366. verify(15, Status::kOk, get_value(16, 0), get_value(14, 0),
  367. create_blob_iterator, check_is_blob(false));
  368. #ifndef ROCKSDB_LITE
  369. // Iterator with blob support and using seek.
  370. ASSERT_OK(dbfull()->SetOptions(
  371. cfh(), {{"max_sequential_skip_in_iterations", "0"}}));
  372. verify(1, Status::kOk, get_value(1, 0), get_value(1, 0),
  373. create_blob_iterator, check_is_blob(true));
  374. verify(3, Status::kOk, get_value(3, 0), get_value(3, 0),
  375. create_blob_iterator, check_is_blob(true));
  376. verify(5, Status::kOk, get_value(5, 0), get_value(5, 0),
  377. create_blob_iterator, check_is_blob(false));
  378. verify(7, Status::kOk, get_value(8, 0), get_value(6, 0),
  379. create_blob_iterator, check_is_blob(false));
  380. verify(9, Status::kOk, get_value(10, 0), get_value(8, 0),
  381. create_blob_iterator, check_is_blob(false));
  382. verify(11, Status::kNotSupported, "", "", create_blob_iterator);
  383. verify(13, Status::kOk,
  384. get_value(13, 2) + "," + get_value(13, 1) + "," + get_value(13, 0),
  385. get_value(13, 2) + "," + get_value(13, 1) + "," + get_value(13, 0),
  386. create_blob_iterator, check_is_blob(false));
  387. verify(15, Status::kOk, get_value(16, 0), get_value(14, 0),
  388. create_blob_iterator, check_is_blob(false));
  389. #endif // !ROCKSDB_LITE
  390. for (auto* snapshot : snapshots) {
  391. dbfull()->ReleaseSnapshot(snapshot);
  392. }
  393. }
  394. }
  395. } // namespace ROCKSDB_NAMESPACE
  396. int main(int argc, char** argv) {
  397. ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
  398. ::testing::InitGoogleTest(&argc, argv);
  399. return RUN_ALL_TESTS();
  400. }