1/ Duration anomaly tracker with alarm.
2/ Init anomaly from config based on the public language.
3/ Unit tests for anomaly detection in count/gauge producer.
4/ Revisit the duration tracker logic.
Test: unit test passed.
Change-Id: I2423c0e0f05b1e37626954de9e749303423963f2
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
index b8150d0..e0200f27 100644
--- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "src/anomaly/DiscreteAnomalyTracker.h"
+#include "src/anomaly/AnomalyTracker.h"
#include <gtest/gtest.h>
#include <stdio.h>
@@ -37,7 +37,7 @@
}
}
-std::shared_ptr<DimToValMap> MockeBucket(
+std::shared_ptr<DimToValMap> MockBucket(
const std::vector<std::pair<string, long>>& key_value_pair_list) {
std::shared_ptr<DimToValMap> bucket = std::make_shared<DimToValMap>();
AddValueToBucket(key_value_pair_list, bucket);
@@ -45,190 +45,240 @@
}
TEST(AnomalyTrackerTest, TestConsecutiveBuckets) {
+ const int64_t bucketSizeNs = 30 * NS_PER_SEC;
Alert alert;
alert.set_number_of_buckets(3);
- alert.set_refractory_period_in_buckets(3);
+ alert.set_refractory_period_secs(2 * bucketSizeNs / NS_PER_SEC);
alert.set_trigger_if_sum_gt(2);
- DiscreteAnomalyTracker anomaly_tracker(alert);
+ AnomalyTracker anomalyTracker(alert, bucketSizeNs);
- std::shared_ptr<DimToValMap> bucket0 = MockeBucket({{"a", 1}, {"b", 2}, {"c", 1}});
- // Adds bucket #0
- anomaly_tracker.addOrUpdateBucket(bucket0, 0);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- EXPECT_FALSE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 0L);
+ std::shared_ptr<DimToValMap> bucket0 = MockBucket({{"a", 1}, {"b", 2}, {"c", 1}});
+ int64_t eventTimestamp0 = 10;
+ std::shared_ptr<DimToValMap> bucket1 = MockBucket({{"a", 1}});
+ int64_t eventTimestamp1 = bucketSizeNs + 11;
+ std::shared_ptr<DimToValMap> bucket2 = MockBucket({{"b", 1}});
+ int64_t eventTimestamp2 = 2 * bucketSizeNs + 12;
+ std::shared_ptr<DimToValMap> bucket3 = MockBucket({{"a", 2}});
+ int64_t eventTimestamp3 = 3 * bucketSizeNs + 13;
+ std::shared_ptr<DimToValMap> bucket4 = MockBucket({{"b", 1}});
+ int64_t eventTimestamp4 = 4 * bucketSizeNs + 14;
+ std::shared_ptr<DimToValMap> bucket5 = MockBucket({{"a", 2}});
+ int64_t eventTimestamp5 = 5 * bucketSizeNs + 15;
+ std::shared_ptr<DimToValMap> bucket6 = MockBucket({{"a", 2}});
+ int64_t eventTimestamp6 = 6 * bucketSizeNs + 16;
- // Adds bucket #0 again. The sum does not change.
- anomaly_tracker.addOrUpdateBucket(bucket0, 0);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 0L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- EXPECT_FALSE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 0L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, -1L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0u);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, -1LL);
+ EXPECT_FALSE(anomalyTracker.detectAnomaly(0, *bucket0));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp0, 0, *bucket0);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, -1L);
- // Adds bucket #1.
- std::shared_ptr<DimToValMap> bucket1 = MockeBucket({{"b", 2}});
- anomaly_tracker.addOrUpdateBucket(bucket1, 1);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 1L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- // Alarm.
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
+ // Adds past bucket #0
+ anomalyTracker.addPastBucket(bucket0, 0);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3u);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 1LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 0LL);
+ EXPECT_FALSE(anomalyTracker.detectAnomaly(1, *bucket1));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp1, 1, *bucket1);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, -1L);
- // Adds bucket #1 again. The sum does not change.
- anomaly_tracker.addOrUpdateBucket(bucket1, 1);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 1L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- // Alarm.
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
+ // Adds past bucket #0 again. The sum does not change.
+ anomalyTracker.addPastBucket(bucket0, 0);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3u);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 1LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 0LL);
+ EXPECT_FALSE(anomalyTracker.detectAnomaly(1, *bucket1));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp1 + 1, 1, *bucket1);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, -1L);
- // Adds bucket #2.
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}}), 2);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 2L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 2);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ // Adds past bucket #1.
+ anomalyTracker.addPastBucket(bucket1, 1);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 1L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(2, *bucket2));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp2, 2, *bucket2);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
+
+ // Adds past bucket #1 again. Nothing changes.
+ anomalyTracker.addPastBucket(bucket1, 1);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 1L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(2, *bucket2));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp2 + 1, 2, *bucket2);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
+
+ // Adds past bucket #2.
+ anomalyTracker.addPastBucket(bucket2, 2);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 2L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 1LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(3, *bucket3));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp3, 3, *bucket3);
// Within refractory period.
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
// Adds bucket #3.
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}}), 3);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 3L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 2UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 2);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
- EXPECT_FALSE(anomaly_tracker.detectAnomaly());
+ anomalyTracker.addPastBucket(bucket3, 3L);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 3L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+ EXPECT_FALSE(anomalyTracker.detectAnomaly(4, *bucket4));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp4, 4, *bucket4);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
- // Adds bucket #3.
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 2}}), 4);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 4L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 4);
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
- // Within refractory period.
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
+ // Adds bucket #4.
+ anomalyTracker.addPastBucket(bucket4, 4);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 4L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(5, *bucket5));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp5, 5, *bucket5);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp5);
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}}), 5);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 5L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 4);
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ // Adds bucket #5.
+ anomalyTracker.addPastBucket(bucket5, 5);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 5L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(6, *bucket6));
// Within refractory period.
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 2L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 5L);
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp6, 6, *bucket6);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp5);
}
TEST(AnomalyTrackerTest, TestSparseBuckets) {
+ const int64_t bucketSizeNs = 30 * NS_PER_SEC;
Alert alert;
alert.set_number_of_buckets(3);
- alert.set_refractory_period_in_buckets(3);
+ alert.set_refractory_period_secs(2 * bucketSizeNs / NS_PER_SEC);
alert.set_trigger_if_sum_gt(2);
- DiscreteAnomalyTracker anomaly_tracker(alert);
+ AnomalyTracker anomalyTracker(alert, bucketSizeNs);
- // Add bucket #9
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}, {"b", 2}, {"c", 1}}), 9);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 9L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- EXPECT_FALSE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 0L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, -1L);
+ std::shared_ptr<DimToValMap> bucket9 = MockBucket({{"a", 1}, {"b", 2}, {"c", 1}});
+ std::shared_ptr<DimToValMap> bucket16 = MockBucket({{"b", 4}});
+ std::shared_ptr<DimToValMap> bucket18 = MockBucket({{"b", 1}, {"c", 1}});
+ std::shared_ptr<DimToValMap> bucket20 = MockBucket({{"b", 3}, {"c", 1}});
+ std::shared_ptr<DimToValMap> bucket25 = MockBucket({{"d", 1}});
+ std::shared_ptr<DimToValMap> bucket28 = MockBucket({{"e", 2}});
- // Add bucket #16
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 4}}), 16);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 16L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 16L);
+ int64_t eventTimestamp1 = bucketSizeNs * 8 + 1;
+ int64_t eventTimestamp2 = bucketSizeNs * 15 + 11;
+ int64_t eventTimestamp3 = bucketSizeNs * 17 + 1;
+ int64_t eventTimestamp4 = bucketSizeNs * 19 + 2;
+ int64_t eventTimestamp5 = bucketSizeNs * 24 + 3;
+ int64_t eventTimestamp6 = bucketSizeNs * 27 + 3;
- // Add bucket #18
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 1}, {"c", 1}}), 18);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 18L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 2UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 5);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, -1LL);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ EXPECT_FALSE(anomalyTracker.detectAnomaly(9, *bucket9));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp1, 9, *bucket9);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, -1);
+
+ // Add past bucket #9
+ anomalyTracker.addPastBucket(bucket9, 9);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 9L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 1LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(16, *bucket16));
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 15L);
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp2, 16, *bucket16);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 15L);
+
+ // Add past bucket #16
+ anomalyTracker.addPastBucket(bucket16, 16);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 16L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 4LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(18, *bucket18));
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 4LL);
// Within refractory period.
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 16L);
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp3, 18, *bucket18);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 4LL);
- // Add bucket #18 again.
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 1}, {"c", 1}}), 18);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 18L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 2UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 5);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 16L);
+ // Add past bucket #18
+ anomalyTracker.addPastBucket(bucket18, 18);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 18L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(20, *bucket20));
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 19L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp4, 20, *bucket20);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp4);
- // Add bucket #20
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 3}, {"d", 1}}), 20);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 20L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("d")->second, 1);
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 2L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 20L);
+ // Add bucket #18 again. Nothing changes.
+ anomalyTracker.addPastBucket(bucket18, 18);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 19L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(20, *bucket20));
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp4 + 1, 20, *bucket20);
+ // Within refractory period.
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp4);
- // Add bucket #25
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"d", 1}}), 25);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 25L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("d")->second, 1L);
- EXPECT_FALSE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 2L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 20L);
+ // Add past bucket #20
+ anomalyTracker.addPastBucket(bucket20, 20);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 20L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 3LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ EXPECT_FALSE(anomalyTracker.detectAnomaly(25, *bucket25));
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 24L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp5, 25, *bucket25);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp4);
- // Add bucket #28
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"e", 5}}), 28);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 28L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("e")->second, 5L);
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 3L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 28L);
+ // Add past bucket #25
+ anomalyTracker.addPastBucket(bucket25, 25);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 25L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("d"), 1LL);
+ EXPECT_FALSE(anomalyTracker.detectAnomaly(28, *bucket28));
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 27L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp6, 28, *bucket28);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp4);
+
+ // Updates current bucket #28.
+ (*bucket28)["e"] = 5;
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(28, *bucket28));
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 27L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp6 + 7, 28, *bucket28);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp6 + 7);
}
} // namespace statsd