/Users/deen/code/yugabyte-db/src/yb/docdb/consensus_frontier-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 | | #include <google/protobuf/any.pb.h> |
14 | | |
15 | | #include "yb/docdb/consensus_frontier.h" |
16 | | #include "yb/docdb/docdb.pb.h" |
17 | | #include "yb/gutil/casts.h" |
18 | | #include "yb/rocksdb/metadata.h" |
19 | | #include "yb/util/test_util.h" |
20 | | |
21 | | using rocksdb::UpdateUserValueType; |
22 | | |
23 | | namespace yb { |
24 | | namespace docdb { |
25 | | |
26 | | class ConsensusFrontierTest : public YBTest { |
27 | | }; |
28 | | |
29 | | namespace { |
30 | | |
31 | 0 | std::string PbToString(const ConsensusFrontierPB& pb) { |
32 | 0 | google::protobuf::Any any; |
33 | 0 | any.PackFrom(pb); |
34 | 0 | ConsensusFrontier frontier; |
35 | 0 | frontier.FromPB(any); |
36 | 0 | return frontier.ToString(); |
37 | 0 | } |
38 | | |
39 | | } // anonymous namespace |
40 | | |
41 | | TEST_F(ConsensusFrontierTest, TestUpdates) { |
42 | | { |
43 | | ConsensusFrontier frontier; |
44 | | EXPECT_TRUE(frontier.Equals(frontier)); |
45 | | EXPECT_EQ( |
46 | | "{ op_id: 0.0 hybrid_time: <invalid> history_cutoff: <invalid> " |
47 | | "hybrid_time_filter: <invalid> max_value_level_ttl_expiration_time: <invalid> }", |
48 | | frontier.ToString()); |
49 | | EXPECT_TRUE(frontier.IsUpdateValid(frontier, UpdateUserValueType::kLargest)); |
50 | | EXPECT_TRUE(frontier.IsUpdateValid(frontier, UpdateUserValueType::kSmallest)); |
51 | | |
52 | | ConsensusFrontier opid1{{0, 1}, HybridTime::kInvalid, HybridTime::kInvalid}; |
53 | | EXPECT_TRUE(frontier.IsUpdateValid(opid1, UpdateUserValueType::kLargest)); |
54 | | } |
55 | | |
56 | | { |
57 | | ConsensusFrontier frontier{{1, 1}, 1000_usec_ht, 500_usec_ht}; |
58 | | EXPECT_EQ( |
59 | | "{ op_id: 1.1 hybrid_time: { physical: 1000 } history_cutoff: { physical: 500 } " |
60 | | "hybrid_time_filter: <invalid> max_value_level_ttl_expiration_time: <invalid> }", |
61 | | frontier.ToString()); |
62 | | ConsensusFrontier higher_idx{{1, 2}, 1000_usec_ht, 500_usec_ht}; |
63 | | ConsensusFrontier higher_ht{{1, 1}, 1001_usec_ht, 500_usec_ht}; |
64 | | ConsensusFrontier higher_cutoff{{1, 1}, 1000_usec_ht, 501_usec_ht}; |
65 | | ConsensusFrontier higher_idx_lower_ht{{1, 2}, 999_usec_ht, 500_usec_ht}; |
66 | | |
67 | | EXPECT_TRUE(higher_idx.Dominates(frontier, UpdateUserValueType::kLargest)); |
68 | | EXPECT_TRUE(higher_ht.Dominates(frontier, UpdateUserValueType::kLargest)); |
69 | | EXPECT_TRUE(higher_cutoff.Dominates(frontier, UpdateUserValueType::kLargest)); |
70 | | EXPECT_FALSE(higher_idx.Dominates(frontier, UpdateUserValueType::kSmallest)); |
71 | | EXPECT_FALSE(higher_ht.Dominates(frontier, UpdateUserValueType::kSmallest)); |
72 | | EXPECT_FALSE(higher_cutoff.Dominates(frontier, UpdateUserValueType::kSmallest)); |
73 | | EXPECT_FALSE(frontier.Dominates(higher_idx, UpdateUserValueType::kLargest)); |
74 | | EXPECT_FALSE(frontier.Dominates(higher_ht, UpdateUserValueType::kLargest)); |
75 | | EXPECT_FALSE(frontier.Dominates(higher_cutoff, UpdateUserValueType::kLargest)); |
76 | | EXPECT_TRUE(frontier.Dominates(higher_idx, UpdateUserValueType::kSmallest)); |
77 | | EXPECT_TRUE(frontier.Dominates(higher_ht, UpdateUserValueType::kSmallest)); |
78 | | EXPECT_TRUE(frontier.Dominates(higher_cutoff, UpdateUserValueType::kSmallest)); |
79 | | |
80 | | // frontier and higher_idx_lower_ht are "incomparable" according to the "dominates" ordering. |
81 | | EXPECT_FALSE(frontier.Dominates(higher_idx_lower_ht, UpdateUserValueType::kSmallest)); |
82 | | EXPECT_FALSE(frontier.Dominates(higher_idx_lower_ht, UpdateUserValueType::kLargest)); |
83 | | EXPECT_FALSE(higher_idx_lower_ht.Dominates(frontier, UpdateUserValueType::kSmallest)); |
84 | | EXPECT_FALSE(higher_idx_lower_ht.Dominates(frontier, UpdateUserValueType::kLargest)); |
85 | | |
86 | | EXPECT_TRUE(frontier.IsUpdateValid(higher_idx, UpdateUserValueType::kLargest)); |
87 | | EXPECT_TRUE(frontier.IsUpdateValid(higher_ht, UpdateUserValueType::kLargest)); |
88 | | EXPECT_FALSE(higher_idx.IsUpdateValid(frontier, UpdateUserValueType::kLargest)); |
89 | | EXPECT_FALSE(higher_ht.IsUpdateValid(frontier, UpdateUserValueType::kLargest)); |
90 | | EXPECT_FALSE(frontier.IsUpdateValid(higher_idx, UpdateUserValueType::kSmallest)); |
91 | | EXPECT_FALSE(frontier.IsUpdateValid(higher_ht, UpdateUserValueType::kSmallest)); |
92 | | EXPECT_TRUE(higher_idx.IsUpdateValid(frontier, UpdateUserValueType::kSmallest)); |
93 | | EXPECT_TRUE(higher_ht.IsUpdateValid(frontier, UpdateUserValueType::kSmallest)); |
94 | | |
95 | | EXPECT_FALSE(higher_idx_lower_ht.IsUpdateValid(frontier, UpdateUserValueType::kLargest)); |
96 | | EXPECT_FALSE(frontier.IsUpdateValid(higher_idx_lower_ht, UpdateUserValueType::kSmallest)); |
97 | | |
98 | | // It is OK if a later compaction runs at a lower history_cutoff. |
99 | | EXPECT_TRUE(frontier.IsUpdateValid(higher_cutoff, UpdateUserValueType::kLargest)); |
100 | | EXPECT_TRUE(frontier.IsUpdateValid(higher_cutoff, UpdateUserValueType::kSmallest)); |
101 | | EXPECT_TRUE(higher_cutoff.IsUpdateValid(frontier, UpdateUserValueType::kLargest)); |
102 | | EXPECT_TRUE(higher_cutoff.IsUpdateValid(frontier, UpdateUserValueType::kSmallest)); |
103 | | |
104 | | // Zero OpId should be considered as an undefined value, not causing any errors. |
105 | | ConsensusFrontier zero_op_id{{0, 0}, HybridTime::kInvalid, HybridTime::kInvalid}; |
106 | | EXPECT_TRUE(frontier.IsUpdateValid(zero_op_id, UpdateUserValueType::kLargest)); |
107 | | EXPECT_TRUE(frontier.IsUpdateValid(zero_op_id, UpdateUserValueType::kSmallest)); |
108 | | EXPECT_TRUE(zero_op_id.IsUpdateValid(frontier, UpdateUserValueType::kLargest)); |
109 | | EXPECT_TRUE(zero_op_id.IsUpdateValid(frontier, UpdateUserValueType::kSmallest)); |
110 | | } |
111 | | |
112 | | ConsensusFrontierPB pb; |
113 | | pb.mutable_op_id()->set_term(0); |
114 | | pb.mutable_op_id()->set_index(0); |
115 | | EXPECT_EQ( |
116 | | PbToString(pb), |
117 | | "{ op_id: 0.0 hybrid_time: <min> history_cutoff: <invalid> " |
118 | | "hybrid_time_filter: <invalid> max_value_level_ttl_expiration_time: <invalid> }"); |
119 | | |
120 | | pb.mutable_op_id()->set_term(2); |
121 | | pb.mutable_op_id()->set_index(3); |
122 | | EXPECT_EQ( |
123 | | PbToString(pb), |
124 | | "{ op_id: 2.3 hybrid_time: <min> history_cutoff: <invalid> " |
125 | | "hybrid_time_filter: <invalid> max_value_level_ttl_expiration_time: <invalid> }"); |
126 | | |
127 | | pb.set_hybrid_time(100000); |
128 | | EXPECT_EQ( |
129 | | PbToString(pb), |
130 | | "{ op_id: 2.3 hybrid_time: { physical: 24 logical: 1696 } history_cutoff: <invalid> " |
131 | | "hybrid_time_filter: <invalid> max_value_level_ttl_expiration_time: <invalid> }"); |
132 | | |
133 | | pb.set_history_cutoff(200000); |
134 | | EXPECT_EQ( |
135 | | PbToString(pb), |
136 | | "{ op_id: 2.3 hybrid_time: { physical: 24 logical: 1696 } " |
137 | | "history_cutoff: { physical: 48 logical: 3392 } " |
138 | | "hybrid_time_filter: <invalid> max_value_level_ttl_expiration_time: <invalid> }"); |
139 | | } |
140 | | |
141 | | TEST_F(ConsensusFrontierTest, TestUpdateExpirationTime) { |
142 | | const HybridTime smallHT = 1000_usec_ht; |
143 | | const HybridTime largeHT = 2000_usec_ht; |
144 | | const HybridTime maxHT = HybridTime::kMax; |
145 | | |
146 | | // Three frontiers with the same op_id, ht, and history_cuttoff, |
147 | | // but different expiration times. |
148 | | ConsensusFrontier noExpiry{{1, 1}, 1000_usec_ht, 500_usec_ht}; |
149 | | EXPECT_EQ(noExpiry.max_value_level_ttl_expiration_time(), HybridTime::kInvalid); |
150 | | |
151 | | ConsensusFrontier smallExpiry{{1, 1}, 1000_usec_ht, 500_usec_ht}; |
152 | | smallExpiry.set_max_value_level_ttl_expiration_time(smallHT); |
153 | | EXPECT_EQ(smallExpiry.max_value_level_ttl_expiration_time(), smallHT); |
154 | | |
155 | | ConsensusFrontier largeExpiry{{1, 1}, 1000_usec_ht, 500_usec_ht}; |
156 | | largeExpiry.set_max_value_level_ttl_expiration_time(largeHT); |
157 | | EXPECT_EQ(largeExpiry.max_value_level_ttl_expiration_time(), largeHT); |
158 | | |
159 | | ConsensusFrontier maxExpiry{{1, 1}, 1000_usec_ht, 500_usec_ht}; |
160 | | maxExpiry.set_max_value_level_ttl_expiration_time(maxHT); |
161 | | EXPECT_EQ(maxExpiry.max_value_level_ttl_expiration_time(), maxHT); |
162 | | |
163 | | // Update no expiration frontier with another invalid expiration. |
164 | | auto expiryClone = noExpiry.Clone(); |
165 | | ConsensusFrontier noExpiry2 = {{2, 2}, 2000_usec_ht, 1000_usec_ht}; |
166 | | expiryClone->Update(noExpiry2, UpdateUserValueType::kLargest); |
167 | | auto consensusClone = down_cast<ConsensusFrontier&>(*expiryClone); |
168 | | EXPECT_EQ(consensusClone.max_value_level_ttl_expiration_time(), HybridTime::kInvalid); |
169 | | |
170 | | // Update frontier with no expiration with one with an expiration. |
171 | | expiryClone->Update(smallExpiry, UpdateUserValueType::kLargest); |
172 | | consensusClone = down_cast<ConsensusFrontier&>(*expiryClone); |
173 | | EXPECT_EQ(consensusClone.max_value_level_ttl_expiration_time(), smallHT); |
174 | | |
175 | | // Update same frontier with a larger expiration |
176 | | expiryClone->Update(largeExpiry, UpdateUserValueType::kLargest); |
177 | | consensusClone = down_cast<ConsensusFrontier&>(*expiryClone); |
178 | | EXPECT_EQ(consensusClone.max_value_level_ttl_expiration_time(), largeHT); |
179 | | |
180 | | // Try to update same frontier with the smaller expiration. Should keep the higher expiration. |
181 | | expiryClone->Update(smallExpiry, UpdateUserValueType::kLargest); |
182 | | consensusClone = down_cast<ConsensusFrontier&>(*expiryClone); |
183 | | EXPECT_EQ(consensusClone.max_value_level_ttl_expiration_time(), largeHT); |
184 | | |
185 | | // Update with the maximum expiration. |
186 | | expiryClone->Update(maxExpiry, UpdateUserValueType::kLargest); |
187 | | consensusClone = down_cast<ConsensusFrontier&>(*expiryClone); |
188 | | EXPECT_EQ(consensusClone.max_value_level_ttl_expiration_time(), maxHT); |
189 | | |
190 | | // Update with another invalid expiration time. |
191 | | expiryClone->Update(noExpiry2, UpdateUserValueType::kLargest); |
192 | | consensusClone = down_cast<ConsensusFrontier&>(*expiryClone); |
193 | | EXPECT_EQ(consensusClone.max_value_level_ttl_expiration_time(), maxHT); |
194 | | } |
195 | | |
196 | | } // namespace docdb |
197 | | } // namespace yb |