mod common;
use std::fs;
use crate::common::*;
use glean_core::metrics::*;
use glean_core::CommonMetricData;
use glean_core::Lifetime;
use rkv::{Rkv, StoreOptions};
use uuid::uuid;
static RKV_DATABASE: &[u8] = include_bytes!("77ca0472-5124-4f6b-971d-4a2a928fb158.safe.bin");
fn clientid_metric() -> UuidMetric {
UuidMetric::new(CommonMetricData {
name: "client_id".into(),
category: "".into(),
send_in_pings: vec!["glean_client_info".into()],
lifetime: Lifetime::User,
..Default::default()
})
}
struct MigrationMetrics {
metrics_in_sqlite: CounterMetric,
migrated_metrics: CounterMetric,
failed_metrics: CounterMetric,
migration_duration: TimingDistributionMetric,
migration_error: CounterMetric,
}
impl MigrationMetrics {
fn new() -> Self {
Self {
migrated_metrics: CounterMetric::new(CommonMetricData {
name: "migrated_metrics".into(),
category: "glean.migration".into(),
send_in_pings: vec!["metrics".into(), "health".into()],
lifetime: Lifetime::Ping,
..Default::default()
}),
metrics_in_sqlite: CounterMetric::new(CommonMetricData {
name: "metrics_in_sqlite".into(),
category: "glean.migration".into(),
send_in_pings: vec!["metrics".into(), "health".into()],
lifetime: Lifetime::Ping,
..Default::default()
}),
failed_metrics: CounterMetric::new(CommonMetricData {
name: "failed_metrics".into(),
category: "glean.migration".into(),
send_in_pings: vec!["metrics".into(), "health".into()],
lifetime: Lifetime::Ping,
..Default::default()
}),
migration_duration: TimingDistributionMetric::new(
CommonMetricData {
name: "migration_duration".into(),
category: "glean.migration".into(),
send_in_pings: vec!["metrics".into(), "health".into()],
lifetime: Lifetime::Ping,
..Default::default()
},
TimeUnit::Millisecond,
),
migration_error: CounterMetric::new(CommonMetricData {
name: "error".into(),
category: "glean.migration".into(),
send_in_pings: vec!["metrics".into(), "health".into()],
lifetime: Lifetime::Ping,
..Default::default()
}),
}
}
}
#[test]
fn migration_succeeds() {
let temp = tempfile::tempdir().unwrap();
let db_path = temp.path().join("db");
fs::create_dir_all(&db_path).unwrap();
let safe_bin = db_path.join("data.safe.bin");
fs::write(safe_bin, RKV_DATABASE).unwrap();
let exp_client_id = uuid!("77ca0472-5124-4f6b-971d-4a2a928fb158");
let (glean, _temp) = new_glean(Some(temp));
let client_id = clientid_metric().get_value(&glean, None).unwrap();
assert_eq!(exp_client_id, client_id);
let metrics = MigrationMetrics::new();
assert_eq!(Some(13), metrics.migrated_metrics.get_value(&glean, None));
assert_eq!(Some(13), metrics.metrics_in_sqlite.get_value(&glean, None));
assert_eq!(None, metrics.failed_metrics.get_value(&glean, None));
assert!(metrics.migration_duration.get_value(&glean, None).is_some());
assert_eq!(None, metrics.migration_error.get_value(&glean, None));
}
#[test]
fn migration_skipped_if_database_exists() {
let (first_client_id, temp) = {
let (glean, temp) = new_glean(None);
let client_id = clientid_metric().get_value(&glean, None).unwrap();
drop(glean);
(client_id, temp)
};
let safe_bin = temp.path().join("db").join("data.safe.bin");
fs::write(
&safe_bin,
include_bytes!("77ca0472-5124-4f6b-971d-4a2a928fb158.safe.bin"),
)
.unwrap();
let rkv_client_id = uuid!("77ca0472-5124-4f6b-971d-4a2a928fb158");
let (glean, _temp) = new_glean(Some(temp));
let client_id = clientid_metric().get_value(&glean, None).unwrap();
assert_eq!(
first_client_id, client_id,
"Client ID should be the one first generated"
);
assert_ne!(
rkv_client_id, client_id,
"Client ID should not be one from the Rkv database"
);
assert!(safe_bin.exists(), "Rkv file should not have been deleted");
let metrics = MigrationMetrics::new();
assert_eq!(None, metrics.migrated_metrics.get_value(&glean, None));
assert_eq!(None, metrics.metrics_in_sqlite.get_value(&glean, None));
assert_eq!(None, metrics.failed_metrics.get_value(&glean, None));
assert_eq!(None, metrics.migration_duration.get_value(&glean, None));
assert_eq!(None, metrics.migration_error.get_value(&glean, None));
}
#[test]
fn migration_succeeds_with_failures() {
let temp = tempfile::tempdir().unwrap();
let db_path = temp.path().join("db");
fs::create_dir_all(&db_path).unwrap();
let safe_bin = db_path.join("data.safe.bin");
fs::write(safe_bin, RKV_DATABASE).unwrap();
let exp_client_id = uuid!("77ca0472-5124-4f6b-971d-4a2a928fb158");
{
let rkv = Rkv::new::<rkv::backend::SafeMode>(&db_path).unwrap();
let ping_store = rkv
.open_single(Lifetime::Ping.as_str(), StoreOptions::create())
.unwrap();
let mut writer = rkv.write().unwrap();
let key = "metrics#a.broken.metric";
let value = rkv::Value::Blob(b"not a value");
ping_store.put(&mut writer, key, &value).unwrap();
let key = "baseline#second.broken.metric";
let value = rkv::Value::I64(31);
ping_store.put(&mut writer, key, &value).unwrap();
writer.commit().unwrap();
}
let (glean, _temp) = new_glean(Some(temp));
let client_id = clientid_metric().get_value(&glean, None).unwrap();
assert_eq!(exp_client_id, client_id);
let metrics = MigrationMetrics::new();
assert_eq!(Some(13), metrics.migrated_metrics.get_value(&glean, None));
assert_eq!(Some(13), metrics.metrics_in_sqlite.get_value(&glean, None));
assert_eq!(Some(2), metrics.failed_metrics.get_value(&glean, None));
assert!(metrics.migration_duration.get_value(&glean, None).is_some());
assert_eq!(None, metrics.migration_error.get_value(&glean, None));
}
#[test]
fn migration_fails() {
let temp = tempfile::tempdir().unwrap();
let db_path = temp.path().join("db");
fs::create_dir_all(&db_path).unwrap();
let safe_bin = db_path.join("data.safe.bin");
fs::write(safe_bin, b"\0").unwrap();
let (glean, _temp) = new_glean(Some(temp));
let metrics = MigrationMetrics::new();
assert_eq!(Some(1), metrics.migration_error.get_value(&glean, None));
assert_eq!(None, metrics.migrated_metrics.get_value(&glean, None));
assert_eq!(None, metrics.metrics_in_sqlite.get_value(&glean, None));
assert_eq!(None, metrics.failed_metrics.get_value(&glean, None));
assert_eq!(None, metrics.migration_duration.get_value(&glean, None));
}