]>
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> | |
1e59de90 | 14 | |
f67539c2 | 15 | #include "db/db_impl/db_impl.h" |
f67539c2 TL |
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 | ||
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 | ||
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 | ||
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 | ||
77 | PersistentStatsHistoryIterator::~PersistentStatsHistoryIterator() {} | |
78 | ||
79 | bool PersistentStatsHistoryIterator::Valid() const { return valid_; } | |
80 | ||
81 | Status PersistentStatsHistoryIterator::status() const { return status_; } | |
82 | ||
83 | void PersistentStatsHistoryIterator::Next() { | |
84 | // increment start_time by 1 to avoid infinite loop | |
85 | AdvanceIteratorByTime(GetStatsTime() + 1, end_time_); | |
86 | } | |
87 | ||
88 | uint64_t PersistentStatsHistoryIterator::GetStatsTime() const { return time_; } | |
89 | ||
90 | const std::map<std::string, uint64_t>& | |
91 | PersistentStatsHistoryIterator::GetStatsMap() const { | |
92 | return stats_map_; | |
93 | } | |
94 | ||
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) { | |
1e59de90 | 102 | result.first = std::numeric_limits<uint64_t>::max(); |
f67539c2 TL |
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) { | |
1e59de90 | 108 | result.first = std::numeric_limits<uint64_t>::max(); |
f67539c2 TL |
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 | |
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 |