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