Skip to content

Commit

Permalink
Merge
Browse files Browse the repository at this point in the history
  • Loading branch information
iambriccardo committed Dec 16, 2024
2 parents 4007d54 + 960e8d9 commit a1a42b0
Show file tree
Hide file tree
Showing 18 changed files with 255 additions and 191 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
**Features**:

- Add data categories for Uptime and Attachment Items. ([#4363](https://github.com/getsentry/relay/pull/4363), [#4374](https://github.com/getsentry/relay/pull/4374))
- Add ability to rate limit attachments by count (not just by the number of bytes). ([#4377](https://github.com/getsentry/relay/pull/4377))

**Internal**:

- Remove support for metrics with profile namespace. ([#4391](https://github.com/getsentry/relay/pull/4391))

## 24.11.2

Expand Down
7 changes: 1 addition & 6 deletions relay-base-schema/src/metrics/mri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,6 @@ pub enum MetricNamespace {
Transactions,
/// Metrics extracted from spans.
Spans,
/// Metrics extracted from profile functions.
Profiles,
/// User-defined metrics directly sent by SDKs and applications.
Custom,
/// Metric stats.
Expand All @@ -134,12 +132,11 @@ pub enum MetricNamespace {

impl MetricNamespace {
/// Returns all namespaces/variants of this enum.
pub fn all() -> [Self; 7] {
pub fn all() -> [Self; 6] {
[
Self::Sessions,
Self::Transactions,
Self::Spans,
Self::Profiles,
Self::Custom,
Self::Stats,
Self::Unsupported,
Expand All @@ -157,7 +154,6 @@ impl MetricNamespace {
Self::Sessions => "sessions",
Self::Transactions => "transactions",
Self::Spans => "spans",
Self::Profiles => "profiles",
Self::Custom => "custom",
Self::Stats => "metric_stats",
Self::Unsupported => "unsupported",
Expand All @@ -173,7 +169,6 @@ impl std::str::FromStr for MetricNamespace {
"sessions" => Ok(Self::Sessions),
"transactions" => Ok(Self::Transactions),
"spans" => Ok(Self::Spans),
"profiles" => Ok(Self::Profiles),
"custom" => Ok(Self::Custom),
"metric_stats" => Ok(Self::Stats),
_ => Ok(Self::Unsupported),
Expand Down
3 changes: 0 additions & 3 deletions relay-cogs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,6 @@ pub enum AppFeature {
MetricsTransactions,
/// Metrics in the spans namespace.
MetricsSpans,
/// Metrics in the profiles namespace.
MetricsProfiles,
/// Metrics in the sessions namespace.
MetricsSessions,
/// Metrics in the custom namespace.
Expand Down Expand Up @@ -167,7 +165,6 @@ impl AppFeature {
Self::Replays => "replays",
Self::MetricsTransactions => "metrics_transactions",
Self::MetricsSpans => "metrics_spans",
Self::MetricsProfiles => "metrics_profiles",
Self::MetricsSessions => "metrics_sessions",
Self::MetricsCustom => "metrics_custom",
Self::MetricsStats => "metrics_metric_stats",
Expand Down
21 changes: 11 additions & 10 deletions relay-dynamic-config/src/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,15 +113,6 @@ fn is_err_or_empty(filters_config: &ErrorBoundary<GenericFiltersConfig>) -> bool
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(default)]
pub struct Options {
/// Kill switch for shutting down profile function metrics
/// ingestion in the generic-metrics platform
#[serde(
rename = "profiling.generic_metrics.functions_ingestion.enabled",
deserialize_with = "default_on_error",
skip_serializing_if = "is_default"
)]
pub profiles_function_generic_metrics_enabled: bool,

/// Kill switch for controlling the cardinality limiter.
#[serde(
rename = "relay.cardinality-limiter.mode",
Expand Down Expand Up @@ -211,6 +202,17 @@ pub struct Options {
)]
pub deprecated2: f32,

/// Deprecated, still forwarded for older downstream Relays.
/// Kill switch for shutting down profile function metrics
/// ingestion in the generic-metrics platform.
#[doc(hidden)]
#[serde(
rename = "profiling.generic_metrics.functions_ingestion.enabled",
deserialize_with = "default_on_error",
skip_serializing_if = "is_default"
)]
pub deprecated3: bool,

/// All other unknown options.
#[serde(flatten)]
other: HashMap<String, Value>,
Expand Down Expand Up @@ -249,7 +251,6 @@ impl BucketEncodings {
match namespace {
MetricNamespace::Transactions => self.transactions,
MetricNamespace::Spans => self.spans,
MetricNamespace::Profiles => self.profiles,
MetricNamespace::Custom => self.custom,
MetricNamespace::Stats => self.metric_stats,
// Always force the legacy encoding for sessions,
Expand Down
27 changes: 10 additions & 17 deletions relay-metrics/src/aggregator/cost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,29 +145,26 @@ mod tests {
}
"#);
cost_tracker.add_cost(MetricNamespace::Custom, project_key1, 50);
cost_tracker.add_cost(MetricNamespace::Profiles, project_key1, 50);
insta::assert_debug_snapshot!(cost_tracker, @r#"
CostTracker {
total_cost: 100,
total_cost: 50,
cost_per_project_key: {
ProjectKey("a94ae32be2584e0bbd7a4cbb95971fed"): 100,
ProjectKey("a94ae32be2584e0bbd7a4cbb95971fed"): 50,
},
cost_per_namespace: {
Profiles: 50,
Custom: 50,
},
}
"#);
cost_tracker.add_cost(namespace, project_key2, 200);
insta::assert_debug_snapshot!(cost_tracker, @r#"
CostTracker {
total_cost: 300,
total_cost: 250,
cost_per_project_key: {
ProjectKey("a94ae32be2584e0bbd7a4cbb95971fed"): 100,
ProjectKey("a94ae32be2584e0bbd7a4cbb95971fed"): 50,
ProjectKey("a94ae32be2584e0bbd7a4cbb95971fee"): 200,
},
cost_per_namespace: {
Profiles: 50,
Custom: 250,
},
}
Expand All @@ -176,13 +173,12 @@ mod tests {
cost_tracker.subtract_cost(namespace, project_key3, 666);
insta::assert_debug_snapshot!(cost_tracker, @r#"
CostTracker {
total_cost: 300,
total_cost: 250,
cost_per_project_key: {
ProjectKey("a94ae32be2584e0bbd7a4cbb95971fed"): 100,
ProjectKey("a94ae32be2584e0bbd7a4cbb95971fed"): 50,
ProjectKey("a94ae32be2584e0bbd7a4cbb95971fee"): 200,
},
cost_per_namespace: {
Profiles: 50,
Custom: 250,
},
}
Expand All @@ -191,34 +187,31 @@ mod tests {
cost_tracker.subtract_cost(namespace, project_key1, 666);
insta::assert_debug_snapshot!(cost_tracker, @r#"
CostTracker {
total_cost: 300,
total_cost: 250,
cost_per_project_key: {
ProjectKey("a94ae32be2584e0bbd7a4cbb95971fed"): 100,
ProjectKey("a94ae32be2584e0bbd7a4cbb95971fed"): 50,
ProjectKey("a94ae32be2584e0bbd7a4cbb95971fee"): 200,
},
cost_per_namespace: {
Profiles: 50,
Custom: 250,
},
}
"#);
cost_tracker.subtract_cost(namespace, project_key2, 20);
insta::assert_debug_snapshot!(cost_tracker, @r#"
CostTracker {
total_cost: 280,
total_cost: 230,
cost_per_project_key: {
ProjectKey("a94ae32be2584e0bbd7a4cbb95971fed"): 100,
ProjectKey("a94ae32be2584e0bbd7a4cbb95971fed"): 50,
ProjectKey("a94ae32be2584e0bbd7a4cbb95971fee"): 180,
},
cost_per_namespace: {
Profiles: 50,
Custom: 230,
},
}
"#);

// Subtract all
cost_tracker.subtract_cost(MetricNamespace::Profiles, project_key1, 50);
cost_tracker.subtract_cost(MetricNamespace::Custom, project_key1, 50);
cost_tracker.subtract_cost(MetricNamespace::Custom, project_key2, 180);
insta::assert_debug_snapshot!(cost_tracker, @r#"
Expand Down
1 change: 0 additions & 1 deletion relay-metrics/src/cogs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ fn to_app_feature(ns: MetricNamespace) -> AppFeature {
MetricNamespace::Sessions => AppFeature::MetricsSessions,
MetricNamespace::Transactions => AppFeature::MetricsTransactions,
MetricNamespace::Spans => AppFeature::MetricsSpans,
MetricNamespace::Profiles => AppFeature::MetricsProfiles,
MetricNamespace::Custom => AppFeature::MetricsCustom,
MetricNamespace::Stats => AppFeature::MetricsStats,
MetricNamespace::Unsupported => AppFeature::MetricsUnsupported,
Expand Down
10 changes: 6 additions & 4 deletions relay-quotas/src/quota.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,12 @@ impl ItemScoping<'_> {

/// The unit in which a data category is measured.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum CategoryUnit {
pub enum CategoryUnit {
/// Counts the number of items.
Count,
/// Counts the number of bytes across items.
Bytes,
Batched,
/// Counts the accumulated times across items.
Milliseconds,
}

Expand All @@ -199,9 +201,9 @@ impl CategoryUnit {
| DataCategory::ProfileChunk
| DataCategory::Uptime
| DataCategory::MetricSecond
| DataCategory::AttachmentItem => Some(Self::Count),
| DataCategory::AttachmentItem
| DataCategory::Session => Some(Self::Count),
DataCategory::Attachment => Some(Self::Bytes),
DataCategory::Session => Some(Self::Batched),
DataCategory::ProfileDuration => Some(Self::Milliseconds),

DataCategory::Unknown => None,
Expand Down
76 changes: 40 additions & 36 deletions relay-server/src/envelope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ use relay_quotas::DataCategory;
use relay_sampling::DynamicSamplingContext;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use smallvec::{smallvec, SmallVec};

use crate::constants::DEFAULT_EVENT_RETENTION;
use crate::extractors::{PartialMeta, RequestMeta};
Expand Down Expand Up @@ -632,6 +632,14 @@ pub struct Item {
payload: Bytes,
}

/// Expresses the purpose of counting quantities.
///
/// Sessions are counted for rate limiting enforcement but not for outcome reporting.
pub enum CountFor {
RateLimits,
Outcomes,
}

impl Item {
/// Creates a new item with the given type.
pub fn new(ty: ItemType) -> Self {
Expand Down Expand Up @@ -670,13 +678,38 @@ impl Item {
/// Returns the number used for counting towards rate limits and producing outcomes.
///
/// For attachments, we count the number of bytes. Other items are counted as 1.
pub fn quantity(&self) -> usize {
pub fn quantities(&self, purpose: CountFor) -> SmallVec<[(DataCategory, usize); 1]> {
match self.ty() {
ItemType::Attachment => self.len().max(1),
// NOTE: This is semantically wrong. An otel trace contains may contain many spans,
// but we cannot easily count these before converting the trace into a series of spans.
ItemType::OtelTracesData => 1,
_ => 1,
ItemType::Event => smallvec![(DataCategory::Error, 1)],
ItemType::Transaction => smallvec![(DataCategory::Transaction, 1)],
ItemType::Security | ItemType::RawSecurity => {
smallvec![(DataCategory::Security, 1)]
}
ItemType::Nel => smallvec![],
ItemType::UnrealReport => smallvec![(DataCategory::Error, 1)],
ItemType::Attachment => smallvec![
(DataCategory::Attachment, self.len().max(1)),
(DataCategory::AttachmentItem, 1)
],
ItemType::Session | ItemType::Sessions => match purpose {
CountFor::RateLimits => smallvec![(DataCategory::Session, 1)],
CountFor::Outcomes => smallvec![],
},
ItemType::Statsd | ItemType::MetricBuckets => smallvec![],
ItemType::FormData => smallvec![],
ItemType::UserReport => smallvec![],
ItemType::UserReportV2 => smallvec![(DataCategory::UserReportV2, 1)],
ItemType::Profile => smallvec![(DataCategory::Profile, 1)],
ItemType::ReplayEvent | ItemType::ReplayRecording | ItemType::ReplayVideo => {
smallvec![(DataCategory::Replay, 1)]
}
ItemType::ClientReport => smallvec![],
ItemType::CheckIn => smallvec![(DataCategory::Monitor, 1)],
ItemType::Span | ItemType::OtelSpan => smallvec![(DataCategory::Span, 1)],
// NOTE: semantically wrong, but too expensive to parse.
ItemType::OtelTracesData => smallvec![(DataCategory::Span, 1)],
ItemType::ProfileChunk => smallvec![(DataCategory::ProfileChunk, 1)], // TODO: should be seconds?
ItemType::Unknown(_) => smallvec![],
}
}

Expand All @@ -688,35 +721,6 @@ impl Item {
)
}

/// Returns the data category used for generating outcomes.
///
/// Returns `None` if outcomes are not generated for this type (e.g. sessions).
pub fn outcome_category(&self) -> Option<DataCategory> {
match self.ty() {
ItemType::Event => Some(DataCategory::Error),
ItemType::Transaction => Some(DataCategory::Transaction),
ItemType::Security | ItemType::RawSecurity => Some(DataCategory::Security),
ItemType::Nel => None,
ItemType::UnrealReport => Some(DataCategory::Error),
ItemType::Attachment => Some(DataCategory::Attachment),
ItemType::Session | ItemType::Sessions => None,
ItemType::Statsd | ItemType::MetricBuckets => None,
ItemType::FormData => None,
ItemType::UserReport => None,
ItemType::UserReportV2 => Some(DataCategory::UserReportV2),
ItemType::Profile => Some(DataCategory::Profile),
ItemType::ReplayEvent | ItemType::ReplayRecording | ItemType::ReplayVideo => {
Some(DataCategory::Replay)
}
ItemType::ClientReport => None,
ItemType::CheckIn => Some(DataCategory::Monitor),
ItemType::Span | ItemType::OtelSpan => Some(DataCategory::Span),
ItemType::OtelTracesData => None,
ItemType::ProfileChunk => Some(DataCategory::ProfileChunk),
ItemType::Unknown(_) => None,
}
}

/// Returns `true` if this item's payload is empty.
pub fn is_empty(&self) -> bool {
self.payload.is_empty()
Expand Down
24 changes: 11 additions & 13 deletions relay-server/src/services/processor/dynamic_sampling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use relay_sampling::config::RuleType;
use relay_sampling::evaluation::{ReservoirEvaluator, SamplingEvaluator};
use relay_sampling::{DynamicSamplingContext, SamplingConfig};

use crate::envelope::ItemType;
use crate::envelope::{CountFor, ItemType};
use crate::services::outcome::Outcome;
use crate::services::processor::{
EventProcessing, ProcessEnvelopeState, Sampling, TransactionGroup,
Expand Down Expand Up @@ -117,18 +117,16 @@ pub fn drop_unsampled_items(state: &mut ProcessEnvelopeState<TransactionGroup>,
.take_items_by(|item| *item.ty() != ItemType::Profile);

for item in dropped_items {
let Some(category) = item.outcome_category() else {
continue;
};

// Dynamic sampling only drops indexed items. Upgrade the category to the index
// category if one exists for this category, for example profiles will be upgraded to profiles indexed,
// but attachments are still emitted as attachments.
let category = category.index_category().unwrap_or(category);

state
.managed_envelope
.track_outcome(outcome.clone(), category, item.quantity());
for (category, quantity) in item.quantities(CountFor::Outcomes) {
// Dynamic sampling only drops indexed items. Upgrade the category to the index
// category if one exists for this category, for example profiles will be upgraded to profiles indexed,
// but attachments are still emitted as attachments.
let category = category.index_category().unwrap_or(category);

state
.managed_envelope
.track_outcome(outcome.clone(), category, quantity);
}
}

// Mark all remaining items in the envelope as un-sampled.
Expand Down
2 changes: 0 additions & 2 deletions relay-server/src/services/processor/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ pub fn is_valid_namespace(bucket: &Bucket, source: BucketSource) -> bool {
MetricNamespace::Sessions => true,
MetricNamespace::Transactions => true,
MetricNamespace::Spans => true,
MetricNamespace::Profiles => true,
MetricNamespace::Custom => true,
MetricNamespace::Stats => source == BucketSource::Internal,
MetricNamespace::Unsupported => false,
Expand Down Expand Up @@ -81,7 +80,6 @@ fn is_metric_namespace_valid(state: &ProjectInfo, namespace: MetricNamespace) ->
MetricNamespace::Sessions => true,
MetricNamespace::Transactions => true,
MetricNamespace::Spans => state.config.features.produces_spans(),
MetricNamespace::Profiles => true,
MetricNamespace::Custom => state.has_feature(Feature::CustomMetrics),
MetricNamespace::Stats => true,
MetricNamespace::Unsupported => false,
Expand Down
Loading

0 comments on commit a1a42b0

Please sign in to comment.