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 /**
7  * The encoding of Cassandra Row Value.
8  *
9  * A Cassandra Row Value could either be a row tombstone,
10  * or contains multiple columns, it has following fields:
11  *
12  * struct row_value {
13  *   int32_t local_deletion_time;  // Time in second when the row is deleted,
14  *                                 // only used for Cassandra tombstone gc.
15  *   int64_t marked_for_delete_at; // Ms that marked this row is deleted.
16  *   struct column_base columns[]; // For non tombstone row, all columns
17  *                                 // are stored here.
18  * }
19  *
20  * If the local_deletion_time and marked_for_delete_at is set, then this is
21  * a tombstone, otherwise it contains multiple columns.
22  *
23  * There are three type of Columns: Normal Column, Expiring Column and Column
24  * Tombstone, which have following fields:
25  *
26  * // Identify the type of the column.
27  * enum mask {
28  *   DELETION_MASK = 0x01,
29  *   EXPIRATION_MASK = 0x02,
30  * };
31  *
32  * struct column  {
33  *   int8_t mask = 0;
34  *   int8_t index;
35  *   int64_t timestamp;
36  *   int32_t value_length;
37  *   char value[value_length];
38  * }
39  *
40  * struct expiring_column  {
41  *   int8_t mask = mask.EXPIRATION_MASK;
42  *   int8_t index;
43  *   int64_t timestamp;
44  *   int32_t value_length;
45  *   char value[value_length];
46  *   int32_t ttl;
47  * }
48  *
49  * struct tombstone_column  {
50  *   int8_t mask = mask.DELETION_MASK;
51  *   int8_t index;
52  *   int32_t local_deletion_time; // Similar to row_value's field.
53  *   int64_t marked_for_delete_at;
54  *  }
55  */
56 
57 #pragma once
58 #include <chrono>
59 #include <memory>
60 #include <vector>
61 #include "rocksdb/merge_operator.h"
62 #include "rocksdb/slice.h"
63 #include "test_util/testharness.h"
64 
65 namespace ROCKSDB_NAMESPACE {
66 namespace cassandra {
67 
68 // Identify the type of the column.
69 enum ColumnTypeMask {
70   DELETION_MASK = 0x01,
71   EXPIRATION_MASK = 0x02,
72 };
73 
74 
75 class ColumnBase {
76 public:
77   ColumnBase(int8_t mask, int8_t index);
78   virtual ~ColumnBase() = default;
79 
80   virtual int64_t Timestamp() const = 0;
81   virtual int8_t Mask() const;
82   virtual int8_t Index() const;
83   virtual std::size_t Size() const;
84   virtual void Serialize(std::string* dest) const;
85   static std::shared_ptr<ColumnBase> Deserialize(const char* src,
86                                                  std::size_t offset);
87 
88 private:
89   int8_t mask_;
90   int8_t index_;
91 };
92 
93 class Column : public ColumnBase {
94 public:
95   Column(int8_t mask, int8_t index, int64_t timestamp,
96     int32_t value_size, const char* value);
97 
98   virtual int64_t Timestamp() const override;
99   virtual std::size_t Size() const override;
100   virtual void Serialize(std::string* dest) const override;
101   static std::shared_ptr<Column> Deserialize(const char* src,
102                                              std::size_t offset);
103 
104 private:
105   int64_t timestamp_;
106   int32_t value_size_;
107   const char* value_;
108 };
109 
110 class Tombstone : public ColumnBase {
111 public:
112   Tombstone(int8_t mask, int8_t index,
113     int32_t local_deletion_time, int64_t marked_for_delete_at);
114 
115   virtual int64_t Timestamp() const override;
116   virtual std::size_t Size() const override;
117   virtual void Serialize(std::string* dest) const override;
118   bool Collectable(int32_t gc_grace_period) const;
119   static std::shared_ptr<Tombstone> Deserialize(const char* src,
120                                                 std::size_t offset);
121 
122 private:
123   int32_t local_deletion_time_;
124   int64_t marked_for_delete_at_;
125 };
126 
127 class ExpiringColumn : public Column {
128 public:
129   ExpiringColumn(int8_t mask, int8_t index, int64_t timestamp,
130     int32_t value_size, const char* value, int32_t ttl);
131 
132   virtual std::size_t Size() const override;
133   virtual void Serialize(std::string* dest) const override;
134   bool Expired() const;
135   std::shared_ptr<Tombstone> ToTombstone() const;
136 
137   static std::shared_ptr<ExpiringColumn> Deserialize(const char* src,
138                                                      std::size_t offset);
139 
140 private:
141   int32_t ttl_;
142   std::chrono::time_point<std::chrono::system_clock> TimePoint() const;
143   std::chrono::seconds Ttl() const;
144 };
145 
146 typedef std::vector<std::shared_ptr<ColumnBase>> Columns;
147 
148 class RowValue {
149 public:
150   // Create a Row Tombstone.
151   RowValue(int32_t local_deletion_time, int64_t marked_for_delete_at);
152   // Create a Row containing columns.
153   RowValue(Columns columns,
154            int64_t last_modified_time);
155   RowValue(const RowValue& /*that*/) = delete;
156   RowValue(RowValue&& /*that*/) noexcept = default;
157   RowValue& operator=(const RowValue& /*that*/) = delete;
158   RowValue& operator=(RowValue&& /*that*/) = default;
159 
160   std::size_t Size() const;;
161   bool IsTombstone() const;
162   // For Tombstone this returns the marked_for_delete_at_,
163   // otherwise it returns the max timestamp of containing columns.
164   int64_t LastModifiedTime() const;
165   void Serialize(std::string* dest) const;
166   RowValue RemoveExpiredColumns(bool* changed) const;
167   RowValue ConvertExpiredColumnsToTombstones(bool* changed) const;
168   RowValue RemoveTombstones(int32_t gc_grace_period) const;
169   bool Empty() const;
170 
171   static RowValue Deserialize(const char* src, std::size_t size);
172   // Merge multiple rows according to their timestamp.
173   static RowValue Merge(std::vector<RowValue>&& values);
174 
175 private:
176   int32_t local_deletion_time_;
177   int64_t marked_for_delete_at_;
178   Columns columns_;
179   int64_t last_modified_time_;
180 
181   FRIEND_TEST(RowValueTest, PurgeTtlShouldRemvoeAllColumnsExpired);
182   FRIEND_TEST(RowValueTest, ExpireTtlShouldConvertExpiredColumnsToTombstones);
183   FRIEND_TEST(RowValueMergeTest, Merge);
184   FRIEND_TEST(RowValueMergeTest, MergeWithRowTombstone);
185   FRIEND_TEST(CassandraFunctionalTest, SimpleMergeTest);
186   FRIEND_TEST(
187     CassandraFunctionalTest, CompactionShouldConvertExpiredColumnsToTombstone);
188   FRIEND_TEST(
189     CassandraFunctionalTest, CompactionShouldPurgeExpiredColumnsIfPurgeTtlIsOn);
190   FRIEND_TEST(
191     CassandraFunctionalTest, CompactionShouldRemoveRowWhenAllColumnExpiredIfPurgeTtlIsOn);
192   FRIEND_TEST(CassandraFunctionalTest,
193               CompactionShouldRemoveTombstoneExceedingGCGracePeriod);
194 };
195 
196 } // namepsace cassandrda
197 }  // namespace ROCKSDB_NAMESPACE
198