YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/rocksdb/db/prefix_test.cc
Line
Count
Source (jump to first uncovered line)
1
//  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
2
//  This source code is licensed under the BSD-style license found in the
3
//  LICENSE file in the root directory of this source tree. An additional grant
4
//  of patent rights can be found in the PATENTS file in the same directory.
5
//
6
// The following only applies to changes made to this file as part of YugaByte development.
7
//
8
// Portions Copyright (c) YugaByte, Inc.
9
//
10
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
11
// in compliance with the License.  You may obtain a copy of the License at
12
//
13
// http://www.apache.org/licenses/LICENSE-2.0
14
//
15
// Unless required by applicable law or agreed to in writing, software distributed under the License
16
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
17
// or implied.  See the License for the specific language governing permissions and limitations
18
// under the License.
19
//
20
21
#ifndef ROCKSDB_LITE
22
23
#ifndef GFLAGS
24
#include <cstdio>
25
int main() {
26
  fprintf(stderr, "Please install gflags to run this test... Skipping...\n");
27
  return 0;
28
}
29
#else
30
31
#include <algorithm>
32
#include <iostream>
33
#include <vector>
34
35
#include <gflags/gflags.h>
36
#include "yb/rocksdb/comparator.h"
37
#include "yb/rocksdb/db.h"
38
#include "yb/rocksdb/filter_policy.h"
39
#include "yb/rocksdb/perf_context.h"
40
#include "yb/rocksdb/slice_transform.h"
41
#include "yb/rocksdb/memtablerep.h"
42
#include "yb/rocksdb/table.h"
43
#include "yb/rocksdb/util/histogram.h"
44
#include "yb/rocksdb/util/stop_watch.h"
45
#include "yb/rocksdb/util/testharness.h"
46
#include "yb/rocksdb/util/testutil.h"
47
48
#include "yb/util/string_util.h"
49
#include "yb/util/test_util.h"
50
51
using GFLAGS::ParseCommandLineFlags;
52
53
DEFINE_bool(trigger_deadlock, false,
54
            "issue delete in range scan to trigger PrefixHashMap deadlock");
55
DEFINE_int32(bucket_count, 100000, "number of buckets");
56
DEFINE_uint64(num_locks, 10001, "number of locks");
57
DEFINE_bool(random_prefix, false, "randomize prefix");
58
DEFINE_uint64(total_prefixes, 100000, "total number of prefixes");
59
DEFINE_uint64(items_per_prefix, 1, "total number of values per prefix");
60
DEFINE_int64(write_buffer_size, 33554432, "");
61
DEFINE_int32(max_write_buffer_number, 2, "");
62
DEFINE_int32(min_write_buffer_number_to_merge, 1, "");
63
DEFINE_int32(skiplist_height, 4, "");
64
DEFINE_int32(memtable_prefix_bloom_bits, 10000000, "");
65
DEFINE_int32(memtable_prefix_bloom_probes, 10, "");
66
DEFINE_int32(memtable_prefix_bloom_huge_page_tlb_size, 2 * 1024 * 1024, "");
67
DEFINE_int32(value_size, 40, "");
68
69
// Path to the database on file system
70
const std::string kDbName = rocksdb::test::TmpDir() + "/prefix_test";
71
72
namespace rocksdb {
73
74
struct TestKey {
75
  uint64_t prefix;
76
  uint64_t sorted;
77
78
  TestKey(uint64_t _prefix, uint64_t _sorted)
79
840k
      : prefix(_prefix), sorted(_sorted) {}
80
};
81
82
// return a slice backed by test_key
83
840k
inline Slice TestKeyToSlice(const TestKey& test_key) {
84
840k
  return Slice((const char*)&test_key, sizeof(test_key));
85
840k
}
86
87
2.84M
inline const TestKey* SliceToTestKey(const Slice& slice) {
88
2.84M
  return (const TestKey*)slice.data();
89
2.84M
}
90
91
class TestKeyComparator : public Comparator {
92
 public:
93
94
  // Compare needs to be aware of the possibility of a and/or b is
95
  // prefix only
96
1.42M
  int Compare(const Slice& a, const Slice& b) const override {
97
1.42M
    const TestKey* key_a = SliceToTestKey(a);
98
1.42M
    const TestKey* key_b = SliceToTestKey(b);
99
1.42M
    if (key_a->prefix != key_b->prefix) {
100
1.01M
      if (key_a->prefix < key_b->prefix) return -1;
101
156k
      if (key_a->prefix > key_b->prefix) return 1;
102
401k
    } else {
103
401k
      EXPECT_TRUE(key_a->prefix == key_b->prefix);
104
      // note, both a and b could be prefix only
105
401k
      if (a.size() != b.size()) {
106
        // one of them is prefix
107
0
        EXPECT_TRUE(
108
0
            (a.size() == sizeof(uint64_t) && b.size() == sizeof(TestKey)) ||
109
0
            (b.size() == sizeof(uint64_t) && a.size() == sizeof(TestKey)));
110
0
        if (a.size() < b.size()) return -1;
111
0
        if (a.size() > b.size()) return 1;
112
401k
      } else {
113
        // both a and b are prefix
114
401k
        if (a.size() == sizeof(uint64_t)) {
115
0
          return 0;
116
0
        }
117
118
        // both a and b are whole key
119
401k
        EXPECT_TRUE(a.size() == sizeof(TestKey) && b.size() == sizeof(TestKey));
120
401k
        if (key_a->sorted < key_b->sorted) return -1;
121
400k
        if (key_a->sorted > key_b->sorted) return 1;
122
400k
        if (key_a->sorted == key_b->sorted) return 0;
123
0
      }
124
401k
    }
125
0
    return 0;
126
0
  }
127
128
68
  const char* Name() const override {
129
68
    return "TestKeyComparator";
130
68
  }
131
132
  virtual void FindShortestSeparator(std::string* start,
133
0
                                     const Slice& limit) const override {}
134
135
16
  void FindShortSuccessor(std::string* key) const override {}
136
};
137
138
namespace {
139
void PutKey(DB* db, WriteOptions write_options, uint64_t prefix,
140
96
            uint64_t suffix, const Slice& value) {
141
96
  TestKey test_key(prefix, suffix);
142
96
  Slice key = TestKeyToSlice(test_key);
143
96
  ASSERT_OK(db->Put(write_options, key, value));
144
96
}
145
146
192
void SeekIterator(Iterator* iter, uint64_t prefix, uint64_t suffix) {
147
192
  TestKey test_key(prefix, suffix);
148
192
  Slice key = TestKeyToSlice(test_key);
149
192
  iter->Seek(key);
150
192
}
151
152
const std::string kNotFoundResult = "NOT_FOUND";
153
154
std::string Get(DB* db, const ReadOptions& read_options, uint64_t prefix,
155
128
                uint64_t suffix) {
156
128
  TestKey test_key(prefix, suffix);
157
128
  Slice key = TestKeyToSlice(test_key);
158
159
128
  std::string result;
160
128
  Status s = db->Get(read_options, key, &result);
161
128
  if (s.IsNotFound()) {
162
40
    result = kNotFoundResult;
163
88
  } else if (!s.ok()) {
164
0
    result = s.ToString();
165
0
  }
166
128
  return result;
167
128
}
168
}  // namespace
169
170
class PrefixTest : public RocksDBTest {
171
 public:
172
20
  std::shared_ptr<DB> OpenDb() {
173
20
    DB* db;
174
175
20
    options.create_if_missing = true;
176
20
    options.write_buffer_size = FLAGS_write_buffer_size;
177
20
    options.max_write_buffer_number = FLAGS_max_write_buffer_number;
178
20
    options.min_write_buffer_number_to_merge =
179
20
      FLAGS_min_write_buffer_number_to_merge;
180
181
20
    options.memtable_prefix_bloom_bits = FLAGS_memtable_prefix_bloom_bits;
182
20
    options.memtable_prefix_bloom_probes = FLAGS_memtable_prefix_bloom_probes;
183
20
    options.memtable_prefix_bloom_huge_page_tlb_size =
184
20
        FLAGS_memtable_prefix_bloom_huge_page_tlb_size;
185
186
20
    options.prefix_extractor.reset(NewFixedPrefixTransform(8));
187
20
    BlockBasedTableOptions bbto;
188
20
    bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
189
20
    bbto.whole_key_filtering = false;
190
20
    options.table_factory.reset(NewBlockBasedTableFactory(bbto));
191
192
20
    Status s = DB::Open(options, kDbName,  &db);
193
20
    EXPECT_OK(s);
194
20
    return std::shared_ptr<DB>(db);
195
20
  }
196
197
4
  void FirstOption() {
198
4
    option_config_ = kBegin;
199
4
  }
200
201
25
  bool NextOptions(int bucket_count) {
202
    // skip some options
203
25
    option_config_++;
204
25
    if (option_config_ < kEnd) {
205
20
      options.prefix_extractor.reset(NewFixedPrefixTransform(8));
206
20
      switch(option_config_) {
207
5
        case kHashSkipList:
208
5
          options.memtable_factory.reset(
209
5
              NewHashSkipListRepFactory(bucket_count, FLAGS_skiplist_height));
210
5
          return true;
211
5
        case kHashLinkList:
212
5
          options.memtable_factory.reset(
213
5
              NewHashLinkListRepFactory(bucket_count));
214
5
          return true;
215
5
        case kHashLinkListHugePageTlb:
216
5
          options.memtable_factory.reset(
217
5
              NewHashLinkListRepFactory(bucket_count, 2 * 1024 * 1024));
218
5
          return true;
219
5
        case kHashLinkListTriggerSkipList:
220
5
          options.memtable_factory.reset(
221
5
              NewHashLinkListRepFactory(bucket_count, 0, 3));
222
5
          return true;
223
0
        default:
224
0
          return false;
225
5
      }
226
5
    }
227
5
    return false;
228
5
  }
229
230
3
  PrefixTest() : option_config_(kBegin) {
231
3
    options.comparator = new TestKeyComparator();
232
3
  }
233
3
  ~PrefixTest() {
234
3
    delete options.comparator;
235
3
  }
236
 protected:
237
  enum OptionConfig {
238
    kBegin,
239
    kHashSkipList,
240
    kHashLinkList,
241
    kHashLinkListHugePageTlb,
242
    kHashLinkListTriggerSkipList,
243
    kEnd
244
  };
245
  int option_config_;
246
  Options options;
247
};
248
249
1
TEST_F(PrefixTest, TestResult) {
250
3
  for (int num_buckets = 1; num_buckets <= 2; num_buckets++) {
251
2
    FirstOption();
252
10
    while (NextOptions(num_buckets)) {
253
8
      std::cout << "*** Mem table: " << options.memtable_factory->Name()
254
8
                << " number of buckets: " << num_buckets
255
8
                << std::endl;
256
8
      ASSERT_OK(DestroyDB(kDbName, Options()));
257
8
      auto db = OpenDb();
258
8
      WriteOptions write_options;
259
8
      ReadOptions read_options;
260
261
      // 1. Insert one row.
262
8
      Slice v16("v16");
263
8
      PutKey(db.get(), write_options, 1, 6, v16);
264
8
      std::unique_ptr<Iterator> iter(db->NewIterator(read_options));
265
8
      SeekIterator(iter.get(), 1, 6);
266
8
      ASSERT_TRUE(iter->Valid());
267
8
      ASSERT_TRUE(v16 == iter->value());
268
8
      SeekIterator(iter.get(), 1, 5);
269
8
      ASSERT_TRUE(iter->Valid());
270
8
      ASSERT_TRUE(v16 == iter->value());
271
8
      SeekIterator(iter.get(), 1, 5);
272
8
      ASSERT_TRUE(iter->Valid());
273
8
      ASSERT_TRUE(v16 == iter->value());
274
8
      iter->Next();
275
8
      ASSERT_TRUE(!iter->Valid());
276
277
8
      SeekIterator(iter.get(), 2, 0);
278
8
      ASSERT_TRUE(!iter->Valid());
279
280
8
      ASSERT_EQ(v16.ToString(), Get(db.get(), read_options, 1, 6));
281
8
      ASSERT_EQ(kNotFoundResult, Get(db.get(), read_options, 1, 5));
282
8
      ASSERT_EQ(kNotFoundResult, Get(db.get(), read_options, 1, 7));
283
8
      ASSERT_EQ(kNotFoundResult, Get(db.get(), read_options, 0, 6));
284
8
      ASSERT_EQ(kNotFoundResult, Get(db.get(), read_options, 2, 6));
285
286
      // 2. Insert an entry for the same prefix as the last entry in the bucket.
287
8
      Slice v17("v17");
288
8
      PutKey(db.get(), write_options, 1, 7, v17);
289
8
      iter.reset(db->NewIterator(read_options));
290
8
      SeekIterator(iter.get(), 1, 7);
291
8
      ASSERT_TRUE(iter->Valid());
292
8
      ASSERT_TRUE(v17 == iter->value());
293
294
8
      SeekIterator(iter.get(), 1, 6);
295
8
      ASSERT_TRUE(iter->Valid());
296
8
      ASSERT_TRUE(v16 == iter->value());
297
8
      iter->Next();
298
8
      ASSERT_TRUE(iter->Valid());
299
8
      ASSERT_TRUE(v17 == iter->value());
300
8
      iter->Next();
301
8
      ASSERT_TRUE(!iter->Valid());
302
303
8
      SeekIterator(iter.get(), 2, 0);
304
8
      ASSERT_TRUE(!iter->Valid());
305
306
      // 3. Insert an entry for the same prefix as the head of the bucket.
307
8
      Slice v15("v15");
308
8
      PutKey(db.get(), write_options, 1, 5, v15);
309
8
      iter.reset(db->NewIterator(read_options));
310
311
8
      SeekIterator(iter.get(), 1, 7);
312
8
      ASSERT_TRUE(iter->Valid());
313
8
      ASSERT_TRUE(v17 == iter->value());
314
315
8
      SeekIterator(iter.get(), 1, 5);
316
8
      ASSERT_TRUE(iter->Valid());
317
8
      ASSERT_TRUE(v15 == iter->value());
318
8
      iter->Next();
319
8
      ASSERT_TRUE(iter->Valid());
320
8
      ASSERT_TRUE(v16 == iter->value());
321
8
      iter->Next();
322
8
      ASSERT_TRUE(iter->Valid());
323
8
      ASSERT_TRUE(v17 == iter->value());
324
325
8
      SeekIterator(iter.get(), 1, 5);
326
8
      ASSERT_TRUE(iter->Valid());
327
8
      ASSERT_TRUE(v15 == iter->value());
328
329
8
      ASSERT_EQ(v15.ToString(), Get(db.get(), read_options, 1, 5));
330
8
      ASSERT_EQ(v16.ToString(), Get(db.get(), read_options, 1, 6));
331
8
      ASSERT_EQ(v17.ToString(), Get(db.get(), read_options, 1, 7));
332
333
      // 4. Insert an entry with a larger prefix
334
8
      Slice v22("v22");
335
8
      PutKey(db.get(), write_options, 2, 2, v22);
336
8
      iter.reset(db->NewIterator(read_options));
337
338
8
      SeekIterator(iter.get(), 2, 2);
339
8
      ASSERT_TRUE(iter->Valid());
340
8
      ASSERT_TRUE(v22 == iter->value());
341
8
      SeekIterator(iter.get(), 2, 0);
342
8
      ASSERT_TRUE(iter->Valid());
343
8
      ASSERT_TRUE(v22 == iter->value());
344
345
8
      SeekIterator(iter.get(), 1, 5);
346
8
      ASSERT_TRUE(iter->Valid());
347
8
      ASSERT_TRUE(v15 == iter->value());
348
349
8
      SeekIterator(iter.get(), 1, 7);
350
8
      ASSERT_TRUE(iter->Valid());
351
8
      ASSERT_TRUE(v17 == iter->value());
352
353
      // 5. Insert an entry with a smaller prefix
354
8
      Slice v02("v02");
355
8
      PutKey(db.get(), write_options, 0, 2, v02);
356
8
      iter.reset(db->NewIterator(read_options));
357
358
8
      SeekIterator(iter.get(), 0, 2);
359
8
      ASSERT_TRUE(iter->Valid());
360
8
      ASSERT_TRUE(v02 == iter->value());
361
8
      SeekIterator(iter.get(), 0, 0);
362
8
      ASSERT_TRUE(iter->Valid());
363
8
      ASSERT_TRUE(v02 == iter->value());
364
365
8
      SeekIterator(iter.get(), 2, 0);
366
8
      ASSERT_TRUE(iter->Valid());
367
8
      ASSERT_TRUE(v22 == iter->value());
368
369
8
      SeekIterator(iter.get(), 1, 5);
370
8
      ASSERT_TRUE(iter->Valid());
371
8
      ASSERT_TRUE(v15 == iter->value());
372
373
8
      SeekIterator(iter.get(), 1, 7);
374
8
      ASSERT_TRUE(iter->Valid());
375
8
      ASSERT_TRUE(v17 == iter->value());
376
377
      // 6. Insert to the beginning and the end of the first prefix
378
8
      Slice v13("v13");
379
8
      Slice v18("v18");
380
8
      PutKey(db.get(), write_options, 1, 3, v13);
381
8
      PutKey(db.get(), write_options, 1, 8, v18);
382
8
      iter.reset(db->NewIterator(read_options));
383
8
      SeekIterator(iter.get(), 1, 7);
384
8
      ASSERT_TRUE(iter->Valid());
385
8
      ASSERT_TRUE(v17 == iter->value());
386
387
8
      SeekIterator(iter.get(), 1, 3);
388
8
      ASSERT_TRUE(iter->Valid());
389
8
      ASSERT_TRUE(v13 == iter->value());
390
8
      iter->Next();
391
8
      ASSERT_TRUE(iter->Valid());
392
8
      ASSERT_TRUE(v15 == iter->value());
393
8
      iter->Next();
394
8
      ASSERT_TRUE(iter->Valid());
395
8
      ASSERT_TRUE(v16 == iter->value());
396
8
      iter->Next();
397
8
      ASSERT_TRUE(iter->Valid());
398
8
      ASSERT_TRUE(v17 == iter->value());
399
8
      iter->Next();
400
8
      ASSERT_TRUE(iter->Valid());
401
8
      ASSERT_TRUE(v18 == iter->value());
402
403
8
      SeekIterator(iter.get(), 0, 0);
404
8
      ASSERT_TRUE(iter->Valid());
405
8
      ASSERT_TRUE(v02 == iter->value());
406
407
8
      SeekIterator(iter.get(), 2, 0);
408
8
      ASSERT_TRUE(iter->Valid());
409
8
      ASSERT_TRUE(v22 == iter->value());
410
411
8
      ASSERT_EQ(v22.ToString(), Get(db.get(), read_options, 2, 2));
412
8
      ASSERT_EQ(v02.ToString(), Get(db.get(), read_options, 0, 2));
413
8
      ASSERT_EQ(v13.ToString(), Get(db.get(), read_options, 1, 3));
414
8
      ASSERT_EQ(v15.ToString(), Get(db.get(), read_options, 1, 5));
415
8
      ASSERT_EQ(v16.ToString(), Get(db.get(), read_options, 1, 6));
416
8
      ASSERT_EQ(v17.ToString(), Get(db.get(), read_options, 1, 7));
417
8
      ASSERT_EQ(v18.ToString(), Get(db.get(), read_options, 1, 8));
418
8
    }
419
2
  }
420
1
}
421
422
// Show results in prefix
423
1
TEST_F(PrefixTest, PrefixValid) {
424
3
  for (int num_buckets = 1; num_buckets <= 2; num_buckets++) {
425
2
    FirstOption();
426
10
    while (NextOptions(num_buckets)) {
427
8
      std::cout << "*** Mem table: " << options.memtable_factory->Name()
428
8
                << " number of buckets: " << num_buckets << std::endl;
429
8
      ASSERT_OK(DestroyDB(kDbName, Options()));
430
8
      auto db = OpenDb();
431
8
      WriteOptions write_options;
432
8
      ReadOptions read_options;
433
434
      // Insert keys with common prefix and one key with different
435
8
      Slice v16("v16");
436
8
      Slice v17("v17");
437
8
      Slice v18("v18");
438
8
      Slice v19("v19");
439
8
      PutKey(db.get(), write_options, 12345, 6, v16);
440
8
      PutKey(db.get(), write_options, 12345, 7, v17);
441
8
      PutKey(db.get(), write_options, 12345, 8, v18);
442
8
      PutKey(db.get(), write_options, 12345, 9, v19);
443
8
      PutKey(db.get(), write_options, 12346, 8, v16);
444
8
      ASSERT_OK(db->Flush(FlushOptions()));
445
8
      ASSERT_OK(db->Delete(write_options, TestKeyToSlice(TestKey(12346, 8))));
446
8
      ASSERT_OK(db->Flush(FlushOptions()));
447
8
      read_options.prefix_same_as_start = true;
448
8
      std::unique_ptr<Iterator> iter(db->NewIterator(read_options));
449
8
      ASSERT_OK(iter->status());
450
8
      SeekIterator(iter.get(), 12345, 6);
451
16
      ASSERT_TRUE(iter->Valid()) << "iter->status(): " << iter->status();
452
8
      ASSERT_TRUE(v16 == iter->value());
453
454
8
      iter->Next();
455
8
      ASSERT_TRUE(iter->Valid());
456
8
      ASSERT_TRUE(v17 == iter->value());
457
458
8
      iter->Next();
459
8
      ASSERT_TRUE(iter->Valid());
460
8
      ASSERT_TRUE(v18 == iter->value());
461
462
8
      iter->Next();
463
8
      ASSERT_TRUE(iter->Valid());
464
8
      ASSERT_TRUE(v19 == iter->value());
465
8
      iter->Next();
466
8
      ASSERT_FALSE(iter->Valid());
467
8
      ASSERT_EQ(kNotFoundResult, Get(db.get(), read_options, 12346, 8));
468
8
    }
469
2
  }
470
1
}
471
472
1
TEST_F(PrefixTest, DynamicPrefixIterator) {
473
5
  while (NextOptions(FLAGS_bucket_count)) {
474
4
    std::cout << "*** Mem table: " << options.memtable_factory->Name()
475
4
        << std::endl;
476
4
    ASSERT_OK(DestroyDB(kDbName, Options()));
477
4
    auto db = OpenDb();
478
4
    WriteOptions write_options;
479
4
    ReadOptions read_options;
480
481
4
    std::vector<uint64_t> prefixes;
482
400k
    for (uint64_t i = 0; i < FLAGS_total_prefixes; ++i) {
483
400k
      prefixes.push_back(i);
484
400k
    }
485
486
4
    if (FLAGS_random_prefix) {
487
0
      std::random_shuffle(prefixes.begin(), prefixes.end());
488
0
    }
489
490
4
    HistogramImpl hist_put_time;
491
4
    HistogramImpl hist_put_comparison;
492
493
    // insert x random prefix, each with y continuous element.
494
400k
    for (auto prefix : prefixes) {
495
800k
       for (uint64_t sorted = 0; sorted < FLAGS_items_per_prefix; sorted++) {
496
400k
        TestKey test_key(prefix, sorted);
497
498
400k
        Slice key = TestKeyToSlice(test_key);
499
400k
        std::string value(FLAGS_value_size, 0);
500
501
400k
        perf_context.Reset();
502
400k
        StopWatchNano timer(Env::Default(), true);
503
400k
        ASSERT_OK(db->Put(write_options, key, value));
504
400k
        hist_put_time.Add(timer.ElapsedNanos());
505
400k
        hist_put_comparison.Add(perf_context.user_key_comparison_count);
506
400k
      }
507
400k
    }
508
509
4
    std::cout << "Put key comparison: \n" << hist_put_comparison.ToString()
510
4
              << "Put time: \n" << hist_put_time.ToString();
511
512
    // test seek existing keys
513
4
    HistogramImpl hist_seek_time;
514
4
    HistogramImpl hist_seek_comparison;
515
516
4
    std::unique_ptr<Iterator> iter(db->NewIterator(read_options));
517
518
400k
    for (auto prefix : prefixes) {
519
400k
      TestKey test_key(prefix, FLAGS_items_per_prefix / 2);
520
400k
      Slice key = TestKeyToSlice(test_key);
521
400k
      std::string value = "v" + ToString(0);
522
523
400k
      perf_context.Reset();
524
400k
      StopWatchNano timer(Env::Default(), true);
525
400k
      auto key_prefix = options.prefix_extractor->Transform(key);
526
400k
      uint64_t total_keys = 0;
527
400k
      for (iter->Seek(key);
528
800k
           iter->Valid() && iter->key().starts_with(key_prefix);
529
400k
           iter->Next()) {
530
400k
        if (FLAGS_trigger_deadlock) {
531
0
          std::cout << "Behold the deadlock!\n";
532
0
          ASSERT_OK(db->Delete(write_options, iter->key()));
533
0
        }
534
400k
        total_keys++;
535
400k
      }
536
400k
      hist_seek_time.Add(timer.ElapsedNanos());
537
400k
      hist_seek_comparison.Add(perf_context.user_key_comparison_count);
538
400k
      ASSERT_EQ(total_keys, FLAGS_items_per_prefix - FLAGS_items_per_prefix/2);
539
400k
    }
540
541
4
    std::cout << "Seek key comparison: \n"
542
4
              << hist_seek_comparison.ToString()
543
4
              << "Seek time: \n"
544
4
              << hist_seek_time.ToString();
545
546
    // test non-existing keys
547
4
    HistogramImpl hist_no_seek_time;
548
4
    HistogramImpl hist_no_seek_comparison;
549
550
4
    for (auto prefix = FLAGS_total_prefixes;
551
40.0k
         prefix < FLAGS_total_prefixes + 10000;
552
40.0k
         prefix++) {
553
40.0k
      TestKey test_key(prefix, 0);
554
40.0k
      Slice key = TestKeyToSlice(test_key);
555
556
40.0k
      perf_context.Reset();
557
40.0k
      StopWatchNano timer(Env::Default(), true);
558
40.0k
      iter->Seek(key);
559
40.0k
      hist_no_seek_time.Add(timer.ElapsedNanos());
560
40.0k
      hist_no_seek_comparison.Add(perf_context.user_key_comparison_count);
561
40.0k
      ASSERT_TRUE(!iter->Valid());
562
40.0k
    }
563
564
4
    std::cout << "non-existing Seek key comparison: \n"
565
4
              << hist_no_seek_comparison.ToString()
566
4
              << "non-existing Seek time: \n"
567
4
              << hist_no_seek_time.ToString();
568
4
  }
569
1
}
570
571
}  // namespace rocksdb
572
573
13.2k
int main(int argc, char** argv) {
574
13.2k
  ::testing::InitGoogleTest(&argc, argv);
575
13.2k
  ParseCommandLineFlags(&argc, &argv, true);
576
13.2k
  std::cout << kDbName << "\n";
577
578
13.2k
  return RUN_ALL_TESTS();
579
13.2k
}
580
581
#endif  // GFLAGS
582
583
#else
584
#include <stdio.h>
585
586
int main(int argc, char** argv) {
587
  fprintf(stderr,
588
          "SKIPPED as HashSkipList and HashLinkList are not supported in "
589
          "ROCKSDB_LITE\n");
590
  return 0;
591
}
592
593
#endif  // !ROCKSDB_LITE