use crate::{InternalError, diagnostics::codes, model::placement::sharding::CreateBlockedReason};
use std::{cell::RefCell, collections::HashMap};
thread_local! {
static SHARDING_METRICS: RefCell<HashMap<ShardingMetricKey, u64>> =
RefCell::new(HashMap::new());
}
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[remain::sorted]
pub enum ShardingMetricOperation {
Assign,
AssignKey,
BootstrapActive,
BootstrapConfig,
BootstrapPool,
CreateShard,
PlanAssign,
ReleaseKey,
}
impl ShardingMetricOperation {
#[must_use]
pub const fn metric_label(self) -> &'static str {
match self {
Self::Assign => "assign",
Self::AssignKey => "assign_key",
Self::BootstrapActive => "bootstrap_active",
Self::BootstrapConfig => "bootstrap_config",
Self::BootstrapPool => "bootstrap_pool",
Self::CreateShard => "create_shard",
Self::PlanAssign => "plan_assign",
Self::ReleaseKey => "release_key",
}
}
}
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[remain::sorted]
pub enum ShardingMetricOutcome {
Completed,
Failed,
Skipped,
Started,
}
impl ShardingMetricOutcome {
#[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 ShardingMetricReason {
AlreadyAssigned,
CreateAllowed,
ExistingCapacity,
InvalidState,
ManagementCall,
NoFreeSlots,
NoInitialShards,
NotAssigned,
Ok,
PolicyDenied,
PoolAtCapacity,
ShardingDisabled,
TargetSatisfied,
Unknown,
}
impl ShardingMetricReason {
#[must_use]
pub const fn metric_label(self) -> &'static str {
match self {
Self::AlreadyAssigned => "already_assigned",
Self::CreateAllowed => "create_allowed",
Self::ExistingCapacity => "existing_capacity",
Self::InvalidState => "invalid_state",
Self::ManagementCall => "management_call",
Self::NoFreeSlots => "no_free_slots",
Self::NoInitialShards => "no_initial_shards",
Self::NotAssigned => "not_assigned",
Self::Ok => "ok",
Self::PolicyDenied => "policy_denied",
Self::PoolAtCapacity => "pool_at_capacity",
Self::ShardingDisabled => "sharding_disabled",
Self::TargetSatisfied => "target_satisfied",
Self::Unknown => "unknown",
}
}
#[must_use]
pub(crate) fn from_error(err: &InternalError) -> Self {
let code = err.code();
let public_code = err.public_error().code();
if code == codes::PLATFORM_FAILED {
Self::ManagementCall
} else if code == codes::STATE_CONFLICT
|| public_code == codes::AUTHORITY_UNAUTHORIZED.raw_code()
|| public_code == codes::REQUEST_INVALID.raw_code()
{
Self::PolicyDenied
} else if code == codes::STATE_INVALID
|| code == codes::STATE_FAILED
|| code == codes::LIFECYCLE_FAILED
{
Self::InvalidState
} else {
Self::Unknown
}
}
#[must_use]
pub(crate) const fn from_create_blocked_reason(reason: &CreateBlockedReason) -> Self {
match reason {
CreateBlockedReason::NoFreeSlots => Self::NoFreeSlots,
CreateBlockedReason::PoolAtCapacity => Self::PoolAtCapacity,
CreateBlockedReason::PolicyViolation(_) => Self::PolicyDenied,
}
}
}
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
pub struct ShardingMetricKey {
pub operation: ShardingMetricOperation,
pub outcome: ShardingMetricOutcome,
pub reason: ShardingMetricReason,
}
pub struct ShardingMetrics;
impl ShardingMetrics {
pub fn record(
operation: ShardingMetricOperation,
outcome: ShardingMetricOutcome,
reason: ShardingMetricReason,
) {
SHARDING_METRICS.with_borrow_mut(|counts| {
let key = ShardingMetricKey {
operation,
outcome,
reason,
};
let entry = counts.entry(key).or_insert(0);
*entry = entry.saturating_add(1);
});
}
#[must_use]
pub fn snapshot() -> Vec<(ShardingMetricKey, u64)> {
SHARDING_METRICS
.with_borrow(std::clone::Clone::clone)
.into_iter()
.collect()
}
#[cfg(test)]
pub fn reset() {
SHARDING_METRICS.with_borrow_mut(HashMap::clear);
}
}
#[cfg(test)]
mod tests {
use super::*;
fn snapshot_map() -> HashMap<ShardingMetricKey, u64> {
ShardingMetrics::snapshot().into_iter().collect()
}
#[test]
fn sharding_metrics_accumulate_by_operation_outcome_and_reason() {
ShardingMetrics::reset();
ShardingMetrics::record(
ShardingMetricOperation::PlanAssign,
ShardingMetricOutcome::Completed,
ShardingMetricReason::ExistingCapacity,
);
ShardingMetrics::record(
ShardingMetricOperation::BootstrapPool,
ShardingMetricOutcome::Skipped,
ShardingMetricReason::TargetSatisfied,
);
ShardingMetrics::record(
ShardingMetricOperation::BootstrapPool,
ShardingMetricOutcome::Skipped,
ShardingMetricReason::TargetSatisfied,
);
ShardingMetrics::record(
ShardingMetricOperation::ReleaseKey,
ShardingMetricOutcome::Skipped,
ShardingMetricReason::NotAssigned,
);
let map = snapshot_map();
assert_eq!(
map.get(&ShardingMetricKey {
operation: ShardingMetricOperation::PlanAssign,
outcome: ShardingMetricOutcome::Completed,
reason: ShardingMetricReason::ExistingCapacity,
}),
Some(&1)
);
assert_eq!(
map.get(&ShardingMetricKey {
operation: ShardingMetricOperation::BootstrapPool,
outcome: ShardingMetricOutcome::Skipped,
reason: ShardingMetricReason::TargetSatisfied,
}),
Some(&2)
);
assert_eq!(
map.get(&ShardingMetricKey {
operation: ShardingMetricOperation::ReleaseKey,
outcome: ShardingMetricOutcome::Skipped,
reason: ShardingMetricReason::NotAssigned,
}),
Some(&1)
);
}
}