sst_file_reader_test.cc 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866
  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 "rocksdb/sst_file_reader.h"
  6. #include <cinttypes>
  7. #include "db/db_test_util.h"
  8. #include "port/stack_trace.h"
  9. #include "rocksdb/convenience.h"
  10. #include "rocksdb/db.h"
  11. #include "rocksdb/sst_file_writer.h"
  12. #include "rocksdb/utilities/types_util.h"
  13. #include "table/sst_file_writer_collectors.h"
  14. #include "test_util/testharness.h"
  15. #include "test_util/testutil.h"
  16. #include "utilities/merge_operators.h"
  17. namespace ROCKSDB_NAMESPACE {
  18. std::string EncodeAsString(uint64_t v) {
  19. char buf[16];
  20. snprintf(buf, sizeof(buf), "%08" PRIu64, v);
  21. return std::string(buf);
  22. }
  23. std::string EncodeAsUint64(uint64_t v) {
  24. std::string dst;
  25. PutFixed64(&dst, v);
  26. return dst;
  27. }
  28. class SstFileReaderTest : public testing::Test {
  29. public:
  30. SstFileReaderTest() {
  31. options_.merge_operator = MergeOperators::CreateUInt64AddOperator();
  32. sst_name_ = test::PerThreadDBPath("sst_file");
  33. Env* base_env = Env::Default();
  34. EXPECT_OK(
  35. test::CreateEnvFromSystem(ConfigOptions(), &base_env, &env_guard_));
  36. EXPECT_NE(nullptr, base_env);
  37. env_ = base_env;
  38. options_.env = env_;
  39. }
  40. ~SstFileReaderTest() {
  41. Status s = env_->DeleteFile(sst_name_);
  42. EXPECT_OK(s);
  43. }
  44. void CreateFile(const std::string& file_name,
  45. const std::vector<std::string>& keys) {
  46. SstFileWriter writer(soptions_, options_);
  47. ASSERT_OK(writer.Open(file_name));
  48. for (size_t i = 0; i + 2 < keys.size(); i += 3) {
  49. ASSERT_OK(writer.Put(keys[i], keys[i]));
  50. ASSERT_OK(writer.Merge(keys[i + 1], EncodeAsUint64(i + 1)));
  51. ASSERT_OK(writer.Delete(keys[i + 2]));
  52. }
  53. ASSERT_OK(writer.Finish());
  54. }
  55. void CheckFile(const std::string& file_name,
  56. const std::vector<std::string>& keys,
  57. bool check_global_seqno = false) {
  58. ReadOptions ropts;
  59. SstFileReader reader(options_);
  60. ASSERT_OK(reader.Open(file_name));
  61. ASSERT_OK(reader.VerifyChecksum());
  62. std::unique_ptr<Iterator> iter(reader.NewIterator(ropts));
  63. iter->SeekToFirst();
  64. for (size_t i = 0; i + 2 < keys.size(); i += 3) {
  65. ASSERT_TRUE(iter->Valid());
  66. ASSERT_EQ(iter->key().compare(keys[i]), 0);
  67. ASSERT_EQ(iter->value().compare(keys[i]), 0);
  68. iter->Next();
  69. ASSERT_TRUE(iter->Valid());
  70. ASSERT_EQ(iter->key().compare(keys[i + 1]), 0);
  71. ASSERT_EQ(iter->value().compare(EncodeAsUint64(i + 1)), 0);
  72. iter->Next();
  73. }
  74. ASSERT_FALSE(iter->Valid());
  75. if (check_global_seqno) {
  76. auto properties = reader.GetTableProperties();
  77. ASSERT_TRUE(properties);
  78. std::string hostname;
  79. ASSERT_OK(env_->GetHostNameString(&hostname));
  80. ASSERT_EQ(properties->db_host_id, hostname);
  81. auto& user_properties = properties->user_collected_properties;
  82. ASSERT_TRUE(
  83. user_properties.count(ExternalSstFilePropertyNames::kGlobalSeqno));
  84. }
  85. }
  86. void CreateFileAndCheck(const std::vector<std::string>& keys) {
  87. CreateFile(sst_name_, keys);
  88. CheckFile(sst_name_, keys);
  89. }
  90. protected:
  91. Options options_;
  92. EnvOptions soptions_;
  93. std::string sst_name_;
  94. std::shared_ptr<Env> env_guard_;
  95. Env* env_;
  96. };
  97. const uint64_t kNumKeys = 100;
  98. TEST_F(SstFileReaderTest, Basic) {
  99. std::vector<std::string> keys;
  100. for (uint64_t i = 0; i < kNumKeys; i++) {
  101. keys.emplace_back(EncodeAsString(i));
  102. }
  103. CreateFileAndCheck(keys);
  104. }
  105. TEST_F(SstFileReaderTest, Uint64Comparator) {
  106. options_.comparator = test::Uint64Comparator();
  107. std::vector<std::string> keys;
  108. for (uint64_t i = 0; i < kNumKeys; i++) {
  109. keys.emplace_back(EncodeAsUint64(i));
  110. }
  111. CreateFileAndCheck(keys);
  112. }
  113. TEST_F(SstFileReaderTest, ReadOptionsOutOfScope) {
  114. // Repro a bug where the SstFileReader depended on its configured ReadOptions
  115. // outliving it.
  116. options_.comparator = test::Uint64Comparator();
  117. std::vector<std::string> keys;
  118. for (uint64_t i = 0; i < kNumKeys; i++) {
  119. keys.emplace_back(EncodeAsUint64(i));
  120. }
  121. CreateFile(sst_name_, keys);
  122. SstFileReader reader(options_);
  123. ASSERT_OK(reader.Open(sst_name_));
  124. std::unique_ptr<Iterator> iter;
  125. {
  126. // Make sure ReadOptions go out of scope ASAP so we know the iterator
  127. // operations do not depend on it.
  128. ReadOptions ropts;
  129. iter.reset(reader.NewIterator(ropts));
  130. }
  131. iter->SeekToFirst();
  132. while (iter->Valid()) {
  133. iter->Next();
  134. }
  135. }
  136. TEST_F(SstFileReaderTest, ReadFileWithGlobalSeqno) {
  137. std::vector<std::string> keys;
  138. for (uint64_t i = 0; i < kNumKeys; i++) {
  139. keys.emplace_back(EncodeAsString(i));
  140. }
  141. // Generate a SST file.
  142. CreateFile(sst_name_, keys);
  143. // Ingest the file into a db, to assign it a global sequence number.
  144. Options options;
  145. options.create_if_missing = true;
  146. std::string db_name = test::PerThreadDBPath("test_db");
  147. DB* db;
  148. ASSERT_OK(DB::Open(options, db_name, &db));
  149. // Bump sequence number.
  150. ASSERT_OK(db->Put(WriteOptions(), keys[0], "foo"));
  151. ASSERT_OK(db->Flush(FlushOptions()));
  152. // Ingest the file.
  153. IngestExternalFileOptions ingest_options;
  154. ingest_options.write_global_seqno = true;
  155. ASSERT_OK(db->IngestExternalFile({sst_name_}, ingest_options));
  156. std::vector<std::string> live_files;
  157. uint64_t manifest_file_size = 0;
  158. ASSERT_OK(db->GetLiveFiles(live_files, &manifest_file_size));
  159. // Get the ingested file.
  160. std::string ingested_file;
  161. for (auto& live_file : live_files) {
  162. if (live_file.substr(live_file.size() - 4, std::string::npos) == ".sst") {
  163. if (ingested_file.empty() || ingested_file < live_file) {
  164. ingested_file = live_file;
  165. }
  166. }
  167. }
  168. ASSERT_FALSE(ingested_file.empty());
  169. delete db;
  170. // Verify the file can be open and read by SstFileReader.
  171. CheckFile(db_name + ingested_file, keys, true /* check_global_seqno */);
  172. // Cleanup.
  173. ASSERT_OK(DestroyDB(db_name, options));
  174. }
  175. TEST_F(SstFileReaderTest, TimestampSizeMismatch) {
  176. SstFileWriter writer(soptions_, options_);
  177. ASSERT_OK(writer.Open(sst_name_));
  178. // Comparator is not timestamp-aware; calls to APIs taking timestamps should
  179. // fail.
  180. ASSERT_NOK(writer.Put("key", EncodeAsUint64(100), "value"));
  181. ASSERT_NOK(writer.Delete("another_key", EncodeAsUint64(200)));
  182. }
  183. class SstFileReaderTimestampTest : public testing::Test {
  184. public:
  185. SstFileReaderTimestampTest() {
  186. Env* env = Env::Default();
  187. EXPECT_OK(test::CreateEnvFromSystem(ConfigOptions(), &env, &env_guard_));
  188. EXPECT_NE(nullptr, env);
  189. options_.env = env;
  190. options_.comparator = test::BytewiseComparatorWithU64TsWrapper();
  191. sst_name_ = test::PerThreadDBPath("sst_file_ts");
  192. }
  193. ~SstFileReaderTimestampTest() {
  194. EXPECT_OK(options_.env->DeleteFile(sst_name_));
  195. }
  196. struct KeyValueDesc {
  197. KeyValueDesc(std::string k, std::string ts, std::string v)
  198. : key(std::move(k)), timestamp(std::move(ts)), value(std::move(v)) {}
  199. std::string key;
  200. std::string timestamp;
  201. std::string value;
  202. };
  203. struct InputKeyValueDesc : public KeyValueDesc {
  204. InputKeyValueDesc(std::string k, std::string ts, std::string v, bool is_del,
  205. bool use_contig_buf)
  206. : KeyValueDesc(std::move(k), std::move(ts), std::move(v)),
  207. is_delete(is_del),
  208. use_contiguous_buffer(use_contig_buf) {}
  209. bool is_delete = false;
  210. bool use_contiguous_buffer = false;
  211. };
  212. struct OutputKeyValueDesc : public KeyValueDesc {
  213. OutputKeyValueDesc(std::string k, std::string ts, std::string v)
  214. : KeyValueDesc(std::move(k), std::string(ts), std::string(v)) {}
  215. };
  216. void CreateFile(const std::vector<InputKeyValueDesc>& descs,
  217. ExternalSstFileInfo* file_info = nullptr) {
  218. SstFileWriter writer(soptions_, options_);
  219. ASSERT_OK(writer.Open(sst_name_));
  220. for (const auto& desc : descs) {
  221. if (desc.is_delete) {
  222. if (desc.use_contiguous_buffer) {
  223. std::string key_with_ts(desc.key + desc.timestamp);
  224. ASSERT_OK(writer.Delete(Slice(key_with_ts.data(), desc.key.size()),
  225. Slice(key_with_ts.data() + desc.key.size(),
  226. desc.timestamp.size())));
  227. } else {
  228. ASSERT_OK(writer.Delete(desc.key, desc.timestamp));
  229. }
  230. } else {
  231. if (desc.use_contiguous_buffer) {
  232. std::string key_with_ts(desc.key + desc.timestamp);
  233. ASSERT_OK(writer.Put(Slice(key_with_ts.data(), desc.key.size()),
  234. Slice(key_with_ts.data() + desc.key.size(),
  235. desc.timestamp.size()),
  236. desc.value));
  237. } else {
  238. ASSERT_OK(writer.Put(desc.key, desc.timestamp, desc.value));
  239. }
  240. }
  241. }
  242. ASSERT_OK(writer.Finish(file_info));
  243. }
  244. void CheckFile(const std::string& timestamp,
  245. const std::vector<OutputKeyValueDesc>& descs) {
  246. SstFileReader reader(options_);
  247. ASSERT_OK(reader.Open(sst_name_));
  248. ASSERT_OK(reader.VerifyChecksum());
  249. Slice ts_slice(timestamp);
  250. ReadOptions read_options;
  251. read_options.timestamp = &ts_slice;
  252. std::unique_ptr<Iterator> iter(reader.NewIterator(read_options));
  253. iter->SeekToFirst();
  254. for (const auto& desc : descs) {
  255. ASSERT_TRUE(iter->Valid());
  256. ASSERT_EQ(iter->key(), desc.key);
  257. ASSERT_EQ(iter->timestamp(), desc.timestamp);
  258. ASSERT_EQ(iter->value(), desc.value);
  259. iter->Next();
  260. }
  261. ASSERT_FALSE(iter->Valid());
  262. ASSERT_OK(iter->status());
  263. }
  264. protected:
  265. std::shared_ptr<Env> env_guard_;
  266. Options options_;
  267. EnvOptions soptions_;
  268. std::string sst_name_;
  269. };
  270. TEST_F(SstFileReaderTimestampTest, Basic) {
  271. std::vector<InputKeyValueDesc> input_descs;
  272. for (uint64_t k = 0; k < kNumKeys; k += 4) {
  273. // A Put with key k, timestamp k that gets overwritten by a subsequent Put
  274. // with timestamp (k + 1). Note that the comparator uses descending order
  275. // for the timestamp part, so we add the later Put first.
  276. input_descs.emplace_back(
  277. /* key */ EncodeAsString(k), /* timestamp */ EncodeAsUint64(k + 1),
  278. /* value */ EncodeAsString(k * 2), /* is_delete */ false,
  279. /* use_contiguous_buffer */ false);
  280. input_descs.emplace_back(
  281. /* key */ EncodeAsString(k), /* timestamp */ EncodeAsUint64(k),
  282. /* value */ EncodeAsString(k * 3), /* is_delete */ false,
  283. /* use_contiguous_buffer */ true);
  284. // A Put with key (k + 2), timestamp (k + 2) that gets cancelled out by a
  285. // Delete with timestamp (k + 3). Note that the comparator uses descending
  286. // order for the timestamp part, so we add the Delete first.
  287. input_descs.emplace_back(/* key */ EncodeAsString(k + 2),
  288. /* timestamp */ EncodeAsUint64(k + 3),
  289. /* value */ std::string(), /* is_delete */ true,
  290. /* use_contiguous_buffer */ (k % 8) == 0);
  291. input_descs.emplace_back(
  292. /* key */ EncodeAsString(k + 2), /* timestamp */ EncodeAsUint64(k + 2),
  293. /* value */ EncodeAsString(k * 5), /* is_delete */ false,
  294. /* use_contiguous_buffer */ (k % 8) != 0);
  295. }
  296. CreateFile(input_descs);
  297. // Note: below, we check the results as of each timestamp in the range,
  298. // updating the expected result as needed.
  299. std::vector<OutputKeyValueDesc> output_descs;
  300. for (uint64_t ts = 0; ts < kNumKeys; ++ts) {
  301. const uint64_t k = ts - (ts % 4);
  302. switch (ts % 4) {
  303. case 0: // Initial Put for key k
  304. output_descs.emplace_back(/* key */ EncodeAsString(k),
  305. /* timestamp */ EncodeAsUint64(ts),
  306. /* value */ EncodeAsString(k * 3));
  307. break;
  308. case 1: // Second Put for key k
  309. assert(output_descs.back().key == EncodeAsString(k));
  310. assert(output_descs.back().timestamp == EncodeAsUint64(ts - 1));
  311. assert(output_descs.back().value == EncodeAsString(k * 3));
  312. output_descs.back().timestamp = EncodeAsUint64(ts);
  313. output_descs.back().value = EncodeAsString(k * 2);
  314. break;
  315. case 2: // Put for key (k + 2)
  316. output_descs.emplace_back(/* key */ EncodeAsString(k + 2),
  317. /* timestamp */ EncodeAsUint64(ts),
  318. /* value */ EncodeAsString(k * 5));
  319. break;
  320. case 3: // Delete for key (k + 2)
  321. assert(output_descs.back().key == EncodeAsString(k + 2));
  322. assert(output_descs.back().timestamp == EncodeAsUint64(ts - 1));
  323. assert(output_descs.back().value == EncodeAsString(k * 5));
  324. output_descs.pop_back();
  325. break;
  326. }
  327. CheckFile(EncodeAsUint64(ts), output_descs);
  328. }
  329. }
  330. TEST_F(SstFileReaderTimestampTest, TimestampsOutOfOrder) {
  331. SstFileWriter writer(soptions_, options_);
  332. ASSERT_OK(writer.Open(sst_name_));
  333. // Note: KVs that have the same user key disregarding timestamps should be in
  334. // descending order of timestamps.
  335. ASSERT_OK(writer.Put("key", EncodeAsUint64(1), "value1"));
  336. ASSERT_NOK(writer.Put("key", EncodeAsUint64(2), "value2"));
  337. }
  338. TEST_F(SstFileReaderTimestampTest, TimestampSizeMismatch) {
  339. SstFileWriter writer(soptions_, options_);
  340. ASSERT_OK(writer.Open(sst_name_));
  341. // Comparator expects 64-bit timestamps; timestamps with other sizes as well
  342. // as calls to the timestamp-less APIs should be rejected.
  343. ASSERT_NOK(writer.Put("key", "not_an_actual_64_bit_timestamp", "value"));
  344. ASSERT_NOK(writer.Delete("another_key", "timestamp_of_unexpected_size"));
  345. ASSERT_NOK(writer.Put("key_without_timestamp", "value"));
  346. ASSERT_NOK(writer.Merge("another_key_missing_a_timestamp", "merge_operand"));
  347. ASSERT_NOK(writer.Delete("yet_another_key_still_no_timestamp"));
  348. ASSERT_NOK(writer.DeleteRange("begin_key_timestamp_absent",
  349. "end_key_with_a_complete_lack_of_timestamps"));
  350. }
  351. class SstFileReaderTimestampNotPersistedTest
  352. : public SstFileReaderTimestampTest {
  353. public:
  354. SstFileReaderTimestampNotPersistedTest() {
  355. Env* env = Env::Default();
  356. EXPECT_OK(test::CreateEnvFromSystem(ConfigOptions(), &env, &env_guard_));
  357. EXPECT_NE(nullptr, env);
  358. options_.env = env;
  359. options_.comparator = test::BytewiseComparatorWithU64TsWrapper();
  360. options_.persist_user_defined_timestamps = false;
  361. sst_name_ = test::PerThreadDBPath("sst_file_ts_not_persisted");
  362. }
  363. ~SstFileReaderTimestampNotPersistedTest() = default;
  364. };
  365. TEST_F(SstFileReaderTimestampNotPersistedTest, Basic) {
  366. std::vector<InputKeyValueDesc> input_descs;
  367. for (uint64_t k = 0; k < kNumKeys; k++) {
  368. input_descs.emplace_back(
  369. /* key */ EncodeAsString(k), /* timestamp */ EncodeAsUint64(0),
  370. /* value */ EncodeAsString(k), /* is_delete */ false,
  371. /* use_contiguous_buffer */ (k % 2) == 0);
  372. }
  373. ExternalSstFileInfo external_sst_file_info;
  374. CreateFile(input_descs, &external_sst_file_info);
  375. std::vector<OutputKeyValueDesc> output_descs;
  376. for (uint64_t k = 0; k < kNumKeys; k++) {
  377. output_descs.emplace_back(/* key */ EncodeAsString(k),
  378. /* timestamp */ EncodeAsUint64(0),
  379. /* value */ EncodeAsString(k));
  380. }
  381. CheckFile(EncodeAsUint64(0), output_descs);
  382. ASSERT_EQ(external_sst_file_info.smallest_key, EncodeAsString(0));
  383. ASSERT_EQ(external_sst_file_info.largest_key, EncodeAsString(kNumKeys - 1));
  384. ASSERT_EQ(external_sst_file_info.smallest_range_del_key, "");
  385. ASSERT_EQ(external_sst_file_info.largest_range_del_key, "");
  386. }
  387. TEST_F(SstFileReaderTimestampNotPersistedTest, NonMinTimestampNotAllowed) {
  388. SstFileWriter writer(soptions_, options_);
  389. ASSERT_OK(writer.Open(sst_name_));
  390. ASSERT_NOK(writer.Delete("baz", EncodeAsUint64(2)));
  391. ASSERT_OK(writer.Put("baz", EncodeAsUint64(0), "foo_val"));
  392. ASSERT_NOK(writer.Put("key", EncodeAsUint64(2), "value1"));
  393. ASSERT_OK(writer.Put("key", EncodeAsUint64(0), "value2"));
  394. // The `SstFileWriter::DeleteRange` API documentation specifies that
  395. // a range deletion tombstone added in the file does NOT delete point
  396. // (Put/Merge/Delete) keys in the same file. While there is no checks in
  397. // `SstFileWriter` to ensure this requirement is met, when such a range
  398. // deletion does exist, it will get over-written by point data in the same
  399. // file after ingestion because they have the same sequence number.
  400. // We allow having a point data entry and having a range deletion entry for
  401. // a key in the same file when timestamps are removed for the same reason.
  402. // After the file is ingested, the range deletion will effectively get
  403. // over-written by the point data since they will have the same sequence
  404. // number and the same user-defined timestamps.
  405. ASSERT_NOK(writer.DeleteRange("bar", "foo", EncodeAsUint64(2)));
  406. ASSERT_OK(writer.DeleteRange("bar", "foo", EncodeAsUint64(0)));
  407. ExternalSstFileInfo external_sst_file_info;
  408. ASSERT_OK(writer.Finish(&external_sst_file_info));
  409. ASSERT_EQ(external_sst_file_info.smallest_key, "baz");
  410. ASSERT_EQ(external_sst_file_info.largest_key, "key");
  411. ASSERT_EQ(external_sst_file_info.smallest_range_del_key, "bar");
  412. ASSERT_EQ(external_sst_file_info.largest_range_del_key, "foo");
  413. }
  414. TEST_F(SstFileReaderTimestampNotPersistedTest, KeyWithoutTimestampOutOfOrder) {
  415. SstFileWriter writer(soptions_, options_);
  416. ASSERT_OK(writer.Open(sst_name_));
  417. ASSERT_OK(writer.Put("foo", EncodeAsUint64(0), "value1"));
  418. ASSERT_NOK(writer.Put("bar", EncodeAsUint64(0), "value2"));
  419. }
  420. TEST_F(SstFileReaderTimestampNotPersistedTest, IncompatibleTimestampFormat) {
  421. SstFileWriter writer(soptions_, options_);
  422. ASSERT_OK(writer.Open(sst_name_));
  423. // Even though in this mode timestamps are not persisted, we require users
  424. // to call the timestamp-aware APIs only.
  425. ASSERT_TRUE(writer.Put("key", "not_an_actual_64_bit_timestamp", "value")
  426. .IsInvalidArgument());
  427. ASSERT_TRUE(writer.Delete("another_key", "timestamp_of_unexpected_size")
  428. .IsInvalidArgument());
  429. ASSERT_TRUE(writer.Put("key_without_timestamp", "value").IsInvalidArgument());
  430. ASSERT_TRUE(writer.Merge("another_key_missing_a_timestamp", "merge_operand")
  431. .IsInvalidArgument());
  432. ASSERT_TRUE(
  433. writer.Delete("yet_another_key_still_no_timestamp").IsInvalidArgument());
  434. ASSERT_TRUE(writer
  435. .DeleteRange("begin_key_timestamp_absent",
  436. "end_key_with_a_complete_lack_of_timestamps")
  437. .IsInvalidArgument());
  438. }
  439. TEST_F(SstFileReaderTest, VerifyNumEntriesBasic) {
  440. std::vector<std::string> keys;
  441. for (uint64_t i = 0; i < kNumKeys; i++) {
  442. keys.emplace_back(EncodeAsUint64(i));
  443. }
  444. CreateFile(sst_name_, keys);
  445. SstFileReader reader(options_);
  446. ASSERT_OK(reader.Open(sst_name_));
  447. ASSERT_OK(reader.VerifyNumEntries(ReadOptions()));
  448. }
  449. TEST_F(SstFileReaderTest, VerifyNumEntriesDeleteRange) {
  450. SstFileWriter writer(soptions_, options_);
  451. ASSERT_OK(writer.Open(sst_name_));
  452. for (uint64_t i = 0; i < kNumKeys; i++) {
  453. ASSERT_OK(writer.Put(EncodeAsUint64(i), EncodeAsUint64(i + 1)));
  454. }
  455. ASSERT_OK(
  456. writer.DeleteRange(EncodeAsUint64(0), EncodeAsUint64(kNumKeys / 2)));
  457. ASSERT_OK(writer.Finish());
  458. SstFileReader reader(options_);
  459. ASSERT_OK(reader.Open(sst_name_));
  460. ASSERT_OK(reader.VerifyNumEntries(ReadOptions()));
  461. }
  462. TEST_F(SstFileReaderTest, VerifyNumEntriesCorruption) {
  463. const int num_keys = 99;
  464. const int corrupted_num_keys = num_keys + 2;
  465. SyncPoint::GetInstance()->SetCallBack(
  466. "PropertyBlockBuilder::AddTableProperty:Start", [&](void* arg) {
  467. TableProperties* props = reinterpret_cast<TableProperties*>(arg);
  468. props->num_entries = corrupted_num_keys;
  469. });
  470. SyncPoint::GetInstance()->EnableProcessing();
  471. std::vector<std::string> keys;
  472. for (uint64_t i = 0; i < num_keys; i++) {
  473. keys.emplace_back(EncodeAsUint64(i));
  474. }
  475. CreateFile(sst_name_, keys);
  476. SstFileReader reader(options_);
  477. ASSERT_OK(reader.Open(sst_name_));
  478. Status s = reader.VerifyNumEntries(ReadOptions());
  479. ASSERT_TRUE(s.IsCorruption());
  480. std::ostringstream oss;
  481. oss << "Table property expects " << corrupted_num_keys
  482. << " entries when excluding range deletions,"
  483. << " but scanning the table returned " << num_keys << " entries";
  484. ASSERT_TRUE(std::strstr(oss.str().c_str(), s.getState()));
  485. }
  486. class SstFileReaderTableIteratorTest : public DBTestBase {
  487. public:
  488. SstFileReaderTableIteratorTest()
  489. : DBTestBase("sst_file_reader_table_iterator_test",
  490. /*env_do_fsync=*/false) {}
  491. void VerifyTableEntry(Iterator* iter, const std::string& user_key,
  492. ValueType value_type,
  493. std::optional<std::string> expected_value,
  494. bool backward_iteration = false) {
  495. ASSERT_TRUE(iter->Valid());
  496. ASSERT_TRUE(iter->status().ok());
  497. ParsedInternalKey pikey;
  498. ASSERT_OK(ParseInternalKey(iter->key(), &pikey, /*log_err_key=*/false));
  499. ASSERT_EQ(pikey.user_key, user_key);
  500. ASSERT_EQ(pikey.type, value_type);
  501. if (expected_value.has_value()) {
  502. ASSERT_EQ(iter->value(), expected_value.value());
  503. }
  504. if (!backward_iteration) {
  505. iter->Next();
  506. } else {
  507. iter->Prev();
  508. }
  509. }
  510. };
  511. TEST_F(SstFileReaderTableIteratorTest, Basic) {
  512. Options options = CurrentOptions();
  513. const Comparator* ucmp = BytewiseComparator();
  514. options.comparator = ucmp;
  515. options.disable_auto_compactions = true;
  516. DestroyAndReopen(options);
  517. // Create a L0 sst file with 4 entries, two for each user key.
  518. // The file should have these entries in ascending internal key order:
  519. // 'bar, seq: 4, type: kTypeValue => val2'
  520. // 'bar, seq: 3, type: kTypeDeletion'
  521. // 'foo, seq: 2, type: kTypeDeletion'
  522. // 'foo, seq: 1, type: kTypeValue => val1'
  523. ASSERT_OK(Put("foo", "val1"));
  524. const Snapshot* snapshot1 = dbfull()->GetSnapshot();
  525. ASSERT_OK(Delete("foo"));
  526. ASSERT_OK(Delete("bar"));
  527. const Snapshot* snapshot2 = dbfull()->GetSnapshot();
  528. ASSERT_OK(Put("bar", "val2"));
  529. ASSERT_OK(Flush());
  530. std::vector<LiveFileMetaData> files;
  531. dbfull()->GetLiveFilesMetaData(&files);
  532. ASSERT_TRUE(files.size() == 1);
  533. ASSERT_TRUE(files[0].level == 0);
  534. std::string file_name = files[0].directory + "/" + files[0].relative_filename;
  535. SstFileReader reader(options);
  536. ASSERT_OK(reader.Open(file_name));
  537. ASSERT_OK(reader.VerifyChecksum());
  538. // When iterating the file as a DB iterator, only one data entry for "bar" is
  539. // visible.
  540. std::unique_ptr<Iterator> db_iter(reader.NewIterator(ReadOptions()));
  541. db_iter->SeekToFirst();
  542. ASSERT_TRUE(db_iter->Valid());
  543. ASSERT_EQ(db_iter->key(), "bar");
  544. ASSERT_EQ(db_iter->value(), "val2");
  545. db_iter->Next();
  546. ASSERT_FALSE(db_iter->Valid());
  547. db_iter.reset();
  548. // When iterating the file with a raw table iterator, all the data entries are
  549. // surfaced in ascending internal key order.
  550. std::unique_ptr<Iterator> table_iter = reader.NewTableIterator();
  551. table_iter->SeekToFirst();
  552. VerifyTableEntry(table_iter.get(), "bar", kTypeValue, "val2");
  553. VerifyTableEntry(table_iter.get(), "bar", kTypeDeletion, std::nullopt);
  554. VerifyTableEntry(table_iter.get(), "foo", kTypeDeletion, std::nullopt);
  555. VerifyTableEntry(table_iter.get(), "foo", kTypeValue, "val1");
  556. ASSERT_FALSE(table_iter->Valid());
  557. std::string seek_key_buf;
  558. ASSERT_OK(GetInternalKeyForSeek("foo", ucmp, &seek_key_buf));
  559. Slice seek_target = seek_key_buf;
  560. table_iter->Seek(seek_target);
  561. VerifyTableEntry(table_iter.get(), "foo", kTypeDeletion, std::nullopt);
  562. VerifyTableEntry(table_iter.get(), "foo", kTypeValue, "val1");
  563. ASSERT_FALSE(table_iter->Valid());
  564. ASSERT_OK(GetInternalKeyForSeekForPrev("bar", ucmp, &seek_key_buf));
  565. Slice seek_for_prev_target = seek_key_buf;
  566. table_iter->SeekForPrev(seek_for_prev_target);
  567. VerifyTableEntry(table_iter.get(), "bar", kTypeDeletion, std::nullopt,
  568. /*backward_iteration=*/true);
  569. VerifyTableEntry(table_iter.get(), "bar", kTypeValue, "val2",
  570. /*backward_iteration=*/true);
  571. ASSERT_FALSE(table_iter->Valid());
  572. dbfull()->ReleaseSnapshot(snapshot1);
  573. dbfull()->ReleaseSnapshot(snapshot2);
  574. Close();
  575. }
  576. TEST_F(SstFileReaderTableIteratorTest, UserDefinedTimestampsEnabled) {
  577. Options options = CurrentOptions();
  578. const Comparator* ucmp = test::BytewiseComparatorWithU64TsWrapper();
  579. options.comparator = ucmp;
  580. options.disable_auto_compactions = true;
  581. DestroyAndReopen(options);
  582. // Create a L0 sst file with 4 entries, two for each user key.
  583. // The file should have these entries in ascending internal key order:
  584. // 'bar, ts=3, seq: 4, type: kTypeValue => val2'
  585. // 'bar, ts=2, seq: 3, type: kTypeDeletionWithTimestamp'
  586. // 'foo, ts=4, seq: 2, type: kTypeDeletionWithTimestamp'
  587. // 'foo, ts=3, seq: 1, type: kTypeValue => val1'
  588. WriteOptions wopt;
  589. ColumnFamilyHandle* cfd = db_->DefaultColumnFamily();
  590. ASSERT_OK(db_->Put(wopt, cfd, "foo", EncodeAsUint64(3), "val1"));
  591. ASSERT_OK(db_->Delete(wopt, cfd, "foo", EncodeAsUint64(4)));
  592. ASSERT_OK(db_->Delete(wopt, cfd, "bar", EncodeAsUint64(2)));
  593. ASSERT_OK(db_->Put(wopt, cfd, "bar", EncodeAsUint64(3), "val2"));
  594. ASSERT_OK(Flush());
  595. std::vector<LiveFileMetaData> files;
  596. dbfull()->GetLiveFilesMetaData(&files);
  597. ASSERT_TRUE(files.size() == 1);
  598. ASSERT_TRUE(files[0].level == 0);
  599. std::string file_name = files[0].directory + "/" + files[0].relative_filename;
  600. SstFileReader reader(options);
  601. ASSERT_OK(reader.Open(file_name));
  602. ASSERT_OK(reader.VerifyChecksum());
  603. // When iterating the file as a DB iterator, only one data entry for "bar" is
  604. // visible.
  605. ReadOptions ropts;
  606. std::string read_ts = EncodeAsUint64(4);
  607. Slice read_ts_slice = read_ts;
  608. ropts.timestamp = &read_ts_slice;
  609. std::unique_ptr<Iterator> db_iter(reader.NewIterator(ropts));
  610. db_iter->SeekToFirst();
  611. ASSERT_TRUE(db_iter->Valid());
  612. ASSERT_EQ(db_iter->key(), "bar");
  613. ASSERT_EQ(db_iter->value(), "val2");
  614. ASSERT_EQ(db_iter->timestamp(), EncodeAsUint64(3));
  615. db_iter->Next();
  616. ASSERT_FALSE(db_iter->Valid());
  617. db_iter.reset();
  618. std::unique_ptr<Iterator> table_iter = reader.NewTableIterator();
  619. table_iter->SeekToFirst();
  620. VerifyTableEntry(table_iter.get(), "bar" + EncodeAsUint64(3), kTypeValue,
  621. "val2");
  622. VerifyTableEntry(table_iter.get(), "bar" + EncodeAsUint64(2),
  623. kTypeDeletionWithTimestamp, std::nullopt);
  624. VerifyTableEntry(table_iter.get(), "foo" + EncodeAsUint64(4),
  625. kTypeDeletionWithTimestamp, std::nullopt);
  626. VerifyTableEntry(table_iter.get(), "foo" + EncodeAsUint64(3), kTypeValue,
  627. "val1");
  628. ASSERT_FALSE(table_iter->Valid());
  629. std::string seek_key_buf;
  630. ASSERT_OK(GetInternalKeyForSeek("foo", ucmp, &seek_key_buf));
  631. Slice seek_target = seek_key_buf;
  632. table_iter->Seek(seek_target);
  633. VerifyTableEntry(table_iter.get(), "foo" + EncodeAsUint64(4),
  634. kTypeDeletionWithTimestamp, std::nullopt);
  635. VerifyTableEntry(table_iter.get(), "foo" + EncodeAsUint64(3), kTypeValue,
  636. "val1");
  637. ASSERT_FALSE(table_iter->Valid());
  638. ASSERT_OK(GetInternalKeyForSeekForPrev("bar", ucmp, &seek_key_buf));
  639. Slice seek_for_prev_target = seek_key_buf;
  640. table_iter->SeekForPrev(seek_for_prev_target);
  641. VerifyTableEntry(table_iter.get(), "bar" + EncodeAsUint64(2),
  642. kTypeDeletionWithTimestamp, std::nullopt,
  643. /*backward_iteration=*/true);
  644. VerifyTableEntry(table_iter.get(), "bar" + EncodeAsUint64(3), kTypeValue,
  645. "val2", /*backward_iteration=*/true);
  646. ASSERT_FALSE(table_iter->Valid());
  647. Close();
  648. }
  649. class SstFileReaderTableMultiGetTest : public DBTestBase {
  650. public:
  651. SstFileReaderTableMultiGetTest()
  652. : DBTestBase("sst_file_reader_table_multi_get_test",
  653. /*env_do_fsync=*/false) {}
  654. void VerifyTableEntry(Iterator* iter, const std::string& user_key,
  655. ValueType value_type,
  656. std::optional<std::string> expected_value,
  657. bool backward_iteration = false) {
  658. ASSERT_TRUE(iter->Valid());
  659. ASSERT_TRUE(iter->status().ok());
  660. ParsedInternalKey pikey;
  661. ASSERT_OK(ParseInternalKey(iter->key(), &pikey, /*log_err_key=*/false));
  662. ASSERT_EQ(pikey.user_key, user_key);
  663. ASSERT_EQ(pikey.type, value_type);
  664. if (expected_value.has_value()) {
  665. ASSERT_EQ(iter->value(), expected_value.value());
  666. }
  667. if (!backward_iteration) {
  668. iter->Next();
  669. } else {
  670. iter->Prev();
  671. }
  672. }
  673. };
  674. TEST_F(SstFileReaderTableMultiGetTest, Basic) {
  675. Options options = CurrentOptions();
  676. const Comparator* ucmp = BytewiseComparator();
  677. options.comparator = ucmp;
  678. options.disable_auto_compactions = true;
  679. options.merge_operator = MergeOperators::CreateStringAppendOperator();
  680. BlockBasedTableOptions bbto;
  681. bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
  682. options.table_factory.reset(NewBlockBasedTableFactory(bbto));
  683. DestroyAndReopen(options);
  684. ASSERT_OK(Put("foo", "val1"));
  685. const Snapshot* snapshot1 = dbfull()->GetSnapshot();
  686. ASSERT_OK(Delete("foo"));
  687. ASSERT_OK(Delete("bar"));
  688. const Snapshot* snapshot2 = dbfull()->GetSnapshot();
  689. ASSERT_OK(Put("bar", "val2"));
  690. ASSERT_OK(Put("baz", "val3"));
  691. ASSERT_OK(Put("aaa", "val4"));
  692. const Snapshot* snapshot3 = dbfull()->GetSnapshot();
  693. ASSERT_OK(Merge("aaa", "val5"));
  694. ASSERT_OK(Flush());
  695. std::vector<LiveFileMetaData> files;
  696. dbfull()->GetLiveFilesMetaData(&files);
  697. ASSERT_TRUE(files.size() == 1);
  698. ASSERT_TRUE(files[0].level == 0);
  699. std::string file_name = files[0].directory + "/" + files[0].relative_filename;
  700. SstFileReader reader(options);
  701. ASSERT_OK(reader.Open(file_name));
  702. ASSERT_OK(reader.VerifyChecksum());
  703. std::vector<Slice> keys;
  704. std::vector<std::string> values;
  705. keys.emplace_back("fo1");
  706. keys.emplace_back("foo");
  707. keys.emplace_back("baz");
  708. keys.emplace_back("bar");
  709. keys.emplace_back("aaa");
  710. auto statuses = reader.MultiGet(ReadOptions(), keys, &values);
  711. ASSERT_TRUE(statuses[0].IsNotFound())
  712. << "Failed: status=" << statuses[0].ToString() << " val=" << values[0];
  713. ASSERT_TRUE(statuses[1].IsNotFound())
  714. << "Failed: status=" << statuses[0].ToString() << " val=" << values[1];
  715. ASSERT_TRUE(statuses[2].ok());
  716. ASSERT_TRUE(statuses[3].ok());
  717. ASSERT_EQ("val3", values[2]);
  718. ASSERT_EQ("val2", values[3]);
  719. ASSERT_TRUE(statuses[4].ok());
  720. ASSERT_EQ("val4,val5", values[4]);
  721. dbfull()->ReleaseSnapshot(snapshot1);
  722. dbfull()->ReleaseSnapshot(snapshot2);
  723. dbfull()->ReleaseSnapshot(snapshot3);
  724. Close();
  725. }
  726. } // namespace ROCKSDB_NAMESPACE
  727. int main(int argc, char** argv) {
  728. ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
  729. ::testing::InitGoogleTest(&argc, argv);
  730. RegisterCustomObjects(argc, argv);
  731. return RUN_ALL_TESTS();
  732. }