-
Notifications
You must be signed in to change notification settings - Fork 94
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(project): Treat invalid project states as pending and unify state types #3770
Changes from 40 commits
dc191af
23819cd
6354f02
b1b551e
9eb1a3c
c75f596
2fe1ef8
d8e4661
d97864e
f54cc25
f144966
c26ab17
41d5305
6aadc85
6e8730a
0f5a299
ad97f80
0fefe75
72321f8
d9cb3dc
da5a98e
ab3384d
8218dfc
5abb665
90dffe2
946939b
80f9ef8
97f67c1
a9622ea
2ba57c5
4e54787
2d11c17
41ac196
39c29ce
e035308
0d89b86
b23ae22
fb58fcd
76bceb7
8cbf021
5c50153
3452ab1
96edc4e
c49469d
018e356
c3ac129
e5e0b8a
d5e984d
4baf239
f192372
1510b07
8ffb253
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 |
---|---|---|
|
@@ -16,7 +16,7 @@ use crate::endpoints::forward; | |
use crate::extractors::SignedJson; | ||
use crate::service::ServiceState; | ||
use crate::services::global_config::{self, StatusResponse}; | ||
use crate::services::project::{LimitedProjectState, ProjectState}; | ||
use crate::services::project::{LimitedProjectInfo, ParsedProjectState, ProjectInfo, ProjectState}; | ||
use crate::services::project_cache::{GetCachedProjectState, GetProjectState}; | ||
|
||
/// V2 version of this endpoint. | ||
|
@@ -39,6 +39,15 @@ struct VersionQuery { | |
version: u16, | ||
} | ||
|
||
#[derive(Debug, Clone, Serialize)] | ||
#[serde(rename_all = "camelCase", remote = "ParsedProjectState")] | ||
struct LimitedParsedProjectState { | ||
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. Should we move this to where 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. It's only required for serialization in the endpoint, so it should in theory be private. But it makes sense to keep the |
||
disabled: bool, | ||
Dav1dde marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#[serde(with = "LimitedProjectInfo")] | ||
#[serde(flatten)] | ||
info: ProjectInfo, | ||
} | ||
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. I introduced a helper struct |
||
|
||
/// The type returned for each requested project config. | ||
/// | ||
/// Wrapper on top the project state which encapsulates information about how ProjectState | ||
|
@@ -49,13 +58,13 @@ struct VersionQuery { | |
#[derive(Debug, Clone, Serialize)] | ||
#[serde(untagged)] | ||
enum ProjectStateWrapper { | ||
Full(ProjectState), | ||
Limited(#[serde(with = "LimitedProjectState")] ProjectState), | ||
Full(ParsedProjectState), | ||
Limited(#[serde(with = "LimitedParsedProjectState")] ParsedProjectState), | ||
} | ||
|
||
impl ProjectStateWrapper { | ||
/// Create a wrapper which forces serialization into external or internal format | ||
pub fn new(state: ProjectState, full: bool) -> Self { | ||
pub fn new(state: ParsedProjectState, full: bool) -> Self { | ||
if full { | ||
Self::Full(state) | ||
} else { | ||
|
@@ -76,7 +85,7 @@ impl ProjectStateWrapper { | |
#[derive(Debug, Serialize)] | ||
#[serde(rename_all = "camelCase")] | ||
struct GetProjectStatesResponseWrapper { | ||
configs: HashMap<ProjectKey, Option<ProjectStateWrapper>>, | ||
configs: HashMap<ProjectKey, ProjectStateWrapper>, | ||
#[serde(skip_serializing_if = "Vec::is_empty")] | ||
pending: Vec<ProjectKey>, | ||
#[serde(skip_serializing_if = "Option::is_none")] | ||
|
@@ -123,7 +132,6 @@ async fn inner( | |
project_cache | ||
.send(GetProjectState::new(project_key).no_cache(no_cache)) | ||
.await | ||
.map(Some) | ||
}; | ||
|
||
(project_key, state_result) | ||
|
@@ -146,23 +154,36 @@ async fn inner( | |
let mut pending = Vec::with_capacity(keys_len); | ||
let mut configs = HashMap::with_capacity(keys_len); | ||
for (project_key, state_result) in future::join_all(futures).await { | ||
let Some(project_state) = state_result? else { | ||
pending.push(project_key); | ||
continue; | ||
let project_info = match state_result? { | ||
ProjectState::Enabled(info) => info, | ||
ProjectState::Disabled => { | ||
// Don't insert project config. Downstream Relay will consider it disabled. | ||
continue; | ||
} | ||
ProjectState::Pending => { | ||
pending.push(project_key); | ||
continue; | ||
} | ||
}; | ||
|
||
// If public key is known (even if rate-limited, which is Some(false)), it has | ||
// access to the project config | ||
let has_access = relay.internal | ||
|| project_state | ||
|| project_info | ||
.config | ||
.trusted_relays | ||
.contains(&relay.public_key); | ||
|
||
if has_access { | ||
let full = relay.internal && inner.full_config; | ||
let wrapper = ProjectStateWrapper::new((*project_state).clone(), full); | ||
configs.insert(project_key, Some(wrapper)); | ||
let wrapper = ProjectStateWrapper::new( | ||
ParsedProjectState { | ||
disabled: false, | ||
info: project_info.as_ref().clone(), | ||
}, | ||
full, | ||
); | ||
configs.insert(project_key, wrapper); | ||
} else { | ||
relay_log::debug!( | ||
relay = %relay.public_key, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,6 +30,7 @@ | |
//! | ||
//! ``` | ||
|
||
use relay_base_schema::project::ProjectKey; | ||
use std::borrow::Borrow; | ||
use std::collections::BTreeMap; | ||
use std::fmt; | ||
|
@@ -52,6 +53,7 @@ use smallvec::SmallVec; | |
|
||
use crate::constants::DEFAULT_EVENT_RETENTION; | ||
use crate::extractors::{PartialMeta, RequestMeta}; | ||
use crate::services::spooler::QueueKey; | ||
|
||
pub const CONTENT_TYPE: &str = "application/x-sentry-envelope"; | ||
|
||
|
@@ -1218,6 +1220,31 @@ impl Envelope { | |
self.headers.sent_at | ||
} | ||
|
||
/// Returns the project key defined in the `trace` header of the envelope. | ||
/// | ||
/// This function returns `None` if: | ||
/// - there is no [`DynamicSamplingContext`] in the envelope headers. | ||
/// - there are no transactions or events in the envelope, since in this case sampling by trace is redundant. | ||
pub fn sampling_key(&self) -> Option<ProjectKey> { | ||
// If the envelope item is not of type transaction or event, we will not return a sampling key | ||
// because it doesn't make sense to load the root project state if we don't perform trace | ||
// sampling. | ||
self.get_item_by(|item| { | ||
matches!( | ||
item.ty(), | ||
ItemType::Transaction | ItemType::Event | ItemType::Span | ||
) | ||
})?; | ||
self.dsc().map(|dsc| dsc.public_key) | ||
} | ||
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. I needed a |
||
|
||
pub fn queue_key(&self) -> QueueKey { | ||
jjbayer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
QueueKey { | ||
own_key: self.meta().public_key(), | ||
sampling_key: self.sampling_key().unwrap_or(self.meta().public_key()), | ||
} | ||
} | ||
|
||
/// Sets the event id on the envelope. | ||
pub fn set_event_id(&mut self, event_id: EventId) { | ||
self.headers.event_id = Some(event_id); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previously the split-out envelope was scoped by a call to
check_envelope
inhandle_processing
. That function takes a validProjectInfo
now, so we have to do it here.