Skip to content

Commit 93d345f

Browse files
committed
metrics: Report database dump size and duration
Successful tar.gz and zip uploads now record archive size and upload duration through WorkerMetrics with the existing format attribute. The legacy direct Datadog submission remains active when configured during the migration.
1 parent e395d90 commit 93d345f

4 files changed

Lines changed: 74 additions & 2 deletions

File tree

src/metrics/consts.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@ pub const DB_CLIENT_CONNECTION_POOL_NAME: &str = "db.client.connection.pool.name
1919
/// The database connection state attribute key.
2020
pub const DB_CLIENT_CONNECTION_STATE: &str = "db.client.connection.state";
2121

22+
/// The database dump size instrument name.
23+
pub const DB_DUMP_SIZE_BYTES: &str = "crates_io.db_dump_size_bytes";
24+
25+
/// The database dump upload duration instrument name.
26+
pub const DB_DUMP_UPLOAD_DURATION_NS: &str = "crates_io.db_dump_upload_duration_ns";
27+
28+
/// The archive format attribute key.
29+
pub const FORMAT: &str = "format";
30+
2231
/// The HTTP request method attribute key.
2332
pub const HTTP_REQUEST_METHOD: &str = "http.request.method";
2433

src/metrics/worker.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
use super::SharedMetrics;
2-
use super::consts::{BACKGROUND_JOBS, CRATES_TOTAL, JOB, PRIORITY, VERSIONS_TOTAL};
2+
use super::consts::{
3+
BACKGROUND_JOBS, CRATES_TOTAL, DB_DUMP_SIZE_BYTES, DB_DUMP_UPLOAD_DURATION_NS, FORMAT, JOB,
4+
PRIORITY, VERSIONS_TOTAL,
5+
};
36
use super::otel::kv;
47
use derive_more::Deref;
58
use opentelemetry::metrics::{Gauge, Meter};
9+
use std::time::Duration;
610

711
/// OpenTelemetry instruments recorded by the background worker.
812
#[derive(Clone, Debug, Deref)]
@@ -13,6 +17,8 @@ pub struct WorkerMetrics {
1317
background_jobs: Gauge<i64>,
1418
crates_total: Gauge<i64>,
1519
versions_total: Gauge<i64>,
20+
db_dump_size: Gauge<u64>,
21+
db_dump_upload_duration: Gauge<u64>,
1622
}
1723

1824
impl WorkerMetrics {
@@ -22,12 +28,16 @@ impl WorkerMetrics {
2228
let background_jobs = meter.i64_gauge(BACKGROUND_JOBS).build();
2329
let crates_total = meter.i64_gauge(CRATES_TOTAL).build();
2430
let versions_total = meter.i64_gauge(VERSIONS_TOTAL).build();
31+
let db_dump_size = meter.u64_gauge(DB_DUMP_SIZE_BYTES).build();
32+
let db_dump_upload_duration = meter.u64_gauge(DB_DUMP_UPLOAD_DURATION_NS).build();
2533

2634
Self {
2735
shared,
2836
background_jobs,
2937
crates_total,
3038
versions_total,
39+
db_dump_size,
40+
db_dump_upload_duration,
3141
}
3242
}
3343

@@ -43,4 +53,13 @@ impl WorkerMetrics {
4353
let attributes = [kv(PRIORITY, priority.clone()), kv(JOB, job.clone())];
4454
self.background_jobs.record(count, &attributes);
4555
}
56+
57+
/// Records the size and upload duration of a database dump archive.
58+
pub fn record_db_dump(&self, format: &'static str, size: u64, upload_duration: Duration) {
59+
let attributes = [kv(FORMAT, format)];
60+
let upload_ns = u64::try_from(upload_duration.as_nanos()).unwrap_or(u64::MAX);
61+
62+
self.db_dump_size.record(size, &attributes);
63+
self.db_dump_upload_duration.record(upload_ns, &attributes);
64+
}
4665
}

src/tests/dump_db.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,31 @@
11
use crate::builders::CrateBuilder;
22
use crate::util::TestApp;
33
use bytes::Buf;
4+
use claims::assert_gt;
5+
use crates_io::metrics::consts::{
6+
DB_DUMP_SIZE_BYTES, DB_DUMP_UPLOAD_DURATION_NS, FORMAT, METER_NAME,
7+
};
48
use crates_io::worker::jobs::DumpDb;
59
use crates_io_worker::BackgroundJob;
610
use flate2::read::GzDecoder;
711
use insta::{assert_debug_snapshot, assert_snapshot};
812
use object_store::ObjectStoreExt;
13+
use opentelemetry::KeyValue;
14+
use opentelemetry::metrics::MeterProvider;
15+
use opentelemetry_sdk::metrics::data::{AggregatedMetrics, MetricData};
16+
use opentelemetry_sdk::metrics::{InMemoryMetricExporter, PeriodicReader, SdkMeterProvider};
917
use regex::regex;
1018
use std::io::{Cursor, Read};
1119
use tar::Archive;
1220

1321
#[tokio::test(flavor = "multi_thread")]
1422
async fn test_dump_db_job() -> anyhow::Result<()> {
15-
let (app, _, _, token) = TestApp::full().with_token().await;
23+
let exporter = InMemoryMetricExporter::default();
24+
let reader = PeriodicReader::builder(exporter.clone()).build();
25+
let provider = SdkMeterProvider::builder().with_reader(reader).build();
26+
let meter = provider.meter(METER_NAME);
27+
28+
let (app, _, _, token) = TestApp::full().with_meter(meter).with_token().await;
1629
let mut conn = app.db_conn().await;
1730

1831
CrateBuilder::new("test-crate", token.as_model().user_id)
@@ -22,6 +35,13 @@ async fn test_dump_db_job() -> anyhow::Result<()> {
2235
DumpDb::for_schema(app.db_schema()).enqueue(&conn).await?;
2336

2437
app.run_pending_background_jobs().await;
38+
provider.force_flush()?;
39+
40+
for metric in [DB_DUMP_SIZE_BYTES, DB_DUMP_UPLOAD_DURATION_NS] {
41+
for format in ["tar.gz", "zip"] {
42+
assert_gt!(metric_value(&exporter, metric, format), 0);
43+
}
44+
}
2545

2646
assert_snapshot!(app.stored_files().await.join("\n"), @r"
2747
db-dump.tar.gz
@@ -108,6 +128,26 @@ async fn test_dump_db_job() -> anyhow::Result<()> {
108128
Ok(())
109129
}
110130

131+
fn metric_value(exporter: &InMemoryMetricExporter, name: &str, format: &str) -> u64 {
132+
let batches = exporter.get_finished_metrics().unwrap();
133+
let format = KeyValue::new(FORMAT, format.to_owned());
134+
let metric = batches
135+
.iter()
136+
.flat_map(|batch| batch.scope_metrics())
137+
.flat_map(|scope| scope.metrics())
138+
.find(|metric| metric.name() == name)
139+
.unwrap();
140+
let AggregatedMetrics::U64(MetricData::Gauge(gauge)) = metric.data() else {
141+
panic!("database dump metrics should be u64 gauges");
142+
};
143+
144+
gauge
145+
.data_points()
146+
.find(|point| point.attributes().any(|attribute| attribute == &format))
147+
.unwrap()
148+
.value()
149+
}
150+
111151
fn tar_paths<R: Read>(archive: &mut Archive<R>) -> Vec<String> {
112152
let path_date_re = regex!(r"^\d{4}-\d{2}-\d{2}-\d{6}");
113153

src/worker/jobs/dump_db.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ impl BackgroundJob for DumpDb {
6868
let upload_start = Instant::now();
6969
ctx.storage.upload_stream(&tar_key, tar_file).await?;
7070
let upload_duration = upload_start.elapsed();
71+
ctx.metrics.record_db_dump("tar.gz", size, upload_duration);
72+
7173
if let Some(datadog) = &ctx.datadog {
7274
let domain = &ctx.config.domain_name;
7375
let result =
@@ -93,6 +95,8 @@ impl BackgroundJob for DumpDb {
9395
let upload_start = Instant::now();
9496
ctx.storage.upload_stream(&zip_key, zip_file).await?;
9597
let upload_duration = upload_start.elapsed();
98+
ctx.metrics.record_db_dump("zip", size, upload_duration);
99+
96100
if let Some(datadog) = &ctx.datadog {
97101
let domain = &ctx.config.domain_name;
98102
let result = report_dump_metrics(datadog, domain, "zip", size, upload_duration).await;

0 commit comments

Comments
 (0)