1 // Copyright (c) 2017-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
5
6 #include <cstring>
7 #include <memory>
8 #include "test_util/testharness.h"
9 #include "utilities/cassandra/format.h"
10 #include "utilities/cassandra/serialize.h"
11 #include "utilities/cassandra/test_utils.h"
12
13
14 namespace ROCKSDB_NAMESPACE {
15 namespace cassandra {
16
TEST(ColumnTest,Column)17 TEST(ColumnTest, Column) {
18 char data[4] = {'d', 'a', 't', 'a'};
19 int8_t mask = 0;
20 int8_t index = 1;
21 int64_t timestamp = 1494022807044;
22 Column c = Column(mask, index, timestamp, sizeof(data), data);
23
24 EXPECT_EQ(c.Index(), index);
25 EXPECT_EQ(c.Timestamp(), timestamp);
26 EXPECT_EQ(c.Size(), 14 + sizeof(data));
27
28 // Verify the serialization.
29 std::string dest;
30 dest.reserve(c.Size() * 2);
31 c.Serialize(&dest);
32
33 EXPECT_EQ(dest.size(), c.Size());
34 std::size_t offset = 0;
35 EXPECT_EQ(Deserialize<int8_t>(dest.c_str(), offset), mask);
36 offset += sizeof(int8_t);
37 EXPECT_EQ(Deserialize<int8_t>(dest.c_str(), offset), index);
38 offset += sizeof(int8_t);
39 EXPECT_EQ(Deserialize<int64_t>(dest.c_str(), offset), timestamp);
40 offset += sizeof(int64_t);
41 EXPECT_EQ(Deserialize<int32_t>(dest.c_str(), offset), sizeof(data));
42 offset += sizeof(int32_t);
43 EXPECT_TRUE(std::memcmp(data, dest.c_str() + offset, sizeof(data)) == 0);
44
45 // Verify the deserialization.
46 std::string saved_dest = dest;
47 std::shared_ptr<Column> c1 = Column::Deserialize(saved_dest.c_str(), 0);
48 EXPECT_EQ(c1->Index(), index);
49 EXPECT_EQ(c1->Timestamp(), timestamp);
50 EXPECT_EQ(c1->Size(), 14 + sizeof(data));
51
52 c1->Serialize(&dest);
53 EXPECT_EQ(dest.size(), 2 * c.Size());
54 EXPECT_TRUE(
55 std::memcmp(dest.c_str(), dest.c_str() + c.Size(), c.Size()) == 0);
56
57 // Verify the ColumnBase::Deserialization.
58 saved_dest = dest;
59 std::shared_ptr<ColumnBase> c2 =
60 ColumnBase::Deserialize(saved_dest.c_str(), c.Size());
61 c2->Serialize(&dest);
62 EXPECT_EQ(dest.size(), 3 * c.Size());
63 EXPECT_TRUE(
64 std::memcmp(dest.c_str() + c.Size(), dest.c_str() + c.Size() * 2, c.Size())
65 == 0);
66 }
67
TEST(ExpiringColumnTest,ExpiringColumn)68 TEST(ExpiringColumnTest, ExpiringColumn) {
69 char data[4] = {'d', 'a', 't', 'a'};
70 int8_t mask = ColumnTypeMask::EXPIRATION_MASK;
71 int8_t index = 3;
72 int64_t timestamp = 1494022807044;
73 int32_t ttl = 3600;
74 ExpiringColumn c = ExpiringColumn(mask, index, timestamp,
75 sizeof(data), data, ttl);
76
77 EXPECT_EQ(c.Index(), index);
78 EXPECT_EQ(c.Timestamp(), timestamp);
79 EXPECT_EQ(c.Size(), 18 + sizeof(data));
80
81 // Verify the serialization.
82 std::string dest;
83 dest.reserve(c.Size() * 2);
84 c.Serialize(&dest);
85
86 EXPECT_EQ(dest.size(), c.Size());
87 std::size_t offset = 0;
88 EXPECT_EQ(Deserialize<int8_t>(dest.c_str(), offset), mask);
89 offset += sizeof(int8_t);
90 EXPECT_EQ(Deserialize<int8_t>(dest.c_str(), offset), index);
91 offset += sizeof(int8_t);
92 EXPECT_EQ(Deserialize<int64_t>(dest.c_str(), offset), timestamp);
93 offset += sizeof(int64_t);
94 EXPECT_EQ(Deserialize<int32_t>(dest.c_str(), offset), sizeof(data));
95 offset += sizeof(int32_t);
96 EXPECT_TRUE(std::memcmp(data, dest.c_str() + offset, sizeof(data)) == 0);
97 offset += sizeof(data);
98 EXPECT_EQ(Deserialize<int32_t>(dest.c_str(), offset), ttl);
99
100 // Verify the deserialization.
101 std::string saved_dest = dest;
102 std::shared_ptr<ExpiringColumn> c1 =
103 ExpiringColumn::Deserialize(saved_dest.c_str(), 0);
104 EXPECT_EQ(c1->Index(), index);
105 EXPECT_EQ(c1->Timestamp(), timestamp);
106 EXPECT_EQ(c1->Size(), 18 + sizeof(data));
107
108 c1->Serialize(&dest);
109 EXPECT_EQ(dest.size(), 2 * c.Size());
110 EXPECT_TRUE(
111 std::memcmp(dest.c_str(), dest.c_str() + c.Size(), c.Size()) == 0);
112
113 // Verify the ColumnBase::Deserialization.
114 saved_dest = dest;
115 std::shared_ptr<ColumnBase> c2 =
116 ColumnBase::Deserialize(saved_dest.c_str(), c.Size());
117 c2->Serialize(&dest);
118 EXPECT_EQ(dest.size(), 3 * c.Size());
119 EXPECT_TRUE(
120 std::memcmp(dest.c_str() + c.Size(), dest.c_str() + c.Size() * 2, c.Size())
121 == 0);
122 }
123
TEST(TombstoneTest,TombstoneCollectable)124 TEST(TombstoneTest, TombstoneCollectable) {
125 int32_t now = (int32_t)time(nullptr);
126 int32_t gc_grace_seconds = 16440;
127 int32_t time_delta_seconds = 10;
128 EXPECT_TRUE(Tombstone(ColumnTypeMask::DELETION_MASK, 0,
129 now - gc_grace_seconds - time_delta_seconds,
130 ToMicroSeconds(now - gc_grace_seconds - time_delta_seconds))
131 .Collectable(gc_grace_seconds));
132 EXPECT_FALSE(Tombstone(ColumnTypeMask::DELETION_MASK, 0,
133 now - gc_grace_seconds + time_delta_seconds,
134 ToMicroSeconds(now - gc_grace_seconds + time_delta_seconds))
135 .Collectable(gc_grace_seconds));
136 }
137
TEST(TombstoneTest,Tombstone)138 TEST(TombstoneTest, Tombstone) {
139 int8_t mask = ColumnTypeMask::DELETION_MASK;
140 int8_t index = 2;
141 int32_t local_deletion_time = 1494022807;
142 int64_t marked_for_delete_at = 1494022807044;
143 Tombstone c = Tombstone(mask, index, local_deletion_time,
144 marked_for_delete_at);
145
146 EXPECT_EQ(c.Index(), index);
147 EXPECT_EQ(c.Timestamp(), marked_for_delete_at);
148 EXPECT_EQ(c.Size(), 14);
149
150 // Verify the serialization.
151 std::string dest;
152 dest.reserve(c.Size() * 2);
153 c.Serialize(&dest);
154
155 EXPECT_EQ(dest.size(), c.Size());
156 std::size_t offset = 0;
157 EXPECT_EQ(Deserialize<int8_t>(dest.c_str(), offset), mask);
158 offset += sizeof(int8_t);
159 EXPECT_EQ(Deserialize<int8_t>(dest.c_str(), offset), index);
160 offset += sizeof(int8_t);
161 EXPECT_EQ(Deserialize<int32_t>(dest.c_str(), offset), local_deletion_time);
162 offset += sizeof(int32_t);
163 EXPECT_EQ(Deserialize<int64_t>(dest.c_str(), offset), marked_for_delete_at);
164
165 // Verify the deserialization.
166 std::shared_ptr<Tombstone> c1 = Tombstone::Deserialize(dest.c_str(), 0);
167 EXPECT_EQ(c1->Index(), index);
168 EXPECT_EQ(c1->Timestamp(), marked_for_delete_at);
169 EXPECT_EQ(c1->Size(), 14);
170
171 c1->Serialize(&dest);
172 EXPECT_EQ(dest.size(), 2 * c.Size());
173 EXPECT_TRUE(
174 std::memcmp(dest.c_str(), dest.c_str() + c.Size(), c.Size()) == 0);
175
176 // Verify the ColumnBase::Deserialization.
177 std::shared_ptr<ColumnBase> c2 =
178 ColumnBase::Deserialize(dest.c_str(), c.Size());
179 c2->Serialize(&dest);
180 EXPECT_EQ(dest.size(), 3 * c.Size());
181 EXPECT_TRUE(
182 std::memcmp(dest.c_str() + c.Size(), dest.c_str() + c.Size() * 2, c.Size())
183 == 0);
184 }
185
186 class RowValueTest : public testing::Test {};
187
TEST(RowValueTest,RowTombstone)188 TEST(RowValueTest, RowTombstone) {
189 int32_t local_deletion_time = 1494022807;
190 int64_t marked_for_delete_at = 1494022807044;
191 RowValue r = RowValue(local_deletion_time, marked_for_delete_at);
192
193 EXPECT_EQ(r.Size(), 12);
194 EXPECT_EQ(r.IsTombstone(), true);
195 EXPECT_EQ(r.LastModifiedTime(), marked_for_delete_at);
196
197 // Verify the serialization.
198 std::string dest;
199 dest.reserve(r.Size() * 2);
200 r.Serialize(&dest);
201
202 EXPECT_EQ(dest.size(), r.Size());
203 std::size_t offset = 0;
204 EXPECT_EQ(Deserialize<int32_t>(dest.c_str(), offset), local_deletion_time);
205 offset += sizeof(int32_t);
206 EXPECT_EQ(Deserialize<int64_t>(dest.c_str(), offset), marked_for_delete_at);
207
208 // Verify the deserialization.
209 RowValue r1 = RowValue::Deserialize(dest.c_str(), r.Size());
210 EXPECT_EQ(r1.Size(), 12);
211 EXPECT_EQ(r1.IsTombstone(), true);
212 EXPECT_EQ(r1.LastModifiedTime(), marked_for_delete_at);
213
214 r1.Serialize(&dest);
215 EXPECT_EQ(dest.size(), 2 * r.Size());
216 EXPECT_TRUE(
217 std::memcmp(dest.c_str(), dest.c_str() + r.Size(), r.Size()) == 0);
218 }
219
TEST(RowValueTest,RowWithColumns)220 TEST(RowValueTest, RowWithColumns) {
221 std::vector<std::shared_ptr<ColumnBase>> columns;
222 int64_t last_modified_time = 1494022807048;
223 std::size_t columns_data_size = 0;
224
225 char e_data[5] = {'e', 'd', 'a', 't', 'a'};
226 int8_t e_index = 0;
227 int64_t e_timestamp = 1494022807044;
228 int32_t e_ttl = 3600;
229 columns.push_back(std::shared_ptr<ExpiringColumn>(
230 new ExpiringColumn(ColumnTypeMask::EXPIRATION_MASK, e_index,
231 e_timestamp, sizeof(e_data), e_data, e_ttl)));
232 columns_data_size += columns[0]->Size();
233
234 char c_data[4] = {'d', 'a', 't', 'a'};
235 int8_t c_index = 1;
236 int64_t c_timestamp = 1494022807048;
237 columns.push_back(std::shared_ptr<Column>(
238 new Column(0, c_index, c_timestamp, sizeof(c_data), c_data)));
239 columns_data_size += columns[1]->Size();
240
241 int8_t t_index = 2;
242 int32_t t_local_deletion_time = 1494022801;
243 int64_t t_marked_for_delete_at = 1494022807043;
244 columns.push_back(std::shared_ptr<Tombstone>(
245 new Tombstone(ColumnTypeMask::DELETION_MASK,
246 t_index, t_local_deletion_time, t_marked_for_delete_at)));
247 columns_data_size += columns[2]->Size();
248
249 RowValue r = RowValue(std::move(columns), last_modified_time);
250
251 EXPECT_EQ(r.Size(), columns_data_size + 12);
252 EXPECT_EQ(r.IsTombstone(), false);
253 EXPECT_EQ(r.LastModifiedTime(), last_modified_time);
254
255 // Verify the serialization.
256 std::string dest;
257 dest.reserve(r.Size() * 2);
258 r.Serialize(&dest);
259
260 EXPECT_EQ(dest.size(), r.Size());
261 std::size_t offset = 0;
262 EXPECT_EQ(Deserialize<int32_t>(dest.c_str(), offset),
263 std::numeric_limits<int32_t>::max());
264 offset += sizeof(int32_t);
265 EXPECT_EQ(Deserialize<int64_t>(dest.c_str(), offset),
266 std::numeric_limits<int64_t>::min());
267 offset += sizeof(int64_t);
268
269 // Column0: ExpiringColumn
270 EXPECT_EQ(Deserialize<int8_t>(dest.c_str(), offset),
271 ColumnTypeMask::EXPIRATION_MASK);
272 offset += sizeof(int8_t);
273 EXPECT_EQ(Deserialize<int8_t>(dest.c_str(), offset), e_index);
274 offset += sizeof(int8_t);
275 EXPECT_EQ(Deserialize<int64_t>(dest.c_str(), offset), e_timestamp);
276 offset += sizeof(int64_t);
277 EXPECT_EQ(Deserialize<int32_t>(dest.c_str(), offset), sizeof(e_data));
278 offset += sizeof(int32_t);
279 EXPECT_TRUE(std::memcmp(e_data, dest.c_str() + offset, sizeof(e_data)) == 0);
280 offset += sizeof(e_data);
281 EXPECT_EQ(Deserialize<int32_t>(dest.c_str(), offset), e_ttl);
282 offset += sizeof(int32_t);
283
284 // Column1: Column
285 EXPECT_EQ(Deserialize<int8_t>(dest.c_str(), offset), 0);
286 offset += sizeof(int8_t);
287 EXPECT_EQ(Deserialize<int8_t>(dest.c_str(), offset), c_index);
288 offset += sizeof(int8_t);
289 EXPECT_EQ(Deserialize<int64_t>(dest.c_str(), offset), c_timestamp);
290 offset += sizeof(int64_t);
291 EXPECT_EQ(Deserialize<int32_t>(dest.c_str(), offset), sizeof(c_data));
292 offset += sizeof(int32_t);
293 EXPECT_TRUE(std::memcmp(c_data, dest.c_str() + offset, sizeof(c_data)) == 0);
294 offset += sizeof(c_data);
295
296 // Column2: Tombstone
297 EXPECT_EQ(Deserialize<int8_t>(dest.c_str(), offset),
298 ColumnTypeMask::DELETION_MASK);
299 offset += sizeof(int8_t);
300 EXPECT_EQ(Deserialize<int8_t>(dest.c_str(), offset), t_index);
301 offset += sizeof(int8_t);
302 EXPECT_EQ(Deserialize<int32_t>(dest.c_str(), offset), t_local_deletion_time);
303 offset += sizeof(int32_t);
304 EXPECT_EQ(Deserialize<int64_t>(dest.c_str(), offset), t_marked_for_delete_at);
305
306 // Verify the deserialization.
307 RowValue r1 = RowValue::Deserialize(dest.c_str(), r.Size());
308 EXPECT_EQ(r1.Size(), columns_data_size + 12);
309 EXPECT_EQ(r1.IsTombstone(), false);
310 EXPECT_EQ(r1.LastModifiedTime(), last_modified_time);
311
312 r1.Serialize(&dest);
313 EXPECT_EQ(dest.size(), 2 * r.Size());
314 EXPECT_TRUE(
315 std::memcmp(dest.c_str(), dest.c_str() + r.Size(), r.Size()) == 0);
316 }
317
TEST(RowValueTest,PurgeTtlShouldRemvoeAllColumnsExpired)318 TEST(RowValueTest, PurgeTtlShouldRemvoeAllColumnsExpired) {
319 int64_t now = time(nullptr);
320
321 auto row_value = CreateTestRowValue({
322 CreateTestColumnSpec(kColumn, 0, ToMicroSeconds(now)),
323 CreateTestColumnSpec(kExpiringColumn, 1, ToMicroSeconds(now - kTtl - 10)), //expired
324 CreateTestColumnSpec(kExpiringColumn, 2, ToMicroSeconds(now)), // not expired
325 CreateTestColumnSpec(kTombstone, 3, ToMicroSeconds(now))
326 });
327
328 bool changed = false;
329 auto purged = row_value.RemoveExpiredColumns(&changed);
330 EXPECT_TRUE(changed);
331 EXPECT_EQ(purged.get_columns().size(), 3);
332 VerifyRowValueColumns(purged.get_columns(), 0, kColumn, 0,
333 ToMicroSeconds(now));
334 VerifyRowValueColumns(purged.get_columns(), 1, kExpiringColumn, 2,
335 ToMicroSeconds(now));
336 VerifyRowValueColumns(purged.get_columns(), 2, kTombstone, 3,
337 ToMicroSeconds(now));
338
339 purged.RemoveExpiredColumns(&changed);
340 EXPECT_FALSE(changed);
341 }
342
TEST(RowValueTest,ExpireTtlShouldConvertExpiredColumnsToTombstones)343 TEST(RowValueTest, ExpireTtlShouldConvertExpiredColumnsToTombstones) {
344 int64_t now = time(nullptr);
345
346 auto row_value = CreateTestRowValue({
347 CreateTestColumnSpec(kColumn, 0, ToMicroSeconds(now)),
348 CreateTestColumnSpec(kExpiringColumn, 1, ToMicroSeconds(now - kTtl - 10)), //expired
349 CreateTestColumnSpec(kExpiringColumn, 2, ToMicroSeconds(now)), // not expired
350 CreateTestColumnSpec(kTombstone, 3, ToMicroSeconds(now))
351 });
352
353 bool changed = false;
354 auto compacted = row_value.ConvertExpiredColumnsToTombstones(&changed);
355 EXPECT_TRUE(changed);
356 EXPECT_EQ(compacted.get_columns().size(), 4);
357 VerifyRowValueColumns(compacted.get_columns(), 0, kColumn, 0,
358 ToMicroSeconds(now));
359 VerifyRowValueColumns(compacted.get_columns(), 1, kTombstone, 1,
360 ToMicroSeconds(now - 10));
361 VerifyRowValueColumns(compacted.get_columns(), 2, kExpiringColumn, 2,
362 ToMicroSeconds(now));
363 VerifyRowValueColumns(compacted.get_columns(), 3, kTombstone, 3,
364 ToMicroSeconds(now));
365
366 compacted.ConvertExpiredColumnsToTombstones(&changed);
367 EXPECT_FALSE(changed);
368 }
369 } // namespace cassandra
370 } // namespace ROCKSDB_NAMESPACE
371
main(int argc,char ** argv)372 int main(int argc, char** argv) {
373 ::testing::InitGoogleTest(&argc, argv);
374 return RUN_ALL_TESTS();
375 }
376