1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-07-07 22:41:18 +03:00

Properly propagate trace contexts

This also fixes a long-running issue where the OTEL context was not properly set in the tracing spans.
This commit is contained in:
Quentin Gliech
2023-04-03 11:14:24 +02:00
parent 1f748f7d1e
commit f4fff72b22
6 changed files with 215 additions and 102 deletions

View File

@ -12,9 +12,12 @@ thiserror = "1.0.39"
futures-util = "0.3.27"
apalis-core = { version = "0.4.0-alpha.4", features = ["tokio-comp"] }
opentelemetry = "0.18.0"
rand_core = "0.6.4"
serde = "1.0.159"
serde_json = "1.0.95"
tracing = "0.1.37"
tracing-opentelemetry = "0.18.0"
url = "2.3.1"
ulid = "1.0.0"

View File

@ -14,10 +14,14 @@
//! Repository to schedule persistent jobs.
use std::{num::ParseIntError, ops::Deref};
pub use apalis_core::job::{Job, JobId};
use async_trait::async_trait;
use serde::Serialize;
use opentelemetry::trace::{SpanContext, SpanId, TraceContextExt, TraceFlags, TraceId, TraceState};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use tracing_opentelemetry::OpenTelemetrySpanExt;
use crate::repository_impl;
@ -27,6 +31,75 @@ pub struct JobSubmission {
payload: Value,
}
#[derive(Serialize, Deserialize)]
struct SerializableSpanContext {
trace_id: String,
span_id: String,
trace_flags: u8,
}
impl From<&SpanContext> for SerializableSpanContext {
fn from(value: &SpanContext) -> Self {
Self {
trace_id: value.trace_id().to_string(),
span_id: value.span_id().to_string(),
trace_flags: value.trace_flags().to_u8(),
}
}
}
impl TryFrom<&SerializableSpanContext> for SpanContext {
type Error = ParseIntError;
fn try_from(value: &SerializableSpanContext) -> Result<Self, Self::Error> {
Ok(SpanContext::new(
TraceId::from_hex(&value.trace_id)?,
SpanId::from_hex(&value.span_id)?,
TraceFlags::new(value.trace_flags),
// XXX: is that fine?
true,
TraceState::default(),
))
}
}
/// A wrapper for [`Job`] which adds the span context in the payload.
#[derive(Serialize, Deserialize)]
pub struct JobWithSpanContext<T> {
#[serde(skip_serializing_if = "Option::is_none")]
span_context: Option<SerializableSpanContext>,
#[serde(flatten)]
payload: T,
}
impl<J: Job> Job for JobWithSpanContext<J> {
const NAME: &'static str = J::NAME;
}
impl<T> Deref for JobWithSpanContext<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.payload
}
}
impl<T> JobWithSpanContext<T> {
/// Get the span context of the job.
///
/// # Returns
///
/// Returns [`None`] if the job has no span context, or if the span context
/// is invalid.
#[must_use]
pub fn span_context(&self) -> Option<SpanContext> {
self.span_context
.as_ref()
.and_then(|ctx| ctx.try_into().ok())
}
}
impl JobSubmission {
/// Create a new job submission out of a [`Job`].
///
@ -35,12 +108,30 @@ impl JobSubmission {
/// Panics if the job cannot be serialized.
#[must_use]
pub fn new<J: Job + Serialize>(job: J) -> Self {
let payload = serde_json::to_value(job).expect("Could not serialize job");
Self {
name: J::NAME,
payload: serde_json::to_value(job).expect("failed to serialize job"),
payload,
}
}
/// Create a new job submission out of a [`Job`] and a [`SpanContext`].
///
/// # Panics
///
/// Panics if the job cannot be serialized.
#[must_use]
pub fn new_with_span_context<J: Job + Serialize>(job: J, span_context: &SpanContext) -> Self {
// Serialize the span context alongside the job.
let span_context = SerializableSpanContext::from(span_context);
Self::new(JobWithSpanContext {
payload: job,
span_context: Some(span_context),
})
}
/// The name of the job.
#[must_use]
pub fn name(&self) -> &'static str {
@ -104,8 +195,21 @@ where
{
type Error = T::Error;
#[tracing::instrument(
name = "db.job.schedule_job",
skip_all,
fields(
job.name = J::NAME,
),
)]
async fn schedule_job<J: Job + Serialize>(&mut self, job: J) -> Result<JobId, Self::Error> {
self.schedule_submission(JobSubmission::new(job)).await
let span = tracing::Span::current();
let ctx = span.context();
let span = ctx.span();
let span_context = span.span_context();
self.schedule_submission(JobSubmission::new_with_span_context(job, span_context))
.await
}
}