Update Alarms/Subscriptions

This is the same as initializing because alarms have no state to carry
over. We can just replace them all without losing anything.

Test: atest statsd_test
Bug: 162323547
Change-Id: Ia2fb33e9ac79476babce57e2e0fb1ca49091e260
This commit is contained in:
Tej Singh
2020-10-29 22:54:41 -07:00
parent d07d0ff02f
commit 8b46bc11b4
7 changed files with 124 additions and 23 deletions

View File

@@ -73,6 +73,7 @@ protected:
FRIEND_TEST(AlarmTrackerTest, TestTriggerTimestamp);
FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms);
FRIEND_TEST(ConfigUpdateTest, TestUpdateAlarms);
};
} // namespace statsd

View File

@@ -211,6 +211,7 @@ bool MetricsManager::updateConfig(const StatsdConfig& config, const int64_t time
unordered_map<int64_t, int> newMetricProducerMap;
vector<sp<AnomalyTracker>> newAnomalyTrackers;
unordered_map<int64_t, int> newAlertTrackerMap;
vector<sp<AlarmTracker>> newPeriodicAlarmTrackers;
mTagIds.clear();
mConditionToMetricMap.clear();
mTrackerToMetricMap.clear();
@@ -226,9 +227,10 @@ bool MetricsManager::updateConfig(const StatsdConfig& config, const int64_t time
mAllAnomalyTrackers, mAlertTrackerMap, mStateProtoHashes, mTagIds,
newAtomMatchingTrackers, newAtomMatchingTrackerMap, newConditionTrackers,
newConditionTrackerMap, newMetricProducers, newMetricProducerMap, newAnomalyTrackers,
newAlertTrackerMap, mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap,
mActivationAtomTrackerToMetricMap, mDeactivationAtomTrackerToMetricMap,
mMetricIndexesWithActivation, newStateProtoHashes, mNoReportMetricIds);
newAlertTrackerMap, newPeriodicAlarmTrackers, mConditionToMetricMap,
mTrackerToMetricMap, mTrackerToConditionMap, mActivationAtomTrackerToMetricMap,
mDeactivationAtomTrackerToMetricMap, mMetricIndexesWithActivation, newStateProtoHashes,
mNoReportMetricIds);
mAllAtomMatchingTrackers = newAtomMatchingTrackers;
mAtomMatchingTrackerMap = newAtomMatchingTrackerMap;
mAllConditionTrackers = newConditionTrackers;
@@ -238,6 +240,7 @@ bool MetricsManager::updateConfig(const StatsdConfig& config, const int64_t time
mStateProtoHashes = newStateProtoHashes;
mAllAnomalyTrackers = newAnomalyTrackers;
mAlertTrackerMap = newAlertTrackerMap;
mAllPeriodicAlarmTrackers = newPeriodicAlarmTrackers;
return mConfigValid;
}

View File

@@ -1017,6 +1017,7 @@ bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const
unordered_map<int64_t, int>& newMetricProducerMap,
vector<sp<AnomalyTracker>>& newAnomalyTrackers,
unordered_map<int64_t, int>& newAlertTrackerMap,
vector<sp<AlarmTracker>>& newPeriodicAlarmTrackers,
unordered_map<int, vector<int>>& conditionToMetricMap,
unordered_map<int, vector<int>>& trackerToMetricMap,
unordered_map<int, vector<int>>& trackerToConditionMap,
@@ -1081,6 +1082,12 @@ bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const
return false;
}
// Alarms do not have any state, so we can reuse the initialization logic.
if (!initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs,
newPeriodicAlarmTrackers)) {
ALOGE("initAlarms failed");
return false;
}
return true;
}

View File

@@ -19,6 +19,7 @@
#include <vector>
#include "anomaly/AlarmMonitor.h"
#include "anomaly/AlarmTracker.h"
#include "condition/ConditionTracker.h"
#include "external/StatsPullerManager.h"
#include "matchers/AtomMatchingTracker.h"
@@ -253,6 +254,7 @@ bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const
std::unordered_map<int64_t, int>& newMetricProducerMap,
std::vector<sp<AnomalyTracker>>& newAlertTrackers,
std::unordered_map<int64_t, int>& newAlertTrackerMap,
std::vector<sp<AlarmTracker>>& newPeriodicAlarmTrackers,
std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
std::unordered_map<int, std::vector<int>>& trackerToConditionMap,

View File

@@ -1143,24 +1143,9 @@ bool initAlarms(const StatsdConfig& config, const ConfigKey& key,
allAlarmTrackers.push_back(
new AlarmTracker(startMillis, currentTimeMillis, alarm, key, periodicAlarmMonitor));
}
for (int i = 0; i < config.subscription_size(); ++i) {
const Subscription& subscription = config.subscription(i);
if (subscription.rule_type() != Subscription::ALARM) {
continue;
}
if (subscription.subscriber_information_case() ==
Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) {
ALOGW("subscription \"%lld\" has no subscriber info.\"", (long long)subscription.id());
return false;
}
const auto& itr = alarmTrackerMap.find(subscription.rule_id());
if (itr == alarmTrackerMap.end()) {
ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"",
(long long)subscription.id(), (long long)subscription.rule_id());
return false;
}
const int trackerIndex = itr->second;
allAlarmTrackers[trackerIndex]->addSubscription(subscription);
if (!initSubscribersForSubscriptionType(config, Subscription::ALARM, alarmTrackerMap,
allAlarmTrackers)) {
return false;
}
return true;
}

View File

@@ -306,6 +306,12 @@ bool initMetrics(
std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
std::vector<int>& metricsWithActivation);
// Initialize alarms
// Is called both on initialize new configs and config updates since alarms do not have any state.
bool initAlarms(const StatsdConfig& config, const ConfigKey& key,
const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
const int64_t currentTimeNs, std::vector<sp<AlarmTracker>>& allAlarmTrackers);
// Initialize MetricsManager from StatsdConfig.
// Parameters are the members of MetricsManager. See MetricsManager for declaration.
bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap,

View File

@@ -52,11 +52,15 @@ namespace statsd {
namespace {
ConfigKey key(123, 456);
const int64_t timeBaseNs = 1000;
const int64_t timeBaseNs = 1000 * NS_PER_SEC;
sp<UidMap> uidMap = new UidMap();
sp<StatsPullerManager> pullerManager = new StatsPullerManager();
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> periodicAlarmMonitor;
sp<AlarmMonitor> periodicAlarmMonitor = new AlarmMonitor(
/*minDiffToUpdateRegisteredAlarmTimeSec=*/0,
[](const shared_ptr<IStatsCompanionService>&, int64_t) {},
[](const shared_ptr<IStatsCompanionService>&) {});
set<int> allTagIds;
vector<sp<AtomMatchingTracker>> oldAtomMatchingTrackers;
unordered_map<int64_t, int> oldAtomMatchingTrackerMap;
@@ -206,6 +210,14 @@ Subscription createSubscription(string name, Subscription_RuleType type, int64_t
subscription.mutable_broadcast_subscriber_details();
return subscription;
}
Alarm createAlarm(string name, int64_t offsetMillis, int64_t periodMillis) {
Alarm alarm;
alarm.set_id(StringToId(name));
alarm.set_offset_millis(offsetMillis);
alarm.set_period_millis(periodMillis);
return alarm;
}
} // anonymous namespace
TEST_F(ConfigUpdateTest, TestSimpleMatcherPreserve) {
@@ -3475,6 +3487,91 @@ TEST_F(ConfigUpdateTest, TestUpdateAlerts) {
EXPECT_THAT(newAnomalyTrackers[alert4Index]->mSubscriptions, IsEmpty());
}
TEST_F(ConfigUpdateTest, TestUpdateAlarms) {
StatsdConfig config;
// Add alarms.
Alarm alarm1 = createAlarm("Alarm1", /*offset*/ 1 * MS_PER_SEC, /*period*/ 50 * MS_PER_SEC);
int64_t alarm1Id = alarm1.id();
*config.add_alarm() = alarm1;
Alarm alarm2 = createAlarm("Alarm2", /*offset*/ 1 * MS_PER_SEC, /*period*/ 2000 * MS_PER_SEC);
int64_t alarm2Id = alarm2.id();
*config.add_alarm() = alarm2;
Alarm alarm3 = createAlarm("Alarm3", /*offset*/ 10 * MS_PER_SEC, /*period*/ 5000 * MS_PER_SEC);
int64_t alarm3Id = alarm3.id();
*config.add_alarm() = alarm3;
// Add Subscriptions.
Subscription subscription1 = createSubscription("S1", Subscription::ALARM, alarm1Id);
*config.add_subscription() = subscription1;
Subscription subscription2 = createSubscription("S2", Subscription::ALARM, alarm1Id);
*config.add_subscription() = subscription2;
Subscription subscription3 = createSubscription("S3", Subscription::ALARM, alarm2Id);
*config.add_subscription() = subscription3;
EXPECT_TRUE(initConfig(config));
ASSERT_EQ(oldAlarmTrackers.size(), 3);
// Config is created at statsd start time, so just add the offsets.
EXPECT_EQ(oldAlarmTrackers[0]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 1);
EXPECT_EQ(oldAlarmTrackers[1]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 1);
EXPECT_EQ(oldAlarmTrackers[2]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 10);
// Change alarm2/alarm3.
config.mutable_alarm(1)->set_offset_millis(5 * MS_PER_SEC);
config.mutable_alarm(2)->set_period_millis(10000 * MS_PER_SEC);
// Move subscription2 to be on alarm2 and make a new subscription.
config.mutable_subscription(1)->set_rule_id(alarm2Id);
Subscription subscription4 = createSubscription("S4", Subscription::ALARM, alarm1Id);
*config.add_subscription() = subscription4;
// Update time is 2 seconds after the base time.
int64_t currentTimeNs = timeBaseNs + 2 * NS_PER_SEC;
vector<sp<AlarmTracker>> newAlarmTrackers;
EXPECT_TRUE(initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs,
newAlarmTrackers));
ASSERT_EQ(newAlarmTrackers.size(), 3);
// Config is updated 2 seconds after statsd start
// The offset has passed for alarm1, but not for alarms 2/3.
EXPECT_EQ(newAlarmTrackers[0]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 1 + 50);
EXPECT_EQ(newAlarmTrackers[1]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 5);
EXPECT_EQ(newAlarmTrackers[2]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 10);
// Verify alarms have the correct subscriptions. Use subscription id as proxy for equivalency.
vector<int64_t> alarm1Subscriptions;
for (const Subscription& subscription : newAlarmTrackers[0]->mSubscriptions) {
alarm1Subscriptions.push_back(subscription.id());
}
EXPECT_THAT(alarm1Subscriptions, UnorderedElementsAre(subscription1.id(), subscription4.id()));
vector<int64_t> alarm2Subscriptions;
for (const Subscription& subscription : newAlarmTrackers[1]->mSubscriptions) {
alarm2Subscriptions.push_back(subscription.id());
}
EXPECT_THAT(alarm2Subscriptions, UnorderedElementsAre(subscription2.id(), subscription3.id()));
EXPECT_THAT(newAlarmTrackers[2]->mSubscriptions, IsEmpty());
// Verify the alarm monitor is updated accordingly once the old alarms are removed.
// Alarm2 fires the earliest.
oldAlarmTrackers.clear();
EXPECT_EQ(periodicAlarmMonitor->getRegisteredAlarmTimeSec(), timeBaseNs / NS_PER_SEC + 5);
// Do another update 60 seconds after config creation time, after the offsets of each alarm.
currentTimeNs = timeBaseNs + 60 * NS_PER_SEC;
newAlarmTrackers.clear();
EXPECT_TRUE(initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs,
newAlarmTrackers));
ASSERT_EQ(newAlarmTrackers.size(), 3);
// Config is updated one minute after statsd start.
// Two periods have passed for alarm 1, one has passed for alarms2/3.
EXPECT_EQ(newAlarmTrackers[0]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 1 + 2 * 50);
EXPECT_EQ(newAlarmTrackers[1]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 5 + 2000);
EXPECT_EQ(newAlarmTrackers[2]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 10 + 10000);
}
} // namespace statsd
} // namespace os
} // namespace android