Skip to content
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

ref(server): Maintain copy of event item headers #3414

Closed
wants to merge 5 commits into from

Conversation

jjbayer
Copy link
Member

@jjbayer jjbayer commented Apr 12, 2024

#3375 added a third event item header that needs to be extracted from the incoming item, updated and then re-serialized back into the event item manually:

flowchart LR
Item --> payload
Item --> headers
headers --> metrics_extracted
headers --> sample_rates
headers --> spans_extracted
payload --> Event
Event --> payload2
metrics_extracted --> headers2
sample_rates --> headers2
spans_extracted --> headers2
payload2 --> Item2
headers2 --> Item2
Loading

By maintaining an instance of ItemHeaders instead of a bunch of individual fields on ProcessEnvelopeState, simplify to:

flowchart LR
Item --> payload
Item --> headers
payload --> Event
Event --> payload2
payload2 --> Item2
headers ---> Item2
Loading

Eventually, we could remove both event and event_headers from the stat, and make them return values of event::extract instead.

#skip-changelog

@jjbayer jjbayer marked this pull request as ready for review April 16, 2024 11:45
@jjbayer jjbayer requested a review from a team as a code owner April 16, 2024 11:45
Copy link
Member

@Dav1dde Dav1dde left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should just use a default if we don't have headers, then set all the things we need to set and when serializing we can rely on skip_serializing_if.

@@ -1003,6 +1013,11 @@ impl Item {
ItemType::Unknown(_) => false,
}
}

pub fn into_parts(self) -> (ItemHeaders, Bytes) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing docs, isn't there a clippy lint 🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed in favor of destructuring.

///
/// Only applies to envelopes with a transaction item.
spans_extracted: bool,
/// `None` if the envelope does not contain an event item.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we just use a default here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ItemHeaders does not implement Default atm, it would require a default ItemType which sounds dangerous. In the end I think we should remove this from the state (see other comment).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use something like item headers instead which can represent the "default" case and also can be constructed from ItemHeaders as well as be turned into Option<ItemHeaders> again?

@@ -346,7 +350,9 @@ pub fn extract_from_event(

add_span(transaction_span.into());

state.spans_extracted = true;
if let Some(headers) = state.event_headers.as_mut() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if it is not None, we should still set it to true?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realized I can keep the reference from the beginning of the function alive and make it mutable.

Semantically, state.event_headers cannot be None if state.event is not empty, so my first attempt at this PR was to put them in an Option together. But that made event handling more complicated because of the two option levels (Option(Annotated(Option(Event), _))). Long term, I believe we should remove both from the state and pass them as plain old variables, as you suggested.

@@ -481,34 +481,34 @@ fn is_false(val: &bool) -> bool {
pub struct ItemHeaders {
/// The type of the item.
#[serde(rename = "type")]
ty: ItemType,
pub ty: ItemType,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Item and ItemHeaders were some of the few serializable data types on which we restricted field access. Making them public seems more aligned with other data types, and is necessary to access its fields during processing.

Comment on lines +579 to +585
/// The release, used by security reports.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sentry_release: Option<String>,

/// The environment, used by security reports.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sentry_environment: Option<String>,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These were previously set and read using string access.

Comment on lines +256 to +258
let Some(event_headers) = state.event_headers.as_mut() else {
return;
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we log an error here if we don't have any headers? This seems very error prone and is going back to the problem we tried to solve (implicit optional state).

///
/// Only applies to envelopes with a transaction item.
spans_extracted: bool,
/// `None` if the envelope does not contain an event item.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use something like item headers instead which can represent the "default" case and also can be constructed from ItemHeaders as well as be turned into Option<ItemHeaders> again?

Copy link
Member

@Dav1dde Dav1dde left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like a step backward and forward at the same time. It makes a bunch of stuff nicer but at the same time adds more of the implicit state we wanted to get rid of to the ProcessEnvelopeState.

There is maybe a way to make the event_headers not optional (see previous comments), but I can't tell if that's worth the effort.

Overall I think it makes it better and it's just a temporary "bandaid" until we will further improve the processor.

@jjbayer
Copy link
Member Author

jjbayer commented Apr 17, 2024

Should we use something like item headers instead which can represent the "default" case and also can be constructed from ItemHeaders as well as be turned into Option again?

Thanks, this made me realize there is a bug in the PR (which no test caught): Item::set_payload and Item::new modify the headers, so we should not overwrite the entire header struct afterwards. This is a good reason to keep Item::headers a private field.

Let's skip this change and work toward moving these intermediary values off the state instead.

@jjbayer jjbayer closed this Apr 17, 2024
@jan-auer jan-auer deleted the ref/item-headers branch July 4, 2024 11:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants