1 // Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. 2 // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 3 // Use of this source code is governed by a BSD-style license that can be 4 // found in the LICENSE file. See the AUTHORS file for names of contributors. 5 6 #pragma once 7 8 #ifndef ROCKSDB_LITE 9 #include <deque> 10 #include <string> 11 #include <vector> 12 13 #include "db/db_impl/db_impl.h" 14 #include "rocksdb/compaction_filter.h" 15 #include "rocksdb/db.h" 16 #include "rocksdb/env.h" 17 #include "rocksdb/merge_operator.h" 18 #include "rocksdb/utilities/db_ttl.h" 19 #include "rocksdb/utilities/utility_db.h" 20 21 #ifdef _WIN32 22 // Windows API macro interference 23 #undef GetCurrentTime 24 #endif 25 26 namespace ROCKSDB_NAMESPACE { 27 28 class DBWithTTLImpl : public DBWithTTL { 29 public: 30 static void SanitizeOptions(int32_t ttl, ColumnFamilyOptions* options, 31 Env* env); 32 33 explicit DBWithTTLImpl(DB* db); 34 35 virtual ~DBWithTTLImpl(); 36 37 virtual Status Close() override; 38 39 Status CreateColumnFamilyWithTtl(const ColumnFamilyOptions& options, 40 const std::string& column_family_name, 41 ColumnFamilyHandle** handle, 42 int ttl) override; 43 44 Status CreateColumnFamily(const ColumnFamilyOptions& options, 45 const std::string& column_family_name, 46 ColumnFamilyHandle** handle) override; 47 48 using StackableDB::Put; 49 virtual Status Put(const WriteOptions& options, 50 ColumnFamilyHandle* column_family, const Slice& key, 51 const Slice& val) override; 52 53 using StackableDB::Get; 54 virtual Status Get(const ReadOptions& options, 55 ColumnFamilyHandle* column_family, const Slice& key, 56 PinnableSlice* value) override; 57 58 using StackableDB::MultiGet; 59 virtual std::vector<Status> MultiGet( 60 const ReadOptions& options, 61 const std::vector<ColumnFamilyHandle*>& column_family, 62 const std::vector<Slice>& keys, 63 std::vector<std::string>* values) override; 64 65 using StackableDB::KeyMayExist; 66 virtual bool KeyMayExist(const ReadOptions& options, 67 ColumnFamilyHandle* column_family, const Slice& key, 68 std::string* value, 69 bool* value_found = nullptr) override; 70 71 using StackableDB::Merge; 72 virtual Status Merge(const WriteOptions& options, 73 ColumnFamilyHandle* column_family, const Slice& key, 74 const Slice& value) override; 75 76 virtual Status Write(const WriteOptions& opts, WriteBatch* updates) override; 77 78 using StackableDB::NewIterator; 79 virtual Iterator* NewIterator(const ReadOptions& opts, 80 ColumnFamilyHandle* column_family) override; 81 GetBaseDB()82 virtual DB* GetBaseDB() override { return db_; } 83 84 static bool IsStale(const Slice& value, int32_t ttl, Env* env); 85 86 static Status AppendTS(const Slice& val, std::string* val_with_ts, Env* env); 87 88 static Status SanityCheckTimestamp(const Slice& str); 89 90 static Status StripTS(std::string* str); 91 92 static Status StripTS(PinnableSlice* str); 93 94 static const uint32_t kTSLength = sizeof(int32_t); // size of timestamp 95 96 static const int32_t kMinTimestamp = 1368146402; // 05/09/2013:5:40PM GMT-8 97 98 static const int32_t kMaxTimestamp = 2147483647; // 01/18/2038:7:14PM GMT-8 99 SetTtl(int32_t ttl)100 void SetTtl(int32_t ttl) override { SetTtl(DefaultColumnFamily(), ttl); } 101 102 void SetTtl(ColumnFamilyHandle *h, int32_t ttl) override; 103 104 private: 105 // remember whether the Close completes or not 106 bool closed_; 107 }; 108 109 class TtlIterator : public Iterator { 110 111 public: TtlIterator(Iterator * iter)112 explicit TtlIterator(Iterator* iter) : iter_(iter) { assert(iter_); } 113 ~TtlIterator()114 ~TtlIterator() { delete iter_; } 115 Valid()116 bool Valid() const override { return iter_->Valid(); } 117 SeekToFirst()118 void SeekToFirst() override { iter_->SeekToFirst(); } 119 SeekToLast()120 void SeekToLast() override { iter_->SeekToLast(); } 121 Seek(const Slice & target)122 void Seek(const Slice& target) override { iter_->Seek(target); } 123 SeekForPrev(const Slice & target)124 void SeekForPrev(const Slice& target) override { iter_->SeekForPrev(target); } 125 Next()126 void Next() override { iter_->Next(); } 127 Prev()128 void Prev() override { iter_->Prev(); } 129 key()130 Slice key() const override { return iter_->key(); } 131 ttl_timestamp()132 int32_t ttl_timestamp() const { 133 return DecodeFixed32(iter_->value().data() + iter_->value().size() - 134 DBWithTTLImpl::kTSLength); 135 } 136 value()137 Slice value() const override { 138 // TODO: handle timestamp corruption like in general iterator semantics 139 assert(DBWithTTLImpl::SanityCheckTimestamp(iter_->value()).ok()); 140 Slice trimmed_value = iter_->value(); 141 trimmed_value.size_ -= DBWithTTLImpl::kTSLength; 142 return trimmed_value; 143 } 144 status()145 Status status() const override { return iter_->status(); } 146 147 private: 148 Iterator* iter_; 149 }; 150 151 class TtlCompactionFilter : public CompactionFilter { 152 public: 153 TtlCompactionFilter( 154 int32_t ttl, Env* env, const CompactionFilter* user_comp_filter, 155 std::unique_ptr<const CompactionFilter> user_comp_filter_from_factory = 156 nullptr) ttl_(ttl)157 : ttl_(ttl), 158 env_(env), 159 user_comp_filter_(user_comp_filter), 160 user_comp_filter_from_factory_( 161 std::move(user_comp_filter_from_factory)) { 162 // Unlike the merge operator, compaction filter is necessary for TTL, hence 163 // this would be called even if user doesn't specify any compaction-filter 164 if (!user_comp_filter_) { 165 user_comp_filter_ = user_comp_filter_from_factory_.get(); 166 } 167 } 168 Filter(int level,const Slice & key,const Slice & old_val,std::string * new_val,bool * value_changed)169 virtual bool Filter(int level, const Slice& key, const Slice& old_val, 170 std::string* new_val, bool* value_changed) const 171 override { 172 if (DBWithTTLImpl::IsStale(old_val, ttl_, env_)) { 173 return true; 174 } 175 if (user_comp_filter_ == nullptr) { 176 return false; 177 } 178 assert(old_val.size() >= DBWithTTLImpl::kTSLength); 179 Slice old_val_without_ts(old_val.data(), 180 old_val.size() - DBWithTTLImpl::kTSLength); 181 if (user_comp_filter_->Filter(level, key, old_val_without_ts, new_val, 182 value_changed)) { 183 return true; 184 } 185 if (*value_changed) { 186 new_val->append( 187 old_val.data() + old_val.size() - DBWithTTLImpl::kTSLength, 188 DBWithTTLImpl::kTSLength); 189 } 190 return false; 191 } 192 Name()193 virtual const char* Name() const override { return "Delete By TTL"; } 194 195 private: 196 int32_t ttl_; 197 Env* env_; 198 const CompactionFilter* user_comp_filter_; 199 std::unique_ptr<const CompactionFilter> user_comp_filter_from_factory_; 200 }; 201 202 class TtlCompactionFilterFactory : public CompactionFilterFactory { 203 public: TtlCompactionFilterFactory(int32_t ttl,Env * env,std::shared_ptr<CompactionFilterFactory> comp_filter_factory)204 TtlCompactionFilterFactory( 205 int32_t ttl, Env* env, 206 std::shared_ptr<CompactionFilterFactory> comp_filter_factory) 207 : ttl_(ttl), env_(env), user_comp_filter_factory_(comp_filter_factory) {} 208 CreateCompactionFilter(const CompactionFilter::Context & context)209 virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter( 210 const CompactionFilter::Context& context) override { 211 std::unique_ptr<const CompactionFilter> user_comp_filter_from_factory = 212 nullptr; 213 if (user_comp_filter_factory_) { 214 user_comp_filter_from_factory = 215 user_comp_filter_factory_->CreateCompactionFilter(context); 216 } 217 218 return std::unique_ptr<TtlCompactionFilter>(new TtlCompactionFilter( 219 ttl_, env_, nullptr, std::move(user_comp_filter_from_factory))); 220 } 221 SetTtl(int32_t ttl)222 void SetTtl(int32_t ttl) { 223 ttl_ = ttl; 224 } 225 Name()226 virtual const char* Name() const override { 227 return "TtlCompactionFilterFactory"; 228 } 229 230 private: 231 int32_t ttl_; 232 Env* env_; 233 std::shared_ptr<CompactionFilterFactory> user_comp_filter_factory_; 234 }; 235 236 class TtlMergeOperator : public MergeOperator { 237 238 public: TtlMergeOperator(const std::shared_ptr<MergeOperator> & merge_op,Env * env)239 explicit TtlMergeOperator(const std::shared_ptr<MergeOperator>& merge_op, 240 Env* env) 241 : user_merge_op_(merge_op), env_(env) { 242 assert(merge_op); 243 assert(env); 244 } 245 FullMergeV2(const MergeOperationInput & merge_in,MergeOperationOutput * merge_out)246 virtual bool FullMergeV2(const MergeOperationInput& merge_in, 247 MergeOperationOutput* merge_out) const override { 248 const uint32_t ts_len = DBWithTTLImpl::kTSLength; 249 if (merge_in.existing_value && merge_in.existing_value->size() < ts_len) { 250 ROCKS_LOG_ERROR(merge_in.logger, 251 "Error: Could not remove timestamp from existing value."); 252 return false; 253 } 254 255 // Extract time-stamp from each operand to be passed to user_merge_op_ 256 std::vector<Slice> operands_without_ts; 257 for (const auto& operand : merge_in.operand_list) { 258 if (operand.size() < ts_len) { 259 ROCKS_LOG_ERROR( 260 merge_in.logger, 261 "Error: Could not remove timestamp from operand value."); 262 return false; 263 } 264 operands_without_ts.push_back(operand); 265 operands_without_ts.back().remove_suffix(ts_len); 266 } 267 268 // Apply the user merge operator (store result in *new_value) 269 bool good = true; 270 MergeOperationOutput user_merge_out(merge_out->new_value, 271 merge_out->existing_operand); 272 if (merge_in.existing_value) { 273 Slice existing_value_without_ts(merge_in.existing_value->data(), 274 merge_in.existing_value->size() - ts_len); 275 good = user_merge_op_->FullMergeV2( 276 MergeOperationInput(merge_in.key, &existing_value_without_ts, 277 operands_without_ts, merge_in.logger), 278 &user_merge_out); 279 } else { 280 good = user_merge_op_->FullMergeV2( 281 MergeOperationInput(merge_in.key, nullptr, operands_without_ts, 282 merge_in.logger), 283 &user_merge_out); 284 } 285 286 // Return false if the user merge operator returned false 287 if (!good) { 288 return false; 289 } 290 291 if (merge_out->existing_operand.data()) { 292 merge_out->new_value.assign(merge_out->existing_operand.data(), 293 merge_out->existing_operand.size()); 294 merge_out->existing_operand = Slice(nullptr, 0); 295 } 296 297 // Augment the *new_value with the ttl time-stamp 298 int64_t curtime; 299 if (!env_->GetCurrentTime(&curtime).ok()) { 300 ROCKS_LOG_ERROR( 301 merge_in.logger, 302 "Error: Could not get current time to be attached internally " 303 "to the new value."); 304 return false; 305 } else { 306 char ts_string[ts_len]; 307 EncodeFixed32(ts_string, (int32_t)curtime); 308 merge_out->new_value.append(ts_string, ts_len); 309 return true; 310 } 311 } 312 PartialMergeMulti(const Slice & key,const std::deque<Slice> & operand_list,std::string * new_value,Logger * logger)313 virtual bool PartialMergeMulti(const Slice& key, 314 const std::deque<Slice>& operand_list, 315 std::string* new_value, Logger* logger) const 316 override { 317 const uint32_t ts_len = DBWithTTLImpl::kTSLength; 318 std::deque<Slice> operands_without_ts; 319 320 for (const auto& operand : operand_list) { 321 if (operand.size() < ts_len) { 322 ROCKS_LOG_ERROR(logger, 323 "Error: Could not remove timestamp from value."); 324 return false; 325 } 326 327 operands_without_ts.push_back( 328 Slice(operand.data(), operand.size() - ts_len)); 329 } 330 331 // Apply the user partial-merge operator (store result in *new_value) 332 assert(new_value); 333 if (!user_merge_op_->PartialMergeMulti(key, operands_without_ts, new_value, 334 logger)) { 335 return false; 336 } 337 338 // Augment the *new_value with the ttl time-stamp 339 int64_t curtime; 340 if (!env_->GetCurrentTime(&curtime).ok()) { 341 ROCKS_LOG_ERROR( 342 logger, 343 "Error: Could not get current time to be attached internally " 344 "to the new value."); 345 return false; 346 } else { 347 char ts_string[ts_len]; 348 EncodeFixed32(ts_string, (int32_t)curtime); 349 new_value->append(ts_string, ts_len); 350 return true; 351 } 352 } 353 Name()354 virtual const char* Name() const override { return "Merge By TTL"; } 355 356 private: 357 std::shared_ptr<MergeOperator> user_merge_op_; 358 Env* env_; 359 }; 360 } // namespace ROCKSDB_NAMESPACE 361 #endif // ROCKSDB_LITE 362