YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/docdb/docdb-test.cc
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
#include <memory>
15
#include <string>
16
17
#include "yb/common/doc_hybrid_time.h"
18
#include "yb/common/hybrid_time.h"
19
#include "yb/common/ql_type.h"
20
#include "yb/common/ql_value.h"
21
22
#include "yb/docdb/consensus_frontier.h"
23
#include "yb/docdb/doc_key.h"
24
#include "yb/docdb/doc_reader.h"
25
#include "yb/docdb/doc_reader_redis.h"
26
#include "yb/docdb/docdb-internal.h"
27
#include "yb/docdb/docdb.h"
28
#include "yb/docdb/docdb.pb.h"
29
#include "yb/docdb/docdb_compaction_filter.h"
30
#include "yb/docdb/docdb_rocksdb_util.h"
31
#include "yb/docdb/docdb_test_base.h"
32
#include "yb/docdb/docdb_test_util.h"
33
#include "yb/docdb/in_mem_docdb.h"
34
#include "yb/docdb/primitive_value.h"
35
36
#include "yb/gutil/casts.h"
37
#include "yb/gutil/stringprintf.h"
38
#include "yb/gutil/walltime.h"
39
40
#include "yb/rocksdb/cache.h"
41
#include "yb/rocksdb/db.h"
42
43
#include "yb/server/hybrid_clock.h"
44
45
#include "yb/tablet/tablet_options.h"
46
47
#include "yb/util/debug-util.h"
48
#include "yb/util/minmax.h"
49
#include "yb/util/net/net_util.h"
50
#include "yb/util/random_util.h"
51
#include "yb/util/size_literals.h"
52
#include "yb/util/status_log.h"
53
#include "yb/util/string_trim.h"
54
#include "yb/util/strongly_typed_bool.h"
55
#include "yb/util/test_macros.h"
56
#include "yb/util/test_util.h"
57
#include "yb/util/tsan_util.h"
58
#include "yb/util/yb_partition.h"
59
60
using std::cout;
61
using std::endl;
62
using std::make_pair;
63
using std::map;
64
using std::string;
65
using std::unique_ptr;
66
using std::shared_ptr;
67
using std::make_shared;
68
69
using yb::util::TrimStr;
70
using yb::util::ApplyEagerLineContinuation;
71
72
using rocksdb::WriteOptions;
73
74
using namespace std::chrono_literals;
75
76
DECLARE_bool(use_docdb_aware_bloom_filter);
77
DECLARE_int32(max_nexts_to_avoid_seek);
78
DECLARE_bool(TEST_docdb_sort_weak_intents);
79
80
0
#define ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(str) ASSERT_NO_FATALS(AssertDocDbDebugDumpStrEq(str))
81
82
namespace yb {
83
namespace docdb {
84
85
CHECKED_STATUS GetPrimitiveValue(const rocksdb::UserBoundaryValues &values,
86
    size_t index,
87
    PrimitiveValue *out);
88
CHECKED_STATUS GetDocHybridTime(const rocksdb::UserBoundaryValues &values, DocHybridTime *out);
89
90
YB_STRONGLY_TYPED_BOOL(InitMarkerExpired);
91
YB_STRONGLY_TYPED_BOOL(UseIntermediateFlushes);
92
93
class DocDBTest : public DocDBTestBase {
94
 protected:
95
0
  DocDBTest() {
96
0
    SeedRandom();
97
0
  }
98
99
0
  ~DocDBTest() override {
100
0
  }
101
102
  virtual void GetSubDoc(
103
      const KeyBytes& subdoc_key, SubDocument* result, bool* found_result,
104
      const TransactionOperationContext& txn_op_context = TransactionOperationContext(),
105
      const ReadHybridTime& read_time = ReadHybridTime::Max()) = 0;
106
107
  // This is the baseline state of the database that we set up and come back to as we test various
108
  // operations.
109
  static constexpr const char *const kPredefinedDBStateDebugDumpStr = R"#(
110
SubDocKey(DocKey([], ["my_key_where_value_is_a_string"]), [HT{ physical: 1000 }]) -> "value1"
111
SubDocKey(DocKey([], ["mydockey", 123456]), [HT{ physical: 2000 }]) -> {}
112
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_a"; HT{ physical: 2000 w: 1 }]) -> "value_a"
113
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b"; HT{ physical: 7000 }]) -> {}
114
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b"; HT{ physical: 6000 }]) -> DEL
115
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b"; HT{ physical: 3000 }]) -> {}
116
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b", "subkey_c"; HT{ physical: 7000 w: 1 }]) \
117
    -> "value_bc_prime"
118
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b", "subkey_c"; HT{ physical: 5000 }]) -> DEL
119
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b", "subkey_c"; HT{ physical: 3000 w: 1 }]) \
120
    -> "value_bc"
121
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b", "subkey_d"; HT{ physical: 3500 }]) -> \
122
    "value_bd"
123
      )#";
124
125
  static const DocKey kDocKey1;
126
  static const DocKey kDocKey2;
127
  static const KeyBytes kEncodedDocKey1;
128
  static const KeyBytes kEncodedDocKey2;
129
130
  void TestInsertion(
131
      DocPath doc_path,
132
      const PrimitiveValue &value,
133
      HybridTime hybrid_time,
134
      string expected_write_batch_str);
135
136
  void TestDeletion(
137
      DocPath doc_path,
138
      HybridTime hybrid_time,
139
      string expected_write_batch_str);
140
141
0
  void SetupRocksDBState(KeyBytes encoded_doc_key) {
142
0
    SubDocument root;
143
0
    SubDocument a, b, c, d, e, f, b2;
144
145
    // The test plan below:
146
    // Set root = {a: {1: 1, 2: 2}, b: {c: {1: 3}, d: {1: 5, 2: 6}}, u: 7}
147
    // Then set root.a.2 = 11
148
    // Then replace root.b = {e: {1: 8, 2: 9}, y: 10}
149
    // Then extend root.a by {1: 3, 3: 4}
150
    // Then Delete root.b.e.2
151
    // The end result should be {a: {1: 3, 2: 11, 3: 4, x: {}}, b: {e: {1: 8}, y: 10}, u: 7}
152
153
0
#define SET_CHILD(parent, child) parent.SetChild(PrimitiveValue(#child), std::move(child))
154
0
#define SET_VALUE(parent, key, value) parent.SetChild(PrimitiveValue(key), \
155
0
                                                      SubDocument(PrimitiveValue(value)))
156
157
    // Constructing top level document: "root"
158
0
    SET_VALUE(root, "u", "7");
159
0
    SET_VALUE(a, "1", "1");
160
0
    SET_VALUE(a, "2", "2");
161
0
    SET_VALUE(c, "1", "3");
162
0
    SET_VALUE(d, "1", "5");
163
0
    SET_VALUE(d, "2", "6");
164
0
    SET_CHILD(b, c);
165
0
    SET_CHILD(b, d);
166
0
    SET_CHILD(root, a);
167
0
    SET_CHILD(root, b);
168
169
0
    EXPECT_STR_EQ_VERBOSE_TRIMMED(R"#(
170
0
        {
171
0
          "a": {
172
0
            "1": "1",
173
0
            "2": "2"
174
0
          },
175
0
          "b": {
176
0
            "c": {
177
0
              "1": "3"
178
0
            },
179
0
            "d": {
180
0
              "1": "5",
181
0
              "2": "6"
182
0
            }
183
0
          },
184
0
          "u": "7"
185
0
        }
186
0
        )#", root.ToString());
187
188
    // Constructing new version of b = b2 to be inserted later.
189
0
    SET_VALUE(b2, "y", "10");
190
0
    SET_VALUE(e, "1", "8");
191
0
    SET_VALUE(e, "2", "9");
192
0
    SET_CHILD(b2, e);
193
194
0
    EXPECT_STR_EQ_VERBOSE_TRIMMED(R"#(
195
0
{
196
0
  "e": {
197
0
    "1": "8",
198
0
    "2": "9"
199
0
  },
200
0
  "y": "10"
201
0
}
202
0
      )#", b2.ToString());
203
204
    // Constructing a doc with which we will extend a later
205
0
    SET_VALUE(f, "1", "3");
206
0
    SET_VALUE(f, "3", "4");
207
208
0
    EXPECT_STR_EQ_VERBOSE_TRIMMED(R"#(
209
0
{
210
0
  "1": "3",
211
0
  "3": "4"
212
0
}
213
0
      )#", f.ToString());
214
215
0
#undef SET_CHILD
216
0
#undef SET_VALUE
217
218
0
    ASSERT_OK(InsertSubDocument(
219
0
        DocPath(encoded_doc_key), root, 1000_usec_ht));
220
    // The Insert above could have been an Extend with no difference in external behavior.
221
    // Internally however, an insert writes an extra key (with value tombstone).
222
0
    ASSERT_OK(SetPrimitive(
223
0
        DocPath(encoded_doc_key, PrimitiveValue("a"), PrimitiveValue("2")),
224
0
        Value(PrimitiveValue(11)), 2000_usec_ht));
225
0
    ASSERT_OK(InsertSubDocument(DocPath(encoded_doc_key, PrimitiveValue("b")), b2,
226
0
                                3000_usec_ht));
227
0
    ASSERT_OK(ExtendSubDocument(DocPath(encoded_doc_key, PrimitiveValue("a")), f,
228
0
                                4000_usec_ht));
229
0
    ASSERT_OK(SetPrimitive(
230
0
        DocPath(encoded_doc_key, PrimitiveValue("b"), PrimitiveValue("e"), PrimitiveValue("2")),
231
0
        Value(PrimitiveValue::kTombstone), 5000_usec_ht));
232
0
  }
233
234
0
  void VerifySubDocument(SubDocKey subdoc_key, HybridTime ht, string subdoc_string) {
235
0
    SubDocument doc_from_rocksdb;
236
0
    bool subdoc_found_in_rocksdb = false;
237
238
0
    SCOPED_TRACE("\n" + GetStackTrace(StackTraceLineFormat::CLION_CLICKABLE) + "\n" +
239
0
        DocDBDebugDumpToStr());
240
241
    // TODO(dtxn) - check both transaction and non-transaction path?
242
    // https://yugabyte.atlassian.net/browse/ENG-2177
243
0
    auto encoded_subdoc_key = subdoc_key.EncodeWithoutHt();
244
0
    GetSubDoc(
245
0
        encoded_subdoc_key, &doc_from_rocksdb, &subdoc_found_in_rocksdb,
246
0
        kNonTransactionalOperationContext, ReadHybridTime::SingleTime(ht));
247
0
    if (subdoc_string.empty()) {
248
0
      EXPECT_FALSE(subdoc_found_in_rocksdb);
249
0
      return;
250
0
    }
251
0
    EXPECT_TRUE(subdoc_found_in_rocksdb);
252
0
    EXPECT_STR_EQ_VERBOSE_TRIMMED(subdoc_string, doc_from_rocksdb.ToString());
253
0
  }
254
255
  // Tries to read some documents from the DB that is assumed to be in a state described by
256
  // kPredefinedDBStateDebugDumpStr, and verifies the result of those reads. Only the latest logical
257
  // state of documents matters for this check, so it is OK to call this after compacting previous
258
  // history.
259
  void CheckExpectedLatestDBState();
260
261
  // Checks bloom filter useful counter increment to be in range [1;expected_max_increment] and
262
  // table iterators number increment to be expected_num_iterators_increment.
263
  // Updates total_useful, total_iterators
264
  void CheckBloom(const int expected_max_increment, uint64_t *total_useful,
265
                  const int expected_num_iterators_increment, uint64_t *total_iterators) {
266
    if (FLAGS_use_docdb_aware_bloom_filter) {
267
      const auto total_useful_updated =
268
          regular_db_options().statistics->getTickerCount(rocksdb::BLOOM_FILTER_USEFUL);
269
      const auto total_iterators_updated =
270
          regular_db_options().statistics->getTickerCount(rocksdb::NO_TABLE_CACHE_ITERATORS);
271
      if (expected_max_increment > 0) {
272
        ASSERT_GT(total_useful_updated, *total_useful);
273
        ASSERT_LE(total_useful_updated, *total_useful + expected_max_increment);
274
        *total_useful = total_useful_updated;
275
      } else {
276
        ASSERT_EQ(*total_useful, total_useful_updated);
277
      }
278
      ASSERT_EQ(*total_iterators + expected_num_iterators_increment, total_iterators_updated);
279
      *total_iterators = total_iterators_updated;
280
    }
281
  }
282
283
0
  InetAddress GetInetAddress(const std::string &strval) {
284
0
    return InetAddress(CHECK_RESULT(ParseIpAddress(strval)));
285
0
  }
286
287
0
  void InsertInet(const std::string strval) {
288
0
    const DocKey doc_key(PrimitiveValues("mydockey"));
289
0
    KeyBytes encoded_doc_key(doc_key.Encode());
290
0
    ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key, PrimitiveValue(GetInetAddress(strval))),
291
0
                           PrimitiveValue(),
292
0
                           1000_usec_ht));
293
0
  }
294
295
  // Inserts a bunch of subkeys starting with the provided doc key. It also, fills out the
296
  // expected_docdb_str with the expected state of DocDB after the operation.
297
  void AddSubKeys(const KeyBytes& encoded_doc_key, int num_subkeys, int base,
298
0
                  string* expected_docdb_str) {
299
0
    *expected_docdb_str = "";
300
0
    for (int i = 0; i < num_subkeys; i++) {
301
0
      string subkey = "subkey" + std::to_string(base + i);
302
0
      string value = "value" + std::to_string(i);
303
0
      MicrosTime hybrid_time = (i + 1) * 1000;
304
0
      ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key, PrimitiveValue(subkey)),
305
0
                             Value(PrimitiveValue(value)), HybridTime::FromMicros(hybrid_time)));
306
0
      *expected_docdb_str += strings::Substitute(
307
0
          R"#(SubDocKey(DocKey([], ["key"]), ["$0"; HT{ physical: $1 }]) -> "$2")#",
308
0
          subkey, hybrid_time, value);
309
0
      *expected_docdb_str += "\n";
310
0
    }
311
0
  }
312
313
  static constexpr int kNumSubKeysForCollectionsWithTTL = 3;
314
315
0
  void SetUpCollectionWithTTL(DocKey collection_key, UseIntermediateFlushes intermediate_flushes) {
316
0
    SubDocument subdoc;
317
0
    for (int i = 0; i < kNumSubKeysForCollectionsWithTTL; i++) {
318
0
      string key = "k" + std::to_string(i);
319
0
      string value = "v" + std::to_string(i);
320
0
      subdoc.SetChildPrimitive(PrimitiveValue(key), PrimitiveValue(value));
321
0
    }
322
0
    ASSERT_OK(InsertSubDocument(DocPath(collection_key.Encode()), subdoc, 1000_usec_ht, 10s));
323
324
0
    ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(Format(R"#(
325
0
        SubDocKey($0, [HT{ physical: 1000 }]) -> {}; ttl: 10.000s
326
0
        SubDocKey($0, ["k0"; HT{ physical: 1000 w: 1 }]) -> "v0"; ttl: 10.000s
327
0
        SubDocKey($0, ["k1"; HT{ physical: 1000 w: 2 }]) -> "v1"; ttl: 10.000s
328
0
        SubDocKey($0, ["k2"; HT{ physical: 1000 w: 3 }]) -> "v2"; ttl: 10.000s
329
0
        )#", collection_key.ToString()));
330
0
    if (intermediate_flushes) {
331
0
      ASSERT_OK(FlushRocksDbAndWait());
332
0
    }
333
334
    // Set separate TTLs for each element.
335
0
    for (int i = 0; i < kNumSubKeysForCollectionsWithTTL; i++) {
336
0
      SubDocument subdoc;
337
0
      string key = "k" + std::to_string(i);
338
0
      string value = "vv" + std::to_string(i);
339
0
      subdoc.SetChildPrimitive(PrimitiveValue(key), PrimitiveValue(value));
340
0
      ASSERT_OK(ExtendSubDocument(
341
0
          DocPath(collection_key.Encode()), subdoc, 1100_usec_ht,
342
0
          MonoDelta::FromSeconds(20 + i)));
343
0
      if (intermediate_flushes) {
344
0
        ASSERT_OK(FlushRocksDbAndWait());
345
0
      }
346
0
    }
347
348
    // Add new keys as well.
349
0
    for (int i = kNumSubKeysForCollectionsWithTTL; i < kNumSubKeysForCollectionsWithTTL * 2; i++) {
350
0
      SubDocument subdoc;
351
0
      string key = "k" + std::to_string(i);
352
0
      string value = "vv" + std::to_string(i);
353
0
      subdoc.SetChildPrimitive(PrimitiveValue(key), PrimitiveValue(value));
354
0
      ASSERT_OK(ExtendSubDocument(
355
0
          DocPath(collection_key.Encode()), subdoc, 1100_usec_ht,
356
0
          MonoDelta::FromSeconds(20 + i)));
357
0
      if (intermediate_flushes) {
358
0
        ASSERT_OK(FlushRocksDbAndWait());
359
0
      }
360
0
    }
361
0
  }
362
363
  string ExpectedDebugDumpForCollectionWithTTL(DocKey collection_key,
364
0
                                               InitMarkerExpired init_marker_expired) {
365
    // The "file ..." comments below are for the case of intermediate_flushes = true above.
366
0
    const string result_template = init_marker_expired ?
367
        // After the init marker expires, we should not see a tombstone for it. We do not replace
368
        // timed-out collection init markers with tombstones on minor compactions, because that
369
        // could hide keys that
370
0
        R"#(
371
0
            SubDocKey($0, ["k0"; HT{ physical: 1100 }]) -> "vv0"; ttl: 20.000s
372
0
            SubDocKey($0, ["k1"; HT{ physical: 1100 }]) -> "vv1"; ttl: 21.000s
373
0
            SubDocKey($0, ["k2"; HT{ physical: 1100 }]) -> "vv2"; ttl: 22.000s
374
0
            SubDocKey($0, ["k3"; HT{ physical: 1100 }]) -> "vv3"; ttl: 23.000s
375
0
            SubDocKey($0, ["k4"; HT{ physical: 1100 }]) -> "vv4"; ttl: 24.000s
376
0
            SubDocKey($0, ["k5"; HT{ physical: 1100 }]) -> "vv5"; ttl: 25.000s
377
0
        )#" : R"#(
378
0
            SubDocKey($0, [HT{ physical: 1000 }]) -> {}; ttl: 10.000s               // file 1
379
0
            SubDocKey($0, ["k0"; HT{ physical: 1100 }]) -> "vv0"; ttl: 20.000s      // file 2
380
0
            SubDocKey($0, ["k0"; HT{ physical: 1000 w: 1 }]) -> "v0"; ttl: 10.000s  // file 1
381
0
            SubDocKey($0, ["k1"; HT{ physical: 1100 }]) -> "vv1"; ttl: 21.000s      // file 3
382
0
            SubDocKey($0, ["k1"; HT{ physical: 1000 w: 2 }]) -> "v1"; ttl: 10.000s  // file 1
383
0
            SubDocKey($0, ["k2"; HT{ physical: 1100 }]) -> "vv2"; ttl: 22.000s      // file 4
384
0
            SubDocKey($0, ["k2"; HT{ physical: 1000 w: 3 }]) -> "v2"; ttl: 10.000s  // file 1
385
0
            SubDocKey($0, ["k3"; HT{ physical: 1100 }]) -> "vv3"; ttl: 23.000s      // file 5
386
0
            SubDocKey($0, ["k4"; HT{ physical: 1100 }]) -> "vv4"; ttl: 24.000s      // file 6
387
0
            SubDocKey($0, ["k5"; HT{ physical: 1100 }]) -> "vv5"; ttl: 25.000s      // file 7
388
0
        )#";
389
0
    return Format(result_template, collection_key.ToString());
390
0
  }
391
392
  void InitializeCollection(const std::string& key_string,
393
                            std::string* val_string,
394
                            vector<HybridTime>::iterator* time_iter,
395
0
                            std::set<std::pair<string, string>>* docdb_dump) {
396
0
    SubDocument subdoc;
397
0
    DocKey collection_key(PrimitiveValues(key_string));
398
399
0
    for (int i = 0; i < kNumSubKeysForCollectionsWithTTL; i++) {
400
0
      string key = "sk" + std::to_string(i);
401
0
      subdoc.SetChildPrimitive(PrimitiveValue(key), PrimitiveValue(*val_string));
402
0
      (*val_string)[1]++;
403
0
    }
404
405
0
    ASSERT_OK(InsertSubDocument(DocPath(collection_key.Encode()), subdoc, **time_iter));
406
0
    ++*time_iter;
407
408
0
    SubDocument new_subdoc;
409
    // Add new keys as well.
410
0
    for (int i = kNumSubKeysForCollectionsWithTTL / 2;
411
0
         i < 3 * kNumSubKeysForCollectionsWithTTL / 2; i++) {
412
0
      string key = "sk" + std::to_string(i);
413
0
      new_subdoc.SetChildPrimitive(PrimitiveValue(key), PrimitiveValue(*val_string));
414
0
      (*val_string)[1]++;
415
0
    }
416
0
    ASSERT_OK(ExtendSubDocument(
417
0
      DocPath(collection_key.Encode()), new_subdoc, **time_iter));
418
0
    ++*time_iter;
419
420
0
  }
421
};
422
423
void GetSubDocQl(
424
      const DocDB& doc_db, const KeyBytes& subdoc_key, SubDocument* result, bool* found_result,
425
      const TransactionOperationContext& txn_op_context, const ReadHybridTime& read_time,
426
0
      const vector<PrimitiveValue>* projection = nullptr) {
427
0
  auto doc_from_rocksdb_opt = ASSERT_RESULT(TEST_GetSubDocument(
428
0
    subdoc_key, doc_db, rocksdb::kDefaultQueryId, txn_op_context,
429
0
    CoarseTimePoint::max() /* deadline */, read_time, projection));
430
0
  if (doc_from_rocksdb_opt) {
431
0
    *found_result = true;
432
0
    *result = *doc_from_rocksdb_opt;
433
0
  } else {
434
0
    *found_result = false;
435
0
    *result = SubDocument();
436
0
  }
437
0
}
438
439
void GetSubDocRedis(
440
      const DocDB& doc_db, const KeyBytes& subdoc_key, SubDocument* result, bool* found_result,
441
0
      const TransactionOperationContext& txn_op_context, const ReadHybridTime& read_time) {
442
0
  GetRedisSubDocumentData data = { subdoc_key, result, found_result };
443
0
  ASSERT_OK(GetRedisSubDocument(
444
0
      doc_db, data, rocksdb::kDefaultQueryId,
445
0
      txn_op_context, CoarseTimePoint::max() /* deadline */,
446
0
      read_time));
447
0
}
448
449
// The list of types we want to test.
450
YB_DEFINE_ENUM(TestDocDb, (kQlReader)(kRedisReader));
451
452
class DocDBTestWrapper : public DocDBTest, public testing::WithParamInterface<TestDocDb>  {
453
 public:
454
  void GetSubDoc(
455
      const KeyBytes& subdoc_key, SubDocument* result, bool* found_result,
456
      const TransactionOperationContext& txn_op_context = TransactionOperationContext(),
457
0
      const ReadHybridTime& read_time = ReadHybridTime::Max()) override {
458
0
    switch (GetParam()) {
459
0
      case TestDocDb::kQlReader: {
460
0
        GetSubDocQl(doc_db(), subdoc_key, result, found_result, txn_op_context, read_time);
461
0
        break;
462
0
      }
463
0
      case TestDocDb::kRedisReader: {
464
0
        GetSubDocRedis(
465
0
            doc_db(), subdoc_key, result, found_result, txn_op_context, read_time);
466
0
        break;
467
0
      }
468
0
    }
469
0
  }
470
};
471
472
INSTANTIATE_TEST_CASE_P(DocDBTests,
473
                        DocDBTestWrapper,
474
                        testing::Values(TestDocDb::kQlReader, TestDocDb::kRedisReader));
475
476
class DocDBTestQl : public DocDBTest {
477
 public:
478
  void GetSubDoc(
479
      const KeyBytes& subdoc_key, SubDocument* result, bool* found_result,
480
      const TransactionOperationContext& txn_op_context = TransactionOperationContext(),
481
0
      const ReadHybridTime& read_time = ReadHybridTime::Max()) override {
482
0
    GetSubDocQl(doc_db(), subdoc_key, result, found_result, txn_op_context, read_time);
483
0
  }
484
};
485
486
class DocDBTestRedis : public DocDBTest {
487
 public:
488
  void GetSubDoc(
489
      const KeyBytes& subdoc_key, SubDocument* result, bool* found_result,
490
      const TransactionOperationContext& txn_op_context = TransactionOperationContext(),
491
0
      const ReadHybridTime& read_time = ReadHybridTime::Max()) override {
492
0
    GetSubDocRedis(
493
0
        doc_db(), subdoc_key, result, found_result, txn_op_context, read_time);
494
0
  }
495
};
496
497
// Static constant initialization should be completely independent (cannot initialize one using the
498
// other).
499
const DocKey DocDBTest::kDocKey1(PrimitiveValues("row1", 11111));
500
const DocKey DocDBTest::kDocKey2(PrimitiveValues("row2", 22222));
501
const KeyBytes DocDBTest::kEncodedDocKey1(DocKey(PrimitiveValues("row1", 11111)).Encode());
502
const KeyBytes DocDBTest::kEncodedDocKey2(DocKey(PrimitiveValues("row2", 22222)).Encode());
503
504
void DocDBTest::TestInsertion(
505
    const DocPath doc_path,
506
    const PrimitiveValue &value,
507
    HybridTime hybrid_time,
508
0
    string expected_write_batch_str) {
509
0
  auto dwb = MakeDocWriteBatch();
510
  // Set write id to zero on the write path.
511
0
  ASSERT_OK(dwb.SetPrimitive(doc_path, value));
512
0
  ASSERT_OK(WriteToRocksDB(dwb, hybrid_time));
513
0
  string dwb_str;
514
0
  ASSERT_OK(FormatDocWriteBatch(dwb, &dwb_str));
515
0
  EXPECT_STR_EQ_VERBOSE_TRIMMED(ApplyEagerLineContinuation(expected_write_batch_str),
516
0
      dwb_str);
517
0
}
518
519
void DocDBTest::TestDeletion(
520
    DocPath doc_path,
521
    HybridTime hybrid_time,
522
0
    string expected_write_batch_str) {
523
0
  auto dwb = MakeDocWriteBatch();
524
  // Set write id to zero on the write path.
525
0
  ASSERT_OK(dwb.DeleteSubDoc(doc_path));
526
0
  ASSERT_OK(WriteToRocksDB(dwb, hybrid_time));
527
0
  string dwb_str;
528
0
  ASSERT_OK(FormatDocWriteBatch(dwb, &dwb_str));
529
0
  EXPECT_STR_EQ_VERBOSE_TRIMMED(ApplyEagerLineContinuation(expected_write_batch_str),
530
0
      dwb_str);
531
0
}
532
533
0
void DocDBTest::CheckExpectedLatestDBState() {
534
0
  const SubDocKey subdoc_key(DocKey(PrimitiveValues("mydockey", 123456)));
535
536
0
  SubDocument subdoc;
537
0
  bool doc_found = false;
538
  // TODO(dtxn) - check both transaction and non-transaction path?
539
0
  auto encoded_subdoc_key = subdoc_key.EncodeWithoutHt();
540
0
  GetSubDoc(
541
0
      encoded_subdoc_key, &subdoc, &doc_found,
542
0
      kNonTransactionalOperationContext);
543
0
  ASSERT_TRUE(doc_found);
544
0
  ASSERT_STR_EQ_VERBOSE_TRIMMED(
545
0
      R"#(
546
0
{
547
0
  "subkey_a": "value_a",
548
0
  "subkey_b": {
549
0
    "subkey_c": "value_bc_prime"
550
0
  }
551
0
}
552
0
      )#",
553
0
      subdoc.ToString()
554
0
  );
555
0
}
556
557
// ------------------------------------------------------------------------------------------------
558
559
TEST_P(DocDBTestWrapper, DocPathTest) {
560
  DocKey doc_key(PrimitiveValues("mydockey", 10, "mydockey", 20));
561
  DocPath doc_path(doc_key.Encode(), "first_subkey", 123);
562
  ASSERT_EQ(2, doc_path.num_subkeys());
563
  ASSERT_EQ("\"first_subkey\"", doc_path.subkey(0).ToString());
564
  ASSERT_EQ("123", doc_path.subkey(1).ToString());
565
}
566
567
0
TEST_P(DocDBTestWrapper, KeyAsEmptyObjectIsNotMasked) {
568
0
  const DocKey doc_key(PrimitiveValues(DocKeyHash(1234)));
569
0
  KeyBytes encoded_doc_key(doc_key.Encode());
570
0
  ASSERT_OK(SetPrimitive(
571
0
      DocPath(encoded_doc_key), PrimitiveValue::kObject, 252_usec_ht));
572
0
  ASSERT_OK(SetPrimitive(
573
0
      DocPath(encoded_doc_key, PrimitiveValue()), PrimitiveValue::kObject, 617_usec_ht));
574
0
  ASSERT_OK(DeleteSubDoc(
575
0
      DocPath(encoded_doc_key, PrimitiveValue(), PrimitiveValue(ValueType::kFalse)), 675_usec_ht));
576
0
  ASSERT_OK(SetPrimitive(
577
0
      DocPath(encoded_doc_key, PrimitiveValue(), PrimitiveValue(ValueType::kFalse)),
578
0
      PrimitiveValue(12345), 617_usec_ht));
579
0
  ASSERT_OK(SetPrimitive(
580
0
      DocPath(encoded_doc_key, "later"), PrimitiveValue(1), 336_usec_ht));
581
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
582
0
      SubDocKey(DocKey([], [1234]), [HT{ physical: 252 }]) -> {}
583
0
      SubDocKey(DocKey([], [1234]), [null; HT{ physical: 617 }]) -> {}
584
0
      SubDocKey(DocKey([], [1234]), [null, false; HT{ physical: 675 }]) -> DEL
585
0
      SubDocKey(DocKey([], [1234]), [null, false; HT{ physical: 617 }]) -> 12345
586
0
      SubDocKey(DocKey([], [1234]), ["later"; HT{ physical: 336 }]) -> 1
587
0
      )#");
588
0
  VerifySubDocument(SubDocKey(doc_key), 4000_usec_ht,
589
0
                    R"#(
590
0
{
591
0
  null: {},
592
0
  "later": 1
593
0
}
594
0
      )#");
595
0
}
596
597
0
TEST_P(DocDBTestWrapper, NullChildObjectShouldMaskValues) {
598
0
  const DocKey doc_key(PrimitiveValues("mydockey", 123456));
599
0
  KeyBytes encoded_doc_key(doc_key.Encode());
600
0
  ASSERT_OK(SetPrimitive(
601
0
      DocPath(encoded_doc_key), PrimitiveValue::kObject, 1000_usec_ht));
602
0
  ASSERT_OK(SetPrimitive(
603
0
      DocPath(encoded_doc_key, "obj"), PrimitiveValue::kObject, 2000_usec_ht));
604
0
  ASSERT_OK(SetPrimitive(
605
0
      DocPath(encoded_doc_key, "obj", "key"), PrimitiveValue("value"), 2000_usec_ht));
606
0
  ASSERT_OK(SetPrimitive(
607
0
      DocPath(encoded_doc_key, "obj"), PrimitiveValue(ValueType::kNullHigh), 3000_usec_ht));
608
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
609
0
      SubDocKey(DocKey([], ["mydockey", 123456]), [HT{ physical: 1000 }]) -> {}
610
0
      SubDocKey(DocKey([], ["mydockey", 123456]), ["obj"; HT{ physical: 3000 }]) -> null
611
0
      SubDocKey(DocKey([], ["mydockey", 123456]), ["obj"; HT{ physical: 2000 }]) -> {}
612
0
      SubDocKey(DocKey([], ["mydockey", 123456]), ["obj", "key"; HT{ physical: 2000 }]) -> "value"
613
0
      )#");
614
0
  VerifySubDocument(SubDocKey(doc_key), 4000_usec_ht,
615
0
                    R"#(
616
0
{
617
0
  "obj": null
618
0
}
619
0
      )#");
620
0
}
621
622
// This test confirms that we return the appropriate value for doc_found in the case that the last
623
// projection we look at is not present. Previously we had a bug where we would set doc_found to
624
// true if the last projection was present, and false otherwise, reguardless of other projections
625
// considered. This test ensures we have the correct behavior, returning true as long as any
626
// projection is present, even if the last one is absent.
627
0
TEST_F(DocDBTestQl, LastProjectionIsNull) {
628
0
  const DocKey doc_key(PrimitiveValues("mydockey", 123456));
629
0
  KeyBytes encoded_doc_key(doc_key.Encode());
630
0
  ASSERT_OK(SetPrimitive(
631
0
      DocPath(encoded_doc_key), PrimitiveValue::kObject, 1000_usec_ht));
632
0
  ASSERT_OK(SetPrimitive(
633
0
      DocPath(encoded_doc_key, "p1"), PrimitiveValue("value"), 2000_usec_ht));
634
0
  ASSERT_OK(SetPrimitive(
635
0
      DocPath(encoded_doc_key, "p2"), PrimitiveValue(ValueType::kTombstone), 2000_usec_ht));
636
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
637
0
      SubDocKey(DocKey([], ["mydockey", 123456]), [HT{ physical: 1000 }]) -> {}
638
0
      SubDocKey(DocKey([], ["mydockey", 123456]), ["p1"; HT{ physical: 2000 }]) -> "value"
639
0
      SubDocKey(DocKey([], ["mydockey", 123456]), ["p2"; HT{ physical: 2000 }]) -> DEL
640
0
      )#");
641
642
0
  auto subdoc_key = SubDocKey(doc_key);
643
0
  auto encoded_subdoc_key = subdoc_key.EncodeWithoutHt();
644
0
  SubDocument doc_from_rocksdb;
645
0
  bool subdoc_found_in_rocksdb = false;
646
0
  const vector<PrimitiveValue> projection = {
647
0
    PrimitiveValue("p1"),
648
0
    PrimitiveValue("p2")
649
0
  };
650
651
0
  GetSubDocQl(
652
0
      doc_db(), encoded_subdoc_key, &doc_from_rocksdb, &subdoc_found_in_rocksdb,
653
0
      kNonTransactionalOperationContext, ReadHybridTime::SingleTime(4000_usec_ht),
654
0
      &projection);
655
0
  EXPECT_TRUE(subdoc_found_in_rocksdb);
656
0
  EXPECT_STR_EQ_VERBOSE_TRIMMED(R"#(
657
0
{
658
0
  "p1": "value",
659
0
  "p2": DEL
660
0
}
661
0
  )#", doc_from_rocksdb.ToString());
662
0
}
663
664
0
TEST_F(DocDBTestQl, ColocatedTableTombstoneTest) {
665
0
  constexpr PgTableOid pgtable_id(0x4001);
666
0
  DocKey doc_key_1(PrimitiveValues("mydockey", 123456));
667
0
  doc_key_1.set_pgtable_id(pgtable_id);
668
0
  DocKey doc_key_2(PrimitiveValues("mydockey", 789123));
669
0
  doc_key_2.set_pgtable_id(pgtable_id);
670
0
  ASSERT_OK(SetPrimitive(
671
0
      doc_key_1.Encode(), PrimitiveValue(1), 1000_usec_ht));
672
0
  ASSERT_OK(SetPrimitive(
673
0
      doc_key_2.Encode(), PrimitiveValue(2), 1000_usec_ht));
674
675
0
  DocKey doc_key_table;
676
0
  doc_key_table.set_pgtable_id(pgtable_id);
677
0
  ASSERT_OK(DeleteSubDoc(
678
0
      doc_key_table.Encode(), 2000_usec_ht));
679
680
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
681
0
      SubDocKey(DocKey(PgTableId=16385, [], []), [HT{ physical: 2000 }]) -> DEL
682
0
      SubDocKey(DocKey(PgTableId=16385, [], ["mydockey", 123456]), [HT{ physical: 1000 }]) -> 1
683
0
      SubDocKey(DocKey(PgTableId=16385, [], ["mydockey", 789123]), [HT{ physical: 1000 }]) -> 2
684
0
      )#");
685
0
  VerifySubDocument(SubDocKey(doc_key_1), 4000_usec_ht, "");
686
0
  VerifySubDocument(SubDocKey(doc_key_1), 1500_usec_ht, "1");
687
0
}
688
689
0
TEST_P(DocDBTestWrapper, HistoryCompactionFirstRowHandlingRegression) {
690
  // A regression test for a bug in an initial version of compaction cleanup.
691
0
  const DocKey doc_key(PrimitiveValues("mydockey", 123456));
692
0
  KeyBytes encoded_doc_key(doc_key.Encode());
693
0
  ASSERT_OK(SetPrimitive(
694
0
      DocPath(encoded_doc_key), PrimitiveValue::kObject, 1000_usec_ht));
695
0
  ASSERT_OK(SetPrimitive(
696
0
      DocPath(encoded_doc_key, "subkey1"),
697
0
      PrimitiveValue("value1"),
698
0
      1000_usec_ht));
699
0
  ASSERT_OK(SetPrimitive(
700
0
      DocPath(encoded_doc_key, "subkey1"),
701
0
      PrimitiveValue("value2"),
702
0
      2000_usec_ht));
703
0
  ASSERT_OK(SetPrimitive(
704
0
      DocPath(encoded_doc_key, "subkey1"),
705
0
      PrimitiveValue("value3"),
706
0
      3000_usec_ht));
707
0
  ASSERT_OK(SetPrimitive(
708
0
      DocPath(encoded_doc_key), PrimitiveValue::kObject, 4000_usec_ht));
709
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
710
0
      SubDocKey(DocKey([], ["mydockey", 123456]), [HT{ physical: 4000 }]) -> {}
711
0
      SubDocKey(DocKey([], ["mydockey", 123456]), [HT{ physical: 1000 }]) -> {}
712
0
      SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey1"; HT{ physical: 3000 }]) -> "value3"
713
0
      SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey1"; HT{ physical: 2000 }]) -> "value2"
714
0
      SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey1"; HT{ physical: 1000 }]) -> "value1"
715
0
      )#");
716
0
  FullyCompactHistoryBefore(3500_usec_ht);
717
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
718
0
      R"#(
719
0
SubDocKey(DocKey([], ["mydockey", 123456]), [HT{ physical: 4000 }]) -> {}
720
0
SubDocKey(DocKey([], ["mydockey", 123456]), [HT{ physical: 1000 }]) -> {}
721
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey1"; HT{ physical: 3000 }]) -> "value3"
722
0
      )#");
723
0
}
724
725
0
TEST_P(DocDBTestWrapper, SetPrimitiveQL) {
726
0
  const DocKey doc_key(PrimitiveValues("mydockey", 123456));
727
0
  SetupRocksDBState(doc_key.Encode());
728
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
729
0
      R"#(
730
0
SubDocKey(DocKey([], ["mydockey", 123456]), [HT{ physical: 1000 }]) -> {}
731
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["a", "1"; HT{ physical: 4000 }]) -> "3"
732
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["a", "1"; HT{ physical: 1000 w: 1 }]) -> "1"
733
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["a", "2"; HT{ physical: 2000 }]) -> 11
734
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["a", "2"; HT{ physical: 1000 w: 2 }]) -> "2"
735
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["a", "3"; HT{ physical: 4000 w: 1 }]) -> "4"
736
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["b"; HT{ physical: 3000 }]) -> {}
737
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["b", "c", "1"; HT{ physical: 1000 w: 3 }]) -> "3"
738
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["b", "d", "1"; HT{ physical: 1000 w: 4 }]) -> "5"
739
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["b", "d", "2"; HT{ physical: 1000 w: 5 }]) -> "6"
740
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["b", "e", "1"; HT{ physical: 3000 w: 1 }]) -> "8"
741
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["b", "e", "2"; HT{ physical: 5000 }]) -> DEL
742
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["b", "e", "2"; HT{ physical: 3000 w: 2 }]) -> "9"
743
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["b", "y"; HT{ physical: 3000 w: 3 }]) -> "10"
744
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["u"; HT{ physical: 1000 w: 6 }]) -> "7"
745
0
     )#");
746
0
}
747
748
// This tests reads on data without init markers. Basic Test tests with init markers.
749
0
TEST_P(DocDBTestWrapper, GetSubDocumentTest) {
750
0
  const DocKey doc_key(PrimitiveValues("mydockey", 123456));
751
0
  SetupRocksDBState(doc_key.Encode());
752
753
  // We will test the state of the entire document after every operation, using timestamps
754
  // 500, 1500, 2500, 3500, 4500, 5500.
755
756
0
  VerifySubDocument(SubDocKey(doc_key), 500_usec_ht, "");
757
758
0
  VerifySubDocument(SubDocKey(doc_key), 1500_usec_ht,
759
0
                    R"#(
760
0
{
761
0
  "a": {
762
0
    "1": "1",
763
0
    "2": "2"
764
0
  },
765
0
  "b": {
766
0
    "c": {
767
0
      "1": "3"
768
0
    },
769
0
    "d": {
770
0
      "1": "5",
771
0
      "2": "6"
772
0
    }
773
0
  },
774
0
  "u": "7"
775
0
}
776
0
      )#");
777
778
0
  VerifySubDocument(SubDocKey(doc_key), 2500_usec_ht,
779
0
                    R"#(
780
0
{
781
0
  "a": {
782
0
    "1": "1",
783
0
    "2": 11
784
0
  },
785
0
  "b": {
786
0
    "c": {
787
0
      "1": "3"
788
0
    },
789
0
    "d": {
790
0
      "1": "5",
791
0
      "2": "6"
792
0
    }
793
0
  },
794
0
  "u": "7"
795
0
}
796
0
      )#");
797
798
0
  VerifySubDocument(SubDocKey(doc_key), 3500_usec_ht,
799
0
                    R"#(
800
0
{
801
0
  "a": {
802
0
    "1": "1",
803
0
    "2": 11
804
0
  },
805
0
  "b": {
806
0
    "e": {
807
0
      "1": "8",
808
0
      "2": "9"
809
0
    },
810
0
    "y": "10"
811
0
  },
812
0
  "u": "7"
813
0
}
814
0
      )#");
815
816
0
  VerifySubDocument(SubDocKey(doc_key), 4500_usec_ht,
817
0
                    R"#(
818
0
{
819
0
  "a": {
820
0
    "1": "3",
821
0
    "2": 11,
822
0
    "3": "4"
823
0
  },
824
0
  "b": {
825
0
    "e": {
826
0
      "1": "8",
827
0
      "2": "9"
828
0
    },
829
0
    "y": "10"
830
0
  },
831
0
  "u": "7"
832
0
}
833
0
      )#");
834
835
0
  VerifySubDocument(SubDocKey(doc_key), 5500_usec_ht,
836
0
                    R"#(
837
0
{
838
0
  "a": {
839
0
    "1": "3",
840
0
    "2": 11,
841
0
    "3": "4"
842
0
  },
843
0
  "b": {
844
0
    "e": {
845
0
      "1": "8"
846
0
    },
847
0
    "y": "10"
848
0
  },
849
0
  "u": "7"
850
0
}
851
0
      )#");
852
853
  // Test the evolution of SubDoc root.b at various timestamps.
854
855
0
  VerifySubDocument(SubDocKey(doc_key, PrimitiveValue("b")), 500_usec_ht, "");
856
857
0
  VerifySubDocument(SubDocKey(doc_key, PrimitiveValue("b")), 2500_usec_ht,
858
0
                    R"#(
859
0
{
860
0
  "c": {
861
0
    "1": "3"
862
0
  },
863
0
  "d": {
864
0
    "1": "5",
865
0
    "2": "6"
866
0
  }
867
0
}
868
0
      )#");
869
870
0
  VerifySubDocument(SubDocKey(doc_key, PrimitiveValue("b")), 3500_usec_ht,
871
0
                    R"#(
872
0
{
873
0
  "e": {
874
0
    "1": "8",
875
0
    "2": "9"
876
0
  },
877
0
  "y": "10"
878
0
}
879
0
      )#");
880
881
0
  VerifySubDocument(SubDocKey(doc_key, PrimitiveValue("b")), 5500_usec_ht,
882
0
                    R"#(
883
0
{
884
0
  "e": {
885
0
    "1": "8"
886
0
  },
887
0
  "y": "10"
888
0
}
889
0
      )#");
890
891
0
  VerifySubDocument(SubDocKey(
892
0
      doc_key, PrimitiveValue("b"), PrimitiveValue("d")), 10000_usec_ht, "");
893
894
0
  VerifySubDocument(SubDocKey(doc_key, PrimitiveValue("b"), PrimitiveValue("d")),
895
0
                    2500_usec_ht,
896
0
                    R"#(
897
0
  {
898
0
    "1": "5",
899
0
    "2": "6"
900
0
  }
901
0
        )#");
902
903
0
}
904
905
0
TEST_P(DocDBTestWrapper, ListInsertAndGetTest) {
906
0
  SubDocument parent;
907
0
  SubDocument list({PrimitiveValue(10), PrimitiveValue(2)});
908
0
  DocKey doc_key(PrimitiveValues("list_test", 231));
909
0
  KeyBytes encoded_doc_key = doc_key.Encode();
910
0
  parent.SetChild(PrimitiveValue("other"), SubDocument(PrimitiveValue("other_value")));
911
0
  parent.SetChild(PrimitiveValue("list2"), SubDocument(list));
912
0
  ASSERT_OK(InsertSubDocument(DocPath(encoded_doc_key), parent, HybridTime(100)));
913
914
0
  VerifySubDocument(SubDocKey(doc_key), HybridTime(250),
915
0
      R"#(
916
0
  {
917
0
    "list2": {
918
0
      ArrayIndex(1): 10,
919
0
      ArrayIndex(2): 2
920
0
    },
921
0
    "other": "other_value"
922
0
  }
923
0
        )#");
924
925
0
  ASSERT_OK(ExtendSubDocument(DocPath(encoded_doc_key, PrimitiveValue("list1")),
926
0
      SubDocument({PrimitiveValue(1), PrimitiveValue("3"), PrimitiveValue(2), PrimitiveValue(2)}),
927
0
      HybridTime(200)));
928
929
0
  VerifySubDocument(SubDocKey(doc_key), HybridTime(250),
930
0
      R"#(
931
0
  {
932
0
    "list1": {
933
0
      ArrayIndex(3): 1,
934
0
      ArrayIndex(4): "3",
935
0
      ArrayIndex(5): 2,
936
0
      ArrayIndex(6): 2
937
0
    },
938
0
    "list2": {
939
0
      ArrayIndex(1): 10,
940
0
      ArrayIndex(2): 2
941
0
    },
942
0
    "other": "other_value"
943
0
  }
944
0
        )#");
945
946
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
947
0
      R"#(
948
0
SubDocKey(DocKey([], ["list_test", 231]), [HT{ physical: 0 logical: 100 }]) -> {}
949
0
SubDocKey(DocKey([], ["list_test", 231]), ["list1", ArrayIndex(3); \
950
0
    HT{ physical: 0 logical: 200 }]) -> 1
951
0
SubDocKey(DocKey([], ["list_test", 231]), ["list1", ArrayIndex(4); \
952
0
    HT{ physical: 0 logical: 200 w: 1 }]) -> "3"
953
0
SubDocKey(DocKey([], ["list_test", 231]), ["list1", ArrayIndex(5); \
954
0
    HT{ physical: 0 logical: 200 w: 2 }]) -> 2
955
0
SubDocKey(DocKey([], ["list_test", 231]), ["list1", ArrayIndex(6); \
956
0
    HT{ physical: 0 logical: 200 w: 3 }]) -> 2
957
0
SubDocKey(DocKey([], ["list_test", 231]), ["list2", ArrayIndex(1); \
958
0
    HT{ physical: 0 logical: 100 w: 1 }]) -> 10
959
0
SubDocKey(DocKey([], ["list_test", 231]), ["list2", ArrayIndex(2); \
960
0
    HT{ physical: 0 logical: 100 w: 2 }]) -> 2
961
0
SubDocKey(DocKey([], ["list_test", 231]), ["other"; \
962
0
    HT{ physical: 0 logical: 100 w: 3 }]) -> "other_value"
963
0
        )#");
964
0
  ASSERT_OK(ExtendList(DocPath(encoded_doc_key, PrimitiveValue("list2")),
965
0
      SubDocument({PrimitiveValue(5), PrimitiveValue(2)}, ListExtendOrder::PREPEND_BLOCK),
966
0
      HybridTime(300)));
967
0
  ASSERT_OK(ExtendList(DocPath(encoded_doc_key, PrimitiveValue("list2")),
968
0
      SubDocument({PrimitiveValue(7), PrimitiveValue(4)}, ListExtendOrder::APPEND),
969
0
      HybridTime(400)));
970
971
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
972
0
      R"#(
973
0
SubDocKey(DocKey([], ["list_test", 231]), [HT{ physical: 0 logical: 100 }]) -> {}
974
0
SubDocKey(DocKey([], ["list_test", 231]), ["list1", ArrayIndex(3); \
975
0
    HT{ physical: 0 logical: 200 }]) -> 1
976
0
SubDocKey(DocKey([], ["list_test", 231]), ["list1", ArrayIndex(4); \
977
0
    HT{ physical: 0 logical: 200 w: 1 }]) -> "3"
978
0
SubDocKey(DocKey([], ["list_test", 231]), ["list1", ArrayIndex(5); \
979
0
    HT{ physical: 0 logical: 200 w: 2 }]) -> 2
980
0
SubDocKey(DocKey([], ["list_test", 231]), ["list1", ArrayIndex(6); \
981
0
    HT{ physical: 0 logical: 200 w: 3 }]) -> 2
982
0
SubDocKey(DocKey([], ["list_test", 231]), ["list2", ArrayIndex(-8); \
983
0
    HT{ physical: 0 logical: 300 w: 1 }]) -> 5
984
0
SubDocKey(DocKey([], ["list_test", 231]), ["list2", ArrayIndex(-7); \
985
0
    HT{ physical: 0 logical: 300 }]) -> 2
986
0
SubDocKey(DocKey([], ["list_test", 231]), ["list2", ArrayIndex(1); \
987
0
    HT{ physical: 0 logical: 100 w: 1 }]) -> 10
988
0
SubDocKey(DocKey([], ["list_test", 231]), ["list2", ArrayIndex(2); \
989
0
    HT{ physical: 0 logical: 100 w: 2 }]) -> 2
990
0
SubDocKey(DocKey([], ["list_test", 231]), ["list2", ArrayIndex(9); \
991
0
    HT{ physical: 0 logical: 400 }]) -> 7
992
0
SubDocKey(DocKey([], ["list_test", 231]), ["list2", ArrayIndex(10); \
993
0
    HT{ physical: 0 logical: 400 w: 1 }]) -> 4
994
0
SubDocKey(DocKey([], ["list_test", 231]), ["other"; \
995
0
    HT{ physical: 0 logical: 100 w: 3 }]) -> "other_value"
996
0
        )#");
997
998
0
  VerifySubDocument(SubDocKey(doc_key), HybridTime(150),
999
0
      R"#(
1000
0
  {
1001
0
    "list2": {
1002
0
      ArrayIndex(1): 10,
1003
0
      ArrayIndex(2): 2
1004
0
    },
1005
0
    "other": "other_value"
1006
0
  }
1007
0
        )#");
1008
1009
0
  VerifySubDocument(SubDocKey(doc_key), HybridTime(450),
1010
0
      R"#(
1011
0
  {
1012
0
    "list1": {
1013
0
      ArrayIndex(3): 1,
1014
0
      ArrayIndex(4): "3",
1015
0
      ArrayIndex(5): 2,
1016
0
      ArrayIndex(6): 2
1017
0
    },
1018
0
    "list2": {
1019
0
      ArrayIndex(-8): 5,
1020
0
      ArrayIndex(-7): 2,
1021
0
      ArrayIndex(1): 10,
1022
0
      ArrayIndex(2): 2,
1023
0
      ArrayIndex(9): 7,
1024
0
      ArrayIndex(10): 4
1025
0
    },
1026
0
    "other": "other_value"
1027
0
  }
1028
0
        )#");
1029
1030
0
  ReadHybridTime read_ht;
1031
0
  read_ht.read = HybridTime(460);
1032
0
  ASSERT_OK(ReplaceInList(
1033
0
      DocPath(encoded_doc_key, PrimitiveValue("list2")), 1, SubDocument(PrimitiveValue::kTombstone),
1034
0
      read_ht, HybridTime(500), rocksdb::kDefaultQueryId));
1035
0
  ASSERT_OK(ReplaceInList(
1036
0
      DocPath(encoded_doc_key, PrimitiveValue("list2")), 2, SubDocument(PrimitiveValue(17)),
1037
0
      read_ht, HybridTime(500), rocksdb::kDefaultQueryId));
1038
1039
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
1040
0
      R"#(
1041
0
SubDocKey(DocKey([], ["list_test", 231]), [HT{ physical: 0 logical: 100 }]) -> {}
1042
0
SubDocKey(DocKey([], ["list_test", 231]), ["list1", ArrayIndex(3); \
1043
0
    HT{ physical: 0 logical: 200 }]) -> 1
1044
0
SubDocKey(DocKey([], ["list_test", 231]), ["list1", ArrayIndex(4); \
1045
0
    HT{ physical: 0 logical: 200 w: 1 }]) -> "3"
1046
0
SubDocKey(DocKey([], ["list_test", 231]), ["list1", ArrayIndex(5); \
1047
0
    HT{ physical: 0 logical: 200 w: 2 }]) -> 2
1048
0
SubDocKey(DocKey([], ["list_test", 231]), ["list1", ArrayIndex(6); \
1049
0
    HT{ physical: 0 logical: 200 w: 3 }]) -> 2
1050
0
SubDocKey(DocKey([], ["list_test", 231]), ["list2", ArrayIndex(-8); \
1051
0
    HT{ physical: 0 logical: 300 w: 1 }]) -> 5
1052
0
SubDocKey(DocKey([], ["list_test", 231]), ["list2", ArrayIndex(-7); \
1053
0
    HT{ physical: 0 logical: 500 }]) -> DEL
1054
0
SubDocKey(DocKey([], ["list_test", 231]), ["list2", ArrayIndex(-7); \
1055
0
    HT{ physical: 0 logical: 300 }]) -> 2
1056
0
SubDocKey(DocKey([], ["list_test", 231]), ["list2", ArrayIndex(1); \
1057
0
    HT{ physical: 0 logical: 100 w: 1 }]) -> 10
1058
0
SubDocKey(DocKey([], ["list_test", 231]), ["list2", ArrayIndex(2); \
1059
0
    HT{ physical: 0 logical: 500 }]) -> 17
1060
0
SubDocKey(DocKey([], ["list_test", 231]), ["list2", ArrayIndex(2); \
1061
0
    HT{ physical: 0 logical: 100 w: 2 }]) -> 2
1062
0
SubDocKey(DocKey([], ["list_test", 231]), ["list2", ArrayIndex(9); \
1063
0
    HT{ physical: 0 logical: 400 }]) -> 7
1064
0
SubDocKey(DocKey([], ["list_test", 231]), ["list2", ArrayIndex(10); \
1065
0
    HT{ physical: 0 logical: 400 w: 1 }]) -> 4
1066
0
SubDocKey(DocKey([], ["list_test", 231]), ["other"; \
1067
0
    HT{ physical: 0 logical: 100 w: 3 }]) -> "other_value"
1068
0
        )#");
1069
1070
0
  VerifySubDocument(SubDocKey(doc_key), HybridTime(550),
1071
0
      R"#(
1072
0
  {
1073
0
    "list1": {
1074
0
      ArrayIndex(3): 1,
1075
0
      ArrayIndex(4): "3",
1076
0
      ArrayIndex(5): 2,
1077
0
      ArrayIndex(6): 2
1078
0
    },
1079
0
    "list2": {
1080
0
      ArrayIndex(-8): 5,
1081
0
      ArrayIndex(1): 10,
1082
0
      ArrayIndex(2): 17,
1083
0
      ArrayIndex(9): 7,
1084
0
      ArrayIndex(10): 4
1085
0
    },
1086
0
    "other": "other_value"
1087
0
  }
1088
0
        )#");
1089
1090
0
  SubDocKey sub_doc_key(doc_key, PrimitiveValue("list3"));
1091
0
  KeyBytes encoded_sub_doc_key = sub_doc_key.Encode();
1092
0
  SubDocument list3({PrimitiveValue(31), PrimitiveValue(32)});
1093
1094
0
  ASSERT_OK(InsertSubDocument(DocPath(encoded_sub_doc_key), list3, HybridTime(100)));
1095
1096
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
1097
0
      R"#(
1098
0
SubDocKey(DocKey([], ["list_test", 231]), [HT{ physical: 0 logical: 100 }]) -> {}
1099
0
SubDocKey(DocKey([], ["list_test", 231]), ["list1", ArrayIndex(3); \
1100
0
    HT{ physical: 0 logical: 200 }]) -> 1
1101
0
SubDocKey(DocKey([], ["list_test", 231]), ["list1", ArrayIndex(4); \
1102
0
    HT{ physical: 0 logical: 200 w: 1 }]) -> "3"
1103
0
SubDocKey(DocKey([], ["list_test", 231]), ["list1", ArrayIndex(5); \
1104
0
    HT{ physical: 0 logical: 200 w: 2 }]) -> 2
1105
0
SubDocKey(DocKey([], ["list_test", 231]), ["list1", ArrayIndex(6); \
1106
0
    HT{ physical: 0 logical: 200 w: 3 }]) -> 2
1107
0
SubDocKey(DocKey([], ["list_test", 231]), ["list2", ArrayIndex(-8); \
1108
0
    HT{ physical: 0 logical: 300 w: 1 }]) -> 5
1109
0
SubDocKey(DocKey([], ["list_test", 231]), ["list2", ArrayIndex(-7); \
1110
0
    HT{ physical: 0 logical: 500 }]) -> DEL
1111
0
SubDocKey(DocKey([], ["list_test", 231]), ["list2", ArrayIndex(-7); \
1112
0
    HT{ physical: 0 logical: 300 }]) -> 2
1113
0
SubDocKey(DocKey([], ["list_test", 231]), ["list2", ArrayIndex(1); \
1114
0
    HT{ physical: 0 logical: 100 w: 1 }]) -> 10
1115
0
SubDocKey(DocKey([], ["list_test", 231]), ["list2", ArrayIndex(2); \
1116
0
    HT{ physical: 0 logical: 500 }]) -> 17
1117
0
SubDocKey(DocKey([], ["list_test", 231]), ["list2", ArrayIndex(2); \
1118
0
    HT{ physical: 0 logical: 100 w: 2 }]) -> 2
1119
0
SubDocKey(DocKey([], ["list_test", 231]), ["list2", ArrayIndex(9); \
1120
0
    HT{ physical: 0 logical: 400 }]) -> 7
1121
0
SubDocKey(DocKey([], ["list_test", 231]), ["list2", ArrayIndex(10); \
1122
0
    HT{ physical: 0 logical: 400 w: 1 }]) -> 4
1123
0
SubDocKey(DocKey([], ["list_test", 231]), ["list3"; HT{ physical: 0 logical: 100 }]) -> []
1124
0
SubDocKey(DocKey([], ["list_test", 231]), ["list3", ArrayIndex(11); \
1125
0
    HT{ physical: 0 logical: 100 w: 1 }]) -> 31
1126
0
SubDocKey(DocKey([], ["list_test", 231]), ["list3", ArrayIndex(12); \
1127
0
    HT{ physical: 0 logical: 100 w: 2 }]) -> 32
1128
0
SubDocKey(DocKey([], ["list_test", 231]), ["other"; \
1129
0
    HT{ physical: 0 logical: 100 w: 3 }]) -> "other_value"
1130
0
        )#");
1131
1132
0
  VerifySubDocument(SubDocKey(doc_key), HybridTime(550),
1133
0
      R"#(
1134
0
  {
1135
0
    "list1": {
1136
0
      ArrayIndex(3): 1,
1137
0
      ArrayIndex(4): "3",
1138
0
      ArrayIndex(5): 2,
1139
0
      ArrayIndex(6): 2
1140
0
    },
1141
0
    "list2": {
1142
0
      ArrayIndex(-8): 5,
1143
0
      ArrayIndex(1): 10,
1144
0
      ArrayIndex(2): 17,
1145
0
      ArrayIndex(9): 7,
1146
0
      ArrayIndex(10): 4
1147
0
    },
1148
0
    "list3": {
1149
0
      ArrayIndex(11): 31,
1150
0
      ArrayIndex(12): 32
1151
0
    },
1152
0
    "other": "other_value"
1153
0
  }
1154
0
        )#");
1155
0
}
1156
1157
0
TEST_P(DocDBTestWrapper, ListOverwriteAndInsertTest) {
1158
0
  SubDocument parent;
1159
0
  DocKey doc_key(PrimitiveValues("list_test", 231));
1160
0
  KeyBytes encoded_doc_key = doc_key.Encode();
1161
0
  ASSERT_OK(InsertSubDocument(DocPath(encoded_doc_key), parent, HybridTime(100)));
1162
1163
0
  auto write_list = [&](const std::vector<PrimitiveValue>& children, const int logical_time) {
1164
0
    SubDocument list;
1165
0
    int idx = 1;
1166
0
    for (const auto& child : children) {
1167
0
      list.SetChild(PrimitiveValue::ArrayIndex(idx++), SubDocument(child));
1168
0
    }
1169
0
    ASSERT_OK(InsertSubDocument(DocPath(encoded_doc_key, "list"), list, HybridTime(logical_time)));
1170
0
  };
1171
1172
0
  write_list(PrimitiveValues(1, 2, 3, 4, 5), 200);
1173
0
  write_list(PrimitiveValues(6, 7, 8), 300);
1174
1175
0
  VerifySubDocument(SubDocKey(doc_key), HybridTime(350),
1176
0
      R"#(
1177
0
  {
1178
0
    "list": {
1179
0
      ArrayIndex(1): 6,
1180
0
      ArrayIndex(2): 7,
1181
0
      ArrayIndex(3): 8
1182
0
    }
1183
0
  }
1184
0
        )#");
1185
1186
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
1187
0
      R"#(
1188
0
SubDocKey(DocKey([], ["list_test", 231]), [HT{ physical: 0 logical: 100 }]) -> {}
1189
0
SubDocKey(DocKey([], ["list_test", 231]), ["list"; HT{ physical: 0 logical: 300 }]) -> {}
1190
0
SubDocKey(DocKey([], ["list_test", 231]), ["list"; HT{ physical: 0 logical: 200 }]) -> {}
1191
0
SubDocKey(DocKey([], ["list_test", 231]), ["list", ArrayIndex(1); \
1192
0
    HT{ physical: 0 logical: 300 w: 1 }]) -> 6
1193
0
SubDocKey(DocKey([], ["list_test", 231]), ["list", ArrayIndex(1); \
1194
0
    HT{ physical: 0 logical: 200 w: 1 }]) -> 1
1195
0
SubDocKey(DocKey([], ["list_test", 231]), ["list", ArrayIndex(2); \
1196
0
    HT{ physical: 0 logical: 300 w: 2 }]) -> 7
1197
0
SubDocKey(DocKey([], ["list_test", 231]), ["list", ArrayIndex(2); \
1198
0
    HT{ physical: 0 logical: 200 w: 2 }]) -> 2
1199
0
SubDocKey(DocKey([], ["list_test", 231]), ["list", ArrayIndex(3); \
1200
0
    HT{ physical: 0 logical: 300 w: 3 }]) -> 8
1201
0
SubDocKey(DocKey([], ["list_test", 231]), ["list", ArrayIndex(3); \
1202
0
    HT{ physical: 0 logical: 200 w: 3 }]) -> 3
1203
0
SubDocKey(DocKey([], ["list_test", 231]), ["list", ArrayIndex(4); \
1204
0
    HT{ physical: 0 logical: 200 w: 4 }]) -> 4
1205
0
SubDocKey(DocKey([], ["list_test", 231]), ["list", ArrayIndex(5); \
1206
0
    HT{ physical: 0 logical: 200 w: 5 }]) -> 5
1207
0
        )#");
1208
1209
  // Replacing cql index 1 with 17 should work as expected, ignoring overwritten DocDB entries
1210
0
  ASSERT_OK(ReplaceInList(
1211
0
      DocPath(encoded_doc_key, PrimitiveValue("list")), 1, SubDocument(PrimitiveValue(17)),
1212
0
      ReadHybridTime::SingleTime(HybridTime(400)), HybridTime(500), rocksdb::kDefaultQueryId));
1213
  // Replacing cql index 3 should fail, rather than overwrite an old overwritten index
1214
0
  ASSERT_NOK(ReplaceInList(
1215
0
      DocPath(encoded_doc_key, PrimitiveValue("list")), 3, SubDocument(PrimitiveValue(17)),
1216
0
      ReadHybridTime::SingleTime(HybridTime(400)), HybridTime(500), rocksdb::kDefaultQueryId));
1217
0
  VerifySubDocument(SubDocKey(doc_key), HybridTime(500),
1218
0
      R"#(
1219
0
  {
1220
0
    "list": {
1221
0
      ArrayIndex(1): 6,
1222
0
      ArrayIndex(2): 17,
1223
0
      ArrayIndex(3): 8
1224
0
    }
1225
0
  }
1226
0
        )#");
1227
0
}
1228
1229
0
TEST_P(DocDBTestWrapper, ExpiredValueCompactionTest) {
1230
0
  const DocKey doc_key(PrimitiveValues("k1"));
1231
0
  const MonoDelta one_ms = 1ms;
1232
0
  const MonoDelta two_ms = 2ms;
1233
0
  const HybridTime t0 = 1000_usec_ht;
1234
0
  HybridTime t1 = server::HybridClock::AddPhysicalTimeToHybridTime(t0, two_ms);
1235
0
  HybridTime t2 = server::HybridClock::AddPhysicalTimeToHybridTime(t1, two_ms);
1236
0
  KeyBytes encoded_doc_key(doc_key.Encode());
1237
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key, PrimitiveValue("s1")),
1238
0
      Value(PrimitiveValue("v11"), one_ms), t0));
1239
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key, PrimitiveValue("s1")),
1240
0
      PrimitiveValue("v14"), t2));
1241
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key, PrimitiveValue("s2")),
1242
0
      Value(PrimitiveValue("v21"), 3ms), t0));
1243
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key, PrimitiveValue("s2")),
1244
0
      PrimitiveValue("v24"), t2));
1245
1246
  // Note: HT{ physical: 1000 } + 4ms = HT{ physical: 5000 }
1247
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
1248
0
      SubDocKey(DocKey([], ["k1"]), ["s1"; HT{ physical: 5000 }]) -> "v14"
1249
0
      SubDocKey(DocKey([], ["k1"]), ["s1"; HT{ physical: 1000 }]) -> "v11"; ttl: 0.001s
1250
0
      SubDocKey(DocKey([], ["k1"]), ["s2"; HT{ physical: 5000 }]) -> "v24"
1251
0
      SubDocKey(DocKey([], ["k1"]), ["s2"; HT{ physical: 1000 }]) -> "v21"; ttl: 0.003s
1252
0
      )#");
1253
0
  FullyCompactHistoryBefore(t1);
1254
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
1255
0
      R"#(
1256
0
SubDocKey(DocKey([], ["k1"]), ["s1"; HT{ physical: 5000 }]) -> "v14"
1257
0
SubDocKey(DocKey([], ["k1"]), ["s2"; HT{ physical: 5000 }]) -> "v24"
1258
0
SubDocKey(DocKey([], ["k1"]), ["s2"; HT{ physical: 1000 }]) -> "v21"; ttl: 0.003s
1259
0
      )#");
1260
0
}
1261
1262
0
TEST_P(DocDBTestWrapper, GetDocTwoLists) {
1263
0
  SubDocument parent;
1264
0
  SubDocument list1({PrimitiveValue(10), PrimitiveValue(2)});
1265
0
  DocKey doc_key(PrimitiveValues("foo", 231));
1266
1267
0
  KeyBytes encoded_doc_key = doc_key.Encode();
1268
0
  parent.SetChild(PrimitiveValue("key1"), SubDocument(list1));
1269
0
  ASSERT_OK(InsertSubDocument(DocPath(encoded_doc_key), parent, HybridTime(100)));
1270
1271
0
  SubDocKey sub_doc_key(doc_key, PrimitiveValue("key2"));
1272
0
  KeyBytes encoded_sub_doc_key = sub_doc_key.Encode();
1273
0
  SubDocument list2({PrimitiveValue(31), PrimitiveValue(32)});
1274
1275
0
  ASSERT_OK(InsertSubDocument(DocPath(encoded_sub_doc_key), list2, HybridTime(100)));
1276
1277
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
1278
0
      R"#(
1279
0
SubDocKey(DocKey([], ["foo", 231]), [HT{ physical: 0 logical: 100 }]) -> {}
1280
0
SubDocKey(DocKey([], ["foo", 231]), ["key1", ArrayIndex(1); HT{ physical: 0 logical: 100 w: 1 }]) \
1281
0
-> 10
1282
0
SubDocKey(DocKey([], ["foo", 231]), ["key1", ArrayIndex(2); HT{ physical: 0 logical: 100 w: 2 }]) \
1283
0
-> 2
1284
0
SubDocKey(DocKey([], ["foo", 231]), ["key2"; HT{ physical: 0 logical: 100 }]) -> []
1285
0
SubDocKey(DocKey([], ["foo", 231]), ["key2", ArrayIndex(3); HT{ physical: 0 logical: 100 w: 1 }]) \
1286
0
-> 31
1287
0
SubDocKey(DocKey([], ["foo", 231]), ["key2", ArrayIndex(4); HT{ physical: 0 logical: 100 w: 2 }]) \
1288
0
-> 32
1289
0
      )#");
1290
1291
0
  VerifySubDocument(SubDocKey(doc_key), HybridTime(550),
1292
0
      R"#(
1293
0
  {
1294
0
    "key1": {
1295
0
      ArrayIndex(1): 10,
1296
0
      ArrayIndex(2): 2
1297
0
    },
1298
0
    "key2": {
1299
0
      ArrayIndex(3): 31,
1300
0
      ArrayIndex(4): 32
1301
0
    }
1302
0
  }
1303
0
      )#");
1304
0
}
1305
1306
1307
// Compaction testing with TTL merge records for generic Redis collections.
1308
// Observe that because only collection-level merge records are supported,
1309
// all tests begin with initializing a vanilla collection and adding TTL over it.
1310
0
TEST_P(DocDBTestWrapper, RedisCollectionTTLCompactionTest) {
1311
0
  const MonoDelta one_ms = 1ms;
1312
0
  string key_string = "k0";
1313
0
  string val_string = "v0";
1314
0
  int n_times = 35;
1315
0
  vector<HybridTime> t(n_times);
1316
0
  t[0] = 1000_usec_ht;
1317
0
  for (int i = 1; i < n_times; ++i) {
1318
0
    t[i] = server::HybridClock::AddPhysicalTimeToHybridTime(t[i-1], one_ms);
1319
0
  }
1320
1321
0
  std::set<std::pair<string, string>> docdb_dump;
1322
0
  auto time_iter = t.begin();
1323
1324
  // Stack 1
1325
0
  InitializeCollection(key_string, &val_string, &time_iter, &docdb_dump);
1326
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
1327
0
    Value(PrimitiveValue(ValueType::kTombstone)), *time_iter));
1328
0
  ++time_iter;
1329
0
  InitializeCollection(key_string, &val_string, &time_iter, &docdb_dump);
1330
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
1331
0
    Value(PrimitiveValue(ValueType::kObject), 21ms, Value::kInvalidUserTimestamp, Value::kTtlFlag),
1332
0
                         *time_iter));
1333
0
  ++time_iter;
1334
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
1335
0
    Value(PrimitiveValue(ValueType::kObject), 9ms, Value::kInvalidUserTimestamp, Value::kTtlFlag),
1336
0
                         *time_iter));
1337
0
  time_iter = t.begin();
1338
0
  ++key_string[1];
1339
1340
  // Stack 2
1341
0
  InitializeCollection(key_string, &val_string, &time_iter, &docdb_dump);
1342
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
1343
0
    Value(PrimitiveValue(ValueType::kObject), 18ms, Value::kInvalidUserTimestamp, Value::kTtlFlag),
1344
0
                         *time_iter));
1345
0
  ++time_iter;
1346
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
1347
0
    Value(PrimitiveValue(ValueType::kObject), 15ms, Value::kInvalidUserTimestamp, Value::kTtlFlag),
1348
0
                         *time_iter));
1349
0
  ++time_iter;
1350
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
1351
0
    Value(PrimitiveValue(ValueType::kTombstone)), *time_iter));
1352
0
  time_iter = t.begin();
1353
0
  ++key_string[1];
1354
1355
  // Stack 3
1356
0
  InitializeCollection(key_string, &val_string, &time_iter, &docdb_dump);
1357
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
1358
0
    Value(PrimitiveValue(ValueType::kObject), 15ms, Value::kInvalidUserTimestamp, Value::kTtlFlag),
1359
0
                         *time_iter));
1360
0
  ++time_iter;
1361
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
1362
0
    Value(PrimitiveValue(ValueType::kObject), 18ms, Value::kInvalidUserTimestamp, Value::kTtlFlag),
1363
0
                         *time_iter));
1364
0
  ++time_iter;
1365
0
  InitializeCollection(key_string, &val_string, &time_iter, &docdb_dump);
1366
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
1367
0
    Value(PrimitiveValue(ValueType::kObject), 21ms, Value::kInvalidUserTimestamp, Value::kTtlFlag),
1368
0
                         *time_iter));
1369
0
  ++time_iter;
1370
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
1371
0
    Value(PrimitiveValue(ValueType::kObject), 9ms, Value::kInvalidUserTimestamp, Value::kTtlFlag),
1372
0
                         *time_iter));
1373
0
  ++time_iter;
1374
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
1375
0
    Value(PrimitiveValue(ValueType::kObject), 12ms, Value::kInvalidUserTimestamp, Value::kTtlFlag),
1376
0
                         *time_iter));
1377
0
  time_iter = t.begin();
1378
0
  ++key_string[1];
1379
1380
  // Stack 4
1381
0
  InitializeCollection(key_string, &val_string, &time_iter, &docdb_dump);
1382
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
1383
0
    Value(PrimitiveValue(ValueType::kObject), 15ms, Value::kInvalidUserTimestamp, Value::kTtlFlag),
1384
0
                         *time_iter));
1385
0
  ++time_iter;
1386
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
1387
0
    Value(PrimitiveValue(ValueType::kObject), 6ms, Value::kInvalidUserTimestamp, Value::kTtlFlag),
1388
0
                         *time_iter));
1389
0
  ++time_iter;
1390
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
1391
0
    Value(PrimitiveValue(ValueType::kObject), 18ms, Value::kInvalidUserTimestamp, Value::kTtlFlag),
1392
0
                         *time_iter));
1393
0
  ++time_iter;
1394
0
  InitializeCollection(key_string, &val_string, &time_iter, &docdb_dump);
1395
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
1396
0
    Value(PrimitiveValue(ValueType::kObject), 18ms, Value::kInvalidUserTimestamp, Value::kTtlFlag),
1397
0
                         *time_iter));
1398
0
  ++time_iter;
1399
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
1400
0
    Value(PrimitiveValue(ValueType::kObject), 9ms, Value::kInvalidUserTimestamp, Value::kTtlFlag),
1401
0
                         *time_iter));
1402
0
  ++time_iter;
1403
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
1404
0
    Value(PrimitiveValue(ValueType::kObject), Value::kMaxTtl,
1405
0
          Value::kInvalidUserTimestamp, Value::kTtlFlag), *time_iter));
1406
1407
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
1408
0
      R"#(
1409
0
  SubDocKey(DocKey([], ["k0"]), [HT{ physical: 7000 }]) -> {}; merge flags: 1; ttl: 0.009s
1410
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 6000 }]) -> {}; merge flags: 1; ttl: 0.021s
1411
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 4000 }]) -> {}
1412
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 3000 }]) -> DEL
1413
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 1000 }]) -> {}
1414
0
SubDocKey(DocKey([], ["k0"]), ["sk0"; HT{ physical: 4000 w: 1 }]) -> "v6"
1415
0
SubDocKey(DocKey([], ["k0"]), ["sk0"; HT{ physical: 1000 w: 1 }]) -> "v0"
1416
0
SubDocKey(DocKey([], ["k0"]), ["sk1"; HT{ physical: 5000 }]) -> "v9"
1417
0
SubDocKey(DocKey([], ["k0"]), ["sk1"; HT{ physical: 4000 w: 2 }]) -> "v7"
1418
0
SubDocKey(DocKey([], ["k0"]), ["sk1"; HT{ physical: 2000 }]) -> "v3"
1419
0
SubDocKey(DocKey([], ["k0"]), ["sk1"; HT{ physical: 1000 w: 2 }]) -> "v1"
1420
0
SubDocKey(DocKey([], ["k0"]), ["sk2"; HT{ physical: 5000 w: 1 }]) -> "v:"
1421
0
SubDocKey(DocKey([], ["k0"]), ["sk2"; HT{ physical: 4000 w: 3 }]) -> "v8"
1422
0
SubDocKey(DocKey([], ["k0"]), ["sk2"; HT{ physical: 2000 w: 1 }]) -> "v4"
1423
0
SubDocKey(DocKey([], ["k0"]), ["sk2"; HT{ physical: 1000 w: 3 }]) -> "v2"
1424
0
SubDocKey(DocKey([], ["k0"]), ["sk3"; HT{ physical: 5000 w: 2 }]) -> "v;"
1425
0
SubDocKey(DocKey([], ["k0"]), ["sk3"; HT{ physical: 2000 w: 2 }]) -> "v5"
1426
0
SubDocKey(DocKey([], ["k1"]), [HT{ physical: 5000 }]) -> DEL
1427
0
SubDocKey(DocKey([], ["k1"]), [HT{ physical: 4000 }]) -> {}; merge flags: 1; ttl: 0.015s
1428
0
SubDocKey(DocKey([], ["k1"]), [HT{ physical: 3000 }]) -> {}; merge flags: 1; ttl: 0.018s
1429
0
SubDocKey(DocKey([], ["k1"]), [HT{ physical: 1000 }]) -> {}
1430
0
SubDocKey(DocKey([], ["k1"]), ["sk0"; HT{ physical: 1000 w: 1 }]) -> "v<"
1431
0
SubDocKey(DocKey([], ["k1"]), ["sk1"; HT{ physical: 2000 }]) -> "v?"
1432
0
SubDocKey(DocKey([], ["k1"]), ["sk1"; HT{ physical: 1000 w: 2 }]) -> "v="
1433
0
SubDocKey(DocKey([], ["k1"]), ["sk2"; HT{ physical: 2000 w: 1 }]) -> "v@"
1434
0
SubDocKey(DocKey([], ["k1"]), ["sk2"; HT{ physical: 1000 w: 3 }]) -> "v>"
1435
0
SubDocKey(DocKey([], ["k1"]), ["sk3"; HT{ physical: 2000 w: 2 }]) -> "vA"
1436
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 9000 }]) -> {}; merge flags: 1; ttl: 0.012s
1437
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 8000 }]) -> {}; merge flags: 1; ttl: 0.009s
1438
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 7000 }]) -> {}; merge flags: 1; ttl: 0.021s
1439
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 5000 }]) -> {}
1440
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 4000 }]) -> {}; merge flags: 1; ttl: 0.018s
1441
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 3000 }]) -> {}; merge flags: 1; ttl: 0.015s
1442
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 1000 }]) -> {}
1443
0
SubDocKey(DocKey([], ["k2"]), ["sk0"; HT{ physical: 5000 w: 1 }]) -> "vH"
1444
0
SubDocKey(DocKey([], ["k2"]), ["sk0"; HT{ physical: 1000 w: 1 }]) -> "vB"
1445
0
SubDocKey(DocKey([], ["k2"]), ["sk1"; HT{ physical: 6000 }]) -> "vK"
1446
0
SubDocKey(DocKey([], ["k2"]), ["sk1"; HT{ physical: 5000 w: 2 }]) -> "vI"
1447
0
SubDocKey(DocKey([], ["k2"]), ["sk1"; HT{ physical: 2000 }]) -> "vE"
1448
0
SubDocKey(DocKey([], ["k2"]), ["sk1"; HT{ physical: 1000 w: 2 }]) -> "vC"
1449
0
SubDocKey(DocKey([], ["k2"]), ["sk2"; HT{ physical: 6000 w: 1 }]) -> "vL"
1450
0
SubDocKey(DocKey([], ["k2"]), ["sk2"; HT{ physical: 5000 w: 3 }]) -> "vJ"
1451
0
SubDocKey(DocKey([], ["k2"]), ["sk2"; HT{ physical: 2000 w: 1 }]) -> "vF"
1452
0
SubDocKey(DocKey([], ["k2"]), ["sk2"; HT{ physical: 1000 w: 3 }]) -> "vD"
1453
0
SubDocKey(DocKey([], ["k2"]), ["sk3"; HT{ physical: 6000 w: 2 }]) -> "vM"
1454
0
SubDocKey(DocKey([], ["k2"]), ["sk3"; HT{ physical: 2000 w: 2 }]) -> "vG"
1455
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 10000 }]) -> {}; merge flags: 1
1456
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 9000 }]) -> {}; merge flags: 1; ttl: 0.009s
1457
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 8000 }]) -> {}; merge flags: 1; ttl: 0.018s
1458
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 6000 }]) -> {}
1459
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 5000 }]) -> {}; merge flags: 1; ttl: 0.018s
1460
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 4000 }]) -> {}; merge flags: 1; ttl: 0.006s
1461
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 3000 }]) -> {}; merge flags: 1; ttl: 0.015s
1462
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 1000 }]) -> {}
1463
0
SubDocKey(DocKey([], ["k3"]), ["sk0"; HT{ physical: 6000 w: 1 }]) -> "vT"
1464
0
SubDocKey(DocKey([], ["k3"]), ["sk0"; HT{ physical: 1000 w: 1 }]) -> "vN"
1465
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 7000 }]) -> "vW"
1466
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 6000 w: 2 }]) -> "vU"
1467
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 2000 }]) -> "vQ"
1468
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 1000 w: 2 }]) -> "vO"
1469
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 7000 w: 1 }]) -> "vX"
1470
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 6000 w: 3 }]) -> "vV"
1471
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 2000 w: 1 }]) -> "vR"
1472
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 1000 w: 3 }]) -> "vP"
1473
0
SubDocKey(DocKey([], ["k3"]), ["sk3"; HT{ physical: 7000 w: 2 }]) -> "vY"
1474
0
SubDocKey(DocKey([], ["k3"]), ["sk3"; HT{ physical: 2000 w: 2 }]) -> "vS"
1475
0
      )#");
1476
0
  FullyCompactHistoryBefore(t[0]);
1477
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
1478
0
      R"#(
1479
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 7000 }]) -> {}; merge flags: 1; ttl: 0.009s
1480
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 6000 }]) -> {}; merge flags: 1; ttl: 0.021s
1481
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 4000 }]) -> {}
1482
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 3000 }]) -> DEL
1483
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 1000 }]) -> {}
1484
0
SubDocKey(DocKey([], ["k0"]), ["sk0"; HT{ physical: 4000 w: 1 }]) -> "v6"
1485
0
SubDocKey(DocKey([], ["k0"]), ["sk0"; HT{ physical: 1000 w: 1 }]) -> "v0"
1486
0
SubDocKey(DocKey([], ["k0"]), ["sk1"; HT{ physical: 5000 }]) -> "v9"
1487
0
SubDocKey(DocKey([], ["k0"]), ["sk1"; HT{ physical: 4000 w: 2 }]) -> "v7"
1488
0
SubDocKey(DocKey([], ["k0"]), ["sk1"; HT{ physical: 2000 }]) -> "v3"
1489
0
SubDocKey(DocKey([], ["k0"]), ["sk1"; HT{ physical: 1000 w: 2 }]) -> "v1"
1490
0
SubDocKey(DocKey([], ["k0"]), ["sk2"; HT{ physical: 5000 w: 1 }]) -> "v:"
1491
0
SubDocKey(DocKey([], ["k0"]), ["sk2"; HT{ physical: 4000 w: 3 }]) -> "v8"
1492
0
SubDocKey(DocKey([], ["k0"]), ["sk2"; HT{ physical: 2000 w: 1 }]) -> "v4"
1493
0
SubDocKey(DocKey([], ["k0"]), ["sk2"; HT{ physical: 1000 w: 3 }]) -> "v2"
1494
0
SubDocKey(DocKey([], ["k0"]), ["sk3"; HT{ physical: 5000 w: 2 }]) -> "v;"
1495
0
SubDocKey(DocKey([], ["k0"]), ["sk3"; HT{ physical: 2000 w: 2 }]) -> "v5"
1496
0
SubDocKey(DocKey([], ["k1"]), [HT{ physical: 5000 }]) -> DEL
1497
0
SubDocKey(DocKey([], ["k1"]), [HT{ physical: 4000 }]) -> {}; merge flags: 1; ttl: 0.015s
1498
0
SubDocKey(DocKey([], ["k1"]), [HT{ physical: 3000 }]) -> {}; merge flags: 1; ttl: 0.018s
1499
0
SubDocKey(DocKey([], ["k1"]), [HT{ physical: 1000 }]) -> {}
1500
0
SubDocKey(DocKey([], ["k1"]), ["sk0"; HT{ physical: 1000 w: 1 }]) -> "v<"
1501
0
SubDocKey(DocKey([], ["k1"]), ["sk1"; HT{ physical: 2000 }]) -> "v?"
1502
0
SubDocKey(DocKey([], ["k1"]), ["sk1"; HT{ physical: 1000 w: 2 }]) -> "v="
1503
0
SubDocKey(DocKey([], ["k1"]), ["sk2"; HT{ physical: 2000 w: 1 }]) -> "v@"
1504
0
SubDocKey(DocKey([], ["k1"]), ["sk2"; HT{ physical: 1000 w: 3 }]) -> "v>"
1505
0
SubDocKey(DocKey([], ["k1"]), ["sk3"; HT{ physical: 2000 w: 2 }]) -> "vA"
1506
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 9000 }]) -> {}; merge flags: 1; ttl: 0.012s
1507
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 8000 }]) -> {}; merge flags: 1; ttl: 0.009s
1508
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 7000 }]) -> {}; merge flags: 1; ttl: 0.021s
1509
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 5000 }]) -> {}
1510
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 4000 }]) -> {}; merge flags: 1; ttl: 0.018s
1511
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 3000 }]) -> {}; merge flags: 1; ttl: 0.015s
1512
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 1000 }]) -> {}
1513
0
SubDocKey(DocKey([], ["k2"]), ["sk0"; HT{ physical: 5000 w: 1 }]) -> "vH"
1514
0
SubDocKey(DocKey([], ["k2"]), ["sk0"; HT{ physical: 1000 w: 1 }]) -> "vB"
1515
0
SubDocKey(DocKey([], ["k2"]), ["sk1"; HT{ physical: 6000 }]) -> "vK"
1516
0
SubDocKey(DocKey([], ["k2"]), ["sk1"; HT{ physical: 5000 w: 2 }]) -> "vI"
1517
0
SubDocKey(DocKey([], ["k2"]), ["sk1"; HT{ physical: 2000 }]) -> "vE"
1518
0
SubDocKey(DocKey([], ["k2"]), ["sk1"; HT{ physical: 1000 w: 2 }]) -> "vC"
1519
0
SubDocKey(DocKey([], ["k2"]), ["sk2"; HT{ physical: 6000 w: 1 }]) -> "vL"
1520
0
SubDocKey(DocKey([], ["k2"]), ["sk2"; HT{ physical: 5000 w: 3 }]) -> "vJ"
1521
0
SubDocKey(DocKey([], ["k2"]), ["sk2"; HT{ physical: 2000 w: 1 }]) -> "vF"
1522
0
SubDocKey(DocKey([], ["k2"]), ["sk2"; HT{ physical: 1000 w: 3 }]) -> "vD"
1523
0
SubDocKey(DocKey([], ["k2"]), ["sk3"; HT{ physical: 6000 w: 2 }]) -> "vM"
1524
0
SubDocKey(DocKey([], ["k2"]), ["sk3"; HT{ physical: 2000 w: 2 }]) -> "vG"
1525
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 10000 }]) -> {}; merge flags: 1
1526
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 9000 }]) -> {}; merge flags: 1; ttl: 0.009s
1527
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 8000 }]) -> {}; merge flags: 1; ttl: 0.018s
1528
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 6000 }]) -> {}
1529
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 5000 }]) -> {}; merge flags: 1; ttl: 0.018s
1530
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 4000 }]) -> {}; merge flags: 1; ttl: 0.006s
1531
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 3000 }]) -> {}; merge flags: 1; ttl: 0.015s
1532
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 1000 }]) -> {}
1533
0
SubDocKey(DocKey([], ["k3"]), ["sk0"; HT{ physical: 6000 w: 1 }]) -> "vT"
1534
0
SubDocKey(DocKey([], ["k3"]), ["sk0"; HT{ physical: 1000 w: 1 }]) -> "vN"
1535
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 7000 }]) -> "vW"
1536
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 6000 w: 2 }]) -> "vU"
1537
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 2000 }]) -> "vQ"
1538
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 1000 w: 2 }]) -> "vO"
1539
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 7000 w: 1 }]) -> "vX"
1540
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 6000 w: 3 }]) -> "vV"
1541
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 2000 w: 1 }]) -> "vR"
1542
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 1000 w: 3 }]) -> "vP"
1543
0
SubDocKey(DocKey([], ["k3"]), ["sk3"; HT{ physical: 7000 w: 2 }]) -> "vY"
1544
0
SubDocKey(DocKey([], ["k3"]), ["sk3"; HT{ physical: 2000 w: 2 }]) -> "vS"
1545
0
      )#");
1546
0
  FullyCompactHistoryBefore(t[1]);
1547
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
1548
0
      R"#(
1549
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 7000 }]) -> {}; merge flags: 1; ttl: 0.009s
1550
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 6000 }]) -> {}; merge flags: 1; ttl: 0.021s
1551
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 4000 }]) -> {}
1552
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 3000 }]) -> DEL
1553
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 1000 }]) -> {}
1554
0
SubDocKey(DocKey([], ["k0"]), ["sk0"; HT{ physical: 4000 w: 1 }]) -> "v6"
1555
0
SubDocKey(DocKey([], ["k0"]), ["sk0"; HT{ physical: 1000 w: 1 }]) -> "v0"
1556
0
SubDocKey(DocKey([], ["k0"]), ["sk1"; HT{ physical: 5000 }]) -> "v9"
1557
0
SubDocKey(DocKey([], ["k0"]), ["sk1"; HT{ physical: 4000 w: 2 }]) -> "v7"
1558
0
SubDocKey(DocKey([], ["k0"]), ["sk1"; HT{ physical: 2000 }]) -> "v3"
1559
0
SubDocKey(DocKey([], ["k0"]), ["sk2"; HT{ physical: 5000 w: 1 }]) -> "v:"
1560
0
SubDocKey(DocKey([], ["k0"]), ["sk2"; HT{ physical: 4000 w: 3 }]) -> "v8"
1561
0
SubDocKey(DocKey([], ["k0"]), ["sk2"; HT{ physical: 2000 w: 1 }]) -> "v4"
1562
0
SubDocKey(DocKey([], ["k0"]), ["sk3"; HT{ physical: 5000 w: 2 }]) -> "v;"
1563
0
SubDocKey(DocKey([], ["k0"]), ["sk3"; HT{ physical: 2000 w: 2 }]) -> "v5"
1564
0
SubDocKey(DocKey([], ["k1"]), [HT{ physical: 5000 }]) -> DEL
1565
0
SubDocKey(DocKey([], ["k1"]), [HT{ physical: 4000 }]) -> {}; merge flags: 1; ttl: 0.015s
1566
0
SubDocKey(DocKey([], ["k1"]), [HT{ physical: 3000 }]) -> {}; merge flags: 1; ttl: 0.018s
1567
0
SubDocKey(DocKey([], ["k1"]), [HT{ physical: 1000 }]) -> {}
1568
0
SubDocKey(DocKey([], ["k1"]), ["sk0"; HT{ physical: 1000 w: 1 }]) -> "v<"
1569
0
SubDocKey(DocKey([], ["k1"]), ["sk1"; HT{ physical: 2000 }]) -> "v?"
1570
0
SubDocKey(DocKey([], ["k1"]), ["sk2"; HT{ physical: 2000 w: 1 }]) -> "v@"
1571
0
SubDocKey(DocKey([], ["k1"]), ["sk3"; HT{ physical: 2000 w: 2 }]) -> "vA"
1572
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 9000 }]) -> {}; merge flags: 1; ttl: 0.012s
1573
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 8000 }]) -> {}; merge flags: 1; ttl: 0.009s
1574
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 7000 }]) -> {}; merge flags: 1; ttl: 0.021s
1575
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 5000 }]) -> {}
1576
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 4000 }]) -> {}; merge flags: 1; ttl: 0.018s
1577
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 3000 }]) -> {}; merge flags: 1; ttl: 0.015s
1578
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 1000 }]) -> {}
1579
0
SubDocKey(DocKey([], ["k2"]), ["sk0"; HT{ physical: 5000 w: 1 }]) -> "vH"
1580
0
SubDocKey(DocKey([], ["k2"]), ["sk0"; HT{ physical: 1000 w: 1 }]) -> "vB"
1581
0
SubDocKey(DocKey([], ["k2"]), ["sk1"; HT{ physical: 6000 }]) -> "vK"
1582
0
SubDocKey(DocKey([], ["k2"]), ["sk1"; HT{ physical: 5000 w: 2 }]) -> "vI"
1583
0
SubDocKey(DocKey([], ["k2"]), ["sk1"; HT{ physical: 2000 }]) -> "vE"
1584
0
SubDocKey(DocKey([], ["k2"]), ["sk2"; HT{ physical: 6000 w: 1 }]) -> "vL"
1585
0
SubDocKey(DocKey([], ["k2"]), ["sk2"; HT{ physical: 5000 w: 3 }]) -> "vJ"
1586
0
SubDocKey(DocKey([], ["k2"]), ["sk2"; HT{ physical: 2000 w: 1 }]) -> "vF"
1587
0
SubDocKey(DocKey([], ["k2"]), ["sk3"; HT{ physical: 6000 w: 2 }]) -> "vM"
1588
0
SubDocKey(DocKey([], ["k2"]), ["sk3"; HT{ physical: 2000 w: 2 }]) -> "vG"
1589
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 10000 }]) -> {}; merge flags: 1
1590
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 9000 }]) -> {}; merge flags: 1; ttl: 0.009s
1591
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 8000 }]) -> {}; merge flags: 1; ttl: 0.018s
1592
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 6000 }]) -> {}
1593
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 5000 }]) -> {}; merge flags: 1; ttl: 0.018s
1594
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 4000 }]) -> {}; merge flags: 1; ttl: 0.006s
1595
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 3000 }]) -> {}; merge flags: 1; ttl: 0.015s
1596
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 1000 }]) -> {}
1597
0
SubDocKey(DocKey([], ["k3"]), ["sk0"; HT{ physical: 6000 w: 1 }]) -> "vT"
1598
0
SubDocKey(DocKey([], ["k3"]), ["sk0"; HT{ physical: 1000 w: 1 }]) -> "vN"
1599
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 7000 }]) -> "vW"
1600
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 6000 w: 2 }]) -> "vU"
1601
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 2000 }]) -> "vQ"
1602
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 7000 w: 1 }]) -> "vX"
1603
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 6000 w: 3 }]) -> "vV"
1604
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 2000 w: 1 }]) -> "vR"
1605
0
SubDocKey(DocKey([], ["k3"]), ["sk3"; HT{ physical: 7000 w: 2 }]) -> "vY"
1606
0
SubDocKey(DocKey([], ["k3"]), ["sk3"; HT{ physical: 2000 w: 2 }]) -> "vS"
1607
0
      )#");
1608
0
  FullyCompactHistoryBefore(t[2]);
1609
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
1610
0
      R"#(
1611
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 7000 }]) -> {}; merge flags: 1; ttl: 0.009s
1612
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 6000 }]) -> {}; merge flags: 1; ttl: 0.021s
1613
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 4000 }]) -> {}
1614
0
SubDocKey(DocKey([], ["k0"]), ["sk0"; HT{ physical: 4000 w: 1 }]) -> "v6"
1615
0
SubDocKey(DocKey([], ["k0"]), ["sk1"; HT{ physical: 5000 }]) -> "v9"
1616
0
SubDocKey(DocKey([], ["k0"]), ["sk1"; HT{ physical: 4000 w: 2 }]) -> "v7"
1617
0
SubDocKey(DocKey([], ["k0"]), ["sk2"; HT{ physical: 5000 w: 1 }]) -> "v:"
1618
0
SubDocKey(DocKey([], ["k0"]), ["sk2"; HT{ physical: 4000 w: 3 }]) -> "v8"
1619
0
SubDocKey(DocKey([], ["k0"]), ["sk3"; HT{ physical: 5000 w: 2 }]) -> "v;"
1620
0
SubDocKey(DocKey([], ["k1"]), [HT{ physical: 5000 }]) -> DEL
1621
0
SubDocKey(DocKey([], ["k1"]), [HT{ physical: 4000 }]) -> {}; merge flags: 1; ttl: 0.015s
1622
0
SubDocKey(DocKey([], ["k1"]), [HT{ physical: 1000 }]) -> {}; ttl: 0.020s
1623
0
SubDocKey(DocKey([], ["k1"]), ["sk0"; HT{ physical: 1000 w: 1 }]) -> "v<"
1624
0
SubDocKey(DocKey([], ["k1"]), ["sk1"; HT{ physical: 2000 }]) -> "v?"
1625
0
SubDocKey(DocKey([], ["k1"]), ["sk2"; HT{ physical: 2000 w: 1 }]) -> "v@"
1626
0
SubDocKey(DocKey([], ["k1"]), ["sk3"; HT{ physical: 2000 w: 2 }]) -> "vA"
1627
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 9000 }]) -> {}; merge flags: 1; ttl: 0.012s
1628
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 8000 }]) -> {}; merge flags: 1; ttl: 0.009s
1629
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 7000 }]) -> {}; merge flags: 1; ttl: 0.021s
1630
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 5000 }]) -> {}
1631
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 4000 }]) -> {}; merge flags: 1; ttl: 0.018s
1632
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 1000 }]) -> {}; ttl: 0.017s
1633
0
SubDocKey(DocKey([], ["k2"]), ["sk0"; HT{ physical: 5000 w: 1 }]) -> "vH"
1634
0
SubDocKey(DocKey([], ["k2"]), ["sk0"; HT{ physical: 1000 w: 1 }]) -> "vB"
1635
0
SubDocKey(DocKey([], ["k2"]), ["sk1"; HT{ physical: 6000 }]) -> "vK"
1636
0
SubDocKey(DocKey([], ["k2"]), ["sk1"; HT{ physical: 5000 w: 2 }]) -> "vI"
1637
0
SubDocKey(DocKey([], ["k2"]), ["sk1"; HT{ physical: 2000 }]) -> "vE"
1638
0
SubDocKey(DocKey([], ["k2"]), ["sk2"; HT{ physical: 6000 w: 1 }]) -> "vL"
1639
0
SubDocKey(DocKey([], ["k2"]), ["sk2"; HT{ physical: 5000 w: 3 }]) -> "vJ"
1640
0
SubDocKey(DocKey([], ["k2"]), ["sk2"; HT{ physical: 2000 w: 1 }]) -> "vF"
1641
0
SubDocKey(DocKey([], ["k2"]), ["sk3"; HT{ physical: 6000 w: 2 }]) -> "vM"
1642
0
SubDocKey(DocKey([], ["k2"]), ["sk3"; HT{ physical: 2000 w: 2 }]) -> "vG"
1643
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 10000 }]) -> {}; merge flags: 1
1644
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 9000 }]) -> {}; merge flags: 1; ttl: 0.009s
1645
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 8000 }]) -> {}; merge flags: 1; ttl: 0.018s
1646
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 6000 }]) -> {}
1647
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 5000 }]) -> {}; merge flags: 1; ttl: 0.018s
1648
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 4000 }]) -> {}; merge flags: 1; ttl: 0.006s
1649
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 1000 }]) -> {}; ttl: 0.017s
1650
0
SubDocKey(DocKey([], ["k3"]), ["sk0"; HT{ physical: 6000 w: 1 }]) -> "vT"
1651
0
SubDocKey(DocKey([], ["k3"]), ["sk0"; HT{ physical: 1000 w: 1 }]) -> "vN"
1652
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 7000 }]) -> "vW"
1653
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 6000 w: 2 }]) -> "vU"
1654
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 2000 }]) -> "vQ"
1655
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 7000 w: 1 }]) -> "vX"
1656
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 6000 w: 3 }]) -> "vV"
1657
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 2000 w: 1 }]) -> "vR"
1658
0
SubDocKey(DocKey([], ["k3"]), ["sk3"; HT{ physical: 7000 w: 2 }]) -> "vY"
1659
0
SubDocKey(DocKey([], ["k3"]), ["sk3"; HT{ physical: 2000 w: 2 }]) -> "vS"
1660
0
      )#");
1661
0
  FullyCompactHistoryBefore(t[3]);
1662
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
1663
0
      R"#(
1664
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 7000 }]) -> {}; merge flags: 1; ttl: 0.009s
1665
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 6000 }]) -> {}; merge flags: 1; ttl: 0.021s
1666
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 4000 }]) -> {}
1667
0
SubDocKey(DocKey([], ["k0"]), ["sk0"; HT{ physical: 4000 w: 1 }]) -> "v6"
1668
0
SubDocKey(DocKey([], ["k0"]), ["sk1"; HT{ physical: 5000 }]) -> "v9"
1669
0
SubDocKey(DocKey([], ["k0"]), ["sk1"; HT{ physical: 4000 w: 2 }]) -> "v7"
1670
0
SubDocKey(DocKey([], ["k0"]), ["sk2"; HT{ physical: 5000 w: 1 }]) -> "v:"
1671
0
SubDocKey(DocKey([], ["k0"]), ["sk2"; HT{ physical: 4000 w: 3 }]) -> "v8"
1672
0
SubDocKey(DocKey([], ["k0"]), ["sk3"; HT{ physical: 5000 w: 2 }]) -> "v;"
1673
0
SubDocKey(DocKey([], ["k1"]), [HT{ physical: 5000 }]) -> DEL
1674
0
SubDocKey(DocKey([], ["k1"]), [HT{ physical: 1000 }]) -> {}; ttl: 0.018s
1675
0
SubDocKey(DocKey([], ["k1"]), ["sk0"; HT{ physical: 1000 w: 1 }]) -> "v<"
1676
0
SubDocKey(DocKey([], ["k1"]), ["sk1"; HT{ physical: 2000 }]) -> "v?"
1677
0
SubDocKey(DocKey([], ["k1"]), ["sk2"; HT{ physical: 2000 w: 1 }]) -> "v@"
1678
0
SubDocKey(DocKey([], ["k1"]), ["sk3"; HT{ physical: 2000 w: 2 }]) -> "vA"
1679
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 9000 }]) -> {}; merge flags: 1; ttl: 0.012s
1680
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 8000 }]) -> {}; merge flags: 1; ttl: 0.009s
1681
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 7000 }]) -> {}; merge flags: 1; ttl: 0.021s
1682
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 5000 }]) -> {}
1683
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 1000 }]) -> {}; ttl: 0.021s
1684
0
SubDocKey(DocKey([], ["k2"]), ["sk0"; HT{ physical: 5000 w: 1 }]) -> "vH"
1685
0
SubDocKey(DocKey([], ["k2"]), ["sk0"; HT{ physical: 1000 w: 1 }]) -> "vB"
1686
0
SubDocKey(DocKey([], ["k2"]), ["sk1"; HT{ physical: 6000 }]) -> "vK"
1687
0
SubDocKey(DocKey([], ["k2"]), ["sk1"; HT{ physical: 5000 w: 2 }]) -> "vI"
1688
0
SubDocKey(DocKey([], ["k2"]), ["sk1"; HT{ physical: 2000 }]) -> "vE"
1689
0
SubDocKey(DocKey([], ["k2"]), ["sk2"; HT{ physical: 6000 w: 1 }]) -> "vL"
1690
0
SubDocKey(DocKey([], ["k2"]), ["sk2"; HT{ physical: 5000 w: 3 }]) -> "vJ"
1691
0
SubDocKey(DocKey([], ["k2"]), ["sk2"; HT{ physical: 2000 w: 1 }]) -> "vF"
1692
0
SubDocKey(DocKey([], ["k2"]), ["sk3"; HT{ physical: 6000 w: 2 }]) -> "vM"
1693
0
SubDocKey(DocKey([], ["k2"]), ["sk3"; HT{ physical: 2000 w: 2 }]) -> "vG"
1694
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 10000 }]) -> {}; merge flags: 1
1695
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 9000 }]) -> {}; merge flags: 1; ttl: 0.009s
1696
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 8000 }]) -> {}; merge flags: 1; ttl: 0.018s
1697
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 6000 }]) -> {}
1698
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 5000 }]) -> {}; merge flags: 1; ttl: 0.018s
1699
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 1000 }]) -> {}; ttl: 0.009s
1700
0
SubDocKey(DocKey([], ["k3"]), ["sk0"; HT{ physical: 6000 w: 1 }]) -> "vT"
1701
0
SubDocKey(DocKey([], ["k3"]), ["sk0"; HT{ physical: 1000 w: 1 }]) -> "vN"
1702
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 7000 }]) -> "vW"
1703
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 6000 w: 2 }]) -> "vU"
1704
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 2000 }]) -> "vQ"
1705
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 7000 w: 1 }]) -> "vX"
1706
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 6000 w: 3 }]) -> "vV"
1707
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 2000 w: 1 }]) -> "vR"
1708
0
SubDocKey(DocKey([], ["k3"]), ["sk3"; HT{ physical: 7000 w: 2 }]) -> "vY"
1709
0
SubDocKey(DocKey([], ["k3"]), ["sk3"; HT{ physical: 2000 w: 2 }]) -> "vS"
1710
0
      )#");
1711
0
  FullyCompactHistoryBefore(t[4]);
1712
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
1713
0
      R"#(
1714
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 7000 }]) -> {}; merge flags: 1; ttl: 0.009s
1715
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 6000 }]) -> {}; merge flags: 1; ttl: 0.021s
1716
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 4000 }]) -> {}
1717
0
SubDocKey(DocKey([], ["k0"]), ["sk0"; HT{ physical: 4000 w: 1 }]) -> "v6"
1718
0
SubDocKey(DocKey([], ["k0"]), ["sk1"; HT{ physical: 5000 }]) -> "v9"
1719
0
SubDocKey(DocKey([], ["k0"]), ["sk2"; HT{ physical: 5000 w: 1 }]) -> "v:"
1720
0
SubDocKey(DocKey([], ["k0"]), ["sk3"; HT{ physical: 5000 w: 2 }]) -> "v;"
1721
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 9000 }]) -> {}; merge flags: 1; ttl: 0.012s
1722
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 8000 }]) -> {}; merge flags: 1; ttl: 0.009s
1723
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 7000 }]) -> {}; merge flags: 1; ttl: 0.021s
1724
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 5000 }]) -> {}
1725
0
SubDocKey(DocKey([], ["k2"]), ["sk0"; HT{ physical: 5000 w: 1 }]) -> "vH"
1726
0
SubDocKey(DocKey([], ["k2"]), ["sk1"; HT{ physical: 6000 }]) -> "vK"
1727
0
SubDocKey(DocKey([], ["k2"]), ["sk1"; HT{ physical: 5000 w: 2 }]) -> "vI"
1728
0
SubDocKey(DocKey([], ["k2"]), ["sk2"; HT{ physical: 6000 w: 1 }]) -> "vL"
1729
0
SubDocKey(DocKey([], ["k2"]), ["sk2"; HT{ physical: 5000 w: 3 }]) -> "vJ"
1730
0
SubDocKey(DocKey([], ["k2"]), ["sk3"; HT{ physical: 6000 w: 2 }]) -> "vM"
1731
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 10000 }]) -> {}; merge flags: 1
1732
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 9000 }]) -> {}; merge flags: 1; ttl: 0.009s
1733
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 8000 }]) -> {}; merge flags: 1; ttl: 0.018s
1734
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 6000 }]) -> {}
1735
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 1000 }]) -> {}; ttl: 0.022s
1736
0
SubDocKey(DocKey([], ["k3"]), ["sk0"; HT{ physical: 6000 w: 1 }]) -> "vT"
1737
0
SubDocKey(DocKey([], ["k3"]), ["sk0"; HT{ physical: 1000 w: 1 }]) -> "vN"
1738
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 7000 }]) -> "vW"
1739
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 6000 w: 2 }]) -> "vU"
1740
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 2000 }]) -> "vQ"
1741
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 7000 w: 1 }]) -> "vX"
1742
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 6000 w: 3 }]) -> "vV"
1743
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 2000 w: 1 }]) -> "vR"
1744
0
SubDocKey(DocKey([], ["k3"]), ["sk3"; HT{ physical: 7000 w: 2 }]) -> "vY"
1745
0
SubDocKey(DocKey([], ["k3"]), ["sk3"; HT{ physical: 2000 w: 2 }]) -> "vS"
1746
0
      )#");
1747
0
  FullyCompactHistoryBefore(t[5]);
1748
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
1749
0
      R"#(
1750
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 7000 }]) -> {}; merge flags: 1; ttl: 0.009s
1751
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 4000 }]) -> {}; ttl: 0.023s
1752
0
SubDocKey(DocKey([], ["k0"]), ["sk0"; HT{ physical: 4000 w: 1 }]) -> "v6"
1753
0
SubDocKey(DocKey([], ["k0"]), ["sk1"; HT{ physical: 5000 }]) -> "v9"
1754
0
SubDocKey(DocKey([], ["k0"]), ["sk2"; HT{ physical: 5000 w: 1 }]) -> "v:"
1755
0
SubDocKey(DocKey([], ["k0"]), ["sk3"; HT{ physical: 5000 w: 2 }]) -> "v;"
1756
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 9000 }]) -> {}; merge flags: 1; ttl: 0.012s
1757
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 8000 }]) -> {}; merge flags: 1; ttl: 0.009s
1758
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 7000 }]) -> {}; merge flags: 1; ttl: 0.021s
1759
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 5000 }]) -> {}
1760
0
SubDocKey(DocKey([], ["k2"]), ["sk0"; HT{ physical: 5000 w: 1 }]) -> "vH"
1761
0
SubDocKey(DocKey([], ["k2"]), ["sk1"; HT{ physical: 6000 }]) -> "vK"
1762
0
SubDocKey(DocKey([], ["k2"]), ["sk2"; HT{ physical: 6000 w: 1 }]) -> "vL"
1763
0
SubDocKey(DocKey([], ["k2"]), ["sk3"; HT{ physical: 6000 w: 2 }]) -> "vM"
1764
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 10000 }]) -> {}; merge flags: 1
1765
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 9000 }]) -> {}; merge flags: 1; ttl: 0.009s
1766
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 8000 }]) -> {}; merge flags: 1; ttl: 0.018s
1767
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 6000 }]) -> {}
1768
0
SubDocKey(DocKey([], ["k3"]), ["sk0"; HT{ physical: 6000 w: 1 }]) -> "vT"
1769
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 7000 }]) -> "vW"
1770
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 6000 w: 2 }]) -> "vU"
1771
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 7000 w: 1 }]) -> "vX"
1772
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 6000 w: 3 }]) -> "vV"
1773
0
SubDocKey(DocKey([], ["k3"]), ["sk3"; HT{ physical: 7000 w: 2 }]) -> "vY"
1774
0
      )#");
1775
0
  FullyCompactHistoryBefore(t[6]);
1776
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
1777
0
      R"#(
1778
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 4000 }]) -> {}; ttl: 0.012s
1779
0
SubDocKey(DocKey([], ["k0"]), ["sk0"; HT{ physical: 4000 w: 1 }]) -> "v6"
1780
0
SubDocKey(DocKey([], ["k0"]), ["sk1"; HT{ physical: 5000 }]) -> "v9"
1781
0
SubDocKey(DocKey([], ["k0"]), ["sk2"; HT{ physical: 5000 w: 1 }]) -> "v:"
1782
0
SubDocKey(DocKey([], ["k0"]), ["sk3"; HT{ physical: 5000 w: 2 }]) -> "v;"
1783
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 9000 }]) -> {}; merge flags: 1; ttl: 0.012s
1784
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 8000 }]) -> {}; merge flags: 1; ttl: 0.009s
1785
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 5000 }]) -> {}; ttl: 0.023s
1786
0
SubDocKey(DocKey([], ["k2"]), ["sk0"; HT{ physical: 5000 w: 1 }]) -> "vH"
1787
0
SubDocKey(DocKey([], ["k2"]), ["sk1"; HT{ physical: 6000 }]) -> "vK"
1788
0
SubDocKey(DocKey([], ["k2"]), ["sk2"; HT{ physical: 6000 w: 1 }]) -> "vL"
1789
0
SubDocKey(DocKey([], ["k2"]), ["sk3"; HT{ physical: 6000 w: 2 }]) -> "vM"
1790
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 10000 }]) -> {}; merge flags: 1
1791
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 9000 }]) -> {}; merge flags: 1; ttl: 0.009s
1792
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 8000 }]) -> {}; merge flags: 1; ttl: 0.018s
1793
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 6000 }]) -> {}
1794
0
SubDocKey(DocKey([], ["k3"]), ["sk0"; HT{ physical: 6000 w: 1 }]) -> "vT"
1795
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 7000 }]) -> "vW"
1796
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 7000 w: 1 }]) -> "vX"
1797
0
SubDocKey(DocKey([], ["k3"]), ["sk3"; HT{ physical: 7000 w: 2 }]) -> "vY"
1798
0
      )#");
1799
0
  FullyCompactHistoryBefore(t[7]);
1800
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
1801
0
      R"#(
1802
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 4000 }]) -> {}; ttl: 0.012s
1803
0
SubDocKey(DocKey([], ["k0"]), ["sk0"; HT{ physical: 4000 w: 1 }]) -> "v6"
1804
0
SubDocKey(DocKey([], ["k0"]), ["sk1"; HT{ physical: 5000 }]) -> "v9"
1805
0
SubDocKey(DocKey([], ["k0"]), ["sk2"; HT{ physical: 5000 w: 1 }]) -> "v:"
1806
0
SubDocKey(DocKey([], ["k0"]), ["sk3"; HT{ physical: 5000 w: 2 }]) -> "v;"
1807
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 9000 }]) -> {}; merge flags: 1; ttl: 0.012s
1808
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 5000 }]) -> {}; ttl: 0.012s
1809
0
SubDocKey(DocKey([], ["k2"]), ["sk0"; HT{ physical: 5000 w: 1 }]) -> "vH"
1810
0
SubDocKey(DocKey([], ["k2"]), ["sk1"; HT{ physical: 6000 }]) -> "vK"
1811
0
SubDocKey(DocKey([], ["k2"]), ["sk2"; HT{ physical: 6000 w: 1 }]) -> "vL"
1812
0
SubDocKey(DocKey([], ["k2"]), ["sk3"; HT{ physical: 6000 w: 2 }]) -> "vM"
1813
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 10000 }]) -> {}; merge flags: 1
1814
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 9000 }]) -> {}; merge flags: 1; ttl: 0.009s
1815
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 6000 }]) -> {}; ttl: 0.020s
1816
0
SubDocKey(DocKey([], ["k3"]), ["sk0"; HT{ physical: 6000 w: 1 }]) -> "vT"
1817
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 7000 }]) -> "vW"
1818
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 7000 w: 1 }]) -> "vX"
1819
0
SubDocKey(DocKey([], ["k3"]), ["sk3"; HT{ physical: 7000 w: 2 }]) -> "vY"
1820
0
      )#");
1821
0
  FullyCompactHistoryBefore(t[8]);
1822
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
1823
0
      R"#(
1824
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 4000 }]) -> {}; ttl: 0.012s
1825
0
SubDocKey(DocKey([], ["k0"]), ["sk0"; HT{ physical: 4000 w: 1 }]) -> "v6"
1826
0
SubDocKey(DocKey([], ["k0"]), ["sk1"; HT{ physical: 5000 }]) -> "v9"
1827
0
SubDocKey(DocKey([], ["k0"]), ["sk2"; HT{ physical: 5000 w: 1 }]) -> "v:"
1828
0
SubDocKey(DocKey([], ["k0"]), ["sk3"; HT{ physical: 5000 w: 2 }]) -> "v;"
1829
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 5000 }]) -> {}; ttl: 0.016s
1830
0
SubDocKey(DocKey([], ["k2"]), ["sk0"; HT{ physical: 5000 w: 1 }]) -> "vH"
1831
0
SubDocKey(DocKey([], ["k2"]), ["sk1"; HT{ physical: 6000 }]) -> "vK"
1832
0
SubDocKey(DocKey([], ["k2"]), ["sk2"; HT{ physical: 6000 w: 1 }]) -> "vL"
1833
0
SubDocKey(DocKey([], ["k2"]), ["sk3"; HT{ physical: 6000 w: 2 }]) -> "vM"
1834
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 10000 }]) -> {}; merge flags: 1
1835
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 6000 }]) -> {}; ttl: 0.012s
1836
0
SubDocKey(DocKey([], ["k3"]), ["sk0"; HT{ physical: 6000 w: 1 }]) -> "vT"
1837
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 7000 }]) -> "vW"
1838
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 7000 w: 1 }]) -> "vX"
1839
0
SubDocKey(DocKey([], ["k3"]), ["sk3"; HT{ physical: 7000 w: 2 }]) -> "vY"
1840
0
      )#");
1841
0
  FullyCompactHistoryBefore(t[9]);
1842
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
1843
0
      R"#(
1844
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 4000 }]) -> {}; ttl: 0.012s
1845
0
SubDocKey(DocKey([], ["k0"]), ["sk0"; HT{ physical: 4000 w: 1 }]) -> "v6"
1846
0
SubDocKey(DocKey([], ["k0"]), ["sk1"; HT{ physical: 5000 }]) -> "v9"
1847
0
SubDocKey(DocKey([], ["k0"]), ["sk2"; HT{ physical: 5000 w: 1 }]) -> "v:"
1848
0
SubDocKey(DocKey([], ["k0"]), ["sk3"; HT{ physical: 5000 w: 2 }]) -> "v;"
1849
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 5000 }]) -> {}; ttl: 0.016s
1850
0
SubDocKey(DocKey([], ["k2"]), ["sk0"; HT{ physical: 5000 w: 1 }]) -> "vH"
1851
0
SubDocKey(DocKey([], ["k2"]), ["sk1"; HT{ physical: 6000 }]) -> "vK"
1852
0
SubDocKey(DocKey([], ["k2"]), ["sk2"; HT{ physical: 6000 w: 1 }]) -> "vL"
1853
0
SubDocKey(DocKey([], ["k2"]), ["sk3"; HT{ physical: 6000 w: 2 }]) -> "vM"
1854
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 6000 }]) -> {}
1855
0
SubDocKey(DocKey([], ["k3"]), ["sk0"; HT{ physical: 6000 w: 1 }]) -> "vT"
1856
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 7000 }]) -> "vW"
1857
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 7000 w: 1 }]) -> "vX"
1858
0
SubDocKey(DocKey([], ["k3"]), ["sk3"; HT{ physical: 7000 w: 2 }]) -> "vY"
1859
0
      )#");
1860
0
  FullyCompactHistoryBefore(t[16]);
1861
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
1862
0
      R"#(
1863
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 5000 }]) -> {}; ttl: 0.016s
1864
0
SubDocKey(DocKey([], ["k2"]), ["sk0"; HT{ physical: 5000 w: 1 }]) -> "vH"
1865
0
SubDocKey(DocKey([], ["k2"]), ["sk1"; HT{ physical: 6000 }]) -> "vK"
1866
0
SubDocKey(DocKey([], ["k2"]), ["sk2"; HT{ physical: 6000 w: 1 }]) -> "vL"
1867
0
SubDocKey(DocKey([], ["k2"]), ["sk3"; HT{ physical: 6000 w: 2 }]) -> "vM"
1868
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 6000 }]) -> {}
1869
0
SubDocKey(DocKey([], ["k3"]), ["sk0"; HT{ physical: 6000 w: 1 }]) -> "vT"
1870
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 7000 }]) -> "vW"
1871
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 7000 w: 1 }]) -> "vX"
1872
0
SubDocKey(DocKey([], ["k3"]), ["sk3"; HT{ physical: 7000 w: 2 }]) -> "vY"
1873
0
      )#");
1874
0
  FullyCompactHistoryBefore(t[21]);
1875
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
1876
0
      R"#(
1877
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 6000 }]) -> {}
1878
0
SubDocKey(DocKey([], ["k3"]), ["sk0"; HT{ physical: 6000 w: 1 }]) -> "vT"
1879
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 7000 }]) -> "vW"
1880
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 7000 w: 1 }]) -> "vX"
1881
0
SubDocKey(DocKey([], ["k3"]), ["sk3"; HT{ physical: 7000 w: 2 }]) -> "vY"
1882
0
      )#");
1883
0
  FullyCompactHistoryBefore(t[34]);
1884
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
1885
0
      R"#(
1886
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 6000 }]) -> {}
1887
0
SubDocKey(DocKey([], ["k3"]), ["sk0"; HT{ physical: 6000 w: 1 }]) -> "vT"
1888
0
SubDocKey(DocKey([], ["k3"]), ["sk1"; HT{ physical: 7000 }]) -> "vW"
1889
0
SubDocKey(DocKey([], ["k3"]), ["sk2"; HT{ physical: 7000 w: 1 }]) -> "vX"
1890
0
SubDocKey(DocKey([], ["k3"]), ["sk3"; HT{ physical: 7000 w: 2 }]) -> "vY"
1891
0
      )#");
1892
0
}
1893
1894
// Basic compaction testing for TTL in Redis.
1895
0
TEST_P(DocDBTestWrapper, RedisTTLCompactionTest) {
1896
0
  const MonoDelta one_ms = 1ms;
1897
0
  string key_string = "k0";
1898
0
  string val_string = "v0";
1899
0
  int n_times = 20;
1900
0
  vector<HybridTime> t(n_times);
1901
0
  t[0] = 1000_usec_ht;
1902
0
  for (int i = 1; i < n_times; ++i) {
1903
0
    t[i] = server::HybridClock::AddPhysicalTimeToHybridTime(t[i-1], one_ms);
1904
0
  }
1905
  // Compact at t10
1906
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(), // k0
1907
0
                         Value(PrimitiveValue(val_string), 4ms), t[2]));
1908
0
  val_string[1]++;
1909
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
1910
0
                         Value(PrimitiveValue(val_string), 3ms), t[0]));
1911
0
  val_string[1]++;
1912
0
  key_string[1]++;
1913
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(), // k1
1914
0
                         Value(PrimitiveValue(val_string), 8ms), t[3]));
1915
0
  val_string[1]++;
1916
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
1917
0
                         Value(PrimitiveValue(val_string), 1ms), t[5]));
1918
0
  val_string[1]++;
1919
0
  key_string[1]++;
1920
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(), // k2
1921
0
                         Value(PrimitiveValue(val_string), 3ms), t[5]));
1922
0
  val_string[1]++;
1923
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
1924
0
                         Value(PrimitiveValue(val_string), 5ms), t[7]));
1925
0
  val_string[1]++;
1926
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
1927
0
                         Value(PrimitiveValue(val_string), Value::kMaxTtl), t[11]));
1928
0
  key_string[1]++;
1929
0
  val_string[1]++;
1930
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(), // k3
1931
0
                         Value(PrimitiveValue(val_string), 4ms), t[1]));
1932
0
  val_string[1]++;
1933
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
1934
0
                         Value(PrimitiveValue(val_string), Value::kMaxTtl), t[4]));
1935
0
  val_string[1]++;
1936
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
1937
0
                         Value(PrimitiveValue(val_string), 1ms), t[13]));
1938
0
  val_string[1]++;
1939
0
  key_string[1]++;
1940
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(), // k4
1941
0
                         Value(PrimitiveValue::kTombstone), t[12]));
1942
0
  key_string[1]++;
1943
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(), // k5
1944
0
                         Value(PrimitiveValue(val_string), 9ms), t[8]));
1945
0
  val_string[1]++;
1946
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
1947
0
                         Value(PrimitiveValue::kTombstone), t[9]));
1948
0
  key_string[1]++;
1949
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(), // k6
1950
0
                         Value(PrimitiveValue(val_string), 9ms), t[8]));
1951
0
  val_string[1]++;
1952
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
1953
0
                         Value(PrimitiveValue::kTombstone), t[6]));
1954
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
1955
0
      R"#(
1956
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 3000 }]) -> "v0"; ttl: 0.004s
1957
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 1000 }]) -> "v1"; ttl: 0.003s
1958
0
SubDocKey(DocKey([], ["k1"]), [HT{ physical: 6000 }]) -> "v3"; ttl: 0.001s
1959
0
SubDocKey(DocKey([], ["k1"]), [HT{ physical: 4000 }]) -> "v2"; ttl: 0.008s
1960
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 12000 }]) -> "v6"
1961
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 8000 }]) -> "v5"; ttl: 0.005s
1962
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 6000 }]) -> "v4"; ttl: 0.003s
1963
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 14000 }]) -> "v9"; ttl: 0.001s
1964
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 5000 }]) -> "v8"
1965
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 2000 }]) -> "v7"; ttl: 0.004s
1966
0
SubDocKey(DocKey([], ["k4"]), [HT{ physical: 13000 }]) -> DEL
1967
0
SubDocKey(DocKey([], ["k5"]), [HT{ physical: 10000 }]) -> DEL
1968
0
SubDocKey(DocKey([], ["k5"]), [HT{ physical: 9000 }]) -> "v:"; ttl: 0.009s
1969
0
SubDocKey(DocKey([], ["k6"]), [HT{ physical: 9000 }]) -> "v;"; ttl: 0.009s
1970
0
SubDocKey(DocKey([], ["k6"]), [HT{ physical: 7000 }]) -> DEL
1971
0
      )#");
1972
0
  FullyCompactHistoryBefore(t[10]);
1973
1974
  // Major compaction
1975
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
1976
0
      R"#(
1977
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 12000 }]) -> "v6"
1978
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 8000 }]) -> "v5"; ttl: 0.005s
1979
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 14000 }]) -> "v9"; ttl: 0.001s
1980
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 5000 }]) -> "v8"
1981
0
SubDocKey(DocKey([], ["k4"]), [HT{ physical: 13000 }]) -> DEL
1982
0
SubDocKey(DocKey([], ["k6"]), [HT{ physical: 9000 }]) -> "v;"; ttl: 0.009s
1983
0
      )#");
1984
1985
0
  FullyCompactHistoryBefore(t[14]);
1986
1987
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
1988
0
      R"#(
1989
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 12000 }]) -> "v6"
1990
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 14000 }]) -> "v9"; ttl: 0.001s
1991
0
SubDocKey(DocKey([], ["k6"]), [HT{ physical: 9000 }]) -> "v;"; ttl: 0.009s
1992
0
      )#");
1993
1994
0
  FullyCompactHistoryBefore(t[19]);
1995
1996
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
1997
0
      R"#(
1998
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 12000 }]) -> "v6"
1999
0
      )#");
2000
2001
0
  key_string = "k0";
2002
0
  val_string = "v0";
2003
  // Checking TTL rows now
2004
0
  ASSERT_OK(SetPrimitive(
2005
0
      DocKey(PrimitiveValues(key_string)).Encode(), // k0
2006
0
      Value(PrimitiveValue(ValueType::kString), 6ms, Value::kInvalidUserTimestamp, Value::kTtlFlag),
2007
0
      t[5]));
2008
0
  ASSERT_OK(SetPrimitive(
2009
0
      DocKey(PrimitiveValues(key_string)).Encode(),
2010
0
      Value(PrimitiveValue(ValueType::kString), 4ms, Value::kInvalidUserTimestamp, Value::kTtlFlag),
2011
0
      t[2]));
2012
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
2013
0
                         Value(PrimitiveValue(val_string), 3ms), t[0]));
2014
0
  val_string[1]++;
2015
0
  key_string[1]++;
2016
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(), // k1
2017
0
                         Value(PrimitiveValue(val_string), 8ms), t[3]));
2018
0
  val_string[1]++;
2019
0
  ASSERT_OK(SetPrimitive(
2020
0
      DocKey(PrimitiveValues(key_string)).Encode(),
2021
0
      Value(PrimitiveValue(ValueType::kString), 3ms, Value::kInvalidUserTimestamp, Value::kTtlFlag),
2022
0
      t[5]));
2023
0
  key_string[1]++;
2024
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(), // k2
2025
0
                         Value(PrimitiveValue(val_string), 3ms), t[5]));
2026
0
  val_string[1]++;
2027
0
  ASSERT_OK(SetPrimitive(
2028
0
      DocKey(PrimitiveValues(key_string)).Encode(),
2029
0
      Value(PrimitiveValue(ValueType::kString), 5ms, Value::kInvalidUserTimestamp, Value::kTtlFlag),
2030
0
      t[7]));
2031
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
2032
0
      Value(PrimitiveValue(ValueType::kString), Value::kMaxTtl, Value::kInvalidUserTimestamp,
2033
0
      Value::kTtlFlag), t[11]));
2034
0
  key_string[1]++;
2035
0
  ASSERT_OK(SetPrimitive( // k3
2036
0
  DocKey(PrimitiveValues(key_string)).Encode(),
2037
0
                         Value(PrimitiveValue(val_string), 4ms), t[1]));
2038
0
  val_string[1]++;
2039
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
2040
0
      Value(PrimitiveValue(ValueType::kString), Value::kMaxTtl, Value::kInvalidUserTimestamp,
2041
0
      Value::kTtlFlag), t[4]));
2042
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
2043
0
                         Value(PrimitiveValue(val_string), 1ms), t[13]));
2044
0
  val_string[1]++;
2045
0
  key_string[1]++;
2046
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(), // k4
2047
0
                         Value(PrimitiveValue::kTombstone), t[12]));
2048
0
  key_string[1]++;
2049
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(), // k5
2050
0
                         Value(PrimitiveValue(val_string), 9ms), t[8]));
2051
0
  val_string[1]++;
2052
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
2053
0
                         Value(PrimitiveValue::kTombstone), t[9]));
2054
0
  key_string[1]++;
2055
0
  ASSERT_OK(SetPrimitive( // k6
2056
0
      DocKey(PrimitiveValues(key_string)).Encode(),
2057
0
      Value(PrimitiveValue(ValueType::kString), 4ms, Value::kInvalidUserTimestamp, Value::kTtlFlag),
2058
0
      t[10]));
2059
2060
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
2061
0
                         Value(PrimitiveValue(val_string), 9ms), t[8]));
2062
0
  val_string[1]++;
2063
0
  ASSERT_OK(SetPrimitive(DocKey(PrimitiveValues(key_string)).Encode(),
2064
0
                         Value(PrimitiveValue::kTombstone), t[6]));
2065
2066
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
2067
0
      R"#(
2068
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 6000 }]) -> ""; merge flags: 1; ttl: 0.006s
2069
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 3000 }]) -> ""; merge flags: 1; ttl: 0.004s
2070
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 1000 }]) -> "v0"; ttl: 0.003s
2071
0
SubDocKey(DocKey([], ["k1"]), [HT{ physical: 6000 }]) -> ""; merge flags: 1; ttl: 0.003s
2072
0
SubDocKey(DocKey([], ["k1"]), [HT{ physical: 4000 }]) -> "v1"; ttl: 0.008s
2073
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 12000 }]) -> ""; merge flags: 1
2074
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 8000 }]) -> ""; merge flags: 1; ttl: 0.005s
2075
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 6000 }]) -> "v2"; ttl: 0.003s
2076
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 14000 }]) -> "v4"; ttl: 0.001s
2077
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 5000 }]) -> ""; merge flags: 1
2078
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 2000 }]) -> "v3"; ttl: 0.004s
2079
0
SubDocKey(DocKey([], ["k4"]), [HT{ physical: 13000 }]) -> DEL
2080
0
SubDocKey(DocKey([], ["k5"]), [HT{ physical: 10000 }]) -> DEL
2081
0
SubDocKey(DocKey([], ["k5"]), [HT{ physical: 9000 }]) -> "v5"; ttl: 0.009s
2082
0
SubDocKey(DocKey([], ["k6"]), [HT{ physical: 11000 }]) -> ""; merge flags: 1; ttl: 0.004s
2083
0
SubDocKey(DocKey([], ["k6"]), [HT{ physical: 9000 }]) -> "v6"; ttl: 0.009s
2084
0
SubDocKey(DocKey([], ["k6"]), [HT{ physical: 7000 }]) -> DEL
2085
0
      )#");
2086
0
  FullyCompactHistoryBefore(t[9]);
2087
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
2088
0
      R"#(
2089
0
SubDocKey(DocKey([], ["k0"]), [HT{ physical: 1000 }]) -> "v0"; ttl: 0.011s
2090
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 12000 }]) -> ""; merge flags: 1
2091
0
SubDocKey(DocKey([], ["k2"]), [HT{ physical: 6000 }]) -> "v2"; ttl: 0.007s
2092
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 14000 }]) -> "v4"; ttl: 0.001s
2093
0
SubDocKey(DocKey([], ["k3"]), [HT{ physical: 2000 }]) -> "v3"
2094
0
SubDocKey(DocKey([], ["k4"]), [HT{ physical: 13000 }]) -> DEL
2095
0
SubDocKey(DocKey([], ["k6"]), [HT{ physical: 11000 }]) -> ""; merge flags: 1; ttl: 0.004s
2096
0
SubDocKey(DocKey([], ["k6"]), [HT{ physical: 9000 }]) -> "v6"; ttl: 0.009s
2097
0
      )#");
2098
0
}
2099
2100
0
TEST_P(DocDBTestWrapper, TTLCompactionTest) {
2101
0
  const DocKey doc_key(PrimitiveValues("k1"));
2102
0
  const MonoDelta one_ms = 1ms;
2103
0
  const HybridTime t0 = 1000_usec_ht;
2104
0
  HybridTime t1 = server::HybridClock::AddPhysicalTimeToHybridTime(t0, one_ms);
2105
0
  HybridTime t2 = server::HybridClock::AddPhysicalTimeToHybridTime(t1, one_ms);
2106
0
  HybridTime t3 = server::HybridClock::AddPhysicalTimeToHybridTime(t2, one_ms);
2107
0
  HybridTime t4 = server::HybridClock::AddPhysicalTimeToHybridTime(t3, one_ms);
2108
0
  KeyBytes encoded_doc_key(doc_key.Encode());
2109
  // First row.
2110
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key, PrimitiveValue::kLivenessColumn),
2111
0
                         Value(PrimitiveValue(), 1ms), t0));
2112
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key, PrimitiveValue(ColumnId(0))),
2113
0
      Value(PrimitiveValue("v1"), 2ms), t0));
2114
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key, PrimitiveValue(ColumnId(1))),
2115
0
      Value(PrimitiveValue("v2"), 3ms), t0));
2116
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key, PrimitiveValue(ColumnId(2))),
2117
0
      Value(PrimitiveValue("v3"), Value::kMaxTtl), t0));
2118
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key, PrimitiveValue(ColumnId(3))),
2119
0
      Value(PrimitiveValue("v4"), Value::kMaxTtl), t0));
2120
  // Second row.
2121
0
  const DocKey doc_key_row2(PrimitiveValues("k2"));
2122
0
  KeyBytes encoded_doc_key_row2(doc_key_row2.Encode());
2123
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key_row2, PrimitiveValue::kLivenessColumn),
2124
0
                         Value(PrimitiveValue(), 3ms), t0));
2125
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key_row2, PrimitiveValue(ColumnId(0))),
2126
0
      Value(PrimitiveValue("v1"), 2ms), t0));
2127
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key_row2, PrimitiveValue(ColumnId(1))),
2128
0
      Value(PrimitiveValue("v2"), 1ms), t0));
2129
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
2130
0
      R"#(
2131
0
SubDocKey(DocKey([], ["k1"]), [SystemColumnId(0); HT{ physical: 1000 }]) -> null; ttl: 0.001s
2132
0
SubDocKey(DocKey([], ["k1"]), [ColumnId(0); HT{ physical: 1000 }]) -> "v1"; ttl: 0.002s
2133
0
SubDocKey(DocKey([], ["k1"]), [ColumnId(1); HT{ physical: 1000 }]) -> "v2"; ttl: 0.003s
2134
0
SubDocKey(DocKey([], ["k1"]), [ColumnId(2); HT{ physical: 1000 }]) -> "v3"
2135
0
SubDocKey(DocKey([], ["k1"]), [ColumnId(3); HT{ physical: 1000 }]) -> "v4"
2136
0
SubDocKey(DocKey([], ["k2"]), [SystemColumnId(0); HT{ physical: 1000 }]) -> null; ttl: 0.003s
2137
0
SubDocKey(DocKey([], ["k2"]), [ColumnId(0); HT{ physical: 1000 }]) -> "v1"; ttl: 0.002s
2138
0
SubDocKey(DocKey([], ["k2"]), [ColumnId(1); HT{ physical: 1000 }]) -> "v2"; ttl: 0.001s
2139
0
      )#");
2140
2141
0
  FullyCompactHistoryBefore(t2);
2142
2143
  // Liveness column is gone for row1, v2 gone for row2.
2144
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
2145
0
      R"#(
2146
0
SubDocKey(DocKey([], ["k1"]), [ColumnId(0); HT{ physical: 1000 }]) -> "v1"; ttl: 0.002s
2147
0
SubDocKey(DocKey([], ["k1"]), [ColumnId(1); HT{ physical: 1000 }]) -> "v2"; ttl: 0.003s
2148
0
SubDocKey(DocKey([], ["k1"]), [ColumnId(2); HT{ physical: 1000 }]) -> "v3"
2149
0
SubDocKey(DocKey([], ["k1"]), [ColumnId(3); HT{ physical: 1000 }]) -> "v4"
2150
0
SubDocKey(DocKey([], ["k2"]), [SystemColumnId(0); HT{ physical: 1000 }]) -> null; ttl: 0.003s
2151
0
SubDocKey(DocKey([], ["k2"]), [ColumnId(0); HT{ physical: 1000 }]) -> "v1"; ttl: 0.002s
2152
0
      )#");
2153
2154
0
  FullyCompactHistoryBefore(t3);
2155
2156
  // v1 is gone.
2157
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
2158
0
      R"#(
2159
0
SubDocKey(DocKey([], ["k1"]), [ColumnId(1); HT{ physical: 1000 }]) -> "v2"; ttl: 0.003s
2160
0
SubDocKey(DocKey([], ["k1"]), [ColumnId(2); HT{ physical: 1000 }]) -> "v3"
2161
0
SubDocKey(DocKey([], ["k1"]), [ColumnId(3); HT{ physical: 1000 }]) -> "v4"
2162
0
SubDocKey(DocKey([], ["k2"]), [SystemColumnId(0); HT{ physical: 1000 }]) -> null; ttl: 0.003s
2163
0
      )#");
2164
2165
0
  FullyCompactHistoryBefore(t4);
2166
  // v2 is gone for row 1, liveness column gone for row 2.
2167
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
2168
0
      R"#(
2169
0
SubDocKey(DocKey([], ["k1"]), [ColumnId(2); HT{ physical: 1000 }]) -> "v3"
2170
0
SubDocKey(DocKey([], ["k1"]), [ColumnId(3); HT{ physical: 1000 }]) -> "v4"
2171
0
      )#");
2172
2173
  // Delete values.
2174
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key, PrimitiveValue(ColumnId(2))),
2175
0
      Value(PrimitiveValue::kTombstone, Value::kMaxTtl), t1));
2176
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key, PrimitiveValue(ColumnId(3))),
2177
0
      Value(PrimitiveValue::kTombstone, Value::kMaxTtl), t1));
2178
2179
  // Values are now marked with tombstones.
2180
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
2181
0
      R"#(
2182
0
SubDocKey(DocKey([], ["k1"]), [ColumnId(2); HT{ physical: 2000 }]) -> DEL
2183
0
SubDocKey(DocKey([], ["k1"]), [ColumnId(2); HT{ physical: 1000 }]) -> "v3"
2184
0
SubDocKey(DocKey([], ["k1"]), [ColumnId(3); HT{ physical: 2000 }]) -> DEL
2185
0
SubDocKey(DocKey([], ["k1"]), [ColumnId(3); HT{ physical: 1000 }]) -> "v4"
2186
0
      )#");
2187
2188
0
  FullyCompactHistoryBefore(t0);
2189
  // Nothing is removed.
2190
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
2191
0
      R"#(
2192
0
SubDocKey(DocKey([], ["k1"]), [ColumnId(2); HT{ physical: 2000 }]) -> DEL
2193
0
SubDocKey(DocKey([], ["k1"]), [ColumnId(2); HT{ physical: 1000 }]) -> "v3"
2194
0
SubDocKey(DocKey([], ["k1"]), [ColumnId(3); HT{ physical: 2000 }]) -> DEL
2195
0
SubDocKey(DocKey([], ["k1"]), [ColumnId(3); HT{ physical: 1000 }]) -> "v4"
2196
0
      )#");
2197
2198
0
  FullyCompactHistoryBefore(t1);
2199
  // Next compactions removes everything.
2200
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
2201
0
      R"#(
2202
0
      )#");
2203
0
}
2204
2205
0
TEST_P(DocDBTestWrapper, TableTTLCompactionTest) {
2206
0
  const DocKey doc_key(PrimitiveValues("k1"));
2207
0
  const HybridTime t1 = 1000_usec_ht;
2208
0
  const HybridTime t2 = 2000_usec_ht;
2209
0
  const HybridTime t3 = 3000_usec_ht;
2210
0
  const HybridTime t4 = 4000_usec_ht;
2211
0
  const HybridTime t5 = 5000_usec_ht;
2212
0
  KeyBytes encoded_doc_key(doc_key.Encode());
2213
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key, PrimitiveValue("s1")),
2214
0
      Value(PrimitiveValue("v1"), 1ms), t1));
2215
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key, PrimitiveValue("s2")),
2216
0
      Value(PrimitiveValue("v2"), Value::kMaxTtl), t1));
2217
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key, PrimitiveValue("s3")),
2218
0
      Value(PrimitiveValue("v3"), 0ms), t2));
2219
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key, PrimitiveValue("s4")),
2220
0
      Value(PrimitiveValue("v4"), 3ms), t1));
2221
  // Note: HT{ physical: 1000 } + 1ms = HT{ physical: 4097000 }
2222
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
2223
0
      SubDocKey(DocKey([], ["k1"]), ["s1"; HT{ physical: 1000 }]) -> "v1"; ttl: 0.001s
2224
0
      SubDocKey(DocKey([], ["k1"]), ["s2"; HT{ physical: 1000 }]) -> "v2"
2225
0
      SubDocKey(DocKey([], ["k1"]), ["s3"; HT{ physical: 2000 }]) -> "v3"; ttl: 0.000s
2226
0
      SubDocKey(DocKey([], ["k1"]), ["s4"; HT{ physical: 1000 }]) -> "v4"; ttl: 0.003s
2227
0
      )#");
2228
0
  SetTableTTL(2);
2229
0
  FullyCompactHistoryBefore(t3);
2230
2231
  // v1 compacted due to column level ttl.
2232
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
2233
0
      R"#(
2234
0
SubDocKey(DocKey([], ["k1"]), ["s2"; HT{ physical: 1000 }]) -> "v2"
2235
0
SubDocKey(DocKey([], ["k1"]), ["s3"; HT{ physical: 2000 }]) -> "v3"; ttl: 0.000s
2236
0
SubDocKey(DocKey([], ["k1"]), ["s4"; HT{ physical: 1000 }]) -> "v4"; ttl: 0.003s
2237
0
      )#");
2238
2239
0
  FullyCompactHistoryBefore(t4);
2240
  // v2 compacted due to table level ttl.
2241
  // init marker compacted due to table level ttl.
2242
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
2243
0
      R"#(
2244
0
SubDocKey(DocKey([], ["k1"]), ["s3"; HT{ physical: 2000 }]) -> "v3"; ttl: 0.000s
2245
0
SubDocKey(DocKey([], ["k1"]), ["s4"; HT{ physical: 1000 }]) -> "v4"; ttl: 0.003s
2246
0
      )#");
2247
2248
0
  FullyCompactHistoryBefore(t5);
2249
  // v4 compacted due to column level ttl.
2250
  // v3 stays forever due to ttl being set to 0.
2251
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
2252
0
      R"#(
2253
0
SubDocKey(DocKey([], ["k1"]), ["s3"; HT{ physical: 2000 }]) -> "v3"; ttl: 0.000s
2254
0
      )#");
2255
0
}
2256
2257
// Test table tombstones for colocated tables.
2258
0
TEST_P(DocDBTestWrapper, TableTombstoneCompaction) {
2259
0
  constexpr PgTableOid pgtable_id(0x4001);
2260
0
  HybridTime t = 1000_usec_ht;
2261
2262
  // Simulate SQL:
2263
  //   INSERT INTO t VALUES ("r1"), ("r2"), ("r3");
2264
0
  for (int i = 1; i <= 3; ++i) {
2265
0
    DocKey doc_key;
2266
0
    std::string range_key_str = Format("r$0", i);
2267
2268
0
    doc_key.set_pgtable_id(pgtable_id);
2269
0
    doc_key.ResizeRangeComponents(1);
2270
0
    doc_key.SetRangeComponent(PrimitiveValue(range_key_str), 0 /* idx */);
2271
0
    ASSERT_OK(SetPrimitive(
2272
0
        DocPath(doc_key.Encode(), PrimitiveValue::kLivenessColumn),
2273
0
        Value(PrimitiveValue()),
2274
0
        t));
2275
0
    t = server::HybridClock::AddPhysicalTimeToHybridTime(t, 1ms);
2276
0
  }
2277
0
  ASSERT_OK(FlushRocksDbAndWait());
2278
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
2279
0
SubDocKey(DocKey(PgTableId=16385, [], ["r1"]), [SystemColumnId(0); HT{ physical: 1000 }]) -> null
2280
0
SubDocKey(DocKey(PgTableId=16385, [], ["r2"]), [SystemColumnId(0); HT{ physical: 2000 }]) -> null
2281
0
SubDocKey(DocKey(PgTableId=16385, [], ["r3"]), [SystemColumnId(0); HT{ physical: 3000 }]) -> null
2282
0
      )#");
2283
2284
  // Simulate SQL (set table tombstone):
2285
  //   TRUNCATE TABLE t;
2286
0
  {
2287
0
    DocKey doc_key(pgtable_id);
2288
0
    ASSERT_OK(SetPrimitive(
2289
0
        DocPath(doc_key.Encode()),
2290
0
        Value(PrimitiveValue::kTombstone),
2291
0
        t));
2292
0
    t = server::HybridClock::AddPhysicalTimeToHybridTime(t, 1ms);
2293
0
  }
2294
0
  ASSERT_OK(FlushRocksDbAndWait());
2295
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
2296
0
SubDocKey(DocKey(PgTableId=16385, [], []), [HT{ physical: 4000 }]) -> DEL
2297
0
SubDocKey(DocKey(PgTableId=16385, [], ["r1"]), [SystemColumnId(0); HT{ physical: 1000 }]) -> null
2298
0
SubDocKey(DocKey(PgTableId=16385, [], ["r2"]), [SystemColumnId(0); HT{ physical: 2000 }]) -> null
2299
0
SubDocKey(DocKey(PgTableId=16385, [], ["r3"]), [SystemColumnId(0); HT{ physical: 3000 }]) -> null
2300
0
      )#");
2301
2302
  // Simulate SQL:
2303
  //  INSERT INTO t VALUES ("r1"), ("r2");
2304
0
  for (int i = 1; i <= 2; ++i) {
2305
0
    DocKey doc_key;
2306
0
    std::string range_key_str = Format("r$0", i);
2307
2308
0
    doc_key.set_pgtable_id(pgtable_id);
2309
0
    doc_key.ResizeRangeComponents(1);
2310
0
    doc_key.SetRangeComponent(PrimitiveValue(range_key_str), 0 /* idx */);
2311
0
    ASSERT_OK(SetPrimitive(
2312
0
        DocPath(doc_key.Encode(), PrimitiveValue::kLivenessColumn),
2313
0
        Value(PrimitiveValue()),
2314
0
        t));
2315
0
    t = server::HybridClock::AddPhysicalTimeToHybridTime(t, 1ms);
2316
0
  }
2317
0
  ASSERT_OK(FlushRocksDbAndWait());
2318
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
2319
0
SubDocKey(DocKey(PgTableId=16385, [], []), [HT{ physical: 4000 }]) -> DEL
2320
0
SubDocKey(DocKey(PgTableId=16385, [], ["r1"]), [SystemColumnId(0); HT{ physical: 5000 }]) -> null
2321
0
SubDocKey(DocKey(PgTableId=16385, [], ["r1"]), [SystemColumnId(0); HT{ physical: 1000 }]) -> null
2322
0
SubDocKey(DocKey(PgTableId=16385, [], ["r2"]), [SystemColumnId(0); HT{ physical: 6000 }]) -> null
2323
0
SubDocKey(DocKey(PgTableId=16385, [], ["r2"]), [SystemColumnId(0); HT{ physical: 2000 }]) -> null
2324
0
SubDocKey(DocKey(PgTableId=16385, [], ["r3"]), [SystemColumnId(0); HT{ physical: 3000 }]) -> null
2325
0
      )#");
2326
2327
  // Simulate SQL:
2328
  //  DELETE FROM t WHERE c = "r2";
2329
0
  {
2330
0
    DocKey doc_key;
2331
0
    std::string range_key_str = Format("r$0", 2);
2332
2333
0
    doc_key.set_pgtable_id(pgtable_id);
2334
0
    doc_key.ResizeRangeComponents(1);
2335
0
    doc_key.SetRangeComponent(PrimitiveValue(range_key_str), 0 /* idx */);
2336
0
    ASSERT_OK(SetPrimitive(
2337
0
        DocPath(doc_key.Encode()),
2338
0
        Value(PrimitiveValue::kTombstone),
2339
0
        t));
2340
0
    t = server::HybridClock::AddPhysicalTimeToHybridTime(t, 1ms);
2341
0
  }
2342
0
  ASSERT_OK(FlushRocksDbAndWait());
2343
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
2344
0
SubDocKey(DocKey(PgTableId=16385, [], []), [HT{ physical: 4000 }]) -> DEL
2345
0
SubDocKey(DocKey(PgTableId=16385, [], ["r1"]), [SystemColumnId(0); HT{ physical: 5000 }]) -> null
2346
0
SubDocKey(DocKey(PgTableId=16385, [], ["r1"]), [SystemColumnId(0); HT{ physical: 1000 }]) -> null
2347
0
SubDocKey(DocKey(PgTableId=16385, [], ["r2"]), [HT{ physical: 7000 }]) -> DEL
2348
0
SubDocKey(DocKey(PgTableId=16385, [], ["r2"]), [SystemColumnId(0); HT{ physical: 6000 }]) -> null
2349
0
SubDocKey(DocKey(PgTableId=16385, [], ["r2"]), [SystemColumnId(0); HT{ physical: 2000 }]) -> null
2350
0
SubDocKey(DocKey(PgTableId=16385, [], ["r3"]), [SystemColumnId(0); HT{ physical: 3000 }]) -> null
2351
0
      )#");
2352
2353
  // Major compact.
2354
0
  FullyCompactHistoryBefore(10000_usec_ht);
2355
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
2356
0
SubDocKey(DocKey(PgTableId=16385, [], ["r1"]), [SystemColumnId(0); HT{ physical: 5000 }]) -> null
2357
0
      )#");
2358
0
}
2359
2360
0
TEST_P(DocDBTestWrapper, MinorCompactionNoDeletions) {
2361
0
  ASSERT_OK(DisableCompactions());
2362
0
  const DocKey doc_key(PrimitiveValues("k"));
2363
0
  KeyBytes encoded_doc_key(doc_key.Encode());
2364
0
  for (int i = 1; i <= 6; ++i) {
2365
0
    auto value_str = Format("v$0", i);
2366
0
    PrimitiveValue pv(value_str);
2367
0
    ASSERT_OK(SetPrimitive(
2368
0
        DocPath(encoded_doc_key), Value(pv), HybridTime::FromMicros(i * 1000)));
2369
0
    ASSERT_OK(FlushRocksDbAndWait());
2370
0
  }
2371
2372
0
  ASSERT_EQ(6, NumSSTableFiles());
2373
0
  const char* kInitialDocDbStateStr = R"#(
2374
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 6000 }]) -> "v6"  // file 6
2375
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 5000 }]) -> "v5"  // file 5
2376
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 4000 }]) -> "v4"  // file 4
2377
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 3000 }]) -> "v3"  // file 3
2378
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 2000 }]) -> "v2"  // file 2
2379
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 1000 }]) -> "v1"  // file 1
2380
0
      )#";
2381
2382
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(kInitialDocDbStateStr);
2383
0
  MinorCompaction(5000_usec_ht, /* num_files_to_compact */ 2);
2384
2385
0
  ASSERT_EQ(5, NumSSTableFiles());
2386
  // No changes in DocDB rows as we still need the entry at 5000_ms_ht.
2387
  // Let's call the output file resulting from the last compaction "file 7".
2388
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(kInitialDocDbStateStr);
2389
2390
0
  MinorCompaction(5000_usec_ht, /* num_files_to_compact */ 2);
2391
0
  ASSERT_EQ(4, NumSSTableFiles());
2392
  // Removed the entry at 4000_ms_ht as it was overwritten at time 5000. Earlier entries are in
2393
  // other files that haven't been compacted yet.
2394
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
2395
0
      R"#(
2396
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 6000 }]) -> "v6"  // file 8
2397
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 5000 }]) -> "v5"  // file 8
2398
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 3000 }]) -> "v3"  // file 3
2399
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 2000 }]) -> "v2"  // file 2
2400
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 1000 }]) -> "v1"  // file 1
2401
0
      )#");
2402
2403
0
  MinorCompaction(5000_usec_ht, /* num_files_to_compact */ 2);
2404
0
  ASSERT_EQ(3, NumSSTableFiles());
2405
  // Removed the entry at 3000_ms_ht.
2406
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
2407
0
      R"#(
2408
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 6000 }]) -> "v6"  // file 9
2409
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 5000 }]) -> "v5"  // file 9
2410
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 2000 }]) -> "v2"  // file 2
2411
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 1000 }]) -> "v1"  // file 1
2412
0
      )#");
2413
2414
0
  MinorCompaction(5000_usec_ht, /* num_files_to_compact */ 2);
2415
0
  ASSERT_EQ(2, NumSSTableFiles());
2416
  // Removed the entry at 2000_ms_ht.
2417
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
2418
0
      R"#(
2419
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 6000 }]) -> "v6"  // file 10
2420
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 5000 }]) -> "v5"  // file 10
2421
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 1000 }]) -> "v1"  // file 1
2422
0
      )#");
2423
2424
0
  MinorCompaction(5000_usec_ht, /* num_files_to_compact */ 2);
2425
0
  ASSERT_EQ(1, NumSSTableFiles());
2426
  // Removed the entry at 2000_ms_ht.
2427
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
2428
0
      R"#(
2429
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 6000 }]) -> "v6"  // file 11
2430
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 5000 }]) -> "v5"  // file 11
2431
0
      )#");
2432
0
}
2433
2434
0
TEST_P(DocDBTestWrapper, MinorCompactionWithDeletions) {
2435
0
  ASSERT_OK(DisableCompactions());
2436
0
  const DocKey doc_key(PrimitiveValues("k"));
2437
0
  KeyBytes encoded_doc_key(doc_key.Encode());
2438
0
  for (int i = 1; i <= 6; ++i) {
2439
0
    auto value_str = Format("v$0", i);
2440
0
    PrimitiveValue pv = i == 5 ? PrimitiveValue::kTombstone : PrimitiveValue(value_str);
2441
0
    ASSERT_OK(SetPrimitive(
2442
0
        DocPath(encoded_doc_key), Value(pv), HybridTime::FromMicros(i * 1000)));
2443
0
    ASSERT_OK(FlushRocksDbAndWait());
2444
0
  }
2445
2446
0
  ASSERT_EQ(6, NumSSTableFiles());
2447
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
2448
0
      R"#(
2449
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 6000 }]) -> "v6"  // file 6
2450
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 5000 }]) -> DEL   // file 5
2451
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 4000 }]) -> "v4"  // file 4
2452
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 3000 }]) -> "v3"  // file 3
2453
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 2000 }]) -> "v2"  // file 2
2454
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 1000 }]) -> "v1"  // file 1
2455
0
      )#");
2456
0
  MinorCompaction(5000_usec_ht, /* num_files_to_compact */ 2);
2457
2458
0
  ASSERT_EQ(5, NumSSTableFiles());
2459
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
2460
0
      R"#(
2461
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 6000 }]) -> "v6"  // file 7
2462
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 5000 }]) -> DEL   // file 7 as well
2463
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 4000 }]) -> "v4"  // file 4
2464
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 3000 }]) -> "v3"  // file 3
2465
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 2000 }]) -> "v2"  // file 2
2466
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 1000 }]) -> "v1"  // file 1
2467
0
      )#");
2468
2469
0
  MinorCompaction(5000_usec_ht, /* num_files_to_compact */ 2);
2470
0
  ASSERT_EQ(4, NumSSTableFiles());
2471
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
2472
0
      R"#(
2473
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 6000 }]) -> "v6"  // file 8
2474
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 5000 }]) -> DEL   // file 8
2475
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 3000 }]) -> "v3"  // file 3
2476
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 2000 }]) -> "v2"  // file 2
2477
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 1000 }]) -> "v1"  // file 1
2478
0
      )#");
2479
2480
0
  MinorCompaction(5000_usec_ht, /* num_files_to_compact */ 2);
2481
0
  ASSERT_EQ(3, NumSSTableFiles());
2482
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
2483
0
      R"#(
2484
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 6000 }]) -> "v6"  // file 9
2485
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 5000 }]) -> DEL   // file 9
2486
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 2000 }]) -> "v2"  // file 2
2487
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 1000 }]) -> "v1"  // file 1
2488
0
      )#");
2489
2490
0
  MinorCompaction(5000_usec_ht, /* num_files_to_compact */ 2);
2491
0
  ASSERT_EQ(2, NumSSTableFiles());
2492
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
2493
0
      R"#(
2494
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 6000 }]) -> "v6"  // file 10
2495
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 5000 }]) -> DEL   // file 10
2496
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 1000 }]) -> "v1"  // file 1
2497
0
      )#");
2498
2499
  // Now the minor compaction turns into a major compaction and we end up with one file.
2500
  // The tombstone is now gone as well.
2501
0
  MinorCompaction(5000_usec_ht, /* num_files_to_compact */ 2);
2502
0
  ASSERT_EQ(1, NumSSTableFiles());
2503
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
2504
0
      R"#(
2505
0
SubDocKey(DocKey([], ["k"]), [HT{ physical: 6000 }]) -> "v6"  // file 11
2506
0
      )#");
2507
0
}
2508
2509
0
TEST_P(DocDBTestWrapper, BasicTest) {
2510
  // A few points to make it easier to understand the expected binary representations here:
2511
  // - Initial bytes such as 'S' (kString), 'I' (kInt64) correspond to members of the enum
2512
  //   ValueType.
2513
  // - Strings are terminated with \x00\x00.
2514
  // - Groups of key components in the document key ("hashed" and "range" components) are terminated
2515
  //   with '!' (kGroupEnd).
2516
  // - 64-bit signed integers are encoded in the key using big-endian format with sign bit
2517
  //   inverted.
2518
  // - HybridTimes are represented as 64-bit unsigned integers with all bits inverted, so that's
2519
  //   where we get a lot of \xff bytes from.
2520
2521
0
  SetInitMarkerBehavior(InitMarkerBehavior::kRequired);
2522
2523
0
  DocKey string_valued_doc_key(PrimitiveValues("my_key_where_value_is_a_string"));
2524
0
  ASSERT_STR_EQ_VERBOSE_TRIMMED(
2525
  // Two zeros indicate the end of a string primitive field, and the '!' indicates the end
2526
  // of the "range" part of the DocKey. There is no "hash" part, because the first
2527
  // PrimitiveValue is not a hash value.
2528
0
      "\"Smy_key_where_value_is_a_string\\x00\\x00!\"",
2529
0
      string_valued_doc_key.Encode().ToString());
2530
2531
0
  TestInsertion(
2532
0
      DocPath(string_valued_doc_key.Encode()),
2533
0
      PrimitiveValue("value1"),
2534
0
      1000_usec_ht,
2535
0
      R"#(1. PutCF('Smy_key_where_value_is_a_string\x00\x00\
2536
0
                    !', 'Svalue1'))#");
2537
2538
0
  DocKey doc_key(PrimitiveValues("mydockey", 123456));
2539
0
  KeyBytes encoded_doc_key(doc_key.Encode());
2540
2541
0
  TestInsertion(
2542
0
      DocPath(encoded_doc_key, "subkey_a"),
2543
0
      PrimitiveValue("value_a"),
2544
0
      2000_usec_ht,
2545
0
      R"#(
2546
0
1. PutCF('Smydockey\x00\x00\
2547
0
          I\x80\x00\x00\x00\x00\x01\xe2@\
2548
0
          !', '{')
2549
0
2. PutCF('Smydockey\x00\x00\
2550
0
          I\x80\x00\x00\x00\x00\x01\xe2@\
2551
0
          !\
2552
0
          Ssubkey_a\x00\x00', 'Svalue_a')
2553
0
      )#");
2554
2555
0
  TestInsertion(
2556
0
      DocPath(encoded_doc_key, "subkey_b", "subkey_c"),
2557
0
      PrimitiveValue("value_bc"),
2558
0
      3000_usec_ht,
2559
0
      R"#(
2560
0
1. PutCF('Smydockey\x00\x00\
2561
0
          I\x80\x00\x00\x00\x00\x01\xe2@\
2562
0
          !\
2563
0
          Ssubkey_b\x00\x00', '{')
2564
0
2. PutCF('Smydockey\x00\x00\
2565
0
          I\x80\x00\x00\x00\x00\x01\xe2@\
2566
0
          !\
2567
0
          Ssubkey_b\x00\x00\
2568
0
          Ssubkey_c\x00\x00', 'Svalue_bc')
2569
0
      )#");
2570
2571
  // This only has one insertion, because the object at subkey "subkey_b" already exists.
2572
0
  TestInsertion(
2573
0
      DocPath(encoded_doc_key, "subkey_b", "subkey_d"),
2574
0
      PrimitiveValue("value_bd"),
2575
0
      3500_usec_ht,
2576
0
      R"#(
2577
0
1. PutCF('Smydockey\x00\x00\
2578
0
          I\x80\x00\x00\x00\x00\x01\xe2@\
2579
0
          !\
2580
0
          Ssubkey_b\x00\x00\
2581
0
          Ssubkey_d\x00\x00', 'Svalue_bd')
2582
0
      )#");
2583
2584
  // Delete a non-existent top-level document. We don't expect any tombstones to be created.
2585
0
  TestDeletion(
2586
0
      DocPath(encoded_doc_key, "subkey_x"),
2587
0
      4000_usec_ht,
2588
0
      "");
2589
2590
  // Delete a leaf-level value in a subdocument.
2591
0
  TestDeletion(
2592
0
      DocPath(encoded_doc_key, "subkey_b", "subkey_c"),
2593
0
      5000_usec_ht,
2594
0
      R"#(
2595
0
1. PutCF('Smydockey\x00\x00\
2596
0
          I\x80\x00\x00\x00\x00\x01\xe2@\
2597
0
          !\
2598
0
          Ssubkey_b\x00\x00\
2599
0
          Ssubkey_c\x00\x00', 'X')
2600
0
      )#");
2601
2602
  // Now delete an entire object.
2603
0
  TestDeletion(
2604
0
      DocPath(encoded_doc_key, "subkey_b"),
2605
0
      6000_usec_ht,
2606
0
      R"#(
2607
0
1. PutCF('Smydockey\x00\x00\
2608
0
          I\x80\x00\x00\x00\x00\x01\xe2@\
2609
0
          !\
2610
0
          Ssubkey_b\x00\x00', 'X')
2611
0
      )#");
2612
2613
  // Re-insert a value at subkey_b.subkey_c. This should see the tombstone from the previous
2614
  // operation and create a new object at subkey_b at the new hybrid_time, hence two writes.
2615
0
  TestInsertion(
2616
0
      DocPath(encoded_doc_key, "subkey_b", "subkey_c"),
2617
0
      PrimitiveValue("value_bc_prime"),
2618
0
      7000_usec_ht,
2619
0
      R"#(
2620
0
1. PutCF('Smydockey\x00\x00\
2621
0
          I\x80\x00\x00\x00\x00\x01\xe2@\
2622
0
          !\
2623
0
          Ssubkey_b\x00\x00', '{')
2624
0
2. PutCF('Smydockey\x00\x00\
2625
0
          I\x80\x00\x00\x00\x00\x01\xe2@\
2626
0
          !\
2627
0
          Ssubkey_b\x00\x00\
2628
0
          Ssubkey_c\x00\x00', 'Svalue_bc_prime')
2629
0
      )#");
2630
2631
  // Check the final state of the database.
2632
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(kPredefinedDBStateDebugDumpStr);
2633
0
  CheckExpectedLatestDBState();
2634
2635
  // Compaction cleanup testing.
2636
2637
0
  ClearLogicalSnapshots();
2638
0
  CaptureLogicalSnapshot();
2639
0
  FullyCompactHistoryBefore(5000_usec_ht);
2640
  // The following entry gets deleted because it is invisible at hybrid_time 5000:
2641
  // SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b", "subkey_c"; HT{ physical: 3000 }])
2642
  //     -> "value_bc"
2643
  //
2644
  // This entry is deleted because we can always remove deletes at or below the cutoff hybrid_time:
2645
  // SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b", "subkey_c"; HT{ physical: 5000 }])
2646
  //     -> DEL
2647
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
2648
0
SubDocKey(DocKey([], ["my_key_where_value_is_a_string"]), [HT{ physical: 1000 }]) -> "value1"
2649
0
SubDocKey(DocKey([], ["mydockey", 123456]), [HT{ physical: 2000 }]) -> {}
2650
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_a"; HT{ physical: 2000 w: 1 }]) -> "value_a"
2651
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b"; HT{ physical: 7000 }]) -> {}
2652
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b"; HT{ physical: 6000 }]) -> DEL
2653
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b"; HT{ physical: 3000 }]) -> {}
2654
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b", "subkey_c"; HT{ physical: 7000 w: 1 }]) \
2655
0
    -> "value_bc_prime"
2656
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b", "subkey_d"; HT{ physical: 3500 }]) -> \
2657
0
    "value_bd"
2658
0
      )#");
2659
0
  CheckExpectedLatestDBState();
2660
0
  CaptureLogicalSnapshot();
2661
  // Perform the next history compaction starting both from the initial state as well as from the
2662
  // state with the first history compaction (at hybrid_time 5000) already performed.
2663
0
  for (const auto &snapshot : logical_snapshots()) {
2664
0
    snapshot.RestoreTo(rocksdb());
2665
0
    FullyCompactHistoryBefore(6000_usec_ht);
2666
    // Now the following entries get deleted, because the entire subdocument at "subkey_b" gets
2667
    // deleted at hybrid_time 6000, so we won't look at these records if we do a scan at
2668
    // HT{ physical: 6000 }:
2669
    //
2670
    // SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b"; HT{ physical: 3000 }]) -> {}
2671
    // SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b", "subkey_c"; HT{ physical: 5000 }])
2672
    //     -> DEL
2673
    // SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b", "subkey_d"; HT{ physical: 3500 }])
2674
    //     -> "value_bd"
2675
    //
2676
    // And the deletion itself is removed because it is at the history cutoff hybrid_time:
2677
    // SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b"; HT{ physical: 6000 }]) -> DEL
2678
0
    ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
2679
0
SubDocKey(DocKey([], ["my_key_where_value_is_a_string"]), [HT{ physical: 1000 }]) -> "value1"
2680
0
SubDocKey(DocKey([], ["mydockey", 123456]), [HT{ physical: 2000 }]) -> {}
2681
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_a"; HT{ physical: 2000 w: 1 }]) -> "value_a"
2682
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b"; HT{ physical: 7000 }]) -> {}
2683
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b", "subkey_c"; HT{ physical: 7000 w: 1 }]) \
2684
0
    -> "value_bc_prime"
2685
0
        )#");
2686
0
    CheckExpectedLatestDBState();
2687
0
  }
2688
0
  CaptureLogicalSnapshot();
2689
  // Also test the next compaction starting with all previously captured states, (1) initial,
2690
  // (2) after a compaction at hybrid_time 5000, and (3) after a compaction at hybrid_time 6000.
2691
  // We are going through snapshots in reverse order so that we end with the initial snapshot that
2692
  // does not have any history trimming done yet.
2693
0
  for (auto i = num_logical_snapshots(); i > 0;) {
2694
0
    --i;
2695
0
    RestoreToRocksDBLogicalSnapshot(i);
2696
    // Test overwriting an entire document with an empty object. This should ideally happen with no
2697
    // reads.
2698
0
    TestInsertion(
2699
0
        DocPath(encoded_doc_key),
2700
0
        PrimitiveValue::kObject,
2701
0
        8000_usec_ht,
2702
0
        R"#(
2703
0
1. PutCF('Smydockey\x00\x00\
2704
0
          I\x80\x00\x00\x00\x00\x01\xe2@\
2705
0
          !', '{')
2706
0
        )#");
2707
0
    VerifySubDocument(SubDocKey(doc_key), 8000_usec_ht, "{}");
2708
0
  }
2709
2710
  // Reset our collection of snapshots now that we've performed one more operation.
2711
0
  ClearLogicalSnapshots();
2712
2713
0
  CaptureLogicalSnapshot();
2714
  // This is similar to the kPredefinedDBStateDebugDumpStr, but has an additional overwrite of the
2715
  // document with an empty object at hybrid_time 8000.
2716
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
2717
0
SubDocKey(DocKey([], ["my_key_where_value_is_a_string"]), [HT{ physical: 1000 }]) -> "value1"
2718
0
SubDocKey(DocKey([], ["mydockey", 123456]), [HT{ physical: 8000 }]) -> {}
2719
0
SubDocKey(DocKey([], ["mydockey", 123456]), [HT{ physical: 2000 }]) -> {}
2720
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_a"; HT{ physical: 2000 w: 1 }]) -> "value_a"
2721
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b"; HT{ physical: 7000 }]) -> {}
2722
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b"; HT{ physical: 6000 }]) -> DEL
2723
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b"; HT{ physical: 3000 }]) -> {}
2724
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b", "subkey_c"; HT{ physical: 7000 w: 1 }]) \
2725
0
    -> "value_bc_prime"
2726
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b", "subkey_c"; HT{ physical: 5000 }]) -> DEL
2727
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b", "subkey_c"; HT{ physical: 3000 w: 1 }]) \
2728
0
    -> "value_bc"
2729
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b", "subkey_d"; HT{ physical: 3500 }]) -> \
2730
0
    "value_bd"
2731
0
      )#");
2732
0
  FullyCompactHistoryBefore(7999_usec_ht);
2733
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
2734
0
SubDocKey(DocKey([], ["my_key_where_value_is_a_string"]), [HT{ physical: 1000 }]) -> "value1"
2735
0
SubDocKey(DocKey([], ["mydockey", 123456]), [HT{ physical: 8000 }]) -> {}
2736
0
SubDocKey(DocKey([], ["mydockey", 123456]), [HT{ physical: 2000 }]) -> {}
2737
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_a"; HT{ physical: 2000 w: 1 }]) -> "value_a"
2738
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b"; HT{ physical: 7000 }]) -> {}
2739
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey_b", "subkey_c"; HT{ physical: 7000 w: 1 }]) \
2740
0
    -> "value_bc_prime"
2741
0
      )#");
2742
0
  CaptureLogicalSnapshot();
2743
  // Starting with each snapshot, perform the final history compaction and verify we always get the
2744
  // same result.
2745
0
  for (size_t i = 0; i < logical_snapshots().size(); ++i) {
2746
0
    RestoreToRocksDBLogicalSnapshot(i);
2747
0
    FullyCompactHistoryBefore(8000_usec_ht);
2748
0
    ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
2749
0
SubDocKey(DocKey([], ["my_key_where_value_is_a_string"]), [HT{ physical: 1000 }]) -> "value1"
2750
0
SubDocKey(DocKey([], ["mydockey", 123456]), [HT{ physical: 8000 }]) -> {}
2751
0
        )#");
2752
0
  }
2753
0
}
2754
2755
0
TEST_P(DocDBTestWrapper, MultiOperationDocWriteBatch) {
2756
0
  const auto encoded_doc_key = DocKey(PrimitiveValues("a")).Encode();
2757
0
  auto dwb = MakeDocWriteBatch();
2758
0
  ASSERT_OK(dwb.SetPrimitive(DocPath(encoded_doc_key, "b"), PrimitiveValue("v1")));
2759
0
  ASSERT_OK(dwb.SetPrimitive(DocPath(encoded_doc_key, "c", "d"), PrimitiveValue("v2")));
2760
0
  ASSERT_OK(dwb.SetPrimitive(DocPath(encoded_doc_key, "c", "e"), PrimitiveValue("v3")));
2761
2762
0
  ASSERT_OK(WriteToRocksDB(dwb, 1000_usec_ht));
2763
2764
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
2765
0
      SubDocKey(DocKey([], ["a"]), ["b"; HT{ physical: 1000 }]) -> "v1"
2766
0
      SubDocKey(DocKey([], ["a"]), ["c", "d"; HT{ physical: 1000 w: 1 }]) -> "v2"
2767
0
      SubDocKey(DocKey([], ["a"]), ["c", "e"; HT{ physical: 1000 w: 2 }]) -> "v3"
2768
0
      )#");
2769
2770
0
  string dwb_str;
2771
0
  ASSERT_OK(FormatDocWriteBatch(dwb, &dwb_str));
2772
0
  EXPECT_STR_EQ_VERBOSE_TRIMMED(
2773
0
      R"#(
2774
0
          1. PutCF('Sa\x00\x00!Sb\x00\x00', 'Sv1')
2775
0
          2. PutCF('Sa\x00\x00!Sc\x00\x00Sd\x00\x00', 'Sv2')
2776
0
          3. PutCF('Sa\x00\x00!Sc\x00\x00Se\x00\x00', 'Sv3')
2777
0
      )#", dwb_str);
2778
0
}
2779
2780
class DocDBTestBoundaryValues: public DocDBTestWrapper {
2781
 protected:
2782
0
  void TestBoundaryValues(size_t flush_rate) {
2783
0
    struct Trackers {
2784
0
      MinMaxTracker<int64_t> key_ints;
2785
0
      MinMaxTracker<std::string> key_strs;
2786
0
      MinMaxTracker<HybridTime> times;
2787
0
    };
2788
2789
0
    auto dwb = MakeDocWriteBatch();
2790
0
    constexpr int kTotalRows = 1000;
2791
0
    constexpr std::mt19937_64::result_type kSeed = 2886476510;
2792
2793
0
    std::mt19937_64 rng(kSeed);
2794
0
    std::uniform_int_distribution<int64_t> distribution(0, std::numeric_limits<int64_t>::max());
2795
2796
0
    std::vector<Trackers> trackers;
2797
0
    for (int i = 0; i != kTotalRows; ++i) {
2798
0
      if (i % flush_rate == 0) {
2799
0
        trackers.emplace_back();
2800
0
        ASSERT_OK(FlushRocksDbAndWait());
2801
0
      }
2802
0
      auto key_str = "key_" + std::to_string(distribution(rng));
2803
0
      auto key_int = distribution(rng);
2804
0
      auto value_str = "value_" + std::to_string(distribution(rng));
2805
0
      auto time = HybridTime::FromMicros(distribution(rng));
2806
0
      auto key = DocKey(PrimitiveValues(key_str, key_int)).Encode();
2807
0
      DocPath path(key);
2808
0
      ASSERT_OK(SetPrimitive(path, PrimitiveValue(value_str), time));
2809
0
      trackers.back().key_ints(key_int);
2810
0
      trackers.back().key_strs(key_str);
2811
0
      trackers.back().times(time);
2812
0
    }
2813
2814
0
    string dwb_str;
2815
0
    ASSERT_OK(FormatDocWriteBatch(dwb, &dwb_str));
2816
0
    SCOPED_TRACE("\nWrite batch:\n" + dwb_str);
2817
0
    ASSERT_OK(WriteToRocksDB(dwb, 1000_usec_ht));
2818
0
    ASSERT_OK(FlushRocksDbAndWait());
2819
2820
0
    for (auto i = 0; i != 2; ++i) {
2821
0
      if (i) {
2822
0
        ASSERT_OK(ReopenRocksDB());
2823
0
      }
2824
0
      std::vector<rocksdb::LiveFileMetaData> files;
2825
0
      rocksdb()->GetLiveFilesMetaData(&files);
2826
0
      ASSERT_EQ(trackers.size(), files.size());
2827
0
      sort(files.begin(), files.end(), [](const auto &lhs, const auto &rhs) {
2828
0
        return lhs.name < rhs.name;
2829
0
      });
2830
2831
0
      for (size_t j = 0; j != trackers.size(); ++j) {
2832
0
        const auto &file = files[j];
2833
0
        const auto &smallest = file.smallest.user_values;
2834
0
        const auto &largest = file.largest.user_values;
2835
0
        {
2836
0
          auto &times = trackers[j].times;
2837
0
          DocHybridTime temp;
2838
0
          ASSERT_OK(GetDocHybridTime(smallest, &temp));
2839
0
          ASSERT_EQ(times.min, temp.hybrid_time());
2840
0
          ASSERT_OK(GetDocHybridTime(largest, &temp));
2841
0
          ASSERT_EQ(times.max, temp.hybrid_time());
2842
0
        }
2843
0
        {
2844
0
          auto &key_ints = trackers[j].key_ints;
2845
0
          auto &key_strs = trackers[j].key_strs;
2846
0
          PrimitiveValue temp;
2847
0
          ASSERT_OK(GetPrimitiveValue(smallest, 0, &temp));
2848
0
          ASSERT_EQ(PrimitiveValue(key_strs.min), temp);
2849
0
          ASSERT_OK(GetPrimitiveValue(largest, 0, &temp));
2850
0
          ASSERT_EQ(PrimitiveValue(key_strs.max), temp);
2851
0
          ASSERT_OK(GetPrimitiveValue(smallest, 1, &temp));
2852
0
          ASSERT_EQ(PrimitiveValue(key_ints.min), temp);
2853
0
          ASSERT_OK(GetPrimitiveValue(largest, 1, &temp));
2854
0
          ASSERT_EQ(PrimitiveValue(key_ints.max), temp);
2855
0
        }
2856
0
      }
2857
0
    }
2858
0
  }
2859
};
2860
2861
2862
0
TEST_F_EX(DocDBTest, BoundaryValues, DocDBTestBoundaryValues) {
2863
0
  TestBoundaryValues(std::numeric_limits<size_t>::max());
2864
0
}
2865
2866
0
TEST_F_EX(DocDBTest, BoundaryValuesMultiFiles, DocDBTestBoundaryValues) {
2867
0
  TestBoundaryValues(350);
2868
0
}
2869
2870
0
TEST_P(DocDBTestWrapper, BloomFilterTest) {
2871
  // Turn off "next instead of seek" optimization, because this test rely on DocDB to do seeks.
2872
0
  FLAGS_max_nexts_to_avoid_seek = 0;
2873
  // Write batch and flush options.
2874
0
  auto dwb = MakeDocWriteBatch();
2875
0
  ASSERT_OK(FlushRocksDbAndWait());
2876
2877
0
  DocKey key1(0, PrimitiveValues("key1"), PrimitiveValues());
2878
0
  DocKey key2(0, PrimitiveValues("key2"), PrimitiveValues());
2879
0
  DocKey key3(0, PrimitiveValues("key3"), PrimitiveValues());
2880
0
  HybridTime ht;
2881
2882
0
  SubDocument doc_from_rocksdb;
2883
0
  bool subdoc_found_in_rocksdb = false;
2884
0
  uint64_t total_bloom_useful = 0;
2885
0
  uint64_t total_table_iterators = 0;
2886
2887
0
  auto flush_rocksdb = [this, &total_table_iterators]() {
2888
0
    ASSERT_OK(FlushRocksDbAndWait());
2889
0
    total_table_iterators =
2890
0
        regular_db_options().statistics->getTickerCount(rocksdb::NO_TABLE_CACHE_ITERATORS);
2891
0
  };
2892
2893
  // The following code will set 2/3 keys at a time and flush those 2 writes in a new file. That
2894
  // way we can control and know exactly when the bloom filter is useful.
2895
  // We first write out k1 and k3 and confirm the bloom filter usage is bumped only for checking for
2896
  // k2, as the file does not contain it:
2897
  // file1: k1, k3
2898
  //
2899
  // We then proceed to write k1 and k2 in a new file and check the bloom usage again. At this
2900
  // point, we have:
2901
  // file1: k1, k3
2902
  // file2: k1, k2
2903
  // So the blooms will prune out one file each for k2 and k3 and nothing for k1.
2904
  //
2905
  // Finally, we write out k2 and k3 in a third file, leaving us with:
2906
  // file1: k1, k3
2907
  // file2: k1, k2
2908
  // file3: k2, k3
2909
  // At this point, the blooms will effectively filter out one file for each key.
2910
2911
0
  dwb.Clear();
2912
0
  ASSERT_OK(ht.FromUint64(1000));
2913
0
  ASSERT_OK(dwb.SetPrimitive(DocPath(key1.Encode()), PrimitiveValue("value")));
2914
0
  ASSERT_OK(dwb.SetPrimitive(DocPath(key3.Encode()), PrimitiveValue("value")));
2915
0
  ASSERT_OK(WriteToRocksDB(dwb, ht));
2916
0
  flush_rocksdb();
2917
2918
0
  auto get_doc = [this, &doc_from_rocksdb, &subdoc_found_in_rocksdb](const DocKey &key) {
2919
0
    auto encoded_subdoc_key = SubDocKey(key).EncodeWithoutHt();
2920
0
    GetSubDoc(encoded_subdoc_key, &doc_from_rocksdb, &subdoc_found_in_rocksdb);
2921
0
  };
2922
2923
0
  ASSERT_NO_FATALS(CheckBloom(0, &total_bloom_useful, 0, &total_table_iterators));
2924
0
  ASSERT_NO_FATALS(get_doc(key1));
2925
0
  ASSERT_TRUE(subdoc_found_in_rocksdb);
2926
0
  ASSERT_NO_FATALS(CheckBloom(0, &total_bloom_useful, 1, &total_table_iterators));
2927
2928
0
  ASSERT_NO_FATALS(get_doc(key2));
2929
0
  ASSERT_TRUE(!subdoc_found_in_rocksdb);
2930
  // Bloom filter excluded this file.
2931
  // docdb::TEST_GetSubDocument sometimes seeks twice - first time on key2 and second time to
2932
  // advance out of it, because key2 was found.
2933
0
  ASSERT_NO_FATALS(CheckBloom(2, &total_bloom_useful, 0, &total_table_iterators));
2934
2935
0
  ASSERT_NO_FATALS(get_doc(key3));
2936
0
  ASSERT_TRUE(subdoc_found_in_rocksdb);
2937
0
  ASSERT_NO_FATALS(CheckBloom(0, &total_bloom_useful, 1, &total_table_iterators));
2938
0
  dwb.Clear();
2939
0
  ASSERT_OK(ht.FromUint64(2000));
2940
0
  ASSERT_OK(dwb.SetPrimitive(DocPath(key1.Encode()), PrimitiveValue("value")));
2941
0
  ASSERT_OK(dwb.SetPrimitive(DocPath(key2.Encode()), PrimitiveValue("value")));
2942
0
  ASSERT_OK(WriteToRocksDB(dwb, ht));
2943
0
  flush_rocksdb();
2944
0
  ASSERT_NO_FATALS(get_doc(key1));
2945
2946
0
  ASSERT_NO_FATALS(CheckBloom(0, &total_bloom_useful, 2, &total_table_iterators));
2947
0
  ASSERT_NO_FATALS(get_doc(key2));
2948
0
  ASSERT_NO_FATALS(CheckBloom(2, &total_bloom_useful, 1, &total_table_iterators));
2949
0
  ASSERT_NO_FATALS(get_doc(key3));
2950
0
  ASSERT_NO_FATALS(CheckBloom(2, &total_bloom_useful, 1, &total_table_iterators));
2951
2952
0
  dwb.Clear();
2953
0
  ASSERT_OK(ht.FromUint64(3000));
2954
0
  ASSERT_OK(dwb.SetPrimitive(DocPath(key2.Encode()), PrimitiveValue("value")));
2955
0
  ASSERT_OK(dwb.SetPrimitive(DocPath(key3.Encode()), PrimitiveValue("value")));
2956
0
  ASSERT_OK(WriteToRocksDB(dwb, ht));
2957
0
  flush_rocksdb();
2958
0
  ASSERT_NO_FATALS(get_doc(key1));
2959
0
  ASSERT_NO_FATALS(CheckBloom(2, &total_bloom_useful, 2, &total_table_iterators));
2960
0
  ASSERT_NO_FATALS(get_doc(key2));
2961
0
  ASSERT_NO_FATALS(CheckBloom(2, &total_bloom_useful, 2, &total_table_iterators));
2962
0
  ASSERT_NO_FATALS(get_doc(key3));
2963
0
  ASSERT_NO_FATALS(CheckBloom(2, &total_bloom_useful, 2, &total_table_iterators));
2964
0
}
2965
2966
0
TEST_P(DocDBTestWrapper, BloomFilterCorrectness) {
2967
  // Write batch and flush options.
2968
0
  auto dwb = MakeDocWriteBatch();
2969
0
  ASSERT_OK(FlushRocksDbAndWait());
2970
2971
  // We need to write enough keys for fixed-size bloom filter to have more than one block.
2972
0
  constexpr auto kNumKeys = 100000;
2973
0
  const ColumnId kColumnId(11);
2974
0
  const HybridTime ht(1000);
2975
2976
0
  const auto get_value = [](const int32_t i) {
2977
0
    return PrimitiveValue::Int32(i);
2978
0
  };
2979
2980
0
  const auto get_doc_key = [&](const int32_t i, const bool is_range_key) {
2981
0
    if (is_range_key) {
2982
0
      return DocKey({ PrimitiveValue::Int32(i) });
2983
0
    }
2984
0
    const auto hash_component = PrimitiveValue::Int32(i);
2985
0
    auto doc_key = DocKey(i, { hash_component });
2986
0
    {
2987
0
      std::string hash_components_buf;
2988
0
      QLValuePB hash_component_pb;
2989
0
      PrimitiveValue::ToQLValuePB(
2990
0
          hash_component, QLType::Create(DataType::INT32), &hash_component_pb);
2991
0
      AppendToKey(hash_component_pb, &hash_components_buf);
2992
0
      doc_key.set_hash(YBPartition::HashColumnCompoundValue(hash_components_buf));
2993
0
    }
2994
0
    return doc_key;
2995
0
  };
2996
2997
0
  const auto get_sub_doc_key = [&](const int32_t i, const bool is_range_key) {
2998
0
    return SubDocKey(get_doc_key(i, is_range_key), PrimitiveValue(kColumnId));
2999
0
  };
3000
3001
0
  for (const auto is_range_key : { false, true }) {
3002
0
    for (int32_t i = 0; i < kNumKeys; ++i) {
3003
0
      const auto sub_doc_key = get_sub_doc_key(i, is_range_key);
3004
0
      const auto value = get_value(i);
3005
0
      dwb.Clear();
3006
0
      ASSERT_OK(
3007
0
          dwb.SetPrimitive(DocPath(sub_doc_key.doc_key().Encode(), sub_doc_key.subkeys()), value));
3008
0
      ASSERT_OK(WriteToRocksDB(dwb, ht));
3009
0
    }
3010
0
    ASSERT_OK(FlushRocksDbAndWait());
3011
3012
0
    for (int32_t i = 0; i < kNumKeys; ++i) {
3013
0
      const auto sub_doc_key = get_sub_doc_key(i, is_range_key);
3014
0
      const auto value = get_value(i);
3015
0
      const auto encoded_subdoc_key = sub_doc_key.EncodeWithoutHt();
3016
0
      SubDocument sub_doc;
3017
0
      bool sub_doc_found;
3018
0
      GetSubDoc(encoded_subdoc_key, &sub_doc, &sub_doc_found);
3019
0
      ASSERT_TRUE(sub_doc_found) << "Entry for key #" << i
3020
0
                                 << " not found, is_range_key: " << is_range_key;
3021
0
      ASSERT_EQ(static_cast<PrimitiveValue>(sub_doc), value);
3022
0
    }
3023
0
  }
3024
3025
0
  rocksdb::TablePropertiesCollection props;
3026
0
  ASSERT_OK(rocksdb()->GetPropertiesOfAllTables(&props));
3027
0
  for (const auto& prop : props) {
3028
0
    ASSERT_GE(prop.second->num_filter_blocks, 2) << Format(
3029
0
        "To test rolling over filter block we need at least 2 filter blocks, but got $0 for $1. "
3030
0
        "Increase kNumKeys in this test.",
3031
0
        prop.second->num_filter_blocks, prop.first);
3032
0
  }
3033
0
}
3034
3035
0
TEST_P(DocDBTestWrapper, MergingIterator) {
3036
  // Test for the case described in https://yugabyte.atlassian.net/browse/ENG-1677.
3037
3038
  // Turn off "next instead of seek" optimization, because this test rely on DocDB to do seeks.
3039
0
  FLAGS_max_nexts_to_avoid_seek = 0;
3040
3041
0
  HybridTime ht;
3042
0
  ASSERT_OK(ht.FromUint64(1000));
3043
3044
  // Put smaller key into SST file.
3045
0
  DocKey key1(123, PrimitiveValues("key1"), PrimitiveValues());
3046
0
  auto dwb = MakeDocWriteBatch();
3047
0
  ASSERT_OK(dwb.SetPrimitive(DocPath(key1.Encode()), PrimitiveValue("value1")));
3048
0
  ASSERT_OK(WriteToRocksDB(dwb, ht));
3049
0
  ASSERT_OK(FlushRocksDbAndWait());
3050
3051
  // Put bigger key into memtable.
3052
0
  DocKey key2(234, PrimitiveValues("key2"), PrimitiveValues());
3053
0
  dwb.Clear();
3054
0
  ASSERT_OK(dwb.SetPrimitive(DocPath(key2.Encode()), PrimitiveValue("value2")));
3055
0
  ASSERT_OK(WriteToRocksDB(dwb, ht));
3056
3057
  // Get key2 from DocDB. Bloom filter will skip SST file and it should invalidate SST file
3058
  // iterator in order for MergingIterator to not pickup key1 incorrectly.
3059
0
  VerifySubDocument(SubDocKey(key2), ht, "\"value2\"");
3060
0
}
3061
3062
0
TEST_P(DocDBTestWrapper, SetPrimitiveWithInitMarker) {
3063
  // Both required and optional init marker should be ok.
3064
0
  for (auto init_marker_behavior : kInitMarkerBehaviorList) {
3065
0
    auto dwb = MakeDocWriteBatch(init_marker_behavior);
3066
0
    ASSERT_OK(dwb.SetPrimitive(DocPath(kEncodedDocKey1), PrimitiveValue::kObject));
3067
0
  }
3068
0
}
3069
3070
0
TEST_P(DocDBTestWrapper, TestInetSortOrder) {
3071
0
  InsertInet("1.2.3.4");
3072
0
  InsertInet("2.2.3.4");
3073
0
  InsertInet("::1");
3074
0
  InsertInet("::ffff:ffff");
3075
0
  InsertInet("::ff:ffff:ffff");
3076
0
  InsertInet("180::2978:9018:b288:3f6c");
3077
0
  InsertInet("fe80::2978:9018:b288:3f6c");
3078
0
  InsertInet("255.255.255.255");
3079
0
  InsertInet("ffff:ffff::");
3080
0
  InsertInet("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
3081
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
3082
0
SubDocKey(DocKey([], ["mydockey"]), [::1; HT{ physical: 1000 }]) -> null
3083
0
SubDocKey(DocKey([], ["mydockey"]), [::255.255.255.255; HT{ physical: 1000 }]) -> null
3084
0
SubDocKey(DocKey([], ["mydockey"]), [::ff:ffff:ffff; HT{ physical: 1000 }]) -> null
3085
0
SubDocKey(DocKey([], ["mydockey"]), [1.2.3.4; HT{ physical: 1000 }]) -> null
3086
0
SubDocKey(DocKey([], ["mydockey"]), [180::2978:9018:b288:3f6c; HT{ physical: 1000 }]) -> null
3087
0
SubDocKey(DocKey([], ["mydockey"]), [2.2.3.4; HT{ physical: 1000 }]) -> null
3088
0
SubDocKey(DocKey([], ["mydockey"]), [fe80::2978:9018:b288:3f6c; HT{ physical: 1000 }]) -> null
3089
0
SubDocKey(DocKey([], ["mydockey"]), [255.255.255.255; HT{ physical: 1000 }]) -> null
3090
0
SubDocKey(DocKey([], ["mydockey"]), [ffff:ffff::; HT{ physical: 1000 }]) -> null
3091
0
SubDocKey(DocKey([], ["mydockey"]), [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff; \
3092
0
    HT{ physical: 1000 }]) -> null
3093
0
      )#");
3094
0
}
3095
3096
0
TEST_P(DocDBTestWrapper, TestDisambiguationOnWriteId) {
3097
  // Set a column and then delete the entire row in the same write batch. The row disappears.
3098
0
  auto dwb = MakeDocWriteBatch();
3099
0
  ASSERT_OK(dwb.SetPrimitive(
3100
0
      DocPath(kEncodedDocKey1, PrimitiveValue(ColumnId(10))),
3101
0
      PrimitiveValue("value1")));
3102
0
  ASSERT_OK(dwb.SetPrimitive(
3103
0
      DocPath(kEncodedDocKey1), PrimitiveValue::kTombstone));
3104
0
  ASSERT_OK(WriteToRocksDBAndClear(&dwb, 1000_usec_ht));
3105
3106
0
  SubDocKey subdoc_key(kDocKey1);
3107
0
  SubDocument subdoc;
3108
0
  bool doc_found = false;
3109
  // TODO(dtxn) - check both transaction and non-transaction path?
3110
0
  auto encoded_subdoc_key = subdoc_key.EncodeWithoutHt();
3111
0
  GetSubDoc(encoded_subdoc_key, &subdoc, &doc_found, kNonTransactionalOperationContext);
3112
0
  ASSERT_FALSE(doc_found);
3113
3114
0
  CaptureLogicalSnapshot();
3115
0
  for (int cutoff_time_ms = 1000; cutoff_time_ms <= 1001; ++cutoff_time_ms) {
3116
0
    RestoreToLastLogicalRocksDBSnapshot();
3117
3118
    // The row should still be absent after a compaction.
3119
    // TODO(dtxn) - check both transaction and non-transaction path?
3120
0
    FullyCompactHistoryBefore(HybridTime::FromMicros(cutoff_time_ms));
3121
0
    GetSubDoc(encoded_subdoc_key, &subdoc, &doc_found, kNonTransactionalOperationContext);
3122
0
    ASSERT_FALSE(doc_found);
3123
0
    ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ("");
3124
0
  }
3125
3126
  // Delete the row first, and then set a column. This row will exist.
3127
0
  ASSERT_OK(dwb.SetPrimitive(
3128
0
      DocPath(kEncodedDocKey2), PrimitiveValue::kTombstone));
3129
0
  ASSERT_OK(dwb.SetPrimitive(
3130
0
      DocPath(kEncodedDocKey2, PrimitiveValue(ColumnId(10))),
3131
0
      PrimitiveValue("value2")));
3132
0
  ASSERT_OK(WriteToRocksDBAndClear(&dwb, 2000_usec_ht));
3133
  // TODO(dtxn) - check both transaction and non-transaction path?
3134
0
  SubDocKey subdoc_key2(kDocKey2);
3135
0
  auto encoded_subdoc_key2 = subdoc_key2.EncodeWithoutHt();
3136
0
  GetSubDoc(encoded_subdoc_key2, &subdoc, &doc_found, kNonTransactionalOperationContext);
3137
0
  ASSERT_TRUE(doc_found);
3138
3139
  // The row should still exist after a compaction. The deletion marker should be compacted away.
3140
0
  CaptureLogicalSnapshot();
3141
0
  for (int cutoff_time_ms = 2000; cutoff_time_ms <= 2001; ++cutoff_time_ms) {
3142
0
    RestoreToLastLogicalRocksDBSnapshot();
3143
0
    FullyCompactHistoryBefore(HybridTime::FromMicros(cutoff_time_ms));
3144
    // TODO(dtxn) - check both transaction and non-transaction path?
3145
0
    GetSubDoc(encoded_subdoc_key2, &subdoc, &doc_found, kNonTransactionalOperationContext);
3146
0
    ASSERT_TRUE(doc_found);
3147
0
    ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
3148
0
SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(10); HT{ physical: 2000 w: 1 }]) -> "value2"
3149
0
        )#");
3150
0
  }
3151
0
}
3152
3153
0
TEST_P(DocDBTestWrapper, StaticColumnCompaction) {
3154
0
  const DocKey hk(0, PrimitiveValues("h1")); // hash key
3155
0
  const DocKey pk1(hk.hash(), hk.hashed_group(), PrimitiveValues("r1")); // primary key
3156
0
  const DocKey pk2(hk.hash(), hk.hashed_group(), PrimitiveValues("r2")); //   "      "
3157
0
  const KeyBytes encoded_hk(hk.Encode());
3158
0
  const KeyBytes encoded_pk1(pk1.Encode());
3159
0
  const KeyBytes encoded_pk2(pk2.Encode());
3160
3161
0
  const MonoDelta one_ms = 1ms;
3162
0
  const MonoDelta two_ms = 2ms;
3163
0
  const HybridTime t0 = 1000_usec_ht;
3164
0
  const HybridTime t1 = server::HybridClock::AddPhysicalTimeToHybridTime(t0, two_ms);
3165
0
  const HybridTime t2 = server::HybridClock::AddPhysicalTimeToHybridTime(t1, two_ms);
3166
3167
  // Add some static columns: s1 and s2 with TTL, s3 and s4 without.
3168
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_hk, PrimitiveValue("s1")),
3169
0
      Value(PrimitiveValue("v1"), one_ms), t0));
3170
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_hk, PrimitiveValue("s2")),
3171
0
      Value(PrimitiveValue("v2"), two_ms), t0));
3172
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_hk, PrimitiveValue("s3")),
3173
0
      Value(PrimitiveValue("v3old")), t0));
3174
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_hk, PrimitiveValue("s4")),
3175
0
      Value(PrimitiveValue("v4")), t0));
3176
3177
  // Add some non-static columns for pk1: c5 and c6 with TTL, c7 and c8 without.
3178
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_pk1, PrimitiveValue("c5")),
3179
0
      Value(PrimitiveValue("v51"), one_ms), t0));
3180
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_pk1, PrimitiveValue("c6")),
3181
0
      Value(PrimitiveValue("v61"), two_ms), t0));
3182
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_pk1, PrimitiveValue("c7")),
3183
0
      Value(PrimitiveValue("v71old")), t0));
3184
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_pk1, PrimitiveValue("c8")),
3185
0
      Value(PrimitiveValue("v81")), t0));
3186
3187
  // More non-static columns for another primary key pk2.
3188
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_pk2, PrimitiveValue("c5")),
3189
0
      Value(PrimitiveValue("v52"), one_ms), t0));
3190
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_pk2, PrimitiveValue("c6")),
3191
0
      Value(PrimitiveValue("v62"), two_ms), t0));
3192
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_pk2, PrimitiveValue("c7")),
3193
0
      Value(PrimitiveValue("v72")), t0));
3194
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_pk2, PrimitiveValue("c8")),
3195
0
      Value(PrimitiveValue("v82")), t0));
3196
3197
  // Update s3 and delete s4 at t1.
3198
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_hk, PrimitiveValue("s3")),
3199
0
      Value(PrimitiveValue("v3new")), t1));
3200
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_hk, PrimitiveValue("s4")),
3201
0
      Value(PrimitiveValue::kTombstone), t1));
3202
3203
  // Update c7 of pk1 at t1 also.
3204
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_pk1, PrimitiveValue("c7")),
3205
0
      Value(PrimitiveValue("v71new")), t1));
3206
3207
  // Delete c8 of pk2 at t2.
3208
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_pk2, PrimitiveValue("c8")),
3209
0
      Value(PrimitiveValue::kTombstone), t2));
3210
3211
  // Verify before compaction.
3212
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
3213
0
SubDocKey(DocKey(0x0000, ["h1"], []), ["s1"; HT{ physical: 1000 }]) -> "v1"; ttl: 0.001s
3214
0
SubDocKey(DocKey(0x0000, ["h1"], []), ["s2"; HT{ physical: 1000 }]) -> "v2"; ttl: 0.002s
3215
0
SubDocKey(DocKey(0x0000, ["h1"], []), ["s3"; HT{ physical: 3000 }]) -> "v3new"
3216
0
SubDocKey(DocKey(0x0000, ["h1"], []), ["s3"; HT{ physical: 1000 }]) -> "v3old"
3217
0
SubDocKey(DocKey(0x0000, ["h1"], []), ["s4"; HT{ physical: 3000 }]) -> DEL
3218
0
SubDocKey(DocKey(0x0000, ["h1"], []), ["s4"; HT{ physical: 1000 }]) -> "v4"
3219
0
SubDocKey(DocKey(0x0000, ["h1"], ["r1"]), ["c5"; HT{ physical: 1000 }]) -> "v51"; ttl: 0.001s
3220
0
SubDocKey(DocKey(0x0000, ["h1"], ["r1"]), ["c6"; HT{ physical: 1000 }]) -> "v61"; ttl: 0.002s
3221
0
SubDocKey(DocKey(0x0000, ["h1"], ["r1"]), ["c7"; HT{ physical: 3000 }]) -> "v71new"
3222
0
SubDocKey(DocKey(0x0000, ["h1"], ["r1"]), ["c7"; HT{ physical: 1000 }]) -> "v71old"
3223
0
SubDocKey(DocKey(0x0000, ["h1"], ["r1"]), ["c8"; HT{ physical: 1000 }]) -> "v81"
3224
0
SubDocKey(DocKey(0x0000, ["h1"], ["r2"]), ["c5"; HT{ physical: 1000 }]) -> "v52"; ttl: 0.001s
3225
0
SubDocKey(DocKey(0x0000, ["h1"], ["r2"]), ["c6"; HT{ physical: 1000 }]) -> "v62"; ttl: 0.002s
3226
0
SubDocKey(DocKey(0x0000, ["h1"], ["r2"]), ["c7"; HT{ physical: 1000 }]) -> "v72"
3227
0
SubDocKey(DocKey(0x0000, ["h1"], ["r2"]), ["c8"; HT{ physical: 5000 }]) -> DEL
3228
0
SubDocKey(DocKey(0x0000, ["h1"], ["r2"]), ["c8"; HT{ physical: 1000 }]) -> "v82"
3229
0
      )#");
3230
3231
  // Compact at t1 = HT{ physical: 3000 }.
3232
0
  FullyCompactHistoryBefore(t1);
3233
3234
  // Verify after compaction:
3235
  //   s1 -> expired
3236
  //   s4 -> deleted
3237
  //   s3 = v3old -> compacted
3238
  //   pk1.c5 -> expired
3239
  //   pk1.c7 = v71old -> compacted
3240
  //   pk2.c5 -> expired
3241
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
3242
0
SubDocKey(DocKey(0x0000, ["h1"], []), ["s2"; HT{ physical: 1000 }]) -> "v2"; ttl: 0.002s
3243
0
SubDocKey(DocKey(0x0000, ["h1"], []), ["s3"; HT{ physical: 3000 }]) -> "v3new"
3244
0
SubDocKey(DocKey(0x0000, ["h1"], ["r1"]), ["c6"; HT{ physical: 1000 }]) -> "v61"; ttl: 0.002s
3245
0
SubDocKey(DocKey(0x0000, ["h1"], ["r1"]), ["c7"; HT{ physical: 3000 }]) -> "v71new"
3246
0
SubDocKey(DocKey(0x0000, ["h1"], ["r1"]), ["c8"; HT{ physical: 1000 }]) -> "v81"
3247
0
SubDocKey(DocKey(0x0000, ["h1"], ["r2"]), ["c6"; HT{ physical: 1000 }]) -> "v62"; ttl: 0.002s
3248
0
SubDocKey(DocKey(0x0000, ["h1"], ["r2"]), ["c7"; HT{ physical: 1000 }]) -> "v72"
3249
0
SubDocKey(DocKey(0x0000, ["h1"], ["r2"]), ["c8"; HT{ physical: 5000 }]) -> DEL
3250
0
SubDocKey(DocKey(0x0000, ["h1"], ["r2"]), ["c8"; HT{ physical: 1000 }]) -> "v82"
3251
0
      )#");
3252
0
}
3253
3254
0
TEST_P(DocDBTestWrapper, TestUserTimestamp) {
3255
0
  const DocKey doc_key(PrimitiveValues("k1"));
3256
0
  KeyBytes encoded_doc_key(doc_key.Encode());
3257
3258
  // Only optional init marker supported for user timestamp.
3259
0
  SetInitMarkerBehavior(InitMarkerBehavior::kRequired);
3260
0
  ASSERT_NOK(SetPrimitive(DocPath(encoded_doc_key, PrimitiveValue("s10")),
3261
0
                          Value(PrimitiveValue("v10"), Value::kMaxTtl, 1000),
3262
0
                          1000_usec_ht));
3263
3264
0
  SetInitMarkerBehavior(InitMarkerBehavior::kOptional);
3265
3266
0
  HybridTime ht = 10000_usec_ht;
3267
  // Use same doc_write_batch to test cache.
3268
0
  auto doc_write_batch = MakeDocWriteBatch();
3269
0
  ASSERT_OK(doc_write_batch.SetPrimitive(
3270
0
      DocPath(encoded_doc_key, PrimitiveValue("s1"), PrimitiveValue("s2")),
3271
0
      Value(PrimitiveValue("v1"), Value::kMaxTtl, 1000)));
3272
0
  ASSERT_OK(doc_write_batch.SetPrimitive(
3273
0
      DocPath(encoded_doc_key, PrimitiveValue("s1")),
3274
0
      Value(PrimitiveValue::kObject, Value::kMaxTtl, 500)));
3275
0
  ASSERT_OK(WriteToRocksDB(doc_write_batch, ht));
3276
3277
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
3278
0
SubDocKey(DocKey([], ["k1"]), ["s1"; HT{ physical: 10000 w: 1 }]) -> {}; user timestamp: 500
3279
0
SubDocKey(DocKey([], ["k1"]), ["s1", "s2"; HT{ physical: 10000 }]) -> "v1"; user timestamp: 1000
3280
0
      )#");
3281
3282
0
  doc_write_batch.Clear();
3283
  // Use same doc_write_batch to test cache.
3284
0
  ASSERT_OK(doc_write_batch.SetPrimitive(
3285
0
      DocPath(encoded_doc_key, PrimitiveValue("s3")),
3286
0
      Value(PrimitiveValue::kObject, Value::kMaxTtl, 1000)));
3287
0
  ASSERT_OK(doc_write_batch.SetPrimitive(
3288
0
      DocPath(encoded_doc_key, PrimitiveValue("s3"), PrimitiveValue("s4")),
3289
0
      Value(PrimitiveValue("v1"), Value::kMaxTtl, 500)));
3290
0
  ASSERT_OK(WriteToRocksDB(doc_write_batch, ht));
3291
3292
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
3293
0
SubDocKey(DocKey([], ["k1"]), ["s1"; HT{ physical: 10000 w: 1 }]) -> {}; user timestamp: 500
3294
0
SubDocKey(DocKey([], ["k1"]), ["s1", "s2"; HT{ physical: 10000 }]) -> "v1"; user timestamp: 1000
3295
0
SubDocKey(DocKey([], ["k1"]), ["s3"; HT{ physical: 10000 }]) -> {}; user timestamp: 1000
3296
0
      )#");
3297
3298
0
  doc_write_batch.Clear();
3299
  // Use same doc_write_batch to test cache.
3300
0
  ASSERT_OK(doc_write_batch.SetPrimitive(
3301
0
      DocPath(encoded_doc_key, PrimitiveValue("s3"), PrimitiveValue("s4")),
3302
0
      Value(PrimitiveValue("v1"), Value::kMaxTtl, 2000)));
3303
0
  ASSERT_OK(doc_write_batch.SetPrimitive(
3304
0
      DocPath(encoded_doc_key, PrimitiveValue("s3"), PrimitiveValue("s5")),
3305
0
      Value(PrimitiveValue("v1"), Value::kMaxTtl, 2000)));
3306
0
  ASSERT_OK(WriteToRocksDB(doc_write_batch, ht));
3307
3308
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
3309
0
SubDocKey(DocKey([], ["k1"]), ["s1"; HT{ physical: 10000 w: 1 }]) -> {}; user timestamp: 500
3310
0
SubDocKey(DocKey([], ["k1"]), ["s1", "s2"; HT{ physical: 10000 }]) -> "v1"; user timestamp: 1000
3311
0
SubDocKey(DocKey([], ["k1"]), ["s3"; HT{ physical: 10000 }]) -> {}; user timestamp: 1000
3312
0
SubDocKey(DocKey([], ["k1"]), ["s3", "s4"; HT{ physical: 10000 }]) -> "v1"; user timestamp: 2000
3313
0
SubDocKey(DocKey([], ["k1"]), ["s3", "s5"; HT{ physical: 10000 w: 1 }]) -> "v1"; \
3314
0
    user timestamp: 2000
3315
0
      )#");
3316
0
}
3317
3318
0
CHECKED_STATUS InsertToWriteBatchWithTTL(DocWriteBatch* dwb, const MonoDelta ttl) {
3319
0
  const DocKey doc_key(PrimitiveValues("k1"));
3320
0
  KeyBytes encoded_doc_key(doc_key.Encode());
3321
0
  SubDocument subdoc;
3322
0
  subdoc.SetChildPrimitive(PrimitiveValue("sk1"), PrimitiveValue("v1"));
3323
3324
0
  return dwb->InsertSubDocument(
3325
0
      DocPath(encoded_doc_key, PrimitiveValue("s1"), PrimitiveValue("s2")),
3326
0
      subdoc, ReadHybridTime::Max(), CoarseTimePoint::max(),
3327
0
      rocksdb::kDefaultQueryId, ttl);
3328
0
}
3329
3330
0
TEST_P(DocDBTestWrapper, TestUpdateDocWriteBatchTTL) {
3331
0
  auto dwb = MakeDocWriteBatch();
3332
0
  KeyValueWriteBatchPB kv_pb;
3333
0
  dwb.TEST_CopyToWriteBatchPB(&kv_pb);
3334
0
  ASSERT_FALSE(kv_pb.has_ttl());
3335
3336
  // Write a subdoc with kMaxTtl, which should not show up in the the kv ttl.
3337
0
  ASSERT_OK(InsertToWriteBatchWithTTL(&dwb, Value::kMaxTtl));
3338
0
  dwb.TEST_CopyToWriteBatchPB(&kv_pb);
3339
0
  ASSERT_FALSE(kv_pb.has_ttl());
3340
3341
  // Write a subdoc with 10s TTL, which should show up in the the kv ttl.
3342
0
  ASSERT_OK(InsertToWriteBatchWithTTL(&dwb, 10s));
3343
0
  dwb.TEST_CopyToWriteBatchPB(&kv_pb);
3344
0
  ASSERT_EQ(kv_pb.ttl(), 10 * MonoTime::kNanosecondsPerSecond);
3345
3346
  // Write a subdoc with 5s TTL, which should make the kv ttl unchanged.
3347
0
  ASSERT_OK(InsertToWriteBatchWithTTL(&dwb, 5s));
3348
0
  dwb.TEST_CopyToWriteBatchPB(&kv_pb);
3349
0
  ASSERT_EQ(kv_pb.ttl(), 10 * MonoTime::kNanosecondsPerSecond);
3350
3351
  // Write a subdoc with 15s TTL, which should show up in the the kv ttl.
3352
0
  ASSERT_OK(InsertToWriteBatchWithTTL(&dwb, 15s));
3353
0
  dwb.TEST_CopyToWriteBatchPB(&kv_pb);
3354
0
  ASSERT_EQ(kv_pb.ttl(), 15 * MonoTime::kNanosecondsPerSecond);
3355
3356
  // Write a subdoc with kMaxTTL, which should make the kv ttl unchanged.
3357
0
  ASSERT_OK(InsertToWriteBatchWithTTL(&dwb, Value::kMaxTtl));
3358
0
  dwb.TEST_CopyToWriteBatchPB(&kv_pb);
3359
0
  ASSERT_EQ(kv_pb.ttl(), 15 * MonoTime::kNanosecondsPerSecond);
3360
0
}
3361
3362
0
TEST_P(DocDBTestWrapper, TestCompactionWithUserTimestamp) {
3363
0
  const DocKey doc_key(PrimitiveValues("k1"));
3364
0
  HybridTime t3000 = 3000_usec_ht;
3365
0
  HybridTime t5000 = 5000_usec_ht;
3366
0
  KeyBytes encoded_doc_key(doc_key.Encode());
3367
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key, PrimitiveValue("s1")),
3368
0
                         Value(PrimitiveValue("v11")), t3000));
3369
3370
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
3371
0
      SubDocKey(DocKey([], ["k1"]), ["s1"; HT{ physical: 3000 }]) -> "v11"
3372
0
      )#");
3373
3374
  // Delete the row.
3375
0
  ASSERT_OK(DeleteSubDoc(DocPath(encoded_doc_key, PrimitiveValue("s1")), t5000));
3376
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
3377
0
      SubDocKey(DocKey([], ["k1"]), ["s1"; HT{ physical: 5000 }]) -> DEL
3378
0
      SubDocKey(DocKey([], ["k1"]), ["s1"; HT{ physical: 3000 }]) -> "v11"
3379
0
      )#");
3380
3381
  // Try insert with lower timestamp.
3382
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key, PrimitiveValue("s1")),
3383
0
                         Value(PrimitiveValue("v13"), Value::kMaxTtl, 4000), t3000));
3384
3385
  // No effect on DB.
3386
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
3387
0
      SubDocKey(DocKey([], ["k1"]), ["s1"; HT{ physical: 5000 }]) -> DEL
3388
0
      SubDocKey(DocKey([], ["k1"]), ["s1"; HT{ physical: 3000 }]) -> "v11"
3389
0
      )#");
3390
3391
  // Compaction takes away everything.
3392
0
  FullyCompactHistoryBefore(t5000);
3393
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
3394
0
      )#");
3395
3396
  // Same insert with lower timestamp now works!
3397
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key, PrimitiveValue("s1")),
3398
0
                         Value(PrimitiveValue("v13"), Value::kMaxTtl, 4000), t3000));
3399
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
3400
0
      SubDocKey(DocKey([], ["k1"]), ["s1"; HT{ physical: 3000 }]) -> "v13"; user timestamp: 4000
3401
0
      )#");
3402
3403
  // Now try the same with TTL.
3404
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key, PrimitiveValue("s2")),
3405
0
                         Value(PrimitiveValue("v11"), MonoDelta::FromMicroseconds(1000)), t3000));
3406
3407
  // Insert with TTL.
3408
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
3409
0
      SubDocKey(DocKey([], ["k1"]), ["s1"; HT{ physical: 3000 }]) -> "v13"; user timestamp: 4000
3410
0
      SubDocKey(DocKey([], ["k1"]), ["s2"; HT{ physical: 3000 }]) -> "v11"; ttl: 0.001s
3411
0
      )#");
3412
3413
  // Try insert with lower timestamp.
3414
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key, PrimitiveValue("s2")),
3415
0
                         Value(PrimitiveValue("v13"), Value::kMaxTtl, 2000),
3416
0
                         t3000,
3417
0
                         ReadHybridTime::SingleTime(t3000)));
3418
3419
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
3420
0
      SubDocKey(DocKey([], ["k1"]), ["s1"; HT{ physical: 3000 }]) -> "v13"; user timestamp: 4000
3421
0
      SubDocKey(DocKey([], ["k1"]), ["s2"; HT{ physical: 3000 }]) -> "v11"; ttl: 0.001s
3422
0
      )#");
3423
3424
0
  FullyCompactHistoryBefore(t5000);
3425
3426
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
3427
0
      SubDocKey(DocKey([], ["k1"]), ["s1"; HT{ physical: 3000 }]) -> "v13"; user timestamp: 4000
3428
0
      )#");
3429
3430
  // Insert with lower timestamp after compaction works!
3431
0
  ASSERT_OK(SetPrimitive(DocPath(encoded_doc_key, PrimitiveValue("s2")),
3432
0
                         Value(PrimitiveValue("v13"), Value::kMaxTtl, 2000), t3000));
3433
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
3434
0
      SubDocKey(DocKey([], ["k1"]), ["s1"; HT{ physical: 3000 }]) -> "v13"; user timestamp: 4000
3435
0
      SubDocKey(DocKey([], ["k1"]), ["s2"; HT{ physical: 3000 }]) -> "v13"; user timestamp: 2000
3436
0
      )#");
3437
0
}
3438
3439
void QueryBounds(const DocKey& doc_key, int lower, int upper, int base, const DocDB& doc_db,
3440
                 SubDocument* doc_from_rocksdb, bool* subdoc_found,
3441
0
                 const SubDocKey& subdoc_to_search) {
3442
0
  HybridTime ht = 1000000_usec_ht;
3443
0
  auto lower_key =
3444
0
      SubDocKey(doc_key, PrimitiveValue("subkey" + std::to_string(base + lower))).EncodeWithoutHt();
3445
0
  SliceKeyBound lower_bound(lower_key, BoundType::kInclusiveLower);
3446
0
  auto upper_key =
3447
0
      SubDocKey(doc_key, PrimitiveValue("subkey" + std::to_string(base + upper))).EncodeWithoutHt();
3448
0
  SliceKeyBound upper_bound(upper_key, BoundType::kInclusiveUpper);
3449
0
  auto encoded_subdoc_to_search = subdoc_to_search.EncodeWithoutHt();
3450
0
  GetRedisSubDocumentData data = { encoded_subdoc_to_search, doc_from_rocksdb, subdoc_found };
3451
0
  data.low_subkey = &lower_bound;
3452
0
  data.high_subkey = &upper_bound;
3453
0
  EXPECT_OK(GetRedisSubDocument(
3454
0
      doc_db, data, rocksdb::kDefaultQueryId,
3455
0
      kNonTransactionalOperationContext, CoarseTimePoint::max() /* deadline */,
3456
0
      ReadHybridTime::SingleTime(ht)));
3457
0
}
3458
3459
void VerifyBounds(SubDocument* doc_from_rocksdb, int lower, int upper, int base) {
3460
  EXPECT_EQ(upper - lower + 1, doc_from_rocksdb->object_num_keys());
3461
3462
  for (int i = lower; i <= upper; i++) {
3463
    SubDocument* subdoc = doc_from_rocksdb->GetChild(
3464
        PrimitiveValue("subkey" + std::to_string(base + i)));
3465
    ASSERT_TRUE(subdoc != nullptr);
3466
    EXPECT_EQ("value" + std::to_string(i), subdoc->GetString());
3467
  }
3468
}
3469
3470
void QueryBoundsAndVerify(const DocKey& doc_key, int lower, int upper, int base,
3471
0
                          const DocDB& doc_db, const SubDocKey& subdoc_to_search) {
3472
0
  SubDocument doc_from_rocksdb;
3473
0
  bool subdoc_found = false;
3474
0
  QueryBounds(doc_key, lower, upper, base, doc_db, &doc_from_rocksdb, &subdoc_found,
3475
0
              subdoc_to_search);
3476
0
  EXPECT_TRUE(subdoc_found);
3477
0
  VerifyBounds(&doc_from_rocksdb, lower, upper, base);
3478
0
}
3479
3480
TEST_F(DocDBTestRedis, TestBuildSubDocumentBounds) {
3481
  const DocKey doc_key(PrimitiveValues("key"));
3482
  KeyBytes encoded_doc_key(doc_key.Encode());
3483
  const int nsubkeys = 100;
3484
  const int base = 11000; // To ensure ints can be compared lexicographically.
3485
  string expected_docdb_str;
3486
  AddSubKeys(encoded_doc_key, nsubkeys, base, &expected_docdb_str);
3487
3488
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(expected_docdb_str);
3489
3490
  const SubDocKey subdoc_to_search(doc_key);
3491
3492
  QueryBoundsAndVerify(doc_key, 25, 75, base, doc_db(), subdoc_to_search);
3493
  QueryBoundsAndVerify(doc_key, 50, 60, base, doc_db(), subdoc_to_search);
3494
  QueryBoundsAndVerify(doc_key, 0, nsubkeys - 1, base, doc_db(), subdoc_to_search);
3495
3496
  SubDocument doc_from_rocksdb;
3497
  bool subdoc_found = false;
3498
  QueryBounds(doc_key, -100, 200, base, doc_db(), &doc_from_rocksdb, &subdoc_found,
3499
              subdoc_to_search);
3500
  EXPECT_TRUE(subdoc_found);
3501
  VerifyBounds(&doc_from_rocksdb, 0, nsubkeys - 1, base);
3502
3503
  QueryBounds(doc_key, -100, 50, base, doc_db(), &doc_from_rocksdb, &subdoc_found,
3504
              subdoc_to_search);
3505
  EXPECT_TRUE(subdoc_found);
3506
  VerifyBounds(&doc_from_rocksdb, 0, 50, base);
3507
3508
  QueryBounds(doc_key, 50, 150, base, doc_db(), &doc_from_rocksdb, &subdoc_found,
3509
              subdoc_to_search);
3510
  EXPECT_TRUE(subdoc_found);
3511
  VerifyBounds(&doc_from_rocksdb, 50, nsubkeys - 1, base);
3512
3513
  QueryBounds(doc_key, -100, -50, base, doc_db(), &doc_from_rocksdb, &subdoc_found,
3514
              subdoc_to_search);
3515
  EXPECT_FALSE(subdoc_found);
3516
3517
  QueryBounds(doc_key, 101, 150, base, doc_db(), &doc_from_rocksdb, &subdoc_found,
3518
              subdoc_to_search);
3519
  EXPECT_FALSE(subdoc_found);
3520
3521
  // Try bounds without appropriate doc key.
3522
  QueryBounds(DocKey(PrimitiveValues("abc")), 0, nsubkeys - 1, base, doc_db(), &doc_from_rocksdb,
3523
              &subdoc_found, subdoc_to_search);
3524
  EXPECT_FALSE(subdoc_found);
3525
3526
  // Try bounds different from doc key.
3527
  QueryBounds(doc_key, 0, 99, base, doc_db(), &doc_from_rocksdb, &subdoc_found,
3528
              SubDocKey(DocKey(PrimitiveValues("abc"))));
3529
  EXPECT_FALSE(subdoc_found);
3530
3531
  // Try with bounds pointing to wrong doc key.
3532
  DocKey doc_key_xyz(PrimitiveValues("xyz"));
3533
  AddSubKeys(doc_key_xyz.Encode(), nsubkeys, base, &expected_docdb_str);
3534
  QueryBounds(doc_key_xyz, 0, nsubkeys - 1, base, doc_db(), &doc_from_rocksdb,
3535
              &subdoc_found, subdoc_to_search);
3536
  EXPECT_FALSE(subdoc_found);
3537
}
3538
3539
0
TEST_P(DocDBTestWrapper, TestCompactionForCollectionsWithTTL) {
3540
0
  DocKey collection_key(PrimitiveValues("collection"));
3541
0
  SetUpCollectionWithTTL(collection_key, UseIntermediateFlushes::kFalse);
3542
3543
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(ExpectedDebugDumpForCollectionWithTTL(
3544
0
      collection_key, InitMarkerExpired::kFalse));
3545
3546
0
  FullyCompactHistoryBefore(HybridTime::FromMicros(1050 + 10 * 1000000));
3547
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(ExpectedDebugDumpForCollectionWithTTL(
3548
0
      collection_key, InitMarkerExpired::kTrue));
3549
3550
0
  const auto subdoc_key = SubDocKey(collection_key).EncodeWithoutHt();
3551
0
  SubDocument doc_from_rocksdb;
3552
0
  bool subdoc_found_in_rocksdb = false;
3553
0
  GetSubDoc(
3554
0
      subdoc_key, &doc_from_rocksdb, &subdoc_found_in_rocksdb, kNonTransactionalOperationContext,
3555
0
      ReadHybridTime::FromMicros(1200));
3556
0
  ASSERT_TRUE(subdoc_found_in_rocksdb);
3557
3558
0
  for (int i = 0; i < kNumSubKeysForCollectionsWithTTL * 2; i++) {
3559
0
    SubDocument subdoc;
3560
0
    string key = "k" + std::to_string(i);
3561
0
    string value = "vv" + std::to_string(i);
3562
0
    ASSERT_EQ(value, doc_from_rocksdb.GetChild(PrimitiveValue(key))->GetString());
3563
0
  }
3564
0
}
3565
3566
0
TEST_P(DocDBTestWrapper, MinorCompactionsForCollectionsWithTTL) {
3567
0
  ASSERT_OK(DisableCompactions());
3568
0
  DocKey collection_key(PrimitiveValues("c"));
3569
0
  SetUpCollectionWithTTL(collection_key, UseIntermediateFlushes::kTrue);
3570
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
3571
0
      ExpectedDebugDumpForCollectionWithTTL(collection_key, InitMarkerExpired::kFalse));
3572
0
  MinorCompaction(
3573
0
      HybridTime::FromMicros(1100 + 20 * 1000000 + 1), /* num_files_to_compact */ 2,
3574
0
      /* start_index */ 1);
3575
3576
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
3577
0
SubDocKey(DocKey([], ["c"]), [HT{ physical: 1000 }]) -> {}; ttl: 10.000s               // file 1
3578
0
SubDocKey(DocKey([], ["c"]), ["k0"; HT{ physical: 1100 }]) -> DEL                      // file 8
3579
0
SubDocKey(DocKey([], ["c"]), ["k0"; HT{ physical: 1000 w: 1 }]) -> "v0"; ttl: 10.000s  // file 1
3580
0
SubDocKey(DocKey([], ["c"]), ["k1"; HT{ physical: 1100 }]) -> "vv1"; ttl: 21.000s      // file 8
3581
0
SubDocKey(DocKey([], ["c"]), ["k1"; HT{ physical: 1000 w: 2 }]) -> "v1"; ttl: 10.000s  // file 1
3582
0
SubDocKey(DocKey([], ["c"]), ["k2"; HT{ physical: 1100 }]) -> "vv2"; ttl: 22.000s      // file 4
3583
0
SubDocKey(DocKey([], ["c"]), ["k2"; HT{ physical: 1000 w: 3 }]) -> "v2"; ttl: 10.000s  // file 1
3584
0
SubDocKey(DocKey([], ["c"]), ["k3"; HT{ physical: 1100 }]) -> "vv3"; ttl: 23.000s      // file 5
3585
0
SubDocKey(DocKey([], ["c"]), ["k4"; HT{ physical: 1100 }]) -> "vv4"; ttl: 24.000s      // file 6
3586
0
SubDocKey(DocKey([], ["c"]), ["k5"; HT{ physical: 1100 }]) -> "vv5"; ttl: 25.000s      // file 7
3587
0
  )#");
3588
3589
  // Compact files 4, 5, 6, 7, 8. This should result in creation of a number of delete markers
3590
  // from expired entries. Some expired entries from the first file will stay.
3591
0
  MinorCompaction(
3592
0
      HybridTime::FromMicros(1100 + 24 * 1000000 + 1), /* num_files_to_compact */ 5,
3593
0
      /* start_index */ 1);
3594
3595
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
3596
0
SubDocKey(DocKey([], ["c"]), [HT{ physical: 1000 }]) -> {}; ttl: 10.000s               // file 1
3597
0
SubDocKey(DocKey([], ["c"]), ["k0"; HT{ physical: 1100 }]) -> DEL                      // file 9
3598
0
SubDocKey(DocKey([], ["c"]), ["k0"; HT{ physical: 1000 w: 1 }]) -> "v0"; ttl: 10.000s  // file 1
3599
0
SubDocKey(DocKey([], ["c"]), ["k1"; HT{ physical: 1100 }]) -> DEL                      // file 9
3600
0
SubDocKey(DocKey([], ["c"]), ["k1"; HT{ physical: 1000 w: 2 }]) -> "v1"; ttl: 10.000s  // file 1
3601
0
SubDocKey(DocKey([], ["c"]), ["k2"; HT{ physical: 1100 }]) -> DEL                      // file 9
3602
0
SubDocKey(DocKey([], ["c"]), ["k2"; HT{ physical: 1000 w: 3 }]) -> "v2"; ttl: 10.000s  // file 1
3603
0
SubDocKey(DocKey([], ["c"]), ["k3"; HT{ physical: 1100 }]) -> DEL                      // file 9
3604
0
SubDocKey(DocKey([], ["c"]), ["k4"; HT{ physical: 1100 }]) -> DEL                      // file 9
3605
0
SubDocKey(DocKey([], ["c"]), ["k5"; HT{ physical: 1100 }]) -> "vv5"; ttl: 25.000s      // file 9
3606
0
  )#");
3607
3608
0
}
3609
3610
0
TEST_P(DocDBTestWrapper, CompactionWithTransactions) {
3611
0
  FLAGS_TEST_docdb_sort_weak_intents = true;
3612
3613
0
  const DocKey doc_key(PrimitiveValues("mydockey", 123456));
3614
0
  KeyBytes encoded_doc_key(doc_key.Encode());
3615
0
  ASSERT_OK(SetPrimitive(
3616
0
      DocPath(encoded_doc_key), PrimitiveValue::kObject, 1000_usec_ht));
3617
0
  ASSERT_OK(SetPrimitive(
3618
0
      DocPath(encoded_doc_key, "subkey1"), PrimitiveValue("value1"), 1000_usec_ht));
3619
0
  ASSERT_OK(SetPrimitive(
3620
0
      DocPath(encoded_doc_key, "subkey1"), PrimitiveValue("value2"), 2000_usec_ht));
3621
0
  ASSERT_OK(SetPrimitive(
3622
0
      DocPath(encoded_doc_key, "subkey1"), PrimitiveValue("value3"), 3000_usec_ht));
3623
0
  ASSERT_OK(SetPrimitive(
3624
0
      DocPath(encoded_doc_key), PrimitiveValue::kObject, 4000_usec_ht));
3625
3626
0
  SetTransactionIsolationLevel(IsolationLevel::SNAPSHOT_ISOLATION);
3627
3628
0
  Result<TransactionId> txn1 = FullyDecodeTransactionId("0000000000000001");
3629
0
  const auto kTxn1HT = 5000_usec_ht;
3630
0
  ASSERT_OK(txn1);
3631
0
  SetCurrentTransactionId(*txn1);
3632
0
  ASSERT_OK(SetPrimitive(
3633
0
      DocPath(encoded_doc_key), PrimitiveValue::kObject, kTxn1HT));
3634
0
  ASSERT_OK(SetPrimitive(
3635
0
      DocPath(encoded_doc_key, "subkey1"), PrimitiveValue("value4"), kTxn1HT));
3636
3637
0
  Result<TransactionId> txn2 = FullyDecodeTransactionId("0000000000000002");
3638
0
  const auto kTxn2HT = 6000_usec_ht;
3639
0
  ASSERT_OK(txn2);
3640
0
  SetCurrentTransactionId(*txn2);
3641
0
  ASSERT_OK(SetPrimitive(
3642
0
      DocPath(encoded_doc_key), PrimitiveValue::kObject, kTxn2HT));
3643
0
  ASSERT_OK(SetPrimitive(
3644
0
      DocPath(encoded_doc_key, "subkey2"), PrimitiveValue("value5"), kTxn2HT));
3645
3646
0
  ResetCurrentTransactionId();
3647
0
  TransactionId txn3 = ASSERT_RESULT(FullyDecodeTransactionId("0000000000000003"));
3648
0
  const auto kTxn3HT = 7000_usec_ht;
3649
0
  std::vector<ExternalIntent> intents = {
3650
0
    { DocPath(encoded_doc_key, "subkey3"), Value(PrimitiveValue("value6")) },
3651
0
    { DocPath(encoded_doc_key, "subkey4"), Value(PrimitiveValue("value7")) }
3652
0
  };
3653
0
  Uuid status_tablet = ASSERT_RESULT(Uuid::FromString("4c3e1d91-5ea7-4449-8bb3-8b0a3f9ae903"));
3654
0
  ASSERT_OK(AddExternalIntents(txn3, intents, status_tablet, kTxn3HT));
3655
3656
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
3657
0
SubDocKey(DocKey([], ["mydockey", 123456]), [HT{ physical: 4000 }]) -> {}
3658
0
SubDocKey(DocKey([], ["mydockey", 123456]), [HT{ physical: 1000 }]) -> {}
3659
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey1"; HT{ physical: 3000 }]) -> "value3"
3660
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey1"; HT{ physical: 2000 }]) -> "value2"
3661
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey1"; HT{ physical: 1000 }]) -> "value1"
3662
0
TXN EXT 30303030-3030-3030-3030-303030303033 HT{ physical: 7000 } -> \
3663
0
    IT 03e99a3f0a8bb38b4944a75e911d3e4c [\
3664
0
    SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey3"]) -> "value6", \
3665
0
    SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey4"]) -> "value7"]
3666
0
SubDocKey(DocKey([], []), []) [kWeakRead, kWeakWrite] HT{ physical: 6000 w: 1 } -> \
3667
0
    TransactionId(30303030-3030-3030-3030-303030303032) none
3668
0
SubDocKey(DocKey([], []), []) [kWeakRead, kWeakWrite] HT{ physical: 5000 w: 1 } -> \
3669
0
    TransactionId(30303030-3030-3030-3030-303030303031) none
3670
0
SubDocKey(DocKey([], ["mydockey"]), []) [kWeakRead, kWeakWrite] HT{ physical: 6000 w: 2 } -> \
3671
0
    TransactionId(30303030-3030-3030-3030-303030303032) none
3672
0
SubDocKey(DocKey([], ["mydockey"]), []) [kWeakRead, kWeakWrite] HT{ physical: 5000 w: 2 } -> \
3673
0
    TransactionId(30303030-3030-3030-3030-303030303031) none
3674
0
SubDocKey(DocKey([], ["mydockey", 123456]), []) [kWeakRead, kWeakWrite] HT{ physical: 6000 w: 3 } \
3675
0
    -> TransactionId(30303030-3030-3030-3030-303030303032) none
3676
0
SubDocKey(DocKey([], ["mydockey", 123456]), []) [kWeakRead, kWeakWrite] HT{ physical: 5000 w: 3 } \
3677
0
    -> TransactionId(30303030-3030-3030-3030-303030303031) none
3678
0
SubDocKey(DocKey([], ["mydockey", 123456]), []) [kStrongRead, kStrongWrite] HT{ physical: 6000 } \
3679
0
    -> TransactionId(30303030-3030-3030-3030-303030303032) WriteId(2) {}
3680
0
SubDocKey(DocKey([], ["mydockey", 123456]), []) [kStrongRead, kStrongWrite] HT{ physical: 5000 } \
3681
0
    -> TransactionId(30303030-3030-3030-3030-303030303031) WriteId(0) {}
3682
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey1"]) [kStrongRead, kStrongWrite] \
3683
0
    HT{ physical: 5000 } -> TransactionId(30303030-3030-3030-3030-303030303031) WriteId(1) "value4"
3684
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey2"]) [kStrongRead, kStrongWrite] \
3685
0
    HT{ physical: 6000 } -> TransactionId(30303030-3030-3030-3030-303030303032) WriteId(3) "value5"
3686
0
TXN REV 30303030-3030-3030-3030-303030303031 HT{ physical: 5000 } -> \
3687
0
    SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey1"]) [kStrongRead, kStrongWrite] \
3688
0
    HT{ physical: 5000 }
3689
0
TXN REV 30303030-3030-3030-3030-303030303031 HT{ physical: 5000 w: 1 } -> \
3690
0
    SubDocKey(DocKey([], []), []) [kWeakRead, kWeakWrite] HT{ physical: 5000 w: 1 }
3691
0
TXN REV 30303030-3030-3030-3030-303030303031 HT{ physical: 5000 w: 2 } -> \
3692
0
    SubDocKey(DocKey([], ["mydockey"]), []) [kWeakRead, kWeakWrite] HT{ physical: 5000 w: 2 }
3693
0
TXN REV 30303030-3030-3030-3030-303030303031 HT{ physical: 5000 w: 3 } -> \
3694
0
    SubDocKey(DocKey([], ["mydockey", 123456]), []) [kWeakRead, kWeakWrite] \
3695
0
    HT{ physical: 5000 w: 3 }
3696
0
TXN REV 30303030-3030-3030-3030-303030303032 HT{ physical: 6000 } -> \
3697
0
    SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey2"]) [kStrongRead, kStrongWrite] \
3698
0
    HT{ physical: 6000 }
3699
0
TXN REV 30303030-3030-3030-3030-303030303032 HT{ physical: 6000 w: 1 } -> \
3700
0
    SubDocKey(DocKey([], []), []) [kWeakRead, kWeakWrite] HT{ physical: 6000 w: 1 }
3701
0
TXN REV 30303030-3030-3030-3030-303030303032 HT{ physical: 6000 w: 2 } -> \
3702
0
    SubDocKey(DocKey([], ["mydockey"]), []) [kWeakRead, kWeakWrite] HT{ physical: 6000 w: 2 }
3703
0
TXN REV 30303030-3030-3030-3030-303030303032 HT{ physical: 6000 w: 3 } -> \
3704
0
    SubDocKey(DocKey([], ["mydockey", 123456]), []) [kWeakRead, kWeakWrite] \
3705
0
    HT{ physical: 6000 w: 3 }
3706
0
    )#");
3707
0
  FullyCompactHistoryBefore(3500_usec_ht);
3708
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(
3709
0
      R"#(
3710
0
SubDocKey(DocKey([], ["mydockey", 123456]), [HT{ physical: 4000 }]) -> {}
3711
0
SubDocKey(DocKey([], ["mydockey", 123456]), [HT{ physical: 1000 }]) -> {}
3712
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey1"; HT{ physical: 3000 }]) -> "value3"
3713
0
TXN EXT 30303030-3030-3030-3030-303030303033 HT{ physical: 7000 } -> \
3714
0
    IT 03e99a3f0a8bb38b4944a75e911d3e4c [\
3715
0
    SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey3"]) -> "value6", \
3716
0
    SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey4"]) -> "value7"]
3717
0
SubDocKey(DocKey([], []), []) [kWeakRead, kWeakWrite] HT{ physical: 6000 w: 1 } -> \
3718
0
    TransactionId(30303030-3030-3030-3030-303030303032) none
3719
0
SubDocKey(DocKey([], []), []) [kWeakRead, kWeakWrite] HT{ physical: 5000 w: 1 } -> \
3720
0
    TransactionId(30303030-3030-3030-3030-303030303031) none
3721
0
SubDocKey(DocKey([], ["mydockey"]), []) [kWeakRead, kWeakWrite] HT{ physical: 6000 w: 2 } -> \
3722
0
    TransactionId(30303030-3030-3030-3030-303030303032) none
3723
0
SubDocKey(DocKey([], ["mydockey"]), []) [kWeakRead, kWeakWrite] HT{ physical: 5000 w: 2 } -> \
3724
0
    TransactionId(30303030-3030-3030-3030-303030303031) none
3725
0
SubDocKey(DocKey([], ["mydockey", 123456]), []) [kWeakRead, kWeakWrite] HT{ physical: 6000 w: 3 } \
3726
0
    -> TransactionId(30303030-3030-3030-3030-303030303032) none
3727
0
SubDocKey(DocKey([], ["mydockey", 123456]), []) [kWeakRead, kWeakWrite] HT{ physical: 5000 w: 3 } \
3728
0
    -> TransactionId(30303030-3030-3030-3030-303030303031) none
3729
0
SubDocKey(DocKey([], ["mydockey", 123456]), []) [kStrongRead, kStrongWrite] HT{ physical: 6000 } \
3730
0
    -> TransactionId(30303030-3030-3030-3030-303030303032) WriteId(2) {}
3731
0
SubDocKey(DocKey([], ["mydockey", 123456]), []) [kStrongRead, kStrongWrite] HT{ physical: 5000 } \
3732
0
    -> TransactionId(30303030-3030-3030-3030-303030303031) WriteId(0) {}
3733
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey1"]) [kStrongRead, kStrongWrite] \
3734
0
    HT{ physical: 5000 } -> TransactionId(30303030-3030-3030-3030-303030303031) WriteId(1) "value4"
3735
0
SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey2"]) [kStrongRead, kStrongWrite] \
3736
0
    HT{ physical: 6000 } -> TransactionId(30303030-3030-3030-3030-303030303032) WriteId(3) "value5"
3737
0
TXN REV 30303030-3030-3030-3030-303030303031 HT{ physical: 5000 } -> \
3738
0
    SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey1"]) [kStrongRead, kStrongWrite] \
3739
0
    HT{ physical: 5000 }
3740
0
TXN REV 30303030-3030-3030-3030-303030303031 HT{ physical: 5000 w: 1 } -> \
3741
0
    SubDocKey(DocKey([], []), []) [kWeakRead, kWeakWrite] HT{ physical: 5000 w: 1 }
3742
0
TXN REV 30303030-3030-3030-3030-303030303031 HT{ physical: 5000 w: 2 } -> \
3743
0
    SubDocKey(DocKey([], ["mydockey"]), []) [kWeakRead, kWeakWrite] HT{ physical: 5000 w: 2 }
3744
0
TXN REV 30303030-3030-3030-3030-303030303031 HT{ physical: 5000 w: 3 } -> \
3745
0
    SubDocKey(DocKey([], ["mydockey", 123456]), []) [kWeakRead, kWeakWrite] \
3746
0
    HT{ physical: 5000 w: 3 }
3747
0
TXN REV 30303030-3030-3030-3030-303030303032 HT{ physical: 6000 } -> \
3748
0
    SubDocKey(DocKey([], ["mydockey", 123456]), ["subkey2"]) [kStrongRead, kStrongWrite] \
3749
0
    HT{ physical: 6000 }
3750
0
TXN REV 30303030-3030-3030-3030-303030303032 HT{ physical: 6000 w: 1 } -> \
3751
0
    SubDocKey(DocKey([], []), []) [kWeakRead, kWeakWrite] HT{ physical: 6000 w: 1 }
3752
0
TXN REV 30303030-3030-3030-3030-303030303032 HT{ physical: 6000 w: 2 } -> \
3753
0
    SubDocKey(DocKey([], ["mydockey"]), []) [kWeakRead, kWeakWrite] HT{ physical: 6000 w: 2 }
3754
0
TXN REV 30303030-3030-3030-3030-303030303032 HT{ physical: 6000 w: 3 } -> \
3755
0
    SubDocKey(DocKey([], ["mydockey", 123456]), []) [kWeakRead, kWeakWrite] \
3756
0
    HT{ physical: 6000 w: 3 }
3757
0
    )#");
3758
0
}
3759
3760
0
TEST_P(DocDBTestWrapper, ForceFlushedFrontier) {
3761
  // We run with compactions disabled, because they may interefere with force-setting the OpId.
3762
0
  ASSERT_OK(DisableCompactions());
3763
0
  op_id_ = {1, 1};
3764
0
  rocksdb::UserFrontierPtr flushed_frontier;
3765
0
  for (int i = 1; i < 20; ++i) {
3766
0
    const DocKey doc_key(PrimitiveValues(i));
3767
0
    const KeyBytes encoded_doc_key = doc_key.Encode();
3768
0
    SetupRocksDBState(encoded_doc_key);
3769
0
    ASSERT_OK(FlushRocksDbAndWait());
3770
0
    flushed_frontier = rocksdb()->GetFlushedFrontier();
3771
0
    LOG(INFO) << "Flushed frontier after i=" << i << ": "
3772
0
              << (flushed_frontier ? flushed_frontier->ToString() : "N/A");
3773
0
  }
3774
0
  ASSERT_TRUE(flushed_frontier.get() != nullptr);
3775
0
  ConsensusFrontier consensus_frontier =
3776
0
      down_cast<ConsensusFrontier&>(*flushed_frontier);
3777
0
  ConsensusFrontier new_consensus_frontier = consensus_frontier;
3778
0
  new_consensus_frontier.set_op_id({
3779
0
      consensus_frontier.op_id().term,
3780
0
      consensus_frontier.op_id().index / 2
3781
0
  });
3782
0
  ASSERT_EQ(new_consensus_frontier.op_id().term, consensus_frontier.op_id().term);
3783
0
  ASSERT_LT(new_consensus_frontier.op_id().index, consensus_frontier.op_id().index);
3784
0
  ASSERT_EQ(new_consensus_frontier.hybrid_time(), consensus_frontier.hybrid_time());
3785
0
  ASSERT_EQ(new_consensus_frontier.history_cutoff(), consensus_frontier.history_cutoff());
3786
0
  rocksdb::UserFrontierPtr new_user_frontier_ptr(new ConsensusFrontier(new_consensus_frontier));
3787
3788
0
  LOG(INFO) << "Attempting to change flushed frontier from " << consensus_frontier
3789
0
            << " to " << new_consensus_frontier;
3790
0
  ASSERT_OK(regular_db_->ModifyFlushedFrontier(
3791
0
      new_user_frontier_ptr, rocksdb::FrontierModificationMode::kForce));
3792
0
  LOG(INFO) << "Checking that flushed froniter was set to " << new_consensus_frontier;
3793
0
  ASSERT_EQ(*new_user_frontier_ptr, *regular_db_->GetFlushedFrontier());
3794
3795
0
  LOG(INFO) << "Reopening RocksDB";
3796
0
  ASSERT_OK(ReopenRocksDB());
3797
0
  LOG(INFO) << "Checking that flushed frontier is still set to "
3798
0
            << regular_db_->GetFlushedFrontier()->ToString();
3799
0
  ASSERT_EQ(*new_user_frontier_ptr, *regular_db_->GetFlushedFrontier());
3800
0
}
3801
3802
// Handy code to analyze some DB.
3803
0
TEST_P(DocDBTestWrapper, DISABLED_DumpDB) {
3804
0
  tablet::TabletOptions tablet_options;
3805
0
  rocksdb::Options options;
3806
0
  docdb::InitRocksDBOptions(
3807
0
      &options, "" /* log_prefix */, rocksdb::CreateDBStatisticsForTests(), tablet_options);
3808
3809
0
  rocksdb::DB* rocksdb = nullptr;
3810
0
  std::string db_path = "";
3811
0
  ASSERT_OK(rocksdb::DB::Open(options, db_path, &rocksdb));
3812
3813
0
  rocksdb::ReadOptions read_opts;
3814
0
  read_opts.query_id = rocksdb::kDefaultQueryId;
3815
0
  unique_ptr<rocksdb::Iterator> iter(rocksdb->NewIterator(read_opts));
3816
0
  iter->SeekToFirst();
3817
3818
0
  int txn_meta = 0;
3819
0
  int rev_key = 0;
3820
0
  int intent = 0;
3821
0
  while (iter->Valid()) {
3822
0
    auto key_type = GetKeyType(iter->key(), StorageDbType::kIntents);
3823
0
    if (key_type == KeyType::kTransactionMetadata) {
3824
0
      ++txn_meta;
3825
0
    } else if (key_type == KeyType::kReverseTxnKey) {
3826
0
      ++rev_key;
3827
0
    } else if (key_type == KeyType::kIntentKey) {
3828
0
      ++intent;
3829
0
    } else {
3830
0
      ASSERT_TRUE(false);
3831
0
    }
3832
0
    iter->Next();
3833
0
  }
3834
3835
0
  LOG(INFO) << "TXN meta: " << txn_meta << ", rev key: " << rev_key << ", intents: " << intent;
3836
0
}
3837
3838
0
TEST_P(DocDBTestWrapper, SetHybridTimeFilter) {
3839
0
  auto dwb = MakeDocWriteBatch();
3840
0
  for (int i = 1; i <= 4; ++i) {
3841
0
    ASSERT_OK(WriteSimple(i));
3842
0
  }
3843
3844
0
  ASSERT_OK(FlushRocksDbAndWait());
3845
3846
0
  CloseRocksDB();
3847
3848
0
  RocksDBPatcher patcher(rocksdb_dir_, regular_db_options_);
3849
3850
0
  ASSERT_OK(patcher.Load());
3851
0
  ASSERT_OK(patcher.SetHybridTimeFilter(HybridTime::FromMicros(2000)));
3852
3853
0
  ASSERT_OK(OpenRocksDB());
3854
3855
0
  ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
3856
0
      SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(10); HT{ physical: 1000 }]) -> 1
3857
0
      SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(10); HT{ physical: 2000 }]) -> 2
3858
0
  )#");
3859
3860
0
  ASSERT_OK(WriteSimple(5));
3861
3862
0
  for (int j = 0; j < 3; ++j) {
3863
0
    SCOPED_TRACE(Format("Iteration $0", j));
3864
3865
0
    ASSERT_DOC_DB_DEBUG_DUMP_STR_EQ(R"#(
3866
0
      SubDocKey(DocKey([], ["row1", 11111]), [ColumnId(10); HT{ physical: 1000 }]) -> 1
3867
0
      SubDocKey(DocKey([], ["row2", 22222]), [ColumnId(10); HT{ physical: 2000 }]) -> 2
3868
0
      SubDocKey(DocKey([], ["row5", 55555]), [ColumnId(10); HT{ physical: 5000 }]) -> 5
3869
0
    )#");
3870
3871
0
    if (j == 0) {
3872
0
      ASSERT_OK(FlushRocksDbAndWait());
3873
0
    } else if (j == 1) {
3874
0
      ASSERT_OK(ForceRocksDBCompact(rocksdb()));
3875
0
    }
3876
0
  }
3877
3878
0
}
3879
3880
0
void Append(const char* a, const char* b, std::string* out) {
3881
0
  out->append(a, b);
3882
0
}
3883
3884
0
void PushBack(const std::string& value, std::vector<std::string>* out) {
3885
0
  out->push_back(value);
3886
0
}
3887
3888
0
void Append(const char* a, const char* b, faststring* out) {
3889
0
  out->append(a, b - a);
3890
0
}
3891
3892
0
void PushBack(const faststring& value, std::vector<std::string>* out) {
3893
0
  out->emplace_back(value.c_str(), value.size());
3894
0
}
3895
3896
0
void Append(const char* a, const char* b, boost::container::small_vector_base<char>* out) {
3897
0
  out->insert(out->end(), a, b);
3898
0
}
3899
3900
void PushBack(
3901
0
    const boost::container::small_vector_base<char>& value, std::vector<std::string>* out) {
3902
0
  out->emplace_back(value.begin(), value.end());
3903
0
}
3904
3905
template <size_t SmallLen>
3906
0
void Append(const char* a, const char* b, ByteBuffer<SmallLen>* out) {
3907
0
  out->Append(a, b);
3908
0
}
Unexecuted instantiation: _ZN2yb5docdb6AppendILm8EEEvPKcS3_PNS_10ByteBufferIXT_EEE
Unexecuted instantiation: _ZN2yb5docdb6AppendILm16EEEvPKcS3_PNS_10ByteBufferIXT_EEE
Unexecuted instantiation: _ZN2yb5docdb6AppendILm32EEEvPKcS3_PNS_10ByteBufferIXT_EEE
Unexecuted instantiation: _ZN2yb5docdb6AppendILm64EEEvPKcS3_PNS_10ByteBufferIXT_EEE
3909
3910
template <size_t SmallLen>
3911
0
void PushBack(const ByteBuffer<SmallLen>& value, std::vector<std::string>* out) {
3912
0
  out->push_back(value.ToStringBuffer());
3913
0
}
Unexecuted instantiation: _ZN2yb5docdb8PushBackILm8EEEvRKNS_10ByteBufferIXT_EEEPNSt3__16vectorINS6_12basic_stringIcNS6_11char_traitsIcEENS6_9allocatorIcEEEENSB_ISD_EEEE
Unexecuted instantiation: _ZN2yb5docdb8PushBackILm16EEEvRKNS_10ByteBufferIXT_EEEPNSt3__16vectorINS6_12basic_stringIcNS6_11char_traitsIcEENS6_9allocatorIcEEEENSB_ISD_EEEE
Unexecuted instantiation: _ZN2yb5docdb8PushBackILm32EEEvRKNS_10ByteBufferIXT_EEEPNSt3__16vectorINS6_12basic_stringIcNS6_11char_traitsIcEENS6_9allocatorIcEEEENSB_ISD_EEEE
Unexecuted instantiation: _ZN2yb5docdb8PushBackILm64EEEvRKNS_10ByteBufferIXT_EEEPNSt3__16vectorINS6_12basic_stringIcNS6_11char_traitsIcEENS6_9allocatorIcEEEENSB_ISD_EEEE
3914
3915
constexpr size_t kSourceLen = 32;
3916
const std::string kSource = RandomHumanReadableString(kSourceLen);
3917
3918
template <class T>
3919
0
void TestKeyBytes(const char* title, std::vector<std::string>* out = nullptr) {
3920
#ifdef NDEBUG
3921
  constexpr size_t kIterations = 100000000;
3922
#else
3923
0
  constexpr size_t kIterations = RegularBuildVsSanitizers(10000000, 100000);
3924
0
#endif
3925
0
  const char* source_start = kSource.c_str();
3926
3927
0
  auto start = GetThreadCpuTimeMicros();
3928
0
  T key_bytes;
3929
0
  for (size_t i = kIterations; i-- > 0;) {
3930
0
    key_bytes.clear();
3931
0
    const char* a = source_start + ((i * 102191ULL) & (kSourceLen - 1ULL));
3932
0
    const char* b = source_start + ((i * 99191ULL) & (kSourceLen - 1ULL));
3933
0
    Append(std::min(a, b), std::max(a, b) + 1, &key_bytes);
3934
0
    a = source_start + ((i * 88937ULL) & (kSourceLen - 1ULL));
3935
0
    b = source_start + ((i * 74231ULL) & (kSourceLen - 1ULL));
3936
0
    Append(std::min(a, b), std::max(a, b) + 1, &key_bytes);
3937
0
    a = source_start + ((i * 75983ULL) & (kSourceLen - 1ULL));
3938
0
    b = source_start + ((i * 72977ULL) & (kSourceLen - 1ULL));
3939
0
    Append(std::min(a, b), std::max(a, b) + 1, &key_bytes);
3940
0
    if (out) {
3941
0
      PushBack(key_bytes, out);
3942
0
    }
3943
0
  }
3944
0
  auto time = MonoDelta::FromMicroseconds(GetThreadCpuTimeMicros() - start);
3945
0
  LOG(INFO) << title << ": " << time;
3946
0
}
Unexecuted instantiation: _ZN2yb5docdb12TestKeyBytesINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEEEvPKcPNS2_6vectorIS8_NS6_IS8_EEEE
Unexecuted instantiation: _ZN2yb5docdb12TestKeyBytesINS_10faststringEEEvPKcPNSt3__16vectorINS5_12basic_stringIcNS5_11char_traitsIcEENS5_9allocatorIcEEEENSA_ISC_EEEE
Unexecuted instantiation: _ZN2yb5docdb12TestKeyBytesIN5boost9container12small_vectorIcLm8EvvEEEEvPKcPNSt3__16vectorINS8_12basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEENSD_ISF_EEEE
Unexecuted instantiation: _ZN2yb5docdb12TestKeyBytesIN5boost9container12small_vectorIcLm16EvvEEEEvPKcPNSt3__16vectorINS8_12basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEENSD_ISF_EEEE
Unexecuted instantiation: _ZN2yb5docdb12TestKeyBytesIN5boost9container12small_vectorIcLm32EvvEEEEvPKcPNSt3__16vectorINS8_12basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEENSD_ISF_EEEE
Unexecuted instantiation: _ZN2yb5docdb12TestKeyBytesIN5boost9container12small_vectorIcLm64EvvEEEEvPKcPNSt3__16vectorINS8_12basic_stringIcNS8_11char_traitsIcEENS8_9allocatorIcEEEENSD_ISF_EEEE
Unexecuted instantiation: _ZN2yb5docdb12TestKeyBytesINS_10ByteBufferILm8EEEEEvPKcPNSt3__16vectorINS6_12basic_stringIcNS6_11char_traitsIcEENS6_9allocatorIcEEEENSB_ISD_EEEE
Unexecuted instantiation: _ZN2yb5docdb12TestKeyBytesINS_10ByteBufferILm16EEEEEvPKcPNSt3__16vectorINS6_12basic_stringIcNS6_11char_traitsIcEENS6_9allocatorIcEEEENSB_ISD_EEEE
Unexecuted instantiation: _ZN2yb5docdb12TestKeyBytesINS_10ByteBufferILm32EEEEEvPKcPNSt3__16vectorINS6_12basic_stringIcNS6_11char_traitsIcEENS6_9allocatorIcEEEENSB_ISD_EEEE
Unexecuted instantiation: _ZN2yb5docdb12TestKeyBytesINS_10ByteBufferILm64EEEEEvPKcPNSt3__16vectorINS6_12basic_stringIcNS6_11char_traitsIcEENS6_9allocatorIcEEEENSB_ISD_EEEE
3947
3948
0
TEST_P(DocDBTestWrapper, DISABLED_KeyBuffer) {
3949
0
  TestKeyBytes<std::string>("std::string");
3950
0
  TestKeyBytes<faststring>("faststring");
3951
0
  TestKeyBytes<boost::container::small_vector<char, 8>>("small_vector<char, 8>");
3952
0
  TestKeyBytes<boost::container::small_vector<char, 16>>("small_vector<char, 16>");
3953
0
  TestKeyBytes<boost::container::small_vector<char, 32>>("small_vector<char, 32>");
3954
0
  TestKeyBytes<boost::container::small_vector<char, 64>>("small_vector<char, 64>");
3955
0
  TestKeyBytes<ByteBuffer<8>>("ByteBuffer<8>");
3956
0
  TestKeyBytes<ByteBuffer<16>>("ByteBuffer<16>");
3957
0
  TestKeyBytes<ByteBuffer<32>>("ByteBuffer<32>");
3958
0
  TestKeyBytes<ByteBuffer<64>>("ByteBuffer<64>");
3959
0
}
3960
3961
}  // namespace docdb
3962
}  // namespace yb