Coverage Report

Created: 2025-10-26 07:13

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