2024-03-08 14:03:37 +08:00

180 lines
5.9 KiB
C++

// This is not a standalone header
class SudokuStat : muduo::noncopyable {
public:
SudokuStat(const ThreadPool& pool)
: pool_(pool),
lastSecond_(0),
requests_(kSeconds),
latencies_(kSeconds),
totalRequests_(0),
totalResponses_(0),
totalSolved_(0),
badRequests_(0),
droppedRequests_(0),
totalLatency_(0),
badLatency_(0)
{
}
string report() const
{
LogStream result;
size_t queueSize = pool_.queueSize();
result << "task_queue_size " << queueSize << '\n';
{
MutexLockGuard lock(mutex_);
result << "total_requests " << totalRequests_ << '\n';
result << "total_responses " << totalResponses_ << '\n';
result << "total_solved " << totalSolved_ << '\n';
result << "bad_requests " << badRequests_ << '\n';
result << "dropped_requests " << droppedRequests_ << '\n';
result << "latency_sum_us " << totalLatency_ << '\n';
if (badLatency_ > 0) {
result << "bad_latency" << badLatency_ << '\n';
}
result << "last_second " << lastSecond_ << '\n';
int64_t requests = 0;
result << "requests_per_second";
for (size_t i = 0; i < requests_.size(); ++i) {
requests += requests_[i];
result << ' ' << requests_[i];
}
result << '\n';
result << "requests_60s " << requests << '\n';
int64_t latency = 0;
result << "latency_sum_us_per_second";
for (size_t i = 0; i < latencies_.size(); ++i) {
latency += latencies_[i];
result << ' ' << latencies_[i];
}
result << '\n';
result << "latency_sum_us_60s " << latency << '\n';
int64_t latencyAvg60s = requests == 0 ? 0 : latency / requests;
result << "latency_us_60s " << latencyAvg60s << '\n';
int64_t latencyAvg =
totalResponses_ == 0 ? 0 : totalLatency_ / totalResponses_;
result << "latency_us_avg " << latencyAvg << '\n';
}
return result.buffer().toString();
}
string reset()
{
{
MutexLockGuard lock(mutex_);
lastSecond_ = 0;
requests_.clear();
latencies_.clear();
totalRequests_ = 0;
totalResponses_ = 0;
totalSolved_ = 0;
badRequests_ = 0;
totalLatency_ = 0;
badLatency_ = 0;
}
return "reset done.";
}
void recordResponse(Timestamp now, Timestamp receive, bool solved)
{
const time_t second = now.secondsSinceEpoch();
const int64_t elapsed_us =
now.microSecondsSinceEpoch() - receive.microSecondsSinceEpoch();
MutexLockGuard lock(mutex_);
assert(requests_.size() == latencies_.size());
++totalResponses_;
if (solved) ++totalSolved_;
if (elapsed_us < 0) {
++badLatency_;
return;
}
totalLatency_ += elapsed_us;
const time_t firstSecond =
lastSecond_ - static_cast<ssize_t>(requests_.size()) + 1;
if (lastSecond_ == second) {
// the most common case
++requests_.back();
latencies_.back() += elapsed_us;
} else if (lastSecond_ + 1 == second || lastSecond_ == 0) {
// next second
lastSecond_ = second;
requests_.push_back(0);
latencies_.push_back(0);
++requests_.back();
latencies_.back() += elapsed_us;
} else if (second > lastSecond_) {
// jump ahead
if (second < lastSecond_ + kSeconds) {
// eg. lastSecond_ == 100, second < 160
while (lastSecond_ < second) {
requests_.push_back(0);
latencies_.push_back(0);
++lastSecond_;
}
} else {
// eg. lastSecond_ == 100, second >= 160
requests_.clear();
latencies_.clear();
lastSecond_ = second;
requests_.push_back(0);
latencies_.push_back(0);
}
++requests_.back();
latencies_.back() += elapsed_us;
} else if (second >= firstSecond) {
// jump backwards
// eg. lastSecond_ = 150, size = 10, second > 140
// FIXME: if second > lastSecond_ - kSeconds, push_front()
size_t idx = second - firstSecond;
assert(idx < requests_.size());
++requests_[idx];
latencies_[idx] += elapsed_us;
} else {
assert(second < firstSecond);
// discard
// eg. lastSecond_ = 150, size = 10, second <= 140
}
assert(requests_.size() == latencies_.size());
}
void recordRequest()
{
MutexLockGuard lock(mutex_);
++totalRequests_;
}
void recordBadRequest()
{
MutexLockGuard lock(mutex_);
++badRequests_;
}
void recordDroppedRequest()
{
MutexLockGuard lock(mutex_);
++droppedRequests_;
}
private:
const ThreadPool& pool_; // only for ThreadPool::queueSize()
mutable MutexLock mutex_;
// invariant:
// 0. requests_.size() == latencies_.size()
// 1. if lastSecond_ > 0, requests_.back() is for that second
// 2. requests_.front() is for second (last second - size() + 1)
time_t lastSecond_;
boost::circular_buffer<int64_t> requests_;
boost::circular_buffer<int64_t> latencies_;
int64_t totalRequests_, totalResponses_, totalSolved_, badRequests_,
droppedRequests_, totalLatency_, badLatency_;
// FIXME int128_t for totalLatency_;
static const int kSeconds = 60;
};