blob: 47e3851ef070f104b19838973ce2deb4b7ec3973 [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/segmentation_platform/internal/database/metadata_utils.h"
#include "base/metrics/metrics_hashes.h"
#include "base/notreached.h"
#include "base/time/time.h"
#include "components/optimization_guide/proto/models.pb.h"
#include "components/segmentation_platform/internal/database/signal_key.h"
#include "components/segmentation_platform/internal/proto/aggregation.pb.h"
#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
#include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
#include "components/segmentation_platform/internal/proto/types.pb.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace segmentation_platform {
namespace metadata_utils {
namespace {
uint64_t GetExpectedTensorLength(const proto::Feature& feature) {
switch (feature.aggregation()) {
case proto::Aggregation::COUNT:
case proto::Aggregation::COUNT_BOOLEAN:
case proto::Aggregation::BUCKETED_COUNT_BOOLEAN_TRUE_COUNT:
case proto::Aggregation::SUM:
case proto::Aggregation::SUM_BOOLEAN:
case proto::Aggregation::BUCKETED_SUM_BOOLEAN_TRUE_COUNT:
return feature.bucket_count() == 0 ? 0 : 1;
case proto::Aggregation::BUCKETED_COUNT:
case proto::Aggregation::BUCKETED_COUNT_BOOLEAN:
case proto::Aggregation::BUCKETED_CUMULATIVE_COUNT:
case proto::Aggregation::BUCKETED_SUM:
case proto::Aggregation::BUCKETED_SUM_BOOLEAN:
case proto::Aggregation::BUCKETED_CUMULATIVE_SUM:
return feature.bucket_count();
case proto::Aggregation::UNKNOWN:
NOTREACHED();
return 0;
}
}
} // namespace
ValidationResult ValidateSegmentInfo(const proto::SegmentInfo& segment_info) {
if (segment_info.segment_id() ==
optimization_guide::proto::OPTIMIZATION_TARGET_UNKNOWN) {
return ValidationResult::kSegmentIDNotFound;
}
if (!segment_info.has_model_metadata())
return ValidationResult::kMetadataNotFound;
return ValidateMetadata(segment_info.model_metadata());
}
ValidationResult ValidateMetadata(
const proto::SegmentationModelMetadata& model_metadata) {
if (model_metadata.time_unit() == proto::TimeUnit::UNKNOWN_TIME_UNIT)
return ValidationResult::kTimeUnitInvald;
return ValidationResult::kValidationSuccess;
}
ValidationResult ValidateMetadataFeature(const proto::Feature& feature) {
if (feature.type() == proto::SignalType::UNKNOWN_SIGNAL_TYPE)
return ValidationResult::kSignalTypeInvalid;
if ((feature.type() == proto::SignalType::HISTOGRAM_ENUM ||
feature.type() == proto::SignalType::HISTOGRAM_VALUE) &&
feature.name().empty()) {
return ValidationResult::kFeatureNameNotFound;
}
if (feature.name_hash() == 0)
return ValidationResult::kFeatureNameHashNotFound;
if (!feature.name().empty() &&
base::HashMetricName(feature.name()) != feature.name_hash()) {
return ValidationResult::kFeatureNameHashDoesNotMatchName;
}
if (feature.aggregation() == proto::Aggregation::UNKNOWN)
return ValidationResult::kFeatureAggregationNotFound;
if (GetExpectedTensorLength(feature) != feature.tensor_length())
return ValidationResult::kFeatureTensorLengthInvalid;
return ValidationResult::kValidationSuccess;
}
ValidationResult ValidateMetadataAndFeatures(
const proto::SegmentationModelMetadata& model_metadata) {
auto metadata_result = ValidateMetadata(model_metadata);
if (metadata_result != ValidationResult::kValidationSuccess)
return metadata_result;
for (int i = 0; i < model_metadata.features_size(); ++i) {
auto feature = model_metadata.features(i);
auto feature_result = ValidateMetadataFeature(feature);
if (feature_result != ValidationResult::kValidationSuccess)
return feature_result;
}
return ValidationResult::kValidationSuccess;
}
ValidationResult ValidateSegmentInfoMetadataAndFeatures(
const proto::SegmentInfo& segment_info) {
auto segment_info_result = ValidateSegmentInfo(segment_info);
if (segment_info_result != ValidationResult::kValidationSuccess)
return segment_info_result;
return ValidateMetadataAndFeatures(segment_info.model_metadata());
}
void SetFeatureNameHashesFromName(
proto::SegmentationModelMetadata* model_metadata) {
for (int i = 0; i < model_metadata->features_size(); ++i) {
proto::Feature* feature = model_metadata->mutable_features(i);
feature->set_name_hash(base::HashMetricName(feature->name()));
}
}
bool HasExpiredOrUnavailableResult(const proto::SegmentInfo& segment_info,
const base::Time& now) {
if (!segment_info.has_prediction_result())
return true;
base::Time last_result_timestamp = base::Time::FromDeltaSinceWindowsEpoch(
base::Microseconds(segment_info.prediction_result().timestamp_us()));
base::TimeDelta result_ttl =
segment_info.model_metadata().result_time_to_live() *
GetTimeUnit(segment_info.model_metadata());
return last_result_timestamp + result_ttl < now;
}
bool HasFreshResults(const proto::SegmentInfo& segment_info,
const base::Time& now) {
if (!segment_info.has_prediction_result())
return false;
const proto::SegmentationModelMetadata& metadata =
segment_info.model_metadata();
base::Time last_result_timestamp = base::Time::FromDeltaSinceWindowsEpoch(
base::Microseconds(segment_info.prediction_result().timestamp_us()));
base::TimeDelta result_ttl =
metadata.result_time_to_live() * GetTimeUnit(metadata);
return now - last_result_timestamp < result_ttl;
}
base::TimeDelta GetTimeUnit(
const proto::SegmentationModelMetadata& model_metadata) {
proto::TimeUnit time_unit = model_metadata.time_unit();
switch (time_unit) {
case proto::TimeUnit::YEAR:
return base::Days(365);
case proto::TimeUnit::MONTH:
return base::Days(30);
case proto::TimeUnit::WEEK:
return base::Days(7);
case proto::TimeUnit::DAY:
return base::Days(1);
case proto::TimeUnit::HOUR:
return base::Hours(1);
case proto::TimeUnit::MINUTE:
return base::Minutes(1);
case proto::TimeUnit::SECOND:
return base::Seconds(1);
case proto::TimeUnit::UNKNOWN_TIME_UNIT:
FALLTHROUGH;
default:
NOTREACHED();
return base::TimeDelta();
}
}
SignalKey::Kind SignalTypeToSignalKind(proto::SignalType signal_type) {
switch (signal_type) {
case proto::SignalType::USER_ACTION:
return SignalKey::Kind::USER_ACTION;
case proto::SignalType::HISTOGRAM_ENUM:
return SignalKey::Kind::HISTOGRAM_ENUM;
case proto::SignalType::HISTOGRAM_VALUE:
return SignalKey::Kind::HISTOGRAM_VALUE;
case proto::SignalType::UNKNOWN_SIGNAL_TYPE:
return SignalKey::Kind::UNKNOWN;
}
}
} // namespace metadata_utils
} // namespace segmentation_platform