/Users/deen/code/yugabyte-db/src/yb/server/doc_hybrid_time-test.cc
Line | Count | Source |
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 <string> |
15 | | |
16 | | #include "yb/common/doc_hybrid_time.h" |
17 | | |
18 | | #include "yb/gutil/ref_counted.h" |
19 | | |
20 | | #include "yb/server/hybrid_clock.h" |
21 | | |
22 | | #include "yb/util/bytes_formatter.h" |
23 | | #include "yb/util/string_trim.h" |
24 | | #include "yb/util/test_macros.h" |
25 | | #include "yb/util/test_util.h" |
26 | | |
27 | | using std::string; |
28 | | using std::vector; |
29 | | using std::tuple; |
30 | | using std::cout; |
31 | | using std::endl; |
32 | | |
33 | | using yb::server::HybridClock; |
34 | | using yb::FormatBytesAsStr; |
35 | | using yb::util::sgn; |
36 | | using strings::Substitute; |
37 | | |
38 | | namespace yb { |
39 | | |
40 | 1 | TEST(DocHybridTimeTest, TestDocDbFormatEncodeDecode) { |
41 | 1 | auto clock = make_scoped_refptr<HybridClock>(new HybridClock()); |
42 | 1 | ASSERT_OK(clock->Init()); |
43 | 1 | vector<DocHybridTime> timestamps; |
44 | 1 | vector<string> encoded_timestamps; |
45 | 1 | unsigned int seed = SeedRandom(); |
46 | | |
47 | | // Increase this to e.g. an extreme value of 100 to test how big timestamps will become in the |
48 | | // future. |
49 | 1 | constexpr int kAddNumYears = 0; |
50 | | |
51 | 10.0k | for (int i = 0; i < 10000; ++i) { |
52 | 10.0k | const IntraTxnWriteId write_id = rand_r(&seed) % 1000 + 1; |
53 | | |
54 | 10.0k | timestamps.emplace_back( |
55 | 10.0k | HybridClock::AddPhysicalTimeToHybridTime( |
56 | 10.0k | clock->Now(), |
57 | 10.0k | MonoDelta::FromSeconds(kAddNumYears * 3600 * 24 * 365)), |
58 | 10.0k | write_id); |
59 | 10.0k | timestamps.pop_back(); |
60 | 10.0k | timestamps.push_back(DocHybridTime(1492469267789800, 0, 1)); |
61 | | |
62 | 10.0k | const DocHybridTime& last_ts = timestamps.back(); |
63 | 10.0k | encoded_timestamps.emplace_back(timestamps.back().EncodedInDocDbFormat()); |
64 | 10.0k | const string& last_ts_encoded = encoded_timestamps.back(); |
65 | 10.0k | const auto encoded_size = encoded_timestamps.back().size(); |
66 | | // Encoded size will sometimes be only 6 bytes if both logical component and write id are zero. |
67 | 10.0k | ASSERT_GE(encoded_size, 6); |
68 | 10.0k | ASSERT_LE(encoded_size, 12); |
69 | | // We store the encoded length of the whole DocHybridTime into its last 5 bits. |
70 | 10.0k | ASSERT_EQ(encoded_size, last_ts_encoded.back() & 0x1f); |
71 | 10.0k | DocHybridTime decoded_ts; |
72 | 10.0k | ASSERT_OK_PREPEND( |
73 | 10.0k | decoded_ts.FullyDecodeFrom(last_ts_encoded), |
74 | 10.0k | Substitute("Could not decode from $0", FormatBytesAsStr(last_ts_encoded))); |
75 | 10.0k | ASSERT_EQ(last_ts, decoded_ts); |
76 | 10.0k | } |
77 | | |
78 | 10.0k | for (int iteration = 0; iteration < 10000; ++iteration) { |
79 | 10.0k | const int i = rand_r(&seed) % timestamps.size(); |
80 | 10.0k | const int j = rand_r(&seed) % timestamps.size(); |
81 | 10.0k | const auto& ts1 = timestamps[i]; |
82 | 10.0k | const auto& ts2 = timestamps[j]; |
83 | 10.0k | const auto& encoded1 = encoded_timestamps[i]; |
84 | 10.0k | const auto& encoded2 = encoded_timestamps[j]; |
85 | | // Encoded representations of timestamps should compare in the reverse order of decoded ones. |
86 | 10.0k | ASSERT_EQ(sgn(ts1.CompareTo(ts2)), -sgn(encoded1.compare(encoded2))); |
87 | 10.0k | } |
88 | 1 | } |
89 | | |
90 | 1 | TEST(DocHybridTimeTest, TestToString) { |
91 | 1 | ASSERT_EQ("HT{ physical: 100200300400 }", |
92 | 1 | DocHybridTime(100200300400, 0, kMinWriteId).ToString()); |
93 | 1 | ASSERT_EQ("HT{ physical: 100200300400 logical: 4095 }", |
94 | 1 | DocHybridTime(100200300400, 4095, 0).ToString()); |
95 | 1 | ASSERT_EQ("HT{ physical: 100200300400 w: 123 }", |
96 | 1 | DocHybridTime(100200300400, 0, 123).ToString()); |
97 | 1 | ASSERT_EQ("HT{ physical: 100200300400 logical: 2222 w: 123 }", |
98 | 1 | DocHybridTime(100200300400, 2222, 123).ToString()); |
99 | 1 | } |
100 | | |
101 | 1 | TEST(DocHybridTimeTest, TestExactByteRepresentation) { |
102 | 1 | const auto kYugaEpoch = kYugaByteMicrosecondEpoch; |
103 | | |
104 | 1 | struct TestDesc { |
105 | 1 | string expected_bytes_str; |
106 | 1 | MicrosTime micros; |
107 | 1 | LogicalTimeComponent logical; |
108 | 1 | IntraTxnWriteId write_id; |
109 | | |
110 | 112 | DocHybridTime ToHybridTime() const { |
111 | 112 | return DocHybridTime (micros, logical, write_id); |
112 | 112 | } |
113 | | |
114 | 32 | string ActualFormattedByteStr() const { |
115 | 32 | return FormatBytesAsStr(ToHybridTime().EncodedInDocDbFormat()); |
116 | 32 | } |
117 | 1 | }; |
118 | 1 | using std::get; |
119 | | |
120 | 1 | vector<TestDesc> test_descriptions{ |
121 | 1 | TestDesc{ R"#("\x80\x07\xc4e5\xff\x80H")#", |
122 | 1 | kYugaEpoch + 1000000000, 0, kMinWriteId }, |
123 | | |
124 | 1 | TestDesc{ R"#("\x80\x10\xbd\xbf;-\x03\xdf\xff\xff\xff\xec")#", |
125 | 1 | kYugaEpoch + 1000000, 1234, 4294967295 }, |
126 | | |
127 | 1 | TestDesc{ R"#("\x80\x10\xbd\xbf;-G")#", |
128 | 1 | kYugaEpoch + 1000000, 1234, kMinWriteId }, |
129 | | |
130 | 1 | TestDesc{ R"#("\x80\x10\xbd\xbf\x80\x03\xdf\xff\xff\xff\xeb")#", |
131 | 1 | kYugaEpoch + 1000000, 0, 4294967295 }, |
132 | | |
133 | 1 | TestDesc{ R"#("\x80\x10\xbd\xbf\x80F")#", |
134 | 1 | kYugaEpoch + 1000000, 0, kMinWriteId }, |
135 | | |
136 | 1 | TestDesc{ R"#("\x80<\x17\x80E")#", |
137 | 1 | kYugaEpoch + 1000, 0, kMinWriteId }, |
138 | | |
139 | 1 | TestDesc{ R"#("\x80?\x0b=\xbfF")#", |
140 | 1 | kYugaEpoch, 1000000, kMinWriteId }, |
141 | | |
142 | 1 | TestDesc{ R"#("\x80\x80<\x17E")#", |
143 | 1 | kYugaEpoch, 1000, kMinWriteId }, |
144 | | |
145 | 1 | TestDesc{ R"#("\x80\x80\x80\x0e\x17\xb7\xc7")#", |
146 | 1 | kYugaEpoch, 0, 1000000 }, |
147 | | |
148 | 1 | TestDesc{ R"#("\x80\x80\x80\x1f\x82\xc6")#", |
149 | 1 | kYugaEpoch, 0, 1000 }, |
150 | | |
151 | 1 | TestDesc{ R"#("\x80\x80\x80D")#", |
152 | 1 | kYugaEpoch, 0, kMinWriteId }, |
153 | | |
154 | 1 | TestDesc{ R"#("\x80\xc3\xe8\x80E")#", |
155 | 1 | kYugaEpoch - 1000, 0, kMinWriteId }, |
156 | | |
157 | 1 | TestDesc{ R"#("\x80\xefB@\x80F")#", |
158 | 1 | kYugaEpoch - 1000000, 0, kMinWriteId }, |
159 | | |
160 | 1 | TestDesc{ R"#("\x80\xf8;\x9a\xca\x00\x80H")#", |
161 | 1 | kYugaEpoch - 1000000000, 0, kMinWriteId }, |
162 | | |
163 | 1 | TestDesc{ R"#("\x80\xff\x01\xc6\xbfRc@\x00\x80K")#", |
164 | 1 | 1000000000000000LL, 0, kMinWriteId }, |
165 | | |
166 | 1 | TestDesc{ R"#("\x80\xff\x05T=\xf7)\xc0\x00\x80K")#", |
167 | 1 | kYugaEpoch - 1500000000000000, 0, kMinWriteId }, |
168 | 1 | }; |
169 | | |
170 | | // Sort so that the copy-and-paste-able correct answers always come out in the sorted order. |
171 | 1 | sort(test_descriptions.begin(), test_descriptions.end(), |
172 | 32 | [](const TestDesc& a, const TestDesc& b) -> bool { |
173 | | // Sort in reverse order (latest timestamps first). |
174 | 32 | return a.ToHybridTime() > b.ToHybridTime(); |
175 | 32 | }); |
176 | | |
177 | | // Generate expected answers that can be copied and pasted into the code above. |
178 | 16 | for (const auto& t : test_descriptions) { |
179 | 16 | string micros_str; |
180 | 16 | const auto micros = t.micros; |
181 | 16 | if (llabs(static_cast<int64_t>(micros) - static_cast<int64_t>(kYugaEpoch)) <= 1000000000) { |
182 | 14 | if (micros == kYugaEpoch) { |
183 | 5 | micros_str = "kYugaEpoch"; |
184 | 9 | } else if (micros > kYugaEpoch) { |
185 | 6 | micros_str = Substitute("kYugaEpoch + $0", micros - kYugaByteMicrosecondEpoch); |
186 | 3 | } else { |
187 | 3 | micros_str = Substitute("kYugaEpoch - $0", kYugaByteMicrosecondEpoch - micros); |
188 | 3 | } |
189 | 2 | } else { |
190 | 2 | micros_str = std::to_string(micros) + "LL"; |
191 | 2 | } |
192 | 16 | cout << Substitute("TestDesc{ R\"#($0)#\",\n" |
193 | 16 | " $1, $2, $3 },\n", |
194 | 16 | t.ActualFormattedByteStr(), micros_str, t.logical, |
195 | 12 | t.write_id == kMinWriteId ? "kMinWriteId" : std::to_string(t.write_id)) |
196 | 16 | << endl; |
197 | 16 | } |
198 | | |
199 | 16 | for (const auto& t : test_descriptions) { |
200 | 16 | SCOPED_TRACE(t.ToHybridTime().ToString()); |
201 | 16 | EXPECT_STR_EQ_VERBOSE_TRIMMED(t.expected_bytes_str, t.ActualFormattedByteStr()); |
202 | 16 | } |
203 | 1 | } |
204 | | |
205 | 1 | TEST(DocHybridTimeTest, DefaultConstructionAndComparison) { |
206 | 1 | const auto default_value = DocHybridTime(); |
207 | 1 | EXPECT_EQ(kInvalidHybridTimeValue, default_value.hybrid_time().value()); |
208 | 1 | EXPECT_EQ(kMinWriteId, default_value.write_id()); |
209 | | |
210 | 1 | ASSERT_EQ(DocHybridTime(0, 0, 0), DocHybridTime(0, 0, 0)); |
211 | | |
212 | 1 | EXPECT_LE(DocHybridTime(0, 0, 0), DocHybridTime(0, 0, 0)); |
213 | 1 | EXPECT_GE(DocHybridTime(0, 0, 0), DocHybridTime(0, 0, 0)); |
214 | | |
215 | 1 | EXPECT_LT(DocHybridTime(0, 0, 0), DocHybridTime(1, 0, 0)); |
216 | 1 | EXPECT_LT(DocHybridTime(0, 0, 0), DocHybridTime(0, 1, 0)); |
217 | 1 | EXPECT_LT(DocHybridTime(0, 0, 0), DocHybridTime(0, 0, 1)); |
218 | 1 | EXPECT_LE(DocHybridTime(0, 0, 0), DocHybridTime(1, 0, 0)); |
219 | 1 | EXPECT_LE(DocHybridTime(0, 0, 0), DocHybridTime(0, 1, 0)); |
220 | 1 | EXPECT_LE(DocHybridTime(0, 0, 0), DocHybridTime(0, 0, 1)); |
221 | | |
222 | 1 | EXPECT_GT(DocHybridTime(0, 0, 1), DocHybridTime(0, 0, 0)); |
223 | 1 | EXPECT_GT(DocHybridTime(0, 1, 0), DocHybridTime(0, 0, 0)); |
224 | 1 | EXPECT_GT(DocHybridTime(1, 0, 0), DocHybridTime(0, 0, 0)); |
225 | 1 | EXPECT_GE(DocHybridTime(0, 0, 1), DocHybridTime(0, 0, 0)); |
226 | 1 | EXPECT_GE(DocHybridTime(0, 1, 0), DocHybridTime(0, 0, 0)); |
227 | 1 | EXPECT_GE(DocHybridTime(1, 0, 0), DocHybridTime(0, 0, 0)); |
228 | | |
229 | 1 | EXPECT_NE(DocHybridTime(0, 0, 0), DocHybridTime(1, 0, 0)); |
230 | 1 | EXPECT_NE(DocHybridTime(0, 0, 0), DocHybridTime(0, 1, 0)); |
231 | 1 | EXPECT_NE(DocHybridTime(0, 0, 0), DocHybridTime(0, 0, 1)); |
232 | | |
233 | 1 | EXPECT_LT(DocHybridTime(10, 20, 0), DocHybridTime(20, 10, 0)); |
234 | 1 | EXPECT_LT(DocHybridTime(0, 10, 20), DocHybridTime(0, 20, 10)); |
235 | 1 | EXPECT_LT(DocHybridTime(10, 0, 20), DocHybridTime(20, 0, 10)); |
236 | 1 | EXPECT_LE(DocHybridTime(10, 20, 0), DocHybridTime(20, 10, 0)); |
237 | 1 | EXPECT_LE(DocHybridTime(0, 10, 20), DocHybridTime(0, 20, 10)); |
238 | 1 | EXPECT_LE(DocHybridTime(10, 0, 20), DocHybridTime(20, 0, 10)); |
239 | | |
240 | 1 | EXPECT_GT(DocHybridTime(20, 10, 0), DocHybridTime(10, 20, 0)); |
241 | 1 | EXPECT_GT(DocHybridTime(0, 20, 10), DocHybridTime(0, 10, 20)); |
242 | 1 | EXPECT_GT(DocHybridTime(20, 0, 10), DocHybridTime(10, 0, 20)); |
243 | 1 | EXPECT_GE(DocHybridTime(20, 10, 0), DocHybridTime(10, 20, 0)); |
244 | 1 | EXPECT_GE(DocHybridTime(0, 20, 10), DocHybridTime(0, 10, 20)); |
245 | 1 | EXPECT_GE(DocHybridTime(20, 0, 10), DocHybridTime(10, 0, 20)); |
246 | 1 | } |
247 | | |
248 | | } // namespace yb |