Skip to content

Commit 592bd5d

Browse files
authored
ref(project-cache): Track project cache update latency (#4709)
1 parent d15451b commit 592bd5d

File tree

3 files changed

+49
-7
lines changed

3 files changed

+49
-7
lines changed

relay-server/src/services/projects/cache/service.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ impl ProjectCacheService {
150150

151151
fn handle_completed_fetch(&mut self, fetch: CompletedFetch) {
152152
let project_key = fetch.project_key();
153+
let latency = fetch.latency();
153154

154155
if let Some(fetch) = self.store.complete_fetch(fetch, &self.config) {
155156
relay_log::trace!(
@@ -164,6 +165,10 @@ impl ProjectCacheService {
164165
.project_events_tx
165166
.send(ProjectChange::Ready(project_key));
166167

168+
if let Some(latency) = latency {
169+
metric!(timer(RelayTimers::ProjectCacheUpdateLatency) = latency);
170+
}
171+
167172
metric!(
168173
gauge(RelayGauges::ProjectCacheNotificationChannel) =
169174
self.project_events_tx.len() as u64

relay-server/src/services/projects/cache/state.rs

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use futures::StreamExt;
22
use std::fmt;
33
use std::sync::Arc;
4+
use std::time::Duration;
45
use tokio::time::Instant;
56

67
use arc_swap::ArcSwap;
@@ -66,7 +67,7 @@ impl ProjectStore {
6667
/// [`ProjectState`] is still pending or already deemed expired.
6768
#[must_use = "an incomplete fetch must be retried"]
6869
pub fn complete_fetch(&mut self, fetch: CompletedFetch, config: &Config) -> Option<Fetch> {
69-
let project_key = fetch.project_key;
70+
let project_key = fetch.project_key();
7071

7172
// Eviction is not possible for projects which are currently being fetched.
7273
// Hence if there was a started fetch, the project state must always exist at this stage.
@@ -333,10 +334,7 @@ impl Fetch {
333334

334335
/// Completes the fetch with a result and returns a [`CompletedFetch`].
335336
pub fn complete(self, state: SourceProjectState) -> CompletedFetch {
336-
CompletedFetch {
337-
project_key: self.project_key,
338-
state,
339-
}
337+
CompletedFetch { fetch: self, state }
340338
}
341339

342340
fn with_revision(mut self, revision: Revision) -> Self {
@@ -349,14 +347,45 @@ impl Fetch {
349347
#[must_use = "a completed fetch must be acted upon"]
350348
#[derive(Debug)]
351349
pub struct CompletedFetch {
352-
project_key: ProjectKey,
350+
fetch: Fetch,
353351
state: SourceProjectState,
354352
}
355353

356354
impl CompletedFetch {
357355
/// Returns the [`ProjectKey`] of the project which was fetched.
358356
pub fn project_key(&self) -> ProjectKey {
359-
self.project_key
357+
self.fetch.project_key
358+
}
359+
360+
/// Returns the update latency of the fetched project config from the upstream.
361+
///
362+
/// Is `None`, when no project config could be fetched, or if this was the first
363+
/// fetch of a project config.
364+
///
365+
/// Note: this latency is computed on access, it does not use the time when the [`Fetch`]
366+
/// was marked as (completed)[`Fetch::complete`].
367+
pub fn latency(&self) -> Option<Duration> {
368+
// We're not interested in initial fetches. The latency on the first fetch
369+
// has no meaning about how long it takes for an updated project config to be
370+
// propagated to a Relay.
371+
let is_first_fetch = self.fetch.revision().as_str().is_none();
372+
if is_first_fetch {
373+
return None;
374+
}
375+
376+
let project_info = match &self.state {
377+
SourceProjectState::New(ProjectState::Enabled(project_info)) => project_info,
378+
// Not modified or deleted/disabled -> no latency to track.
379+
_ => return None,
380+
};
381+
382+
// A matching revision is not an update.
383+
if project_info.rev == self.fetch.revision() {
384+
return None;
385+
}
386+
387+
let elapsed = chrono::Utc::now() - project_info.last_change?;
388+
elapsed.to_std().ok()
360389
}
361390

362391
/// Returns `true` if the fetch completed with a pending status.

relay-server/src/statsd.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,13 @@ pub enum RelayTimers {
410410
/// Total time in milliseconds an envelope spends in Relay from the time it is received until it
411411
/// finishes processing and has been submitted to the upstream.
412412
EnvelopeTotalTime,
413+
/// Latency of project config updates until they reach Relay.
414+
///
415+
/// The metric is calculated by using the creation timestamp of the project config
416+
/// and when Relay updates its local cache with the new project config.
417+
///
418+
/// No metric is emitted when Relay fetches a project config for the first time.
419+
ProjectCacheUpdateLatency,
413420
/// Total time in milliseconds spent fetching queued project configuration updates requests to
414421
/// resolve.
415422
///
@@ -597,6 +604,7 @@ impl TimerMetric for RelayTimers {
597604
RelayTimers::ProjectStateRequestDuration => "project_state.request.duration",
598605
#[cfg(feature = "processing")]
599606
RelayTimers::ProjectStateDecompression => "project_state.decompression",
607+
RelayTimers::ProjectCacheUpdateLatency => "project_cache.latency",
600608
RelayTimers::RequestsDuration => "requests.duration",
601609
RelayTimers::MinidumpScrubbing => "scrubbing.minidumps.duration",
602610
RelayTimers::ViewHierarchyScrubbing => "scrubbing.view_hierarchy_scrubbing.duration",

0 commit comments

Comments
 (0)