use std::{cell::RefCell, collections::HashMap};
thread_local! {
static WASM_STORE_METRICS: RefCell<HashMap<WasmStoreMetricKey, u64>> =
RefCell::new(HashMap::new());
}
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[remain::sorted]
pub enum WasmStoreMetricOperation {
BootstrapChunkSync,
ChunkPublish,
ChunkUpload,
ManifestPromote,
Prepare,
ReleasePublish,
SourceResolve,
}
impl WasmStoreMetricOperation {
#[must_use]
pub const fn metric_label(self) -> &'static str {
match self {
Self::BootstrapChunkSync => "bootstrap_chunk_sync",
Self::ChunkPublish => "chunk_publish",
Self::ChunkUpload => "chunk_upload",
Self::ManifestPromote => "manifest_promote",
Self::Prepare => "prepare",
Self::ReleasePublish => "release_publish",
Self::SourceResolve => "source_resolve",
}
}
}
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[remain::sorted]
pub enum WasmStoreMetricSource {
Bootstrap,
Embedded,
ManagedFleet,
Resolver,
Store,
TargetStore,
}
impl WasmStoreMetricSource {
#[must_use]
pub const fn metric_label(self) -> &'static str {
match self {
Self::Bootstrap => "bootstrap",
Self::Embedded => "embedded",
Self::ManagedFleet => "managed_fleet",
Self::Resolver => "resolver",
Self::Store => "store",
Self::TargetStore => "target_store",
}
}
}
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[remain::sorted]
pub enum WasmStoreMetricOutcome {
Completed,
Failed,
Skipped,
Started,
}
impl WasmStoreMetricOutcome {
#[must_use]
pub const fn metric_label(self) -> &'static str {
match self {
Self::Completed => "completed",
Self::Failed => "failed",
Self::Skipped => "skipped",
Self::Started => "started",
}
}
}
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[remain::sorted]
pub enum WasmStoreMetricReason {
CacheHit,
CacheMiss,
Capacity,
HashMismatch,
InvalidState,
ManagementCall,
MissingChunk,
MissingManifest,
Ok,
StoreCall,
UnsupportedInline,
}
impl WasmStoreMetricReason {
#[must_use]
pub const fn metric_label(self) -> &'static str {
match self {
Self::CacheHit => "cache_hit",
Self::CacheMiss => "cache_miss",
Self::Capacity => "capacity",
Self::HashMismatch => "hash_mismatch",
Self::InvalidState => "invalid_state",
Self::ManagementCall => "management_call",
Self::MissingChunk => "missing_chunk",
Self::MissingManifest => "missing_manifest",
Self::Ok => "ok",
Self::StoreCall => "store_call",
Self::UnsupportedInline => "unsupported_inline",
}
}
}
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
pub struct WasmStoreMetricKey {
pub operation: WasmStoreMetricOperation,
pub source: WasmStoreMetricSource,
pub outcome: WasmStoreMetricOutcome,
pub reason: WasmStoreMetricReason,
}
pub struct WasmStoreMetrics;
impl WasmStoreMetrics {
pub fn record(
operation: WasmStoreMetricOperation,
source: WasmStoreMetricSource,
outcome: WasmStoreMetricOutcome,
reason: WasmStoreMetricReason,
) {
WASM_STORE_METRICS.with_borrow_mut(|counts| {
let key = WasmStoreMetricKey {
operation,
source,
outcome,
reason,
};
let entry = counts.entry(key).or_insert(0);
*entry = entry.saturating_add(1);
});
}
#[must_use]
pub fn snapshot() -> Vec<(WasmStoreMetricKey, u64)> {
WASM_STORE_METRICS
.with_borrow(std::clone::Clone::clone)
.into_iter()
.collect()
}
#[cfg(test)]
pub fn reset() {
WASM_STORE_METRICS.with_borrow_mut(HashMap::clear);
}
}
#[cfg(test)]
mod tests {
use super::*;
fn snapshot_map() -> HashMap<WasmStoreMetricKey, u64> {
WasmStoreMetrics::snapshot().into_iter().collect()
}
#[test]
fn wasm_store_metrics_accumulate_by_operation_source_outcome_and_reason() {
WasmStoreMetrics::reset();
WasmStoreMetrics::record(
WasmStoreMetricOperation::SourceResolve,
WasmStoreMetricSource::Bootstrap,
WasmStoreMetricOutcome::Completed,
WasmStoreMetricReason::Ok,
);
WasmStoreMetrics::record(
WasmStoreMetricOperation::ChunkUpload,
WasmStoreMetricSource::Bootstrap,
WasmStoreMetricOutcome::Skipped,
WasmStoreMetricReason::CacheHit,
);
WasmStoreMetrics::record(
WasmStoreMetricOperation::ChunkUpload,
WasmStoreMetricSource::Bootstrap,
WasmStoreMetricOutcome::Skipped,
WasmStoreMetricReason::CacheHit,
);
let map = snapshot_map();
assert_eq!(
map.get(&WasmStoreMetricKey {
operation: WasmStoreMetricOperation::SourceResolve,
source: WasmStoreMetricSource::Bootstrap,
outcome: WasmStoreMetricOutcome::Completed,
reason: WasmStoreMetricReason::Ok,
}),
Some(&1)
);
assert_eq!(
map.get(&WasmStoreMetricKey {
operation: WasmStoreMetricOperation::ChunkUpload,
source: WasmStoreMetricSource::Bootstrap,
outcome: WasmStoreMetricOutcome::Skipped,
reason: WasmStoreMetricReason::CacheHit,
}),
Some(&2)
);
}
}