]> git.proxmox.com Git - ceph.git/blame - ceph/src/rocksdb/db/external_sst_file_basic_test.cc
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / rocksdb / db / external_sst_file_basic_test.cc
CommitLineData
7c673cae 1// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
11fdf7f2
TL
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).
7c673cae
FG
5
6#include <functional>
7
8#include "db/db_test_util.h"
9#include "port/port.h"
10#include "port/stack_trace.h"
11#include "rocksdb/sst_file_writer.h"
f67539c2
TL
12#include "test_util/fault_injection_test_env.h"
13#include "test_util/testutil.h"
7c673cae 14
f67539c2 15namespace ROCKSDB_NAMESPACE {
7c673cae
FG
16
17#ifndef ROCKSDB_LITE
494da23a
TL
18class ExternalSSTFileBasicTest
19 : public DBTestBase,
20 public ::testing::WithParamInterface<std::tuple<bool, bool>> {
7c673cae 21 public:
494da23a 22 ExternalSSTFileBasicTest() : DBTestBase("/external_sst_file_basic_test") {
7c673cae 23 sst_files_dir_ = dbname_ + "/sst_files/";
f67539c2 24 fault_injection_test_env_.reset(new FaultInjectionTestEnv(Env::Default()));
7c673cae
FG
25 DestroyAndRecreateExternalSSTFilesDir();
26 }
27
28 void DestroyAndRecreateExternalSSTFilesDir() {
29 test::DestroyDir(env_, sst_files_dir_);
30 env_->CreateDir(sst_files_dir_);
31 }
32
33 Status DeprecatedAddFile(const std::vector<std::string>& files,
34 bool move_files = false,
35 bool skip_snapshot_check = false) {
36 IngestExternalFileOptions opts;
37 opts.move_files = move_files;
38 opts.snapshot_consistency = !skip_snapshot_check;
39 opts.allow_global_seqno = false;
40 opts.allow_blocking_flush = false;
41 return db_->IngestExternalFile(files, opts);
42 }
43
44 Status GenerateAndAddExternalFile(
11fdf7f2
TL
45 const Options options, std::vector<int> keys,
46 const std::vector<ValueType>& value_types,
47 std::vector<std::pair<int, int>> range_deletions, int file_id,
494da23a 48 bool write_global_seqno, bool verify_checksums_before_ingest,
7c673cae 49 std::map<std::string, std::string>* true_data) {
11fdf7f2 50 assert(value_types.size() == 1 || keys.size() == value_types.size());
7c673cae
FG
51 std::string file_path = sst_files_dir_ + ToString(file_id);
52 SstFileWriter sst_file_writer(EnvOptions(), options);
53
54 Status s = sst_file_writer.Open(file_path);
55 if (!s.ok()) {
56 return s;
57 }
11fdf7f2
TL
58 for (size_t i = 0; i < range_deletions.size(); i++) {
59 // Account for the effect of range deletions on true_data before
60 // all point operators, even though sst_file_writer.DeleteRange
61 // must be called before other sst_file_writer methods. This is
62 // because point writes take precedence over range deletions
63 // in the same ingested sst.
64 std::string start_key = Key(range_deletions[i].first);
65 std::string end_key = Key(range_deletions[i].second);
66 s = sst_file_writer.DeleteRange(start_key, end_key);
67 if (!s.ok()) {
68 sst_file_writer.Finish();
69 return s;
70 }
71 auto start_key_it = true_data->find(start_key);
72 if (start_key_it == true_data->end()) {
73 start_key_it = true_data->upper_bound(start_key);
74 }
75 auto end_key_it = true_data->find(end_key);
76 if (end_key_it == true_data->end()) {
77 end_key_it = true_data->upper_bound(end_key);
78 }
79 true_data->erase(start_key_it, end_key_it);
80 }
81 for (size_t i = 0; i < keys.size(); i++) {
82 std::string key = Key(keys[i]);
83 std::string value = Key(keys[i]) + ToString(file_id);
84 ValueType value_type =
85 (value_types.size() == 1 ? value_types[0] : value_types[i]);
86 switch (value_type) {
87 case ValueType::kTypeValue:
88 s = sst_file_writer.Put(key, value);
89 (*true_data)[key] = value;
90 break;
91 case ValueType::kTypeMerge:
92 s = sst_file_writer.Merge(key, value);
93 // we only use TestPutOperator in this test
94 (*true_data)[key] = value;
95 break;
96 case ValueType::kTypeDeletion:
97 s = sst_file_writer.Delete(key);
98 true_data->erase(key);
99 break;
100 default:
101 return Status::InvalidArgument("Value type is not supported");
102 }
7c673cae
FG
103 if (!s.ok()) {
104 sst_file_writer.Finish();
105 return s;
106 }
107 }
108 s = sst_file_writer.Finish();
109
110 if (s.ok()) {
111 IngestExternalFileOptions ifo;
112 ifo.allow_global_seqno = true;
494da23a
TL
113 ifo.write_global_seqno = write_global_seqno;
114 ifo.verify_checksums_before_ingest = verify_checksums_before_ingest;
7c673cae
FG
115 s = db_->IngestExternalFile({file_path}, ifo);
116 }
7c673cae
FG
117 return s;
118 }
119
11fdf7f2
TL
120 Status GenerateAndAddExternalFile(
121 const Options options, std::vector<int> keys,
122 const std::vector<ValueType>& value_types, int file_id,
494da23a 123 bool write_global_seqno, bool verify_checksums_before_ingest,
11fdf7f2 124 std::map<std::string, std::string>* true_data) {
494da23a
TL
125 return GenerateAndAddExternalFile(
126 options, keys, value_types, {}, file_id, write_global_seqno,
127 verify_checksums_before_ingest, true_data);
11fdf7f2
TL
128 }
129
130 Status GenerateAndAddExternalFile(
131 const Options options, std::vector<int> keys, const ValueType value_type,
494da23a
TL
132 int file_id, bool write_global_seqno, bool verify_checksums_before_ingest,
133 std::map<std::string, std::string>* true_data) {
134 return GenerateAndAddExternalFile(
135 options, keys, std::vector<ValueType>(1, value_type), file_id,
136 write_global_seqno, verify_checksums_before_ingest, true_data);
11fdf7f2
TL
137 }
138
494da23a
TL
139 ~ExternalSSTFileBasicTest() override {
140 test::DestroyDir(env_, sst_files_dir_);
141 }
7c673cae
FG
142
143 protected:
144 std::string sst_files_dir_;
f67539c2 145 std::unique_ptr<FaultInjectionTestEnv> fault_injection_test_env_;
7c673cae
FG
146};
147
148TEST_F(ExternalSSTFileBasicTest, Basic) {
149 Options options = CurrentOptions();
150
151 SstFileWriter sst_file_writer(EnvOptions(), options);
152
153 // Current file size should be 0 after sst_file_writer init and before open a
154 // file.
155 ASSERT_EQ(sst_file_writer.FileSize(), 0);
156
157 // file1.sst (0 => 99)
158 std::string file1 = sst_files_dir_ + "file1.sst";
159 ASSERT_OK(sst_file_writer.Open(file1));
160 for (int k = 0; k < 100; k++) {
11fdf7f2 161 ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val"));
7c673cae
FG
162 }
163 ExternalSstFileInfo file1_info;
164 Status s = sst_file_writer.Finish(&file1_info);
165 ASSERT_TRUE(s.ok()) << s.ToString();
166
167 // Current file size should be non-zero after success write.
168 ASSERT_GT(sst_file_writer.FileSize(), 0);
169
170 ASSERT_EQ(file1_info.file_path, file1);
171 ASSERT_EQ(file1_info.num_entries, 100);
172 ASSERT_EQ(file1_info.smallest_key, Key(0));
173 ASSERT_EQ(file1_info.largest_key, Key(99));
11fdf7f2
TL
174 ASSERT_EQ(file1_info.num_range_del_entries, 0);
175 ASSERT_EQ(file1_info.smallest_range_del_key, "");
176 ASSERT_EQ(file1_info.largest_range_del_key, "");
7c673cae 177 // sst_file_writer already finished, cannot add this value
11fdf7f2
TL
178 s = sst_file_writer.Put(Key(100), "bad_val");
179 ASSERT_FALSE(s.ok()) << s.ToString();
180 s = sst_file_writer.DeleteRange(Key(100), Key(200));
7c673cae
FG
181 ASSERT_FALSE(s.ok()) << s.ToString();
182
183 DestroyAndReopen(options);
184 // Add file using file path
185 s = DeprecatedAddFile({file1});
186 ASSERT_TRUE(s.ok()) << s.ToString();
187 ASSERT_EQ(db_->GetLatestSequenceNumber(), 0U);
188 for (int k = 0; k < 100; k++) {
189 ASSERT_EQ(Get(Key(k)), Key(k) + "_val");
190 }
191
192 DestroyAndRecreateExternalSSTFilesDir();
193}
194
195TEST_F(ExternalSSTFileBasicTest, NoCopy) {
196 Options options = CurrentOptions();
197 const ImmutableCFOptions ioptions(options);
198
199 SstFileWriter sst_file_writer(EnvOptions(), options);
200
201 // file1.sst (0 => 99)
202 std::string file1 = sst_files_dir_ + "file1.sst";
203 ASSERT_OK(sst_file_writer.Open(file1));
204 for (int k = 0; k < 100; k++) {
11fdf7f2 205 ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val"));
7c673cae
FG
206 }
207 ExternalSstFileInfo file1_info;
208 Status s = sst_file_writer.Finish(&file1_info);
209 ASSERT_TRUE(s.ok()) << s.ToString();
210 ASSERT_EQ(file1_info.file_path, file1);
211 ASSERT_EQ(file1_info.num_entries, 100);
212 ASSERT_EQ(file1_info.smallest_key, Key(0));
213 ASSERT_EQ(file1_info.largest_key, Key(99));
214
215 // file2.sst (100 => 299)
216 std::string file2 = sst_files_dir_ + "file2.sst";
217 ASSERT_OK(sst_file_writer.Open(file2));
218 for (int k = 100; k < 300; k++) {
11fdf7f2 219 ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val"));
7c673cae
FG
220 }
221 ExternalSstFileInfo file2_info;
222 s = sst_file_writer.Finish(&file2_info);
223 ASSERT_TRUE(s.ok()) << s.ToString();
224 ASSERT_EQ(file2_info.file_path, file2);
225 ASSERT_EQ(file2_info.num_entries, 200);
226 ASSERT_EQ(file2_info.smallest_key, Key(100));
227 ASSERT_EQ(file2_info.largest_key, Key(299));
228
229 // file3.sst (110 => 124) .. overlap with file2.sst
230 std::string file3 = sst_files_dir_ + "file3.sst";
231 ASSERT_OK(sst_file_writer.Open(file3));
232 for (int k = 110; k < 125; k++) {
11fdf7f2 233 ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val_overlap"));
7c673cae
FG
234 }
235 ExternalSstFileInfo file3_info;
236 s = sst_file_writer.Finish(&file3_info);
237 ASSERT_TRUE(s.ok()) << s.ToString();
238 ASSERT_EQ(file3_info.file_path, file3);
239 ASSERT_EQ(file3_info.num_entries, 15);
240 ASSERT_EQ(file3_info.smallest_key, Key(110));
241 ASSERT_EQ(file3_info.largest_key, Key(124));
11fdf7f2 242
7c673cae
FG
243 s = DeprecatedAddFile({file1}, true /* move file */);
244 ASSERT_TRUE(s.ok()) << s.ToString();
245 ASSERT_EQ(Status::NotFound(), env_->FileExists(file1));
246
247 s = DeprecatedAddFile({file2}, false /* copy file */);
248 ASSERT_TRUE(s.ok()) << s.ToString();
249 ASSERT_OK(env_->FileExists(file2));
250
11fdf7f2
TL
251 // This file has overlapping values with the existing data
252 s = DeprecatedAddFile({file3}, true /* move file */);
7c673cae
FG
253 ASSERT_FALSE(s.ok()) << s.ToString();
254 ASSERT_OK(env_->FileExists(file3));
255
256 for (int k = 0; k < 300; k++) {
257 ASSERT_EQ(Get(Key(k)), Key(k) + "_val");
258 }
259}
260
494da23a
TL
261TEST_P(ExternalSSTFileBasicTest, IngestFileWithGlobalSeqnoPickedSeqno) {
262 bool write_global_seqno = std::get<0>(GetParam());
263 bool verify_checksums_before_ingest = std::get<1>(GetParam());
7c673cae
FG
264 do {
265 Options options = CurrentOptions();
266 DestroyAndReopen(options);
267 std::map<std::string, std::string> true_data;
268
269 int file_id = 1;
270
494da23a
TL
271 ASSERT_OK(GenerateAndAddExternalFile(
272 options, {1, 2, 3, 4, 5, 6}, ValueType::kTypeValue, file_id++,
273 write_global_seqno, verify_checksums_before_ingest, &true_data));
11fdf7f2 274 // File doesn't overwrite any keys, no seqno needed
7c673cae
FG
275 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
276
494da23a
TL
277 ASSERT_OK(GenerateAndAddExternalFile(
278 options, {10, 11, 12, 13}, ValueType::kTypeValue, file_id++,
279 write_global_seqno, verify_checksums_before_ingest, &true_data));
11fdf7f2 280 // File doesn't overwrite any keys, no seqno needed
7c673cae
FG
281 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
282
11fdf7f2 283 ASSERT_OK(GenerateAndAddExternalFile(
494da23a
TL
284 options, {1, 4, 6}, ValueType::kTypeValue, file_id++,
285 write_global_seqno, verify_checksums_before_ingest, &true_data));
11fdf7f2 286 // File overwrites some keys, a seqno will be assigned
7c673cae
FG
287 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 1);
288
11fdf7f2 289 ASSERT_OK(GenerateAndAddExternalFile(
494da23a
TL
290 options, {11, 15, 19}, ValueType::kTypeValue, file_id++,
291 write_global_seqno, verify_checksums_before_ingest, &true_data));
11fdf7f2 292 // File overwrites some keys, a seqno will be assigned
7c673cae
FG
293 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
294
11fdf7f2 295 ASSERT_OK(GenerateAndAddExternalFile(
494da23a
TL
296 options, {120, 130}, ValueType::kTypeValue, file_id++,
297 write_global_seqno, verify_checksums_before_ingest, &true_data));
11fdf7f2 298 // File doesn't overwrite any keys, no seqno needed
7c673cae
FG
299 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
300
11fdf7f2 301 ASSERT_OK(GenerateAndAddExternalFile(
494da23a
TL
302 options, {1, 130}, ValueType::kTypeValue, file_id++, write_global_seqno,
303 verify_checksums_before_ingest, &true_data));
11fdf7f2 304 // File overwrites some keys, a seqno will be assigned
7c673cae
FG
305 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 3);
306
307 // Write some keys through normal write path
308 for (int i = 0; i < 50; i++) {
309 ASSERT_OK(Put(Key(i), "memtable"));
310 true_data[Key(i)] = "memtable";
311 }
312 SequenceNumber last_seqno = dbfull()->GetLatestSequenceNumber();
313
11fdf7f2 314 ASSERT_OK(GenerateAndAddExternalFile(
494da23a
TL
315 options, {60, 61, 62}, ValueType::kTypeValue, file_id++,
316 write_global_seqno, verify_checksums_before_ingest, &true_data));
11fdf7f2 317 // File doesn't overwrite any keys, no seqno needed
7c673cae
FG
318 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno);
319
11fdf7f2 320 ASSERT_OK(GenerateAndAddExternalFile(
494da23a
TL
321 options, {40, 41, 42}, ValueType::kTypeValue, file_id++,
322 write_global_seqno, verify_checksums_before_ingest, &true_data));
11fdf7f2
TL
323 // File overwrites some keys, a seqno will be assigned
324 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 1);
325
326 ASSERT_OK(GenerateAndAddExternalFile(
494da23a
TL
327 options, {20, 30, 40}, ValueType::kTypeValue, file_id++,
328 write_global_seqno, verify_checksums_before_ingest, &true_data));
11fdf7f2
TL
329 // File overwrites some keys, a seqno will be assigned
330 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 2);
331
332 const Snapshot* snapshot = db_->GetSnapshot();
333
334 // We will need a seqno for the file regardless if the file overwrite
335 // keys in the DB or not because we have a snapshot
336 ASSERT_OK(GenerateAndAddExternalFile(
494da23a
TL
337 options, {1000, 1002}, ValueType::kTypeValue, file_id++,
338 write_global_seqno, verify_checksums_before_ingest, &true_data));
11fdf7f2
TL
339 // A global seqno will be assigned anyway because of the snapshot
340 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 3);
341
342 ASSERT_OK(GenerateAndAddExternalFile(
494da23a
TL
343 options, {2000, 3002}, ValueType::kTypeValue, file_id++,
344 write_global_seqno, verify_checksums_before_ingest, &true_data));
11fdf7f2
TL
345 // A global seqno will be assigned anyway because of the snapshot
346 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 4);
347
494da23a
TL
348 ASSERT_OK(GenerateAndAddExternalFile(
349 options, {1, 20, 40, 100, 150}, ValueType::kTypeValue, file_id++,
350 write_global_seqno, verify_checksums_before_ingest, &true_data));
11fdf7f2
TL
351 // A global seqno will be assigned anyway because of the snapshot
352 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
353
354 db_->ReleaseSnapshot(snapshot);
355
356 ASSERT_OK(GenerateAndAddExternalFile(
494da23a
TL
357 options, {5000, 5001}, ValueType::kTypeValue, file_id++,
358 write_global_seqno, verify_checksums_before_ingest, &true_data));
11fdf7f2
TL
359 // No snapshot anymore, no need to assign a seqno
360 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
361
362 size_t kcnt = 0;
363 VerifyDBFromMap(true_data, &kcnt, false);
494da23a 364 } while (ChangeOptionsForFileIngestionTest());
11fdf7f2
TL
365}
366
494da23a
TL
367TEST_P(ExternalSSTFileBasicTest, IngestFileWithMultipleValueType) {
368 bool write_global_seqno = std::get<0>(GetParam());
369 bool verify_checksums_before_ingest = std::get<1>(GetParam());
11fdf7f2
TL
370 do {
371 Options options = CurrentOptions();
372 options.merge_operator.reset(new TestPutOperator());
373 DestroyAndReopen(options);
374 std::map<std::string, std::string> true_data;
375
376 int file_id = 1;
377
494da23a
TL
378 ASSERT_OK(GenerateAndAddExternalFile(
379 options, {1, 2, 3, 4, 5, 6}, ValueType::kTypeValue, file_id++,
380 write_global_seqno, verify_checksums_before_ingest, &true_data));
11fdf7f2
TL
381 // File doesn't overwrite any keys, no seqno needed
382 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
383
494da23a
TL
384 ASSERT_OK(GenerateAndAddExternalFile(
385 options, {10, 11, 12, 13}, ValueType::kTypeValue, file_id++,
386 write_global_seqno, verify_checksums_before_ingest, &true_data));
11fdf7f2
TL
387 // File doesn't overwrite any keys, no seqno needed
388 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
389
390 ASSERT_OK(GenerateAndAddExternalFile(
494da23a
TL
391 options, {1, 4, 6}, ValueType::kTypeMerge, file_id++,
392 write_global_seqno, verify_checksums_before_ingest, &true_data));
11fdf7f2
TL
393 // File overwrites some keys, a seqno will be assigned
394 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 1);
395
494da23a
TL
396 ASSERT_OK(GenerateAndAddExternalFile(
397 options, {11, 15, 19}, ValueType::kTypeDeletion, file_id++,
398 write_global_seqno, verify_checksums_before_ingest, &true_data));
11fdf7f2
TL
399 // File overwrites some keys, a seqno will be assigned
400 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
401
402 ASSERT_OK(GenerateAndAddExternalFile(
494da23a
TL
403 options, {120, 130}, ValueType::kTypeMerge, file_id++,
404 write_global_seqno, verify_checksums_before_ingest, &true_data));
11fdf7f2
TL
405 // File doesn't overwrite any keys, no seqno needed
406 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
407
408 ASSERT_OK(GenerateAndAddExternalFile(
494da23a
TL
409 options, {1, 130}, ValueType::kTypeDeletion, file_id++,
410 write_global_seqno, verify_checksums_before_ingest, &true_data));
11fdf7f2
TL
411 // File overwrites some keys, a seqno will be assigned
412 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 3);
413
494da23a
TL
414 ASSERT_OK(GenerateAndAddExternalFile(
415 options, {120}, {ValueType::kTypeValue}, {{120, 135}}, file_id++,
416 write_global_seqno, verify_checksums_before_ingest, &true_data));
11fdf7f2
TL
417 // File overwrites some keys, a seqno will be assigned
418 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 4);
419
494da23a
TL
420 ASSERT_OK(GenerateAndAddExternalFile(
421 options, {}, {}, {{110, 120}}, file_id++, write_global_seqno,
422 verify_checksums_before_ingest, &true_data));
11fdf7f2
TL
423 // The range deletion ends on a key, but it doesn't actually delete
424 // this key because the largest key in the range is exclusive. Still,
425 // it counts as an overlap so a new seqno will be assigned.
426 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 5);
427
494da23a
TL
428 ASSERT_OK(GenerateAndAddExternalFile(
429 options, {}, {}, {{100, 109}}, file_id++, write_global_seqno,
430 verify_checksums_before_ingest, &true_data));
11fdf7f2
TL
431 // File doesn't overwrite any keys, no seqno needed
432 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 5);
433
434 // Write some keys through normal write path
435 for (int i = 0; i < 50; i++) {
436 ASSERT_OK(Put(Key(i), "memtable"));
437 true_data[Key(i)] = "memtable";
438 }
439 SequenceNumber last_seqno = dbfull()->GetLatestSequenceNumber();
440
441 ASSERT_OK(GenerateAndAddExternalFile(
494da23a
TL
442 options, {60, 61, 62}, ValueType::kTypeValue, file_id++,
443 write_global_seqno, verify_checksums_before_ingest, &true_data));
11fdf7f2
TL
444 // File doesn't overwrite any keys, no seqno needed
445 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno);
446
447 ASSERT_OK(GenerateAndAddExternalFile(
494da23a
TL
448 options, {40, 41, 42}, ValueType::kTypeMerge, file_id++,
449 write_global_seqno, verify_checksums_before_ingest, &true_data));
11fdf7f2 450 // File overwrites some keys, a seqno will be assigned
7c673cae
FG
451 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 1);
452
494da23a
TL
453 ASSERT_OK(GenerateAndAddExternalFile(
454 options, {20, 30, 40}, ValueType::kTypeDeletion, file_id++,
455 write_global_seqno, verify_checksums_before_ingest, &true_data));
11fdf7f2 456 // File overwrites some keys, a seqno will be assigned
7c673cae
FG
457 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 2);
458
459 const Snapshot* snapshot = db_->GetSnapshot();
460
461 // We will need a seqno for the file regardless if the file overwrite
462 // keys in the DB or not because we have a snapshot
11fdf7f2 463 ASSERT_OK(GenerateAndAddExternalFile(
494da23a
TL
464 options, {1000, 1002}, ValueType::kTypeMerge, file_id++,
465 write_global_seqno, verify_checksums_before_ingest, &true_data));
7c673cae
FG
466 // A global seqno will be assigned anyway because of the snapshot
467 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 3);
468
11fdf7f2 469 ASSERT_OK(GenerateAndAddExternalFile(
494da23a
TL
470 options, {2000, 3002}, ValueType::kTypeMerge, file_id++,
471 write_global_seqno, verify_checksums_before_ingest, &true_data));
7c673cae
FG
472 // A global seqno will be assigned anyway because of the snapshot
473 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 4);
474
494da23a
TL
475 ASSERT_OK(GenerateAndAddExternalFile(
476 options, {1, 20, 40, 100, 150}, ValueType::kTypeMerge, file_id++,
477 write_global_seqno, verify_checksums_before_ingest, &true_data));
7c673cae
FG
478 // A global seqno will be assigned anyway because of the snapshot
479 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
480
481 db_->ReleaseSnapshot(snapshot);
482
11fdf7f2 483 ASSERT_OK(GenerateAndAddExternalFile(
494da23a
TL
484 options, {5000, 5001}, ValueType::kTypeValue, file_id++,
485 write_global_seqno, verify_checksums_before_ingest, &true_data));
11fdf7f2
TL
486 // No snapshot anymore, no need to assign a seqno
487 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
488
489 size_t kcnt = 0;
490 VerifyDBFromMap(true_data, &kcnt, false);
494da23a 491 } while (ChangeOptionsForFileIngestionTest());
11fdf7f2
TL
492}
493
494da23a
TL
494TEST_P(ExternalSSTFileBasicTest, IngestFileWithMixedValueType) {
495 bool write_global_seqno = std::get<0>(GetParam());
496 bool verify_checksums_before_ingest = std::get<1>(GetParam());
11fdf7f2
TL
497 do {
498 Options options = CurrentOptions();
499 options.merge_operator.reset(new TestPutOperator());
500 DestroyAndReopen(options);
501 std::map<std::string, std::string> true_data;
502
503 int file_id = 1;
504
505 ASSERT_OK(GenerateAndAddExternalFile(
506 options, {1, 2, 3, 4, 5, 6},
507 {ValueType::kTypeValue, ValueType::kTypeMerge, ValueType::kTypeValue,
508 ValueType::kTypeMerge, ValueType::kTypeValue, ValueType::kTypeMerge},
494da23a
TL
509 file_id++, write_global_seqno, verify_checksums_before_ingest,
510 &true_data));
11fdf7f2
TL
511 // File doesn't overwrite any keys, no seqno needed
512 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
513
514 ASSERT_OK(GenerateAndAddExternalFile(
515 options, {10, 11, 12, 13},
516 {ValueType::kTypeValue, ValueType::kTypeMerge, ValueType::kTypeValue,
517 ValueType::kTypeMerge},
494da23a
TL
518 file_id++, write_global_seqno, verify_checksums_before_ingest,
519 &true_data));
11fdf7f2
TL
520 // File doesn't overwrite any keys, no seqno needed
521 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 0);
522
523 ASSERT_OK(GenerateAndAddExternalFile(
494da23a
TL
524 options, {1, 4, 6},
525 {ValueType::kTypeDeletion, ValueType::kTypeValue,
526 ValueType::kTypeMerge},
527 file_id++, write_global_seqno, verify_checksums_before_ingest,
528 &true_data));
11fdf7f2
TL
529 // File overwrites some keys, a seqno will be assigned
530 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 1);
531
532 ASSERT_OK(GenerateAndAddExternalFile(
494da23a
TL
533 options, {11, 15, 19},
534 {ValueType::kTypeDeletion, ValueType::kTypeMerge,
535 ValueType::kTypeValue},
536 file_id++, write_global_seqno, verify_checksums_before_ingest,
537 &true_data));
11fdf7f2
TL
538 // File overwrites some keys, a seqno will be assigned
539 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
540
541 ASSERT_OK(GenerateAndAddExternalFile(
542 options, {120, 130}, {ValueType::kTypeValue, ValueType::kTypeMerge},
494da23a
TL
543 file_id++, write_global_seqno, verify_checksums_before_ingest,
544 &true_data));
11fdf7f2
TL
545 // File doesn't overwrite any keys, no seqno needed
546 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 2);
547
548 ASSERT_OK(GenerateAndAddExternalFile(
549 options, {1, 130}, {ValueType::kTypeMerge, ValueType::kTypeDeletion},
494da23a
TL
550 file_id++, write_global_seqno, verify_checksums_before_ingest,
551 &true_data));
11fdf7f2
TL
552 // File overwrites some keys, a seqno will be assigned
553 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 3);
554
555 ASSERT_OK(GenerateAndAddExternalFile(
556 options, {150, 151, 152},
557 {ValueType::kTypeValue, ValueType::kTypeMerge,
558 ValueType::kTypeDeletion},
494da23a
TL
559 {{150, 160}, {180, 190}}, file_id++, write_global_seqno,
560 verify_checksums_before_ingest, &true_data));
11fdf7f2
TL
561 // File doesn't overwrite any keys, no seqno needed
562 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 3);
563
564 ASSERT_OK(GenerateAndAddExternalFile(
565 options, {150, 151, 152},
566 {ValueType::kTypeValue, ValueType::kTypeMerge, ValueType::kTypeValue},
494da23a
TL
567 {{200, 250}}, file_id++, write_global_seqno,
568 verify_checksums_before_ingest, &true_data));
11fdf7f2
TL
569 // File overwrites some keys, a seqno will be assigned
570 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 4);
571
572 ASSERT_OK(GenerateAndAddExternalFile(
573 options, {300, 301, 302},
574 {ValueType::kTypeValue, ValueType::kTypeMerge,
575 ValueType::kTypeDeletion},
494da23a
TL
576 {{1, 2}, {152, 154}}, file_id++, write_global_seqno,
577 verify_checksums_before_ingest, &true_data));
11fdf7f2
TL
578 // File overwrites some keys, a seqno will be assigned
579 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), 5);
580
581 // Write some keys through normal write path
582 for (int i = 0; i < 50; i++) {
583 ASSERT_OK(Put(Key(i), "memtable"));
584 true_data[Key(i)] = "memtable";
585 }
586 SequenceNumber last_seqno = dbfull()->GetLatestSequenceNumber();
587
588 ASSERT_OK(GenerateAndAddExternalFile(
589 options, {60, 61, 62},
590 {ValueType::kTypeValue, ValueType::kTypeMerge, ValueType::kTypeValue},
494da23a
TL
591 file_id++, write_global_seqno, verify_checksums_before_ingest,
592 &true_data));
11fdf7f2
TL
593 // File doesn't overwrite any keys, no seqno needed
594 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno);
595
596 ASSERT_OK(GenerateAndAddExternalFile(
597 options, {40, 41, 42},
598 {ValueType::kTypeValue, ValueType::kTypeDeletion,
599 ValueType::kTypeDeletion},
494da23a
TL
600 file_id++, write_global_seqno, verify_checksums_before_ingest,
601 &true_data));
11fdf7f2
TL
602 // File overwrites some keys, a seqno will be assigned
603 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 1);
604
605 ASSERT_OK(GenerateAndAddExternalFile(
606 options, {20, 30, 40},
607 {ValueType::kTypeDeletion, ValueType::kTypeDeletion,
608 ValueType::kTypeDeletion},
494da23a
TL
609 file_id++, write_global_seqno, verify_checksums_before_ingest,
610 &true_data));
11fdf7f2
TL
611 // File overwrites some keys, a seqno will be assigned
612 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 2);
613
614 const Snapshot* snapshot = db_->GetSnapshot();
615
616 // We will need a seqno for the file regardless if the file overwrite
617 // keys in the DB or not because we have a snapshot
618 ASSERT_OK(GenerateAndAddExternalFile(
619 options, {1000, 1002}, {ValueType::kTypeValue, ValueType::kTypeMerge},
494da23a
TL
620 file_id++, write_global_seqno, verify_checksums_before_ingest,
621 &true_data));
11fdf7f2
TL
622 // A global seqno will be assigned anyway because of the snapshot
623 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 3);
624
625 ASSERT_OK(GenerateAndAddExternalFile(
626 options, {2000, 3002}, {ValueType::kTypeValue, ValueType::kTypeMerge},
494da23a
TL
627 file_id++, write_global_seqno, verify_checksums_before_ingest,
628 &true_data));
11fdf7f2
TL
629 // A global seqno will be assigned anyway because of the snapshot
630 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 4);
631
632 ASSERT_OK(GenerateAndAddExternalFile(
633 options, {1, 20, 40, 100, 150},
634 {ValueType::kTypeDeletion, ValueType::kTypeDeletion,
635 ValueType::kTypeValue, ValueType::kTypeMerge, ValueType::kTypeMerge},
494da23a
TL
636 file_id++, write_global_seqno, verify_checksums_before_ingest,
637 &true_data));
11fdf7f2
TL
638 // A global seqno will be assigned anyway because of the snapshot
639 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
640
641 db_->ReleaseSnapshot(snapshot);
642
643 ASSERT_OK(GenerateAndAddExternalFile(
644 options, {5000, 5001}, {ValueType::kTypeValue, ValueType::kTypeMerge},
494da23a
TL
645 file_id++, write_global_seqno, verify_checksums_before_ingest,
646 &true_data));
7c673cae
FG
647 // No snapshot anymore, no need to assign a seqno
648 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno + 5);
649
650 size_t kcnt = 0;
651 VerifyDBFromMap(true_data, &kcnt, false);
494da23a 652 } while (ChangeOptionsForFileIngestionTest());
7c673cae
FG
653}
654
655TEST_F(ExternalSSTFileBasicTest, FadviseTrigger) {
656 Options options = CurrentOptions();
657 const int kNumKeys = 10000;
658
659 size_t total_fadvised_bytes = 0;
f67539c2 660 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
11fdf7f2 661 "SstFileWriter::Rep::InvalidatePageCache", [&](void* arg) {
7c673cae
FG
662 size_t fadvise_size = *(reinterpret_cast<size_t*>(arg));
663 total_fadvised_bytes += fadvise_size;
664 });
f67539c2 665 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
7c673cae
FG
666
667 std::unique_ptr<SstFileWriter> sst_file_writer;
668
669 std::string sst_file_path = sst_files_dir_ + "file_fadvise_disable.sst";
670 sst_file_writer.reset(
671 new SstFileWriter(EnvOptions(), options, nullptr, false));
672 ASSERT_OK(sst_file_writer->Open(sst_file_path));
673 for (int i = 0; i < kNumKeys; i++) {
11fdf7f2 674 ASSERT_OK(sst_file_writer->Put(Key(i), Key(i)));
7c673cae
FG
675 }
676 ASSERT_OK(sst_file_writer->Finish());
677 // fadvise disabled
678 ASSERT_EQ(total_fadvised_bytes, 0);
679
7c673cae
FG
680 sst_file_path = sst_files_dir_ + "file_fadvise_enable.sst";
681 sst_file_writer.reset(
682 new SstFileWriter(EnvOptions(), options, nullptr, true));
683 ASSERT_OK(sst_file_writer->Open(sst_file_path));
684 for (int i = 0; i < kNumKeys; i++) {
11fdf7f2 685 ASSERT_OK(sst_file_writer->Put(Key(i), Key(i)));
7c673cae
FG
686 }
687 ASSERT_OK(sst_file_writer->Finish());
688 // fadvise enabled
689 ASSERT_EQ(total_fadvised_bytes, sst_file_writer->FileSize());
690 ASSERT_GT(total_fadvised_bytes, 0);
691
f67539c2
TL
692 ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
693}
694
695TEST_F(ExternalSSTFileBasicTest, SyncFailure) {
696 Options options;
697 options.create_if_missing = true;
698 options.env = fault_injection_test_env_.get();
699
700 std::vector<std::pair<std::string, std::string>> test_cases = {
701 {"ExternalSstFileIngestionJob::BeforeSyncIngestedFile",
702 "ExternalSstFileIngestionJob::AfterSyncIngestedFile"},
703 {"ExternalSstFileIngestionJob::BeforeSyncDir",
704 "ExternalSstFileIngestionJob::AfterSyncDir"},
705 {"ExternalSstFileIngestionJob::BeforeSyncGlobalSeqno",
706 "ExternalSstFileIngestionJob::AfterSyncGlobalSeqno"}};
707
708 for (size_t i = 0; i < test_cases.size(); i++) {
709 SyncPoint::GetInstance()->SetCallBack(test_cases[i].first, [&](void*) {
710 fault_injection_test_env_->SetFilesystemActive(false);
711 });
712 SyncPoint::GetInstance()->SetCallBack(test_cases[i].second, [&](void*) {
713 fault_injection_test_env_->SetFilesystemActive(true);
714 });
715 SyncPoint::GetInstance()->EnableProcessing();
716
717 DestroyAndReopen(options);
718 if (i == 2) {
719 ASSERT_OK(Put("foo", "v1"));
720 }
721
722 Options sst_file_writer_options;
723 std::unique_ptr<SstFileWriter> sst_file_writer(
724 new SstFileWriter(EnvOptions(), sst_file_writer_options));
725 std::string file_name =
726 sst_files_dir_ + "sync_failure_test_" + ToString(i) + ".sst";
727 ASSERT_OK(sst_file_writer->Open(file_name));
728 ASSERT_OK(sst_file_writer->Put("bar", "v2"));
729 ASSERT_OK(sst_file_writer->Finish());
730
731 IngestExternalFileOptions ingest_opt;
732 if (i == 0) {
733 ingest_opt.move_files = true;
734 }
735 const Snapshot* snapshot = db_->GetSnapshot();
736 if (i == 2) {
737 ingest_opt.write_global_seqno = true;
738 }
739 ASSERT_FALSE(db_->IngestExternalFile({file_name}, ingest_opt).ok());
740 db_->ReleaseSnapshot(snapshot);
741
742 SyncPoint::GetInstance()->DisableProcessing();
743 SyncPoint::GetInstance()->ClearAllCallBacks();
744 Destroy(options);
745 }
746}
747
748TEST_F(ExternalSSTFileBasicTest, VerifyChecksumReadahead) {
749 Options options;
750 options.create_if_missing = true;
751 SpecialEnv senv(Env::Default());
752 options.env = &senv;
753 DestroyAndReopen(options);
754
755 Options sst_file_writer_options;
756 std::unique_ptr<SstFileWriter> sst_file_writer(
757 new SstFileWriter(EnvOptions(), sst_file_writer_options));
758 std::string file_name = sst_files_dir_ + "verify_checksum_readahead_test.sst";
759 ASSERT_OK(sst_file_writer->Open(file_name));
760 Random rnd(301);
761 std::string value = DBTestBase::RandomString(&rnd, 4000);
762 for (int i = 0; i < 5000; i++) {
763 ASSERT_OK(sst_file_writer->Put(DBTestBase::Key(i), value));
764 }
765 ASSERT_OK(sst_file_writer->Finish());
766
767 // Ingest it once without verifying checksums to see the baseline
768 // preads.
769 IngestExternalFileOptions ingest_opt;
770 ingest_opt.move_files = false;
771 senv.count_random_reads_ = true;
772 senv.random_read_bytes_counter_ = 0;
773 ASSERT_OK(db_->IngestExternalFile({file_name}, ingest_opt));
774
775 auto base_num_reads = senv.random_read_counter_.Read();
776 // Make sure the counter is enabled.
777 ASSERT_GT(base_num_reads, 0);
778
779 // Ingest again and observe the reads made for for readahead.
780 ingest_opt.move_files = false;
781 ingest_opt.verify_checksums_before_ingest = true;
782 ingest_opt.verify_checksums_readahead_size = size_t{2 * 1024 * 1024};
783
784 senv.count_random_reads_ = true;
785 senv.random_read_bytes_counter_ = 0;
786 ASSERT_OK(db_->IngestExternalFile({file_name}, ingest_opt));
787
788 // Make sure the counter is enabled.
789 ASSERT_GT(senv.random_read_counter_.Read() - base_num_reads, 0);
790
791 // The SST file is about 20MB. Readahead size is 2MB.
792 // Give a conservative 15 reads for metadata blocks, the number
793 // of random reads should be within 20 MB / 2MB + 15 = 25.
794 ASSERT_LE(senv.random_read_counter_.Read() - base_num_reads, 40);
795
796 Destroy(options);
7c673cae
FG
797}
798
494da23a 799TEST_P(ExternalSSTFileBasicTest, IngestionWithRangeDeletions) {
11fdf7f2
TL
800 int kNumLevels = 7;
801 Options options = CurrentOptions();
802 options.disable_auto_compactions = true;
803 options.num_levels = kNumLevels;
804 Reopen(options);
805
806 std::map<std::string, std::string> true_data;
807 int file_id = 1;
808 // prevent range deletions from being dropped due to becoming obsolete.
809 const Snapshot* snapshot = db_->GetSnapshot();
810
811 // range del [0, 50) in L6 file, [50, 100) in L0 file, [100, 150) in memtable
812 for (int i = 0; i < 3; i++) {
813 if (i != 0) {
814 db_->Flush(FlushOptions());
815 if (i == 1) {
816 MoveFilesToLevel(kNumLevels - 1);
817 }
818 }
819 ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
820 Key(50 * i), Key(50 * (i + 1))));
821 }
822 ASSERT_EQ(1, NumTableFilesAtLevel(0));
823 ASSERT_EQ(0, NumTableFilesAtLevel(kNumLevels - 2));
824 ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 1));
825
494da23a
TL
826 bool write_global_seqno = std::get<0>(GetParam());
827 bool verify_checksums_before_ingest = std::get<1>(GetParam());
11fdf7f2
TL
828 // overlaps with L0 file but not memtable, so flush is skipped and file is
829 // ingested into L0
830 SequenceNumber last_seqno = dbfull()->GetLatestSequenceNumber();
831 ASSERT_OK(GenerateAndAddExternalFile(
832 options, {60, 90}, {ValueType::kTypeValue, ValueType::kTypeValue},
494da23a
TL
833 {{65, 70}, {70, 85}}, file_id++, write_global_seqno,
834 verify_checksums_before_ingest, &true_data));
11fdf7f2
TL
835 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), ++last_seqno);
836 ASSERT_EQ(2, NumTableFilesAtLevel(0));
837 ASSERT_EQ(0, NumTableFilesAtLevel(kNumLevels - 2));
838 ASSERT_EQ(1, NumTableFilesAtLevel(options.num_levels - 1));
839
840 // overlaps with L6 file but not memtable or L0 file, so flush is skipped and
841 // file is ingested into L5
842 ASSERT_OK(GenerateAndAddExternalFile(
843 options, {10, 40}, {ValueType::kTypeValue, ValueType::kTypeValue},
494da23a
TL
844 file_id++, write_global_seqno, verify_checksums_before_ingest,
845 &true_data));
11fdf7f2
TL
846 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), ++last_seqno);
847 ASSERT_EQ(2, NumTableFilesAtLevel(0));
848 ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 2));
849 ASSERT_EQ(1, NumTableFilesAtLevel(options.num_levels - 1));
850
851 // overlaps with L5 file but not memtable or L0 file, so flush is skipped and
852 // file is ingested into L4
494da23a
TL
853 ASSERT_OK(GenerateAndAddExternalFile(
854 options, {}, {}, {{5, 15}}, file_id++, write_global_seqno,
855 verify_checksums_before_ingest, &true_data));
11fdf7f2
TL
856 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), ++last_seqno);
857 ASSERT_EQ(2, NumTableFilesAtLevel(0));
858 ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 2));
859 ASSERT_EQ(1, NumTableFilesAtLevel(options.num_levels - 2));
860 ASSERT_EQ(1, NumTableFilesAtLevel(options.num_levels - 1));
861
862 // ingested file overlaps with memtable, so flush is triggered before the file
863 // is ingested such that the ingested data is considered newest. So L0 file
864 // count increases by two.
865 ASSERT_OK(GenerateAndAddExternalFile(
866 options, {100, 140}, {ValueType::kTypeValue, ValueType::kTypeValue},
494da23a
TL
867 file_id++, write_global_seqno, verify_checksums_before_ingest,
868 &true_data));
11fdf7f2
TL
869 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), ++last_seqno);
870 ASSERT_EQ(4, NumTableFilesAtLevel(0));
871 ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 2));
872 ASSERT_EQ(1, NumTableFilesAtLevel(options.num_levels - 1));
873
874 // snapshot unneeded now that all range deletions are persisted
875 db_->ReleaseSnapshot(snapshot);
876
877 // overlaps with nothing, so places at bottom level and skips incrementing
878 // seqnum.
879 ASSERT_OK(GenerateAndAddExternalFile(
880 options, {151, 175}, {ValueType::kTypeValue, ValueType::kTypeValue},
494da23a
TL
881 {{160, 200}}, file_id++, write_global_seqno,
882 verify_checksums_before_ingest, &true_data));
11fdf7f2
TL
883 ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno);
884 ASSERT_EQ(4, NumTableFilesAtLevel(0));
885 ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 2));
886 ASSERT_EQ(2, NumTableFilesAtLevel(options.num_levels - 1));
887}
888
f67539c2
TL
889TEST_F(ExternalSSTFileBasicTest, AdjacentRangeDeletionTombstones) {
890 Options options = CurrentOptions();
891 SstFileWriter sst_file_writer(EnvOptions(), options);
892
893 // file8.sst (delete 300 => 400)
894 std::string file8 = sst_files_dir_ + "file8.sst";
895 ASSERT_OK(sst_file_writer.Open(file8));
896 ASSERT_OK(sst_file_writer.DeleteRange(Key(300), Key(400)));
897 ExternalSstFileInfo file8_info;
898 Status s = sst_file_writer.Finish(&file8_info);
899 ASSERT_TRUE(s.ok()) << s.ToString();
900 ASSERT_EQ(file8_info.file_path, file8);
901 ASSERT_EQ(file8_info.num_entries, 0);
902 ASSERT_EQ(file8_info.smallest_key, "");
903 ASSERT_EQ(file8_info.largest_key, "");
904 ASSERT_EQ(file8_info.num_range_del_entries, 1);
905 ASSERT_EQ(file8_info.smallest_range_del_key, Key(300));
906 ASSERT_EQ(file8_info.largest_range_del_key, Key(400));
907
908 // file9.sst (delete 400 => 500)
909 std::string file9 = sst_files_dir_ + "file9.sst";
910 ASSERT_OK(sst_file_writer.Open(file9));
911 ASSERT_OK(sst_file_writer.DeleteRange(Key(400), Key(500)));
912 ExternalSstFileInfo file9_info;
913 s = sst_file_writer.Finish(&file9_info);
914 ASSERT_TRUE(s.ok()) << s.ToString();
915 ASSERT_EQ(file9_info.file_path, file9);
916 ASSERT_EQ(file9_info.num_entries, 0);
917 ASSERT_EQ(file9_info.smallest_key, "");
918 ASSERT_EQ(file9_info.largest_key, "");
919 ASSERT_EQ(file9_info.num_range_del_entries, 1);
920 ASSERT_EQ(file9_info.smallest_range_del_key, Key(400));
921 ASSERT_EQ(file9_info.largest_range_del_key, Key(500));
922
923 // Range deletion tombstones are exclusive on their end key, so these SSTs
924 // should not be considered as overlapping.
925 s = DeprecatedAddFile({file8, file9});
926 ASSERT_TRUE(s.ok()) << s.ToString();
927 ASSERT_EQ(db_->GetLatestSequenceNumber(), 0U);
928 DestroyAndRecreateExternalSSTFilesDir();
929}
930
494da23a
TL
931TEST_P(ExternalSSTFileBasicTest, IngestFileWithBadBlockChecksum) {
932 bool change_checksum_called = false;
933 const auto& change_checksum = [&](void* arg) {
934 if (!change_checksum_called) {
935 char* buf = reinterpret_cast<char*>(arg);
936 assert(nullptr != buf);
937 buf[0] ^= 0x1;
938 change_checksum_called = true;
939 }
940 };
941 SyncPoint::GetInstance()->DisableProcessing();
942 SyncPoint::GetInstance()->ClearAllCallBacks();
943 SyncPoint::GetInstance()->SetCallBack(
944 "BlockBasedTableBuilder::WriteRawBlock:TamperWithChecksum",
945 change_checksum);
946 SyncPoint::GetInstance()->EnableProcessing();
947 int file_id = 0;
948 bool write_global_seqno = std::get<0>(GetParam());
949 bool verify_checksums_before_ingest = std::get<1>(GetParam());
950 do {
951 Options options = CurrentOptions();
952 DestroyAndReopen(options);
953 std::map<std::string, std::string> true_data;
954 Status s = GenerateAndAddExternalFile(
955 options, {1, 2, 3, 4, 5, 6}, ValueType::kTypeValue, file_id++,
956 write_global_seqno, verify_checksums_before_ingest, &true_data);
957 if (verify_checksums_before_ingest) {
958 ASSERT_NOK(s);
959 } else {
960 ASSERT_OK(s);
961 }
962 change_checksum_called = false;
963 } while (ChangeOptionsForFileIngestionTest());
964}
965
966TEST_P(ExternalSSTFileBasicTest, IngestFileWithFirstByteTampered) {
967 SyncPoint::GetInstance()->DisableProcessing();
968 int file_id = 0;
969 EnvOptions env_options;
970 do {
971 Options options = CurrentOptions();
972 std::string file_path = sst_files_dir_ + ToString(file_id++);
973 SstFileWriter sst_file_writer(env_options, options);
974 Status s = sst_file_writer.Open(file_path);
975 ASSERT_OK(s);
976 for (int i = 0; i != 100; ++i) {
977 std::string key = Key(i);
978 std::string value = Key(i) + ToString(0);
979 ASSERT_OK(sst_file_writer.Put(key, value));
980 }
981 ASSERT_OK(sst_file_writer.Finish());
982 {
983 // Get file size
984 uint64_t file_size = 0;
985 ASSERT_OK(env_->GetFileSize(file_path, &file_size));
986 ASSERT_GT(file_size, 8);
987 std::unique_ptr<RandomRWFile> rwfile;
988 ASSERT_OK(env_->NewRandomRWFile(file_path, &rwfile, EnvOptions()));
989 // Manually corrupt the file
990 // We deterministically corrupt the first byte because we currently
991 // cannot choose a random offset. The reason for this limitation is that
992 // we do not checksum property block at present.
993 const uint64_t offset = 0;
994 char scratch[8] = {0};
995 Slice buf;
996 ASSERT_OK(rwfile->Read(offset, sizeof(scratch), &buf, scratch));
997 scratch[0] ^= 0xff; // flip one bit
998 ASSERT_OK(rwfile->Write(offset, buf));
999 }
1000 // Ingest file.
1001 IngestExternalFileOptions ifo;
1002 ifo.write_global_seqno = std::get<0>(GetParam());
1003 ifo.verify_checksums_before_ingest = std::get<1>(GetParam());
1004 s = db_->IngestExternalFile({file_path}, ifo);
1005 if (ifo.verify_checksums_before_ingest) {
1006 ASSERT_NOK(s);
1007 } else {
1008 ASSERT_OK(s);
1009 }
1010 } while (ChangeOptionsForFileIngestionTest());
1011}
1012
1013TEST_P(ExternalSSTFileBasicTest, IngestExternalFileWithCorruptedPropsBlock) {
1014 bool verify_checksums_before_ingest = std::get<1>(GetParam());
1015 if (!verify_checksums_before_ingest) {
1016 return;
1017 }
1018 uint64_t props_block_offset = 0;
1019 size_t props_block_size = 0;
1020 const auto& get_props_block_offset = [&](void* arg) {
1021 props_block_offset = *reinterpret_cast<uint64_t*>(arg);
1022 };
1023 const auto& get_props_block_size = [&](void* arg) {
1024 props_block_size = *reinterpret_cast<uint64_t*>(arg);
1025 };
1026 SyncPoint::GetInstance()->DisableProcessing();
1027 SyncPoint::GetInstance()->ClearAllCallBacks();
1028 SyncPoint::GetInstance()->SetCallBack(
1029 "BlockBasedTableBuilder::WritePropertiesBlock:GetPropsBlockOffset",
1030 get_props_block_offset);
1031 SyncPoint::GetInstance()->SetCallBack(
1032 "BlockBasedTableBuilder::WritePropertiesBlock:GetPropsBlockSize",
1033 get_props_block_size);
1034 SyncPoint::GetInstance()->EnableProcessing();
1035 int file_id = 0;
1036 Random64 rand(time(nullptr));
1037 do {
1038 std::string file_path = sst_files_dir_ + ToString(file_id++);
1039 Options options = CurrentOptions();
1040 SstFileWriter sst_file_writer(EnvOptions(), options);
1041 Status s = sst_file_writer.Open(file_path);
1042 ASSERT_OK(s);
1043 for (int i = 0; i != 100; ++i) {
1044 std::string key = Key(i);
1045 std::string value = Key(i) + ToString(0);
1046 ASSERT_OK(sst_file_writer.Put(key, value));
1047 }
1048 ASSERT_OK(sst_file_writer.Finish());
1049
1050 {
1051 std::unique_ptr<RandomRWFile> rwfile;
1052 ASSERT_OK(env_->NewRandomRWFile(file_path, &rwfile, EnvOptions()));
1053 // Manually corrupt the file
1054 ASSERT_GT(props_block_size, 8);
1055 uint64_t offset =
1056 props_block_offset + rand.Next() % (props_block_size - 8);
1057 char scratch[8] = {0};
1058 Slice buf;
1059 ASSERT_OK(rwfile->Read(offset, sizeof(scratch), &buf, scratch));
1060 scratch[0] ^= 0xff; // flip one bit
1061 ASSERT_OK(rwfile->Write(offset, buf));
1062 }
1063
1064 // Ingest file.
1065 IngestExternalFileOptions ifo;
1066 ifo.write_global_seqno = std::get<0>(GetParam());
1067 ifo.verify_checksums_before_ingest = true;
1068 s = db_->IngestExternalFile({file_path}, ifo);
1069 ASSERT_NOK(s);
1070 } while (ChangeOptionsForFileIngestionTest());
1071}
1072
f67539c2
TL
1073TEST_F(ExternalSSTFileBasicTest, OverlappingFiles) {
1074 Options options = CurrentOptions();
1075
1076 std::vector<std::string> files;
1077 {
1078 SstFileWriter sst_file_writer(EnvOptions(), options);
1079 std::string file1 = sst_files_dir_ + "file1.sst";
1080 ASSERT_OK(sst_file_writer.Open(file1));
1081 ASSERT_OK(sst_file_writer.Put("a", "z"));
1082 ASSERT_OK(sst_file_writer.Put("i", "m"));
1083 ExternalSstFileInfo file1_info;
1084 ASSERT_OK(sst_file_writer.Finish(&file1_info));
1085 files.push_back(std::move(file1));
1086 }
1087 {
1088 SstFileWriter sst_file_writer(EnvOptions(), options);
1089 std::string file2 = sst_files_dir_ + "file2.sst";
1090 ASSERT_OK(sst_file_writer.Open(file2));
1091 ASSERT_OK(sst_file_writer.Put("i", "k"));
1092 ExternalSstFileInfo file2_info;
1093 ASSERT_OK(sst_file_writer.Finish(&file2_info));
1094 files.push_back(std::move(file2));
1095 }
1096
1097 IngestExternalFileOptions ifo;
1098 ASSERT_OK(db_->IngestExternalFile(files, ifo));
1099 ASSERT_EQ(Get("a"), "z");
1100 ASSERT_EQ(Get("i"), "k");
1101
1102 int total_keys = 0;
1103 Iterator* iter = db_->NewIterator(ReadOptions());
1104 for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
1105 ASSERT_OK(iter->status());
1106 total_keys++;
1107 }
1108 delete iter;
1109 ASSERT_EQ(total_keys, 2);
1110
1111 ASSERT_EQ(2, NumTableFilesAtLevel(0));
1112}
1113
494da23a
TL
1114INSTANTIATE_TEST_CASE_P(ExternalSSTFileBasicTest, ExternalSSTFileBasicTest,
1115 testing::Values(std::make_tuple(true, true),
1116 std::make_tuple(true, false),
1117 std::make_tuple(false, true),
1118 std::make_tuple(false, false)));
1119
7c673cae
FG
1120#endif // ROCKSDB_LITE
1121
f67539c2 1122} // namespace ROCKSDB_NAMESPACE
7c673cae
FG
1123
1124int main(int argc, char** argv) {
f67539c2 1125 ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
7c673cae
FG
1126 ::testing::InitGoogleTest(&argc, argv);
1127 return RUN_ALL_TESTS();
1128}