diff --git a/relay-server/src/services/processor.rs b/relay-server/src/services/processor.rs index c3137bc9f55..13882ded09f 100644 --- a/relay-server/src/services/processor.rs +++ b/relay-server/src/services/processor.rs @@ -71,6 +71,7 @@ use crate::metrics_extraction::transactions::{ExtractedMetrics, TransactionExtra use crate::service::ServiceError; use crate::services::global_config::GlobalConfigHandle; use crate::services::outcome::{DiscardReason, Outcome, TrackOutcome}; +use crate::services::processor::event::FiltersStatus; use crate::services::project::ProjectState; use crate::services::project_cache::{AddMetricMeta, ProjectCache, UpdateRateLimits}; use crate::services::test_store::{Capture, TestStore}; @@ -1260,8 +1261,11 @@ impl EnvelopeProcessorService { event::finalize(state, &self.inner.config)?; self.light_normalize_event(state)?; - event::filter(state, &self.inner.global_config.current())?; - dynamic_sampling::tag_error_with_sampling_decision(state, &self.inner.config); + let filter_run = event::filter(state, &self.inner.global_config.current())?; + + if self.inner.config.processing_enabled() || matches!(filter_run, FiltersStatus::Ok) { + dynamic_sampling::tag_error_with_sampling_decision(state, &self.inner.config); + } if_processing!(self.inner.config, { event::store(state, &self.inner.config)?; @@ -1294,25 +1298,11 @@ impl EnvelopeProcessorService { event::finalize(state, &self.inner.config)?; self.light_normalize_event(state)?; dynamic_sampling::normalize(state); - event::filter(state, &self.inner.global_config.current())?; - // Don't extract metrics if relay can't apply generic inbound filters. - // An inbound filter applied in another up-to-date relay in chain may - // need to drop the event, and there should not be metrics from dropped - // events. - // In processing relays, always extract metrics to avoid losing them. - let supported_generic_filters = self.inner.global_config.current().filters.is_ok() - && relay_filter::are_generic_filters_supported( - self.inner - .global_config - .current() - .filters() - .map(|f| f.version), - state.project_state.config.filter_settings.generic.version, - ); + let filter_run = event::filter(state, &self.inner.global_config.current())?; let mut sampling_should_drop = false; - if self.inner.config.processing_enabled() || supported_generic_filters { + if self.inner.config.processing_enabled() || matches!(filter_run, FiltersStatus::Ok) { let sampling_result = dynamic_sampling::run(state, &self.inner.config); // Remember sampling decision, before it is reset in `dynamic_sampling::sample_envelope_items`. sampling_should_drop = sampling_result.should_drop(); diff --git a/relay-server/src/services/processor/event.rs b/relay-server/src/services/processor/event.rs index 4c8a86edbf8..fd24b014f28 100644 --- a/relay-server/src/services/processor/event.rs +++ b/relay-server/src/services/processor/event.rs @@ -271,15 +271,36 @@ pub fn finalize( Ok(()) } +/// Status for applying some filters that don't drop the event. +/// +/// The enum represents either the success of running all filters and keeping +/// the event, [`FiltersStatus::Ok`], or not running all the filters because +/// some are unsupported, [`FiltersStatus::Unsupported`]. +/// +/// If there are unsuppported filters, Relay should forward the event upstream +/// so that a more up-to-date Relay can apply filters appropriately. Actions +/// that depend on the outcome of event filtering, such as metric extraction, +/// should be postponed until a filtering decision is made. +#[must_use] +pub enum FiltersStatus { + /// All filters have been applied and the event should be kept. + Ok, + /// Some filters are not supported and were not applied. + /// + /// Relay should forward events upstream for a more up-to-date Relay to apply these filters. + /// Supported filters were applied and they don't reject the event. + Unsupported, +} + pub fn filter( state: &mut ProcessEnvelopeState, global_config: &GlobalConfig, -) -> Result<(), ProcessingError> { +) -> Result { let event = match state.event.value_mut() { Some(event) => event, // Some events are created by processing relays (e.g. unreal), so they do not yet // exist at this point in non-processing relays. - None => return Ok(()), + None => return Ok(FiltersStatus::Ok), }; let client_ip = state.managed_envelope.envelope().meta().client_addr(); @@ -293,7 +314,22 @@ pub fn filter( .reject(Outcome::Filtered(err.clone())); ProcessingError::EventFiltered(err) }) - }) + })?; + + // Don't extract metrics if relay can't apply generic filters. A filter + // applied in another up-to-date relay in chain may need to drop the event, + // and there should not be metrics from dropped events. + // In processing relays, always extract metrics to avoid losing them. + let supported_generic_filters = global_config.filters.is_ok() + && relay_filter::are_generic_filters_supported( + global_config.filters().map(|f| f.version), + state.project_state.config.filter_settings.generic.version, + ); + if supported_generic_filters { + Ok(FiltersStatus::Ok) + } else { + Ok(FiltersStatus::Unsupported) + } } /// Apply data privacy rules to the event payload.