Skip to content

Commit 2aa5416

Browse files
committed
fix: clone vs child
1 parent c72f721 commit 2aa5416

File tree

3 files changed

+91
-50
lines changed

3 files changed

+91
-50
lines changed

src/router.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -337,10 +337,10 @@ where
337337

338338
for req in inbound.iter() {
339339
let req = req.map(|req| {
340-
// Cloning here resets the `TracingInfo`, which means each
340+
// This resets the `TracingInfo`, which means each
341341
// ctx has a separate span with similar metadata.
342-
let ctx = ctx.clone();
343-
let request_span = ctx.init_request_span(self, Some(&batch_span)).clone();
342+
let ctx = ctx.child_ctx(self, Some(&batch_span));
343+
let request_span = ctx.span().clone();
344344

345345
// Several span fields are populated in `HandlerArgs::new`.
346346
let args = HandlerArgs::new(ctx, req);

src/routes/ctx.rs

Lines changed: 72 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ impl From<SendError<WriteItem>> for NotifyError {
3030

3131
/// Tracing information for OpenTelemetry. This struct is used to store
3232
/// information about the current request that can be used for tracing.
33-
#[derive(Debug)]
33+
#[derive(Debug, Clone)]
3434
#[non_exhaustive]
3535
pub struct TracingInfo {
3636
/// The OpenTelemetry service name.
@@ -43,16 +43,6 @@ pub struct TracingInfo {
4343
span: OnceLock<tracing::Span>,
4444
}
4545

46-
impl Clone for TracingInfo {
47-
fn clone(&self) -> Self {
48-
Self {
49-
service: self.service,
50-
context: self.context.clone(),
51-
span: OnceLock::new(),
52-
}
53-
}
54-
}
55-
5646
impl TracingInfo {
5747
/// Create a new tracing info with the given service name and no context.
5848
#[allow(dead_code)] // used in some features
@@ -76,6 +66,43 @@ impl TracingInfo {
7666
}
7767
}
7868

69+
fn make_span<S>(
70+
&self,
71+
router: &Router<S>,
72+
with_notifications: bool,
73+
parent: Option<&tracing::Span>,
74+
) -> tracing::Span
75+
where
76+
S: Clone + Send + Sync + 'static,
77+
{
78+
let span = info_span!(
79+
parent: parent.and_then(|p| p.id()),
80+
"AjjRequest",
81+
"otel.kind" = "server",
82+
"rpc.system" = "jsonrpc",
83+
"rpc.jsonrpc.version" = "2.0",
84+
"rpc.service" = router.service_name(),
85+
notifications_enabled = with_notifications,
86+
"trace_id" = ::tracing::field::Empty,
87+
"otel.name" = ::tracing::field::Empty,
88+
"otel.status_code" = ::tracing::field::Empty,
89+
"rpc.jsonrpc.request_id" = ::tracing::field::Empty,
90+
"rpc.jsonrpc.error_code" = ::tracing::field::Empty,
91+
"rpc.jsonrpc.error_message" = ::tracing::field::Empty,
92+
"rpc.method" = ::tracing::field::Empty,
93+
params = ::tracing::field::Empty,
94+
);
95+
if let Some(context) = &self.context {
96+
let _ = span.set_parent(context.clone());
97+
98+
span.record(
99+
"trace_id",
100+
context.span().span_context().trace_id().to_string(),
101+
);
102+
}
103+
span
104+
}
105+
79106
/// Create a request span for a handler invocation.
80107
fn init_request_span<S>(
81108
&self,
@@ -89,35 +116,23 @@ impl TracingInfo {
89116
// This span is populated with as much detail as possible, and then
90117
// given to the Request. It will be populated with request-specific
91118
// details (e.g. method) during request setup.
92-
self.span.get_or_init(|| {
93-
let span = info_span!(
94-
parent: parent.and_then(|p| p.id()),
95-
"AjjRequest",
96-
"otel.kind" = "server",
97-
"rpc.system" = "jsonrpc",
98-
"rpc.jsonrpc.version" = "2.0",
99-
"rpc.service" = router.service_name(),
100-
notifications_enabled = with_notifications,
101-
"trace_id" = ::tracing::field::Empty,
102-
"otel.name" = ::tracing::field::Empty,
103-
"otel.status_code" = ::tracing::field::Empty,
104-
"rpc.jsonrpc.request_id" = ::tracing::field::Empty,
105-
"rpc.jsonrpc.error_code" = ::tracing::field::Empty,
106-
"rpc.jsonrpc.error_message" = ::tracing::field::Empty,
107-
"rpc.method" = ::tracing::field::Empty,
108-
params = ::tracing::field::Empty,
109-
);
110-
if let Some(context) = &self.context {
111-
let _ = span.set_parent(context.clone());
112-
113-
span.record(
114-
"trace_id",
115-
context.span().span_context().trace_id().to_string(),
116-
);
117-
}
119+
self.span
120+
.get_or_init(|| self.make_span(router, with_notifications, parent))
121+
}
118122

119-
span
120-
})
123+
/// Create a child tracing info for a new handler context.
124+
pub fn child<S: Clone + Send + Sync + 'static>(
125+
&self,
126+
router: &Router<S>,
127+
with_notifications: bool,
128+
parent: Option<&tracing::Span>,
129+
) -> Self {
130+
let span = self.make_span(router, with_notifications, parent);
131+
Self {
132+
service: self.service,
133+
context: self.context.clone(),
134+
span: OnceLock::from(span),
135+
}
121136
}
122137

123138
/// Get a reference to the tracing span for this request.
@@ -186,6 +201,24 @@ impl HandlerCtx {
186201
}
187202
}
188203

204+
/// Create a child handler context for a new handler invocation.
205+
///
206+
/// This is used when handling batch requests, to give each handler
207+
/// its own context.
208+
pub fn child_ctx<S: Clone + Send + Sync + 'static>(
209+
&self,
210+
router: &Router<S>,
211+
parent: Option<&tracing::Span>,
212+
) -> Self {
213+
Self {
214+
notifications: self.notifications.clone(),
215+
tasks: self.tasks.clone(),
216+
tracing: self
217+
.tracing
218+
.child(router, self.notifications_enabled(), parent),
219+
}
220+
}
221+
189222
/// Get a reference to the tracing information for this handler context.
190223
pub const fn tracing_info(&self) -> &TracingInfo {
191224
&self.tracing

tests/common/mod.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,13 +104,17 @@ async fn test_ping<T: TestClient>(client: &mut T) {
104104

105105
/// basic tests of the test router
106106
pub async fn basic_tests<T: TestClient>(client: &mut T) {
107-
test_ping(client).await;
107+
timeout(Duration::from_secs(10), async move {
108+
test_ping(client).await;
108109

109-
test_double(client).await;
110+
test_double(client).await;
110111

111-
test_call_me_later(client).await;
112+
test_call_me_later(client).await;
112113

113-
test_notification(client).await;
114+
test_notification(client).await;
115+
})
116+
.await
117+
.unwrap();
114118
}
115119

116120
/// Test when a full batch request fails to parse
@@ -205,11 +209,15 @@ async fn batch_contains_only_notifications<T: TestClient>(client: &mut T) {
205209

206210
/// Test of batch requests
207211
pub async fn batch_tests<T: TestClient>(client: &mut T) {
208-
batch_parse_fail(client).await;
212+
timeout(Duration::from_secs(10), async move {
213+
batch_parse_fail(client).await;
209214

210-
batch_single_req_parse_fail(client).await;
215+
batch_single_req_parse_fail(client).await;
211216

212-
batch_contains_notification(client).await;
217+
batch_contains_notification(client).await;
213218

214-
batch_contains_only_notifications(client).await;
219+
batch_contains_only_notifications(client).await;
220+
})
221+
.await
222+
.unwrap();
215223
}

0 commit comments

Comments
 (0)