1 // Copyright (c) 2011-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 // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
6 // Use of this source code is governed by a BSD-style license that can be
7 // found in the LICENSE file. See the AUTHORS file for names of contributors.
8 
9 #include "monitoring/persistent_stats_history.h"
10 
11 #include <cstring>
12 #include <string>
13 #include <utility>
14 #include "db/db_impl/db_impl.h"
15 #include "port/likely.h"
16 #include "util/string_util.h"
17 
18 namespace ROCKSDB_NAMESPACE {
19 // 10 digit seconds timestamp => [Sep 9, 2001 ~ Nov 20, 2286]
20 const int kNowSecondsStringLength = 10;
21 const std::string kFormatVersionKeyString =
22     "__persistent_stats_format_version__";
23 const std::string kCompatibleVersionKeyString =
24     "__persistent_stats_compatible_version__";
25 // Every release maintains two versions numbers for persistents stats: Current
26 // format version and compatible format version. Current format version
27 // designates what type of encoding will be used when writing to stats CF;
28 // compatible format version designates the minimum format version that
29 // can decode the stats CF encoded using the current format version.
30 const uint64_t kStatsCFCurrentFormatVersion = 1;
31 const uint64_t kStatsCFCompatibleFormatVersion = 1;
32 
DecodePersistentStatsVersionNumber(DBImpl * db,StatsVersionKeyType type,uint64_t * version_number)33 Status DecodePersistentStatsVersionNumber(DBImpl* db, StatsVersionKeyType type,
34                                           uint64_t* version_number) {
35   if (type >= StatsVersionKeyType::kKeyTypeMax) {
36     return Status::InvalidArgument("Invalid stats version key type provided");
37   }
38   std::string key;
39   if (type == StatsVersionKeyType::kFormatVersion) {
40     key = kFormatVersionKeyString;
41   } else if (type == StatsVersionKeyType::kCompatibleVersion) {
42     key = kCompatibleVersionKeyString;
43   }
44   ReadOptions options;
45   options.verify_checksums = true;
46   std::string result;
47   Status s = db->Get(options, db->PersistentStatsColumnFamily(), key, &result);
48   if (!s.ok() || result.empty()) {
49     return Status::NotFound("Persistent stats version key " + key +
50                             " not found.");
51   }
52 
53   // read version_number but do nothing in current version
54   *version_number = ParseUint64(result);
55   return Status::OK();
56 }
57 
EncodePersistentStatsKey(uint64_t now_seconds,const std::string & key,int size,char * buf)58 int EncodePersistentStatsKey(uint64_t now_seconds, const std::string& key,
59                              int size, char* buf) {
60   char timestamp[kNowSecondsStringLength + 1];
61   // make time stamp string equal in length to allow sorting by time
62   snprintf(timestamp, sizeof(timestamp), "%010d",
63            static_cast<int>(now_seconds));
64   timestamp[kNowSecondsStringLength] = '\0';
65   return snprintf(buf, size, "%s#%s", timestamp, key.c_str());
66 }
67 
OptimizeForPersistentStats(ColumnFamilyOptions * cfo)68 void OptimizeForPersistentStats(ColumnFamilyOptions* cfo) {
69   cfo->write_buffer_size = 2 << 20;
70   cfo->target_file_size_base = 2 * 1048576;
71   cfo->max_bytes_for_level_base = 10 * 1048576;
72   cfo->soft_pending_compaction_bytes_limit = 256 * 1048576;
73   cfo->hard_pending_compaction_bytes_limit = 1073741824ul;
74   cfo->compression = kNoCompression;
75 }
76 
~PersistentStatsHistoryIterator()77 PersistentStatsHistoryIterator::~PersistentStatsHistoryIterator() {}
78 
Valid() const79 bool PersistentStatsHistoryIterator::Valid() const { return valid_; }
80 
status() const81 Status PersistentStatsHistoryIterator::status() const { return status_; }
82 
Next()83 void PersistentStatsHistoryIterator::Next() {
84   // increment start_time by 1 to avoid infinite loop
85   AdvanceIteratorByTime(GetStatsTime() + 1, end_time_);
86 }
87 
GetStatsTime() const88 uint64_t PersistentStatsHistoryIterator::GetStatsTime() const { return time_; }
89 
90 const std::map<std::string, uint64_t>&
GetStatsMap() const91 PersistentStatsHistoryIterator::GetStatsMap() const {
92   return stats_map_;
93 }
94 
parseKey(const Slice & key,uint64_t start_time)95 std::pair<uint64_t, std::string> parseKey(const Slice& key,
96                                           uint64_t start_time) {
97   std::pair<uint64_t, std::string> result;
98   std::string key_str = key.ToString();
99   std::string::size_type pos = key_str.find("#");
100   // TODO(Zhongyi): add counters to track parse failures?
101   if (pos == std::string::npos) {
102     result.first = port::kMaxUint64;
103     result.second.clear();
104   } else {
105     uint64_t parsed_time = ParseUint64(key_str.substr(0, pos));
106     // skip entries with timestamp smaller than start_time
107     if (parsed_time < start_time) {
108       result.first = port::kMaxUint64;
109       result.second = "";
110     } else {
111       result.first = parsed_time;
112       std::string key_resize = key_str.substr(pos + 1);
113       result.second = key_resize;
114     }
115   }
116   return result;
117 }
118 
119 // advance the iterator to the next time between [start_time, end_time)
120 // if success, update time_ and stats_map_ with new_time and stats_map
AdvanceIteratorByTime(uint64_t start_time,uint64_t end_time)121 void PersistentStatsHistoryIterator::AdvanceIteratorByTime(uint64_t start_time,
122                                                            uint64_t end_time) {
123   // try to find next entry in stats_history_ map
124   if (db_impl_ != nullptr) {
125     ReadOptions ro;
126     Iterator* iter =
127         db_impl_->NewIterator(ro, db_impl_->PersistentStatsColumnFamily());
128 
129     char timestamp[kNowSecondsStringLength + 1];
130     snprintf(timestamp, sizeof(timestamp), "%010d",
131              static_cast<int>(std::max(time_, start_time)));
132     timestamp[kNowSecondsStringLength] = '\0';
133 
134     iter->Seek(timestamp);
135     // no more entries with timestamp >= start_time is found or version key
136     // is found to be incompatible
137     if (!iter->Valid()) {
138       valid_ = false;
139       delete iter;
140       return;
141     }
142     time_ = parseKey(iter->key(), start_time).first;
143     valid_ = true;
144     // check parsed time and invalid if it exceeds end_time
145     if (time_ > end_time) {
146       valid_ = false;
147       delete iter;
148       return;
149     }
150     // find all entries with timestamp equal to time_
151     std::map<std::string, uint64_t> new_stats_map;
152     std::pair<uint64_t, std::string> kv;
153     for (; iter->Valid(); iter->Next()) {
154       kv = parseKey(iter->key(), start_time);
155       if (kv.first != time_) {
156         break;
157       }
158       if (kv.second.compare(kFormatVersionKeyString) == 0) {
159         continue;
160       }
161       new_stats_map[kv.second] = ParseUint64(iter->value().ToString());
162     }
163     stats_map_.swap(new_stats_map);
164     delete iter;
165   } else {
166     valid_ = false;
167   }
168 }
169 
170 }  // namespace ROCKSDB_NAMESPACE
171