/** * Copyright (C) 2016 MongoDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects * for all of the code used other than as permitted herein. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you do not * wish to do so, delete this exception statement from your version. If you * delete this exception statement from all source files in the program, * then also delete it in the license file. */ #include "mongo/platform/basic.h" #include "mongo/db/stats/operation_latency_histogram.h" #include #include "mongo/bson/bsonobjbuilder.h" #include "mongo/db/namespace_string.h" #include "mongo/platform/bits.h" namespace mongo { const std::array OperationLatencyHistogram::kLowerBounds = {0, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152, 65536, 98304, 131072, 196608, 262144, 393216, 524288, 786432, 1048576, 1572864, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, 2147483648, 4294967296, 8589934592, 17179869184, 34359738368, 68719476736, 137438953472, 274877906944, 549755813888, 1099511627776}; void OperationLatencyHistogram::_append(const HistogramData& data, const char* key, bool includeHistograms, BSONObjBuilder* builder) const { BSONObjBuilder histogramBuilder(builder->subobjStart(key)); if (includeHistograms) { BSONArrayBuilder arrayBuilder(histogramBuilder.subarrayStart("histogram")); for (int i = 0; i < kMaxBuckets; i++) { if (data.buckets[i] == 0) continue; BSONObjBuilder entryBuilder(arrayBuilder.subobjStart()); entryBuilder.append("micros", static_cast(kLowerBounds[i])); entryBuilder.append("count", static_cast(data.buckets[i])); entryBuilder.doneFast(); } arrayBuilder.doneFast(); } histogramBuilder.append("latency", static_cast(data.sum)); histogramBuilder.append("ops", static_cast(data.entryCount)); histogramBuilder.doneFast(); } void OperationLatencyHistogram::append(bool includeHistograms, BSONObjBuilder* builder) const { _append(_reads, "reads", includeHistograms, builder); _append(_writes, "writes", includeHistograms, builder); _append(_commands, "commands", includeHistograms, builder); } // Computes the log base 2 of value, and checks for cases of split buckets. int OperationLatencyHistogram::_getBucket(uint64_t value) { // Zero is a special case since log(0) is undefined. if (value == 0) { return 0; } int log2 = 63 - countLeadingZeros64(value); // Half splits occur in range [2^11, 2^21) giving 10 extra buckets. if (log2 < 11) { return log2; } else if (log2 < 21) { int extra = log2 - 11; // Split value boundary is at (2^n + 2^(n+1))/2 = 2^n + 2^(n-1). // Which corresponds to (1ULL << log2) | (1ULL << (log2 - 1)) // Which is equivalent to the following: uint64_t splitBoundary = 3ULL << (log2 - 1); if (value >= splitBoundary) { extra++; } return log2 + extra; } else { // Add all of the extra 10 buckets. return std::min(log2 + 10, kMaxBuckets - 1); } } void OperationLatencyHistogram::_incrementData(uint64_t latency, int bucket, HistogramData* data) { data->buckets[bucket]++; data->entryCount++; data->sum += latency; } void OperationLatencyHistogram::increment(uint64_t latency, Command::ReadWriteType type) { int bucket = _getBucket(latency); switch (type) { case Command::ReadWriteType::kRead: _incrementData(latency, bucket, &_reads); break; case Command::ReadWriteType::kWrite: _incrementData(latency, bucket, &_writes); break; case Command::ReadWriteType::kCommand: _incrementData(latency, bucket, &_commands); break; default: MONGO_UNREACHABLE; } } } // namespace mongo