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