Skip to content

Commit

Permalink
feat(err): store result of frame resolution (#26001)
Browse files Browse the repository at this point in the history
  • Loading branch information
oliverb123 authored Nov 6, 2024
1 parent b7d772c commit bdeae6f
Show file tree
Hide file tree
Showing 18 changed files with 507 additions and 16 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,4 @@ ahash = "0.8.11"
aws-config = { version = "1.1.7", features = ["behavior-version-latest"] }
aws-sdk-s3 = "1.58.0"
mockall = "0.13.0"
moka = { version = "0.12.8", features = ["sync"] }
1 change: 1 addition & 0 deletions rust/cymbal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ aws-config = { workspace = true }
aws-sdk-s3 = { workspace = true }
uuid = { workspace = true }
chrono = { workspace = true }
moka = { workspace = true }

[dev-dependencies]
httpmock = { workspace = true }
Expand Down
4 changes: 4 additions & 0 deletions rust/cymbal/src/app_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use tracing::info;
use crate::{
config::Config,
error::Error,
frames::resolver::Resolver,
symbol_store::{
caching::{Caching, SymbolSetCache},
saving::Saving,
Expand All @@ -27,6 +28,7 @@ pub struct AppContext {
pub kafka_producer: FutureProducer<KafkaContext>,
pub pool: PgPool,
pub catalog: Catalog,
pub resolver: Resolver,
}

impl AppContext {
Expand Down Expand Up @@ -73,6 +75,7 @@ impl AppContext {
);

let catalog = Catalog::new(caching_smp);
let resolver = Resolver::new(config);

Ok(Self {
health_registry,
Expand All @@ -81,6 +84,7 @@ impl AppContext {
kafka_producer,
pool,
catalog,
resolver,
})
}
}
6 changes: 6 additions & 0 deletions rust/cymbal/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ pub struct Config {

#[envconfig(default = "sets")]
pub ss_prefix: String,

#[envconfig(default = "100000")]
pub frame_cache_size: u64,

#[envconfig(default = "600")]
pub frame_cache_ttl_seconds: u64,
}

impl Config {
Expand Down
2 changes: 1 addition & 1 deletion rust/cymbal/src/fingerprinting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub fn generate_fingerprint(exception: &[Exception]) -> String {

#[cfg(test)]
mod test {
use crate::types::{frames::Frame, Stacktrace};
use crate::{frames::Frame, types::Stacktrace};

use super::*;

Expand Down
17 changes: 15 additions & 2 deletions rust/cymbal/src/types/frames.rs → rust/cymbal/src/frames/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ use crate::{
error::Error, langs::js::RawJSFrame, metric_consts::PER_FRAME_TIME, symbol_store::Catalog,
};

pub mod records;
pub mod resolver;

// We consume a huge variety of differently shaped stack frames, which we have special-case
// transformation for, to produce a single, unified representation of a frame.
#[derive(Debug, Deserialize, Serialize, Clone)]
Expand All @@ -29,14 +32,24 @@ impl RawFrame {
res
}

pub fn symbol_set_group_key(&self) -> String {
pub fn needs_symbols(&self) -> bool {
// For now, we only support JS, so this is always true
true
}

pub fn symbol_set_ref(&self) -> String {
let RawFrame::JavaScript(raw) = self;
raw.source_url().map(String::from).unwrap_or_default()
}

pub fn frame_id(&self) -> String {
let RawFrame::JavaScript(raw) = self;
raw.frame_id()
}
}

// We emit a single, unified representation of a frame, which is what we pass on to users.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct Frame {
pub mangled_name: String, // Mangled name of the function
pub line: Option<u32>, // Line the function is define on, if known
Expand Down
102 changes: 102 additions & 0 deletions rust/cymbal/src/frames/records.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use sqlx::Executor;
use uuid::Uuid;

use crate::error::Error;

use super::Frame;

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ErrorTrackingStackFrame {
pub raw_id: String,
pub team_id: i32,
pub created_at: DateTime<Utc>,
pub symbol_set_id: Option<Uuid>,
pub contents: Frame,
pub resolved: bool,
}

impl ErrorTrackingStackFrame {
pub fn new(
raw_id: String,
team_id: i32,
symbol_set_id: Option<Uuid>,
contents: Frame,
resolved: bool,
) -> Self {
Self {
raw_id,
team_id,
symbol_set_id,
contents,
resolved,
created_at: Utc::now(),
}
}

pub async fn save<'c, E>(&self, e: E) -> Result<(), Error>
where
E: Executor<'c, Database = sqlx::Postgres>,
{
sqlx::query!(
r#"
INSERT INTO posthog_errortrackingstackframe (raw_id, team_id, created_at, symbol_set_id, contents, resolved, id)
VALUES ($1, $2, $3, $4, $5, $6, $7)
ON CONFLICT (raw_id, team_id) DO UPDATE SET
created_at = $3,
symbol_set_id = $4,
contents = $5,
resolved = $6
"#,
self.raw_id,
self.team_id,
self.created_at,
self.symbol_set_id,
serde_json::to_value(&self.contents)?,
self.resolved,
Uuid::now_v7()
).execute(e).await?;
Ok(())
}

pub async fn load<'c, E>(e: E, team_id: i32, raw_id: &str) -> Result<Option<Self>, Error>
where
E: Executor<'c, Database = sqlx::Postgres>,
{
struct Returned {
raw_id: String,
team_id: i32,
created_at: DateTime<Utc>,
symbol_set_id: Option<Uuid>,
contents: Value,
resolved: bool,
}
let res = sqlx::query_as!(
Returned,
r#"
SELECT raw_id, team_id, created_at, symbol_set_id, contents, resolved
FROM posthog_errortrackingstackframe
WHERE raw_id = $1 AND team_id = $2
"#,
raw_id,
team_id
)
.fetch_optional(e)
.await?;

let Some(found) = res else {
return Ok(None);
};

Ok(Some(Self {
raw_id: found.raw_id,
team_id: found.team_id,
created_at: found.created_at,
symbol_set_id: found.symbol_set_id,
contents: serde_json::from_value(found.contents)?,
resolved: found.resolved,
}))
}
}
Loading

0 comments on commit bdeae6f

Please sign in to comment.