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