Fix possible NaN StandardDeviation in Histogram (#10586)

Summary:
Appears possible after 5de98f2 introduced possible lost
updates. Could be related to 2af132c also. Simply ensure no sqrt of
negative.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/10586

Test Plan: test added

Reviewed By: ajkr

Differential Revision: D39068391

Pulled By: pdillinger

fbshipit-source-id: 230b214a41e6c9ae91a1ef3e8b2a17b46bbb17c2
main
Peter Dillinger 2 years ago committed by Facebook GitHub Bot
parent 9d5b3dabcf
commit 3770d6b74b
  1. 6
      monitoring/histogram.cc
  2. 2
      monitoring/histogram.h
  3. 16
      monitoring/histogram_test.cc

@ -168,10 +168,12 @@ double HistogramStat::StandardDeviation() const {
static_cast<double>(num()); // Use double to avoid integer overflow static_cast<double>(num()); // Use double to avoid integer overflow
double cur_sum = static_cast<double>(sum()); double cur_sum = static_cast<double>(sum());
double cur_sum_squares = static_cast<double>(sum_squares()); double cur_sum_squares = static_cast<double>(sum_squares());
if (cur_num == 0) return 0; if (cur_num == 0.0) {
return 0.0;
}
double variance = double variance =
(cur_sum_squares * cur_num - cur_sum * cur_sum) / (cur_num * cur_num); (cur_sum_squares * cur_num - cur_sum * cur_sum) / (cur_num * cur_num);
return std::sqrt(variance); return std::sqrt(std::max(variance, 0.0));
} }
std::string HistogramStat::ToString() const { std::string HistogramStat::ToString() const {

@ -140,6 +140,8 @@ class HistogramImpl : public Histogram {
virtual ~HistogramImpl() {} virtual ~HistogramImpl() {}
inline HistogramStat& TEST_GetStats() { return stats_; }
private: private:
HistogramStat stats_; HistogramStat stats_;
std::mutex mutex_; std::mutex mutex_;

@ -147,7 +147,8 @@ TEST_F(HistogramTest, HistogramWindowingExpire) {
ASSERT_EQ(histogramWindowing.num(), 100); ASSERT_EQ(histogramWindowing.num(), 100);
ASSERT_EQ(histogramWindowing.min(), 1); ASSERT_EQ(histogramWindowing.min(), 1);
ASSERT_EQ(histogramWindowing.max(), 1); ASSERT_EQ(histogramWindowing.max(), 1);
ASSERT_EQ(histogramWindowing.Average(), 1); ASSERT_EQ(histogramWindowing.Average(), 1.0);
ASSERT_EQ(histogramWindowing.StandardDeviation(), 0.0);
PopulateHistogram(histogramWindowing, 2, 2, 100); PopulateHistogram(histogramWindowing, 2, 2, 100);
clock->SleepForMicroseconds(micros_per_window); clock->SleepForMicroseconds(micros_per_window);
@ -155,6 +156,7 @@ TEST_F(HistogramTest, HistogramWindowingExpire) {
ASSERT_EQ(histogramWindowing.min(), 1); ASSERT_EQ(histogramWindowing.min(), 1);
ASSERT_EQ(histogramWindowing.max(), 2); ASSERT_EQ(histogramWindowing.max(), 2);
ASSERT_EQ(histogramWindowing.Average(), 1.5); ASSERT_EQ(histogramWindowing.Average(), 1.5);
ASSERT_GT(histogramWindowing.StandardDeviation(), 0.0);
PopulateHistogram(histogramWindowing, 3, 3, 100); PopulateHistogram(histogramWindowing, 3, 3, 100);
clock->SleepForMicroseconds(micros_per_window); clock->SleepForMicroseconds(micros_per_window);
@ -162,6 +164,7 @@ TEST_F(HistogramTest, HistogramWindowingExpire) {
ASSERT_EQ(histogramWindowing.min(), 1); ASSERT_EQ(histogramWindowing.min(), 1);
ASSERT_EQ(histogramWindowing.max(), 3); ASSERT_EQ(histogramWindowing.max(), 3);
ASSERT_EQ(histogramWindowing.Average(), 2.0); ASSERT_EQ(histogramWindowing.Average(), 2.0);
ASSERT_GT(histogramWindowing.StandardDeviation(), 0.0);
// dropping oldest window with value 1, remaining 2 ~ 4 // dropping oldest window with value 1, remaining 2 ~ 4
PopulateHistogram(histogramWindowing, 4, 4, 100); PopulateHistogram(histogramWindowing, 4, 4, 100);
@ -170,6 +173,7 @@ TEST_F(HistogramTest, HistogramWindowingExpire) {
ASSERT_EQ(histogramWindowing.min(), 2); ASSERT_EQ(histogramWindowing.min(), 2);
ASSERT_EQ(histogramWindowing.max(), 4); ASSERT_EQ(histogramWindowing.max(), 4);
ASSERT_EQ(histogramWindowing.Average(), 3.0); ASSERT_EQ(histogramWindowing.Average(), 3.0);
ASSERT_GT(histogramWindowing.StandardDeviation(), 0.0);
// dropping oldest window with value 2, remaining 3 ~ 5 // dropping oldest window with value 2, remaining 3 ~ 5
PopulateHistogram(histogramWindowing, 5, 5, 100); PopulateHistogram(histogramWindowing, 5, 5, 100);
@ -178,6 +182,7 @@ TEST_F(HistogramTest, HistogramWindowingExpire) {
ASSERT_EQ(histogramWindowing.min(), 3); ASSERT_EQ(histogramWindowing.min(), 3);
ASSERT_EQ(histogramWindowing.max(), 5); ASSERT_EQ(histogramWindowing.max(), 5);
ASSERT_EQ(histogramWindowing.Average(), 4.0); ASSERT_EQ(histogramWindowing.Average(), 4.0);
ASSERT_GT(histogramWindowing.StandardDeviation(), 0.0);
} }
TEST_F(HistogramTest, HistogramWindowingMerge) { TEST_F(HistogramTest, HistogramWindowingMerge) {
@ -231,6 +236,15 @@ TEST_F(HistogramTest, LargeStandardDeviation) {
ASSERT_LT(fabs(histogram.StandardDeviation() - 288675), 1); ASSERT_LT(fabs(histogram.StandardDeviation() - 288675), 1);
} }
TEST_F(HistogramTest, LostUpdateStandardDeviation) {
HistogramImpl histogram;
PopulateHistogram(histogram, 100, 100, 100);
// Simulate a possible lost update (since they are not atomic)
histogram.TEST_GetStats().sum_squares_ -= 10000;
// Ideally zero, but should never be negative or NaN
ASSERT_GE(histogram.StandardDeviation(), 0.0);
}
} // namespace ROCKSDB_NAMESPACE } // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) { int main(int argc, char** argv) {

Loading…
Cancel
Save