-
Notifications
You must be signed in to change notification settings - Fork 103
ref(client-reports): Move Client Reports to the new processing #5338
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 13 commits
91ba01a
5c1fb4b
70eb032
673958e
5505925
709bdaa
f8d85af
1535881
12316be
843a1c6
c8e8263
c76a024
bc85629
b9fdd3e
089341c
59c8298
af3ee0b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,166 @@ | ||
| use relay_metrics::UnixTimestamp; | ||
| use relay_quotas::DataCategory; | ||
| use relay_system::Addr; | ||
|
|
||
| use crate::envelope::{EnvelopeHeaders, Item, ItemType}; | ||
| use crate::managed::{Counted, Managed, ManagedEnvelope, OutcomeError, Quantities, Rejected}; | ||
| use crate::processing::{self, Context, Nothing, Output}; | ||
| use crate::services::outcome::{Outcome, TrackOutcome}; | ||
|
|
||
| mod process; | ||
|
|
||
| #[derive(Debug, thiserror::Error)] | ||
| pub enum Error { | ||
| /// Client report parsing failed. | ||
| #[error("invalid client report")] | ||
| InvalidClientReport, | ||
| /// Processing relay has client outcomes disabled. | ||
| #[error("client outcomes disabled")] | ||
| OutcomesDisabled, | ||
| /// Client report timestamp is too old. | ||
| #[error("client report timestamp too old")] | ||
| TimestampTooOld, | ||
| /// Client report timestamp is too far in the future. | ||
| #[error("client report timestamp in future")] | ||
| TimestampInFuture, | ||
| /// Client report contains an invalid outcome combination. | ||
| #[error("invalid outcome combination")] | ||
| InvalidOutcome, | ||
| } | ||
|
|
||
| impl OutcomeError for Error { | ||
| type Error = Self; | ||
|
|
||
| fn consume(self) -> (Option<Outcome>, Self::Error) { | ||
| // Currently/historically client reports do not emit outcomes. | ||
| (None, self) | ||
| } | ||
| } | ||
|
|
||
| /// A processor for Client-Reports. | ||
tobias-wilfert marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| pub struct ClientReportsProcessor { | ||
| aggregator: Addr<TrackOutcome>, | ||
| } | ||
|
|
||
| impl ClientReportsProcessor { | ||
| /// Creates a new [`Self`]. | ||
| pub fn new(aggregator: Addr<TrackOutcome>) -> Self { | ||
| Self { aggregator } | ||
| } | ||
| } | ||
|
|
||
| impl processing::Processor for ClientReportsProcessor { | ||
| type UnitOfWork = SerializedClientReports; | ||
| type Output = Nothing; | ||
| type Error = Error; | ||
|
|
||
| fn prepare_envelope( | ||
| &self, | ||
| envelope: &mut ManagedEnvelope, | ||
| ) -> Option<Managed<Self::UnitOfWork>> { | ||
| let headers = envelope.envelope().headers().clone(); | ||
|
|
||
| let client_reports = envelope | ||
| .envelope_mut() | ||
| .take_items_by(|item| matches!(*item.ty(), ItemType::ClientReport)) | ||
| .into_vec(); | ||
tobias-wilfert marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| let work = SerializedClientReports { | ||
| headers, | ||
| client_reports, | ||
| }; | ||
| Some(Managed::from_envelope(envelope, work)) | ||
| } | ||
|
|
||
| async fn process( | ||
| &self, | ||
| client_reports: Managed<Self::UnitOfWork>, | ||
| ctx: Context<'_>, | ||
| ) -> Result<Output<Self::Output>, Rejected<Self::Error>> { | ||
| if !ctx.config.emit_outcomes().any() && ctx.config.processing_enabled() { | ||
| // if a processing relay has client outcomes disabled we drop them without processing. | ||
| return Err(client_reports.reject_err(Error::OutcomesDisabled)); | ||
| } | ||
tobias-wilfert marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
|
Comment on lines
+80
to
+84
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: The condition 🔍 Detailed AnalysisThe condition 💡 Suggested FixCorrect the condition at 🤖 Prompt for AI AgentDid we get this right? 👍 / 👎 to inform future reviews. |
||
| let client_reports = process::expand(client_reports); | ||
|
|
||
| // FIXME: This guard is taken over from the old code not sure if we still need this. | ||
| if !client_reports.output_events.is_empty() { | ||
tobias-wilfert marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| let client_reports = process::validate(client_reports, ctx.config, ctx.project_info)?; | ||
| process::emit(client_reports, &self.aggregator); | ||
| } | ||
|
|
||
| Ok(Output::empty()) | ||
tobias-wilfert marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
cursor[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| /// Client-Reports in their serialized state, as transported in an envelope. | ||
| #[derive(Debug)] | ||
| pub struct SerializedClientReports { | ||
| /// Original envelope headers. | ||
| headers: EnvelopeHeaders, | ||
| /// A list of client-reports waiting to be processed. | ||
| /// | ||
| /// All items contained here must be client-reports. | ||
| client_reports: Vec<Item>, | ||
| } | ||
|
|
||
| impl Counted for SerializedClientReports { | ||
| fn quantities(&self) -> Quantities { | ||
| smallvec::smallvec![] | ||
| } | ||
| } | ||
|
|
||
| /// Client reports which have been parsed and aggregated from their serialized state. | ||
| pub struct ExpandedClientReports { | ||
| /// Original envelope headers. | ||
| headers: EnvelopeHeaders, | ||
| /// The timestamp of when the reports were created. | ||
| timestamp: Option<UnixTimestamp>, | ||
| /// Aggregated outcome events from all parsed client reports. | ||
| output_events: Vec<OutcomeEvent>, | ||
tobias-wilfert marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| impl Counted for ExpandedClientReports { | ||
| fn quantities(&self) -> crate::managed::Quantities { | ||
tobias-wilfert marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| smallvec::smallvec![] | ||
| } | ||
| } | ||
|
|
||
| /// A aggregated single outcome event. | ||
| pub struct OutcomeEvent { | ||
| outcome_type: ClientReportField, | ||
| reason: String, | ||
| category: DataCategory, | ||
| quantity: u32, | ||
| } | ||
|
|
||
| impl Counted for OutcomeEvent { | ||
| fn quantities(&self) -> crate::managed::Quantities { | ||
| smallvec::smallvec![] | ||
| } | ||
| } | ||
|
|
||
| /// Client reports which have been validated and converted to trackable outcomes. | ||
| pub struct ValidatedClientReports { | ||
| outcomes: Vec<TrackOutcome>, | ||
| } | ||
|
|
||
| impl Counted for ValidatedClientReports { | ||
| fn quantities(&self) -> crate::managed::Quantities { | ||
| smallvec::smallvec![] | ||
| } | ||
| } | ||
|
|
||
| /// Fields of client reports that map to specific [`Outcome`]s without content. | ||
| #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] | ||
| pub enum ClientReportField { | ||
| /// The event has been filtered by an inbound data filter. | ||
| Filtered, | ||
| /// The event has been filtered by a sampling rule. | ||
| FilteredSampling, | ||
| /// The event has been rate limited. | ||
| RateLimited, | ||
| /// The event has already been discarded on the client side. | ||
| ClientDiscard, | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.