/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 × = 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 |