use serde::{Deserialize, Serialize};
use crate::key::{AssetCacheKey, FullCacheKey, QueryCacheKey};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct SpanId(pub u64);
impl SpanId {
pub const ZERO: Self = Self(0);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct TraceId(pub u64);
impl TraceId {
pub const ZERO: Self = Self(0);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct SpanContext {
pub span_id: SpanId,
pub trace_id: TraceId,
pub parent_span_id: Option<SpanId>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ExecutionResult {
Changed,
Unchanged,
CacheHit,
Suspended,
CycleDetected,
Error { message: String },
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TracerAssetState {
Loading,
Ready,
NotFound,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum InvalidationReason {
DependencyChanged { dep: FullCacheKey },
AssetChanged { asset: FullCacheKey },
ManualInvalidation,
AssetRemoved { asset: FullCacheKey },
}
pub trait Tracer: Send + Sync + 'static {
fn new_span_id(&self) -> SpanId;
fn new_trace_id(&self) -> TraceId;
#[inline]
fn on_query_start(&self, _ctx: &SpanContext, _query: &QueryCacheKey) {}
#[inline]
fn on_cache_check(&self, _ctx: &SpanContext, _query: &QueryCacheKey, _valid: bool) {}
#[inline]
fn on_query_end(&self, _ctx: &SpanContext, _query: &QueryCacheKey, _result: ExecutionResult) {}
#[inline]
fn on_dependency_registered(
&self,
_ctx: &SpanContext,
_parent: &FullCacheKey,
_dependency: &FullCacheKey,
) {
}
#[inline]
fn on_asset_dependency_registered(
&self,
_ctx: &SpanContext,
_parent: &FullCacheKey,
_asset: &FullCacheKey,
) {
}
#[inline]
fn on_early_cutoff_check(
&self,
_ctx: &SpanContext,
_query: &QueryCacheKey,
_output_changed: bool,
) {
}
#[inline]
fn on_asset_requested(&self, _ctx: &SpanContext, _asset: &AssetCacheKey) {}
#[inline]
fn on_asset_located(
&self,
_ctx: &SpanContext,
_asset: &AssetCacheKey,
_state: TracerAssetState,
) {
}
#[inline]
fn on_asset_resolved(&self, _asset: &AssetCacheKey, _changed: bool) {}
#[inline]
fn on_asset_invalidated(&self, _asset: &AssetCacheKey) {}
#[inline]
fn on_query_invalidated(&self, _query: &QueryCacheKey, _reason: InvalidationReason) {}
#[inline]
fn on_cycle_detected(&self, _path: &[FullCacheKey]) {}
#[inline]
fn on_query_key(&self, _full_key: &FullCacheKey) {}
}
pub struct NoopTracer;
impl Tracer for NoopTracer {
#[inline(always)]
fn new_span_id(&self) -> SpanId {
SpanId::ZERO
}
#[inline(always)]
fn new_trace_id(&self) -> TraceId {
TraceId::ZERO
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::key::QueryCacheKey;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::sync::Arc;
struct CountingTracer {
start_count: AtomicUsize,
end_count: AtomicUsize,
}
impl CountingTracer {
fn new() -> Self {
Self {
start_count: AtomicUsize::new(0),
end_count: AtomicUsize::new(0),
}
}
}
impl Tracer for CountingTracer {
fn new_span_id(&self) -> SpanId {
SpanId(1)
}
fn new_trace_id(&self) -> TraceId {
TraceId(1)
}
fn on_query_start(&self, _ctx: &SpanContext, _query: &QueryCacheKey) {
self.start_count.fetch_add(1, Ordering::Relaxed);
}
fn on_query_end(
&self,
_ctx: &SpanContext,
_query: &QueryCacheKey,
_result: ExecutionResult,
) {
self.end_count.fetch_add(1, Ordering::Relaxed);
}
}
#[test]
fn test_noop_tracer_returns_zero() {
let tracer = NoopTracer;
assert_eq!(tracer.new_span_id(), SpanId::ZERO);
assert_eq!(tracer.new_trace_id(), TraceId::ZERO);
}
#[test]
fn test_counting_tracer() {
let tracer = CountingTracer::new();
let key = QueryCacheKey::new(("TestQuery",));
let ctx1 = SpanContext {
span_id: SpanId(1),
trace_id: TraceId(1),
parent_span_id: None,
};
let ctx2 = SpanContext {
span_id: SpanId(2),
trace_id: TraceId(1),
parent_span_id: Some(SpanId(1)),
};
tracer.on_query_start(&ctx1, &key);
tracer.on_query_start(&ctx2, &key);
tracer.on_query_end(&ctx1, &key, ExecutionResult::Changed);
assert_eq!(tracer.start_count.load(Ordering::Relaxed), 2);
assert_eq!(tracer.end_count.load(Ordering::Relaxed), 1);
}
#[test]
fn test_tracer_is_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<NoopTracer>();
assert_send_sync::<Arc<CountingTracer>>();
}
}