YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/Users/deen/code/yugabyte-db/src/yb/docdb/docdb_test_util.h
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) YugaByte, Inc.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
4
// in compliance with the License.  You may obtain a copy of the License at
5
//
6
// http://www.apache.org/licenses/LICENSE-2.0
7
//
8
// Unless required by applicable law or agreed to in writing, software distributed under the License
9
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
10
// or implied.  See the License for the specific language governing permissions and limitations
11
// under the License.
12
//
13
14
#ifndef YB_DOCDB_DOCDB_TEST_UTIL_H_
15
#define YB_DOCDB_DOCDB_TEST_UTIL_H_
16
17
#include <random>
18
#include <string>
19
#include <utility>
20
#include <vector>
21
22
#include "yb/docdb/docdb.h"
23
#include "yb/docdb/docdb_util.h"
24
#include "yb/docdb/in_mem_docdb.h"
25
#include "yb/docdb/subdocument.h"
26
27
#include "yb/util/strongly_typed_bool.h"
28
29
namespace yb {
30
namespace docdb {
31
32
using RandomNumberGenerator = std::mt19937_64;
33
34
// Maximum number of components in a randomly-generated DocKey.
35
static constexpr int kMaxNumRandomDocKeyParts = 10;
36
37
// Maximum number of subkeys in a randomly-generated SubDocKey.
38
static constexpr int kMaxNumRandomSubKeys = 10;
39
40
YB_STRONGLY_TYPED_BOOL(ResolveIntentsDuringRead);
41
YB_STRONGLY_TYPED_BOOL(UseHash);
42
43
// Intended only for testing, when we want to enable transaction aware code path for cases when we
44
// really have no transactions. This way we will test that transaction aware code path works
45
// correctly in absence of transactions and also doesn't use transaction status provider (it
46
// shouldn't because there are no transactions).
47
// TODO(dtxn) everywhere(?) in tests code where we use kNonTransactionalOperationContext we need
48
// to check both code paths - for transactional tables (passing kNonTransactionalOperationContext)
49
// and non-transactional tables (passing boost::none as transaction context).
50
// https://yugabyte.atlassian.net/browse/ENG-2177.
51
extern const TransactionOperationContext kNonTransactionalOperationContext;
52
53
// Note: test data generator methods below are using a non-const reference for the random number
54
// generator for simplicity, even though it is against Google C++ Style Guide. If we used a pointer,
55
// we would have to invoke the RNG as (*rng)().
56
57
// Generate a random primitive value.
58
PrimitiveValue GenRandomPrimitiveValue(RandomNumberGenerator* rng);
59
ValueRef GenRandomPrimitiveValue(RandomNumberGenerator* rng, QLValuePB* holder);
60
61
// Generate a random sequence of primitive values.
62
std::vector<PrimitiveValue> GenRandomPrimitiveValues(RandomNumberGenerator* rng,
63
                                                     int max_num = kMaxNumRandomDocKeyParts);
64
65
// Generate a "minimal" DocKey.
66
DocKey CreateMinimalDocKey(RandomNumberGenerator* rng, UseHash use_hash);
67
68
// Generate a random DocKey with up to the default number of components.
69
DocKey GenRandomDocKey(RandomNumberGenerator* rng, UseHash use_hash);
70
71
std::vector<DocKey> GenRandomDocKeys(RandomNumberGenerator* rng, UseHash use_hash, int num_keys);
72
73
std::vector<SubDocKey> GenRandomSubDocKeys(RandomNumberGenerator* rng,
74
                                           UseHash use_hash,
75
                                           int num_keys);
76
77
template<typename T>
78
716k
const T& RandomElementOf(const std::vector<T>& v, RandomNumberGenerator* rng) {
79
716k
  return v[(*rng)() % v.size()];
80
716k
}
yb::docdb::DocKey const& yb::docdb::RandomElementOf<yb::docdb::DocKey>(std::__1::vector<yb::docdb::DocKey, std::__1::allocator<yb::docdb::DocKey> > const&, std::__1::mersenne_twister_engine<unsigned long long, 64ul, 312ul, 156ul, 31ul, 13043109905998158313ull, 29ul, 6148914691236517205ull, 17ul, 8202884508482404352ull, 37ul, 18444473444759240704ull, 43ul, 6364136223846793005ull>*)
Line
Count
Source
78
228k
const T& RandomElementOf(const std::vector<T>& v, RandomNumberGenerator* rng) {
79
228k
  return v[(*rng)() % v.size()];
80
228k
}
yb::docdb::PrimitiveValue const& yb::docdb::RandomElementOf<yb::docdb::PrimitiveValue>(std::__1::vector<yb::docdb::PrimitiveValue, std::__1::allocator<yb::docdb::PrimitiveValue> > const&, std::__1::mersenne_twister_engine<unsigned long long, 64ul, 312ul, 156ul, 31ul, 13043109905998158313ull, 29ul, 6148914691236517205ull, 17ul, 8202884508482404352ull, 37ul, 18444473444759240704ull, 43ul, 6364136223846793005ull>*)
Line
Count
Source
78
488k
const T& RandomElementOf(const std::vector<T>& v, RandomNumberGenerator* rng) {
79
488k
  return v[(*rng)() % v.size()];
80
488k
}
81
82
// Represents a full logical snapshot of a RocksDB instance. An instance of this class will record
83
// the state of a RocksDB instance via Capture, which can then be written to a new RocksDB instance
84
// via RestoreTo.
85
class LogicalRocksDBDebugSnapshot {
86
 public:
87
14
  LogicalRocksDBDebugSnapshot() {}
88
  void Capture(rocksdb::DB* rocksdb);
89
  void RestoreTo(rocksdb::DB *rocksdb) const;
90
 private:
91
  std::vector<std::pair<std::string, std::string>> kvs;
92
  string docdb_debug_dump_str;
93
};
94
95
class DocDBRocksDBFixture : public DocDBRocksDBUtil {
96
 public:
97
  void AssertDocDbDebugDumpStrEq(const string &expected);
98
  void FullyCompactHistoryBefore(HybridTime history_cutoff);
99
100
  // num_files_to_compact - number of files that should participate in the minor compaction
101
  // start_index - the index of the file to start with (0 = the oldest file, -1 = compact
102
  //               num_files_to_compact newest files).
103
  void MinorCompaction(
104
      HybridTime history_cutoff, size_t num_files_to_compact, ssize_t start_index = -1);
105
106
  size_t NumSSTableFiles();
107
  StringVector SSTableFileNames();
108
109
  CHECKED_STATUS InitRocksDBDir() override;
110
  CHECKED_STATUS InitRocksDBOptions() override;
111
  TabletId tablet_id() override;
112
  CHECKED_STATUS FormatDocWriteBatch(const DocWriteBatch& dwb, std::string* dwb_str);
113
};
114
115
// Perform a major compaction on the given database.
116
CHECKED_STATUS FullyCompactDB(rocksdb::DB* rocksdb);
117
118
class DocDBLoadGenerator {
119
 public:
120
  static constexpr uint64_t kDefaultRandomSeed = 23874297385L;
121
122
  DocDBLoadGenerator(DocDBRocksDBFixture* fixture,
123
                     int num_doc_keys,
124
                     int num_unique_subkeys,
125
                     UseHash use_hash,
126
                     ResolveIntentsDuringRead resolve_intents = ResolveIntentsDuringRead::kTrue,
127
                     int deletion_chance = 100,
128
                     int max_nesting_level = 10,
129
                     uint64 random_seed = kDefaultRandomSeed,
130
                     int verification_frequency = 100);
131
  ~DocDBLoadGenerator();
132
133
  // Performs a random DocDB operation according to the configured options. This also verifies
134
  // the consistency of RocksDB-backed DocDB (which is close to the production codepath) with an
135
  // in-memory non-thread-safe data structure maintained just for sanity checking. Such verification
136
  // is only performed from time to time, not on every call to PerformOperation.
137
  //
138
  // The caller should wrap calls to this function in NO_FATALS.
139
  //
140
  // @param compact_history If this is set, we perform the RocksDB-backed DocDB read before and
141
  //                        after history cleanup, and verify that the state of the document is
142
  //                        the same in both cases.
143
  void PerformOperation(bool compact_history = false);
144
145
  // @return The next "iteration number" to be performed when PerformOperation is called.
146
  int next_iteration() const { return iteration_; }
147
148
  // The hybrid_time of the last operation performed is always based on the last iteration number.
149
  // Most times it will be one less than what next_iteration() would return, if we convert the
150
  // hybrid_time to an integer. This can only be called after PerformOperation() has been called at
151
  // least once.
152
  //
153
  // @return The hybrid_time of the last operation performed.
154
  HybridTime last_operation_ht() const;
155
156
  void FlushRocksDB();
157
158
  // Generate a random unsiged 64-bit integer using the random number generator maintained by this
159
  // object.
160
47.8k
  uint64_t NextRandom() { return random_(); }
161
162
  // Generate a random integer from 0 to n - 1 using the random number generator maintained by this
163
  // object.
164
47.7k
  int NextRandomInt(int n) { return NextRandom() % n; }
165
166
  // Capture and remember a "logical DocDB snapshot" (not to be confused with what we call
167
  // a "logical RocksDB snapshot"). This keeps track of all document keys and corresponding
168
  // documents existing at the latest hybrid_time.
169
  void CaptureDocDbSnapshot();
170
171
  void VerifyOldestSnapshot();
172
  void VerifyRandomDocDbSnapshot();
173
174
  // Perform a flashback query at the time of the latest snapshot before the given cleanup
175
  // hybrid_time and compare it to the state recorded with the snapshot. Expect the two to diverge
176
  // using ASSERT_TRUE. This is used for testing that old history is actually being cleaned up
177
  // during compactions.
178
  void CheckIfOldestSnapshotIsStillValid(const HybridTime cleanup_ht);
179
180
  // Removes all snapshots taken before the given hybrid_time. This is done to test history cleanup.
181
  void RemoveSnapshotsBefore(HybridTime ht);
182
183
  size_t num_divergent_old_snapshot() { return divergent_snapshot_ht_and_cleanup_ht_.size(); }
184
185
  std::vector<std::pair<int, int>> divergent_snapshot_ht_and_cleanup_ht() {
186
    return divergent_snapshot_ht_and_cleanup_ht_;
187
  }
188
189
 private:
190
0
  rocksdb::DB* rocksdb() { return fixture_->rocksdb(); }
191
234k
  DocDB doc_db() { return fixture_->doc_db(); }
192
193
  DocDBRocksDBFixture* fixture_;
194
  RandomNumberGenerator random_;  // Using default seed.
195
  std::vector<DocKey> doc_keys_;
196
197
  // Whether we should pass transaction context during reads, so DocDB tries to resolve write
198
  // intents.
199
  const ResolveIntentsDuringRead resolve_intents_;
200
201
  std::vector<PrimitiveValue> possible_subkeys_;
202
  int iteration_;
203
  InMemDocDbState in_mem_docdb_;
204
205
  // Deletions happen once every this number of iterations.
206
  const int deletion_chance_;
207
208
  // If this is 1, we'll only use primitive-type documents. If this is 2, we'll make some documents
209
  // objects (maps). If this is 3, we'll use maps of maps, etc.
210
  const int max_nesting_level_;
211
212
  HybridTime last_operation_ht_;
213
214
  std::vector<InMemDocDbState> docdb_snapshots_;
215
216
  // HybridTimes and cleanup hybrid_times of examples when
217
  std::vector<std::pair<int, int>> divergent_snapshot_ht_and_cleanup_ht_;
218
219
  // PerformOperation() will verify DocDB state consistency once in this number of iterations.
220
  const int verification_frequency_;
221
222
  const InMemDocDbState& GetOldestSnapshot();
223
224
  // Perform a "flashback query" in the RocksDB-based DocDB at the hybrid_time
225
  // snapshot.captured_at() and verify that the state matches what's in the provided snapshot.
226
  // This is only invoked on snapshots whose capture hybrid_time has not been garbage-collected,
227
  // and therefore we always expect this verification to succeed.
228
  //
229
  // Calls to this function should be wrapped in NO_FATALS.
230
  void VerifySnapshot(const InMemDocDbState& snapshot);
231
232
  // Look at whether the given snapshot is still valid, and if not, track it in
233
  // divergent_snapshot_ht_and_cleanup_ht_, so we can later verify that some snapshots have become
234
  // invalid after history cleanup.
235
  void RecordSnapshotDivergence(const InMemDocDbState &snapshot, HybridTime cleanup_ht);
236
237
  TransactionOperationContext GetReadOperationTransactionContext();
238
};
239
240
// Used for pre-processing multi-line DocDB debug dump strings in tests.  Removes common indentation
241
// and C++-style comments and applies backslash line continuation.
242
string TrimDocDbDebugDumpStr(const string& debug_dump);
243
244
#define ASSERT_DOCDB_DEBUG_DUMP_STR_EQ(expected) \
245
  do { \
246
    ASSERT_STR_EQ_VERBOSE_TRIMMED( \
247
        ::yb::util::ApplyEagerLineContinuation(expected), DocDBDebugDumpToStr()); \
248
  } while(false)
249
250
}  // namespace docdb
251
}  // namespace yb
252
253
#endif  // YB_DOCDB_DOCDB_TEST_UTIL_H_