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