diff --git a/crates/tracedecay-store-runtime/src/session_registry.rs b/crates/tracedecay-store-runtime/src/session_registry.rs index 75cbde96a9..22e35a594b 100644 --- a/crates/tracedecay-store-runtime/src/session_registry.rs +++ b/crates/tracedecay-store-runtime/src/session_registry.rs @@ -358,6 +358,7 @@ impl ProjectRuntimeOwnerRegistryV1 { self.0.lock() } + #[hotpath::measure(label = "daemon.session_registry.list_ready_sessions")] fn ready_session_projects(&self) -> Result> { let entries = self.lock().map_err(|_| { session_registry_error( @@ -425,6 +426,7 @@ impl ProjectRuntimeOwnerRegistryV1 { } } + #[hotpath::measure(label = "daemon.session_registry.reserve_session_replacement")] fn reserve_session_replacement( &self, project_id: &ProjectId, @@ -483,6 +485,7 @@ impl ProjectRuntimeOwnerRegistryV1 { })) } + #[hotpath::measure(label = "daemon.session_registry.reserve_session_recovery")] fn reserve_session_recovery( &self, project_id: &ProjectId, @@ -528,6 +531,7 @@ impl ProjectRuntimeOwnerRegistryV1 { /// Rebuilds the fail-closed post-restart recovery record. The durable /// quarantine receipt is written only after the old paired owners have /// closed; it never reconstructs or remounts that terminal owner. + #[hotpath::measure(label = "daemon.session_registry.reconstruct_terminal_recovery")] fn reconstruct_durable_terminal_recovery( &self, project_id: &ProjectId, @@ -568,6 +572,7 @@ impl ProjectRuntimeOwnerRegistryV1 { } } +#[hotpath::measure(label = "daemon.session_registry.bind_memory_graph")] fn bind_ready_project_memory_graph( owners: &ProjectRuntimeOwnerRegistryV1, project_id: &ProjectId, @@ -2668,6 +2673,7 @@ impl DaemonSessionRuntimeRegistryV1 { }) } + #[hotpath::measure(label = "daemon.session_registry.admit_remote_node")] fn admit_remote_node_owner( &self, node_id: &BrainNodeId, @@ -2716,6 +2722,7 @@ impl DaemonSessionRuntimeRegistryV1 { )) } + #[hotpath::measure(label = "daemon.session_registry.admit_project_runtime")] fn admit_project_runtime_owner( &self, project_id: &ProjectId, @@ -2784,6 +2791,7 @@ impl DaemonSessionRuntimeRegistryV1 { ))) } + #[hotpath::measure(label = "daemon.session_registry.extend_project_runtime")] fn extend_project_runtime_owner( &self, project_id: &ProjectId, @@ -2833,6 +2841,7 @@ impl DaemonSessionRuntimeRegistryV1 { ))) } + #[hotpath::measure(label = "daemon.session_registry.reserve_runtime_retirement")] fn reserve_project_runtime_retirement( &self, project_id: &ProjectId, @@ -3079,6 +3088,7 @@ impl DaemonSessionRuntimeRegistryV1 { }) } + #[hotpath::measure(label = "daemon.session_registry.retire_session_sync", future = true)] async fn retire_project_session_sync(&self, project_id: &ProjectId) -> Result<()> { self.active_session_sync_service("retire project session sync")? .retire_project(self.identity.profile_id(), project_id) @@ -3087,6 +3097,7 @@ impl DaemonSessionRuntimeRegistryV1 { .map_err(|error| session_registry_error("retire project session sync", error)) } + #[hotpath::measure(label = "daemon.session_registry.rebind_session_sync", future = true)] async fn rebind_project_session_sync( &self, project_id: &ProjectId, @@ -3136,6 +3147,7 @@ impl ProfileRuntime for DaemonSessionRuntimeRegistryV1 { } } +#[hotpath::measure(label = "daemon.session_registry.runtime_incarnation")] fn runtime_incarnation(identity: &LocalProfileIdentityAuthorityV1) -> Result { let process_run_id = tracedecay_runtime_core::runtime_identity::process_run_id(); let daemon_generation = diff --git a/crates/tracedecay/src/daemon/engine.rs b/crates/tracedecay/src/daemon/engine.rs index 9964f0b060..7c2ea8c142 100644 --- a/crates/tracedecay/src/daemon/engine.rs +++ b/crates/tracedecay/src/daemon/engine.rs @@ -215,6 +215,7 @@ impl DaemonEngine { /// A doctor-facing read of one project's watch coverage; `git_watcher` is /// module-private, so the core Doctor route reads through this accessor. + #[hotpath::measure(label = "daemon.engine.git_watcher_health", future = true)] pub(super) async fn git_watcher_health( &self, project_root: Option<&std::path::Path>, @@ -303,6 +304,7 @@ impl DaemonEngine { /// Logs a `daemon_version_skew` event when this handshake's client runs a /// different binary version, deduped per distinct client version. + #[hotpath::measure(label = "daemon.engine.log_version_skew", future = true)] pub(super) async fn log_client_version_skew(&self, handshake: &DaemonHandshake) -> Result<()> { let Some(client_version) = self.client_version_skew_to_log(handshake).await? else { return Ok(()); @@ -334,6 +336,7 @@ impl DaemonEngine { /// exactly what arms its notification for the first request after warm-up /// completes; marking it would strand the provisional catalog for the rest /// of the daemon's life, because this set is never otherwise cleared. + #[hotpath::measure(label = "daemon.engine.claim_catalog_refresh", future = true)] pub(super) async fn claim_catalog_refresh( &self, handshake: &DaemonHandshake, @@ -726,6 +729,7 @@ impl DaemonEngine { }) } + #[hotpath::measure(label = "daemon.engine.cached_open_failure", future = true)] pub(super) async fn cached_project_open_failure( &self, handshake: &DaemonHandshake, diff --git a/crates/tracedecay/src/daemon/http_application.rs b/crates/tracedecay/src/daemon/http_application.rs index 2569410a57..c593b87a4c 100644 --- a/crates/tracedecay/src/daemon/http_application.rs +++ b/crates/tracedecay/src/daemon/http_application.rs @@ -313,6 +313,7 @@ impl DaemonHttpApplicationRegistry { Ok(Some(router)) } + #[hotpath::measure(label = "daemon.http.application.build_registry_router")] fn router( self, admission: LocalHttpAdmission, @@ -408,6 +409,7 @@ async fn provision_remote_node( } } +#[hotpath::measure(label = "daemon.http.application.remote_status_read", future = true)] async fn remote_operational_status( State(registry): State, ) -> Response { @@ -654,6 +656,7 @@ impl LocalHttpAdmission { } } +#[hotpath::measure(label = "daemon.http.application.local_admission", future = true)] async fn require_local_http_admission( State(admission): State, request: Request, @@ -990,6 +993,7 @@ struct RemoteBrainTlsListener { } impl RemoteBrainTlsListener { + #[hotpath::measure(label = "daemon.http.application.tls_bind", future = true)] async fn bind(config: &RemoteBrainTlsConfig) -> Result { let certificates = CertificateDer::pem_file_iter(config.certificate_chain()) .map_err(|error| tls_configuration_error("open Remote Brain TLS certificate", error))? @@ -1041,6 +1045,7 @@ impl RemoteBrainTlsListener { self.listener.local_addr() } + #[hotpath::measure(label = "daemon.http.application.tls_accept", future = true)] async fn accept(&self) -> Option<(RemoteBrainTlsIo, SocketAddr)> { let (stream, address) = match self.listener.accept().await { Ok(accepted) => accepted, @@ -1070,6 +1075,7 @@ impl RemoteBrainTlsListener { } } +#[hotpath::measure(label = "daemon.http.application.tls_validate_identity")] fn validate_remote_brain_tls_identity( certificates: &[CertificateDer<'_>], listen: SocketAddr, @@ -1434,6 +1440,7 @@ impl RemoteBrainTlsIo { } } + #[hotpath::measure(label = "daemon.http.application.observe_http_request")] fn observe_http_request(&mut self, bytes: &[u8]) -> io::Result<()> { if self.request_read_complete { return Ok(()); @@ -1684,6 +1691,7 @@ impl Drop for RemoteBrainTlsIo { } } +#[hotpath::measure(label = "daemon.http.application.parse_body_length")] fn declared_http11_body_length(header_bytes: &[u8]) -> io::Result { let headers = std::str::from_utf8(header_bytes).map_err(|_| { io::Error::new( diff --git a/crates/tracedecay/src/daemon/maintenance.rs b/crates/tracedecay/src/daemon/maintenance.rs index 3df65c52d2..5a30d063da 100644 --- a/crates/tracedecay/src/daemon/maintenance.rs +++ b/crates/tracedecay/src/daemon/maintenance.rs @@ -124,42 +124,45 @@ impl GuardedStoreTelemetryPort { store: &'a StoreKeyV1, observation: TableGrowthObservation, ) -> StorageTelemetryFuture<'a, TableGrowthTelemetryReadV1> { - Box::pin(async move { - if !self.admits(context, store) { - return TableGrowthTelemetryReadV1::Denied { - store: store.clone(), - }; - } - let Ok(current) = self - .handle - .table_size_telemetry(self.reader_wait, || telemetry_interruption(context)) - else { - return TableGrowthTelemetryReadV1::Unknown { - store: store.clone(), - }; - }; - let observed_at = now_micros(); - let mut current_tables = BTreeMap::new(); - for sample in current { - let Ok(table) = TableNameV1::new(sample.table_name) else { + Box::pin(hotpath::future!( + async move { + if !self.admits(context, store) { + return TableGrowthTelemetryReadV1::Denied { + store: store.clone(), + }; + } + let Ok(current) = self + .handle + .table_size_telemetry(self.reader_wait, || telemetry_interruption(context)) + else { return TableGrowthTelemetryReadV1::Unknown { store: store.clone(), }; }; - current_tables.insert(table, StorageByteSizeV1(sample.bytes)); - } - let mut watermarks = match self.table_watermarks.lock() { - Ok(watermarks) => watermarks, - Err(poisoned) => poisoned.into_inner(), - }; - compare_table_growth( - store, - current_tables, - observed_at, - &mut watermarks, - observation, - ) - }) + let observed_at = now_micros(); + let mut current_tables = BTreeMap::new(); + for sample in current { + let Ok(table) = TableNameV1::new(sample.table_name) else { + return TableGrowthTelemetryReadV1::Unknown { + store: store.clone(), + }; + }; + current_tables.insert(table, StorageByteSizeV1(sample.bytes)); + } + let mut watermarks = match self.table_watermarks.lock() { + Ok(watermarks) => watermarks, + Err(poisoned) => poisoned.into_inner(), + }; + compare_table_growth( + store, + current_tables, + observed_at, + &mut watermarks, + observation, + ) + }, + label = "daemon.maintenance.read_table_growth" + )) } } @@ -169,34 +172,37 @@ impl StoreSizeTelemetryPort for GuardedStoreTelemetryPort { context: &'a RequestContext, store: &'a StoreKeyV1, ) -> StorageTelemetryFuture<'a, StorageTelemetryReadV1> { - Box::pin(async move { - if !self.admits(context, store) { - return StorageTelemetryReadV1::Denied { - store: store.clone(), - }; - } - let Ok(sample) = self - .handle - .store_size_telemetry(self.reader_wait, || telemetry_interruption(context)) - else { - return StorageTelemetryReadV1::Unknown { - store: store.clone(), + Box::pin(hotpath::future!( + async move { + if !self.admits(context, store) { + return StorageTelemetryReadV1::Denied { + store: store.clone(), + }; + } + let Ok(sample) = self + .handle + .store_size_telemetry(self.reader_wait, || telemetry_interruption(context)) + else { + return StorageTelemetryReadV1::Unknown { + store: store.clone(), + }; }; - }; - let sample = StoreSizeSampleV1 { - store: store.clone(), - page_size_bytes: sample.page_size_bytes, - page_count: sample.page_count, - freelist_pages: sample.freelist_pages, - observed_at: now_micros(), - }; - if sample.validate().is_err() { - return StorageTelemetryReadV1::Unknown { + let sample = StoreSizeSampleV1 { store: store.clone(), + page_size_bytes: sample.page_size_bytes, + page_count: sample.page_count, + freelist_pages: sample.freelist_pages, + observed_at: now_micros(), }; - } - StorageTelemetryReadV1::Observed { sample } - }) + if sample.validate().is_err() { + return StorageTelemetryReadV1::Unknown { + store: store.clone(), + }; + } + StorageTelemetryReadV1::Observed { sample } + }, + label = "daemon.maintenance.read_store_size" + )) } fn table_growth<'a>( @@ -208,6 +214,7 @@ impl StoreSizeTelemetryPort for GuardedStoreTelemetryPort { } } +#[hotpath::measure(label = "daemon.maintenance.compare_table_growth")] fn compare_table_growth( store: &StoreKeyV1, current_tables: BTreeMap, @@ -720,6 +727,7 @@ impl StoreTelemetrySamplingRegistry { ) } + #[hotpath::measure(label = "daemon.maintenance.sample_store_telemetry", future = true)] async fn advance_registered( &self, active_paths: &BTreeSet, @@ -759,6 +767,7 @@ impl StoreTelemetrySamplingRegistry { } } +#[hotpath::measure(label = "daemon.maintenance.mint_telemetry_context")] fn storage_telemetry_request_context( scope: ResolvedScope, ) -> Result { @@ -1778,11 +1787,13 @@ fn checkpoint_path(profile_root: &Path) -> PathBuf { .join(CHECKPOINT_FILE) } +#[hotpath::measure(label = "daemon.maintenance.load_cursor")] fn load_cursor(path: &Path) -> Option { let bytes = std::fs::read(path).ok()?; serde_json::from_slice(&bytes).ok() } +#[hotpath::measure(label = "daemon.maintenance.persist_cursor")] fn persist_cursor(path: &Path, cursor: &ColdStoreCursorV1) -> std::io::Result<()> { let parent = path .parent() diff --git a/crates/tracedecay/src/daemon/pr_autotrack.rs b/crates/tracedecay/src/daemon/pr_autotrack.rs index 34772fa425..6a93782b18 100644 --- a/crates/tracedecay/src/daemon/pr_autotrack.rs +++ b/crates/tracedecay/src/daemon/pr_autotrack.rs @@ -558,6 +558,7 @@ fn map_pull_heads_to_branches( discovery } +#[hotpath::measure(label = "daemon.pr_autotrack.run_git")] fn run_git_with_control( repo_root: &Path, args: &[&str], @@ -685,6 +686,7 @@ fn discover_open_prs_with_control( discover_via_ls_remote(repo_root, control) } +#[hotpath::measure(label = "daemon.pr_autotrack.discover_gh")] fn discover_via_gh(repo_root: &Path, control: &PrCommandControl) -> Option { let limit = GH_PR_LIST_LIMIT.to_string(); let mut command = std::process::Command::new("gh"); @@ -714,6 +716,7 @@ fn discover_via_gh(repo_root: &Path, control: &PrCommandControl) -> Option/head` into `tracking_ref` and adds a linked worktree /// checked out on a local branch named `label` at that ref. +#[hotpath::measure(label = "daemon.pr_autotrack.prepare_worktree")] fn prepare_pr_worktree( repo_root: &Path, worktree: &Path, @@ -2393,6 +2402,7 @@ async fn cleanup_pr_worktree_off_runtime( } } +#[hotpath::measure(label = "daemon.pr_autotrack.cleanup_worktree")] fn cleanup_pr_worktree( repo_root: &Path, data_root: &Path, diff --git a/crates/tracedecay/src/daemon/scheduler.rs b/crates/tracedecay/src/daemon/scheduler.rs index 82378e194a..d0542ef185 100644 --- a/crates/tracedecay/src/daemon/scheduler.rs +++ b/crates/tracedecay/src/daemon/scheduler.rs @@ -152,6 +152,7 @@ fn scheduler_run_observer( }) } +#[hotpath::measure(label = "daemon.scheduler.settle_retained_automation", future = true)] async fn settle_scheduler_retained_automation( engine: &DaemonEngine, project_id: &tracedecay_domain::ProjectId, @@ -784,6 +785,7 @@ impl DaemonEngine { AutomationSchedulerReconcileOutcome::Started } + #[hotpath::measure(label = "daemon.scheduler.commit_exit", future = true)] async fn commit_automation_scheduler_exit( &self, key: &ProjectServerKey, @@ -870,6 +872,7 @@ impl DaemonEngine { .await } + #[hotpath::measure(label = "daemon.scheduler.retire_scheduler", future = true)] async fn retire_matching_automation_scheduler_locked( &self, key: &ProjectServerKey, @@ -991,6 +994,7 @@ fn observed_scheduler_lifecycle( } } +#[hotpath::measure(label = "daemon.scheduler.retained_project_graph", future = true)] async fn retained_project_graph( engine: &DaemonEngine, key: &ProjectServerKey, @@ -1446,6 +1450,7 @@ fn global_table_retention_config( /// Applies the configured retention windows to the global telemetry tables, /// at most once per [`RETENTION_MIN_INTERVAL_SECS`]. Best-effort: retention is /// housekeeping, so failures are logged and never abort a scheduler tick. +#[hotpath::measure(label = "daemon.scheduler.global_retention", future = true)] async fn maybe_run_global_retention( administration: &super::branch_admin::StoreAdministration, database: &tracedecay_global_db::RegisteredGlobalDb, @@ -1860,6 +1865,7 @@ struct PinnedAutomationConfiguration { settings: tracedecay_automation_runtime::automation::config::AutomationConfig, } +#[hotpath::measure(label = "daemon.scheduler.read_automation_config", future = true)] async fn effective_automation_config_for_project( cg: &crate::tracedecay::TraceDecay, ) -> Result { @@ -1923,6 +1929,7 @@ pub(super) fn automation_scheduler_configured( /// True when the scheduler loop has anything to do for this project: a /// scheduled fixed task or a schedulable user-defined job. +#[hotpath::measure(label = "daemon.scheduler.probe_scheduler_work", future = true)] async fn automation_scheduler_has_work( cg: &crate::tracedecay::TraceDecay, config: &tracedecay_automation_runtime::automation::config::AutomationConfig, @@ -1948,6 +1955,7 @@ async fn automation_scheduler_has_work( /// Ticks every schedulable user-defined job with the same lock/cooldown /// discipline as the fixed tasks (enforced inside the job runner). +#[hotpath::measure(label = "daemon.scheduler.user_jobs_pass", future = true)] async fn run_user_jobs_scheduler_pass( engine: &DaemonEngine, run_control: &AutomationRunControl, @@ -2128,6 +2136,7 @@ async fn run_user_jobs_scheduler_pass( /// Deriving a fresher anchor at append time can bound the scan below an /// already-appended row and silently write a byte-different duplicate. /// `None` means this snapshot held no scheduler-effectful terminal at all. +#[hotpath::measure(label = "daemon.scheduler.mint_user_job_run_id", future = true)] async fn scheduled_user_job_run_id( dashboard_root: &Path, job: &tracedecay_automation_runtime::automation::jobs::AutomationJob, diff --git a/crates/tracedecay/src/host_admission.rs b/crates/tracedecay/src/host_admission.rs index 4aa438eaa9..1daf02116a 100644 --- a/crates/tracedecay/src/host_admission.rs +++ b/crates/tracedecay/src/host_admission.rs @@ -103,6 +103,7 @@ static SESSION_CAPTURE_TEST_RESIDENT_MEMORY: LazyLock Result<()> { if process_background_cpu().is_none() { let memory = SESSION_CAPTURE_TEST_RESIDENT_MEMORY.snapshot(); @@ -183,6 +184,7 @@ impl HostAdmissionTestRuntimeV1 { /// cannot exist — the profile session-relation graph has exactly one /// writer. #[doc(hidden)] + #[hotpath::measure(label = "daemon.host_admission.mount_sibling_project", future = true)] pub async fn sibling_project( &self, project_root: impl AsRef, @@ -245,6 +247,7 @@ impl HostAdmissionTestRuntimeV1 { }) } + #[hotpath::measure(label = "daemon.host_admission.open_runtime", future = true)] async fn open(profile_root: PathBuf, project: Option<(PathBuf, ProjectId)>) -> Result { // Fixture compositions run in-process daemon code that reads the // registered product runtime (handshakes, initialize payloads); @@ -410,6 +413,7 @@ impl HostAdmissionTestRuntimeV1 { } #[doc(hidden)] + #[hotpath::measure(label = "daemon.host_admission.scan_storage_bytes")] pub fn session_database_storage_bytes_for_test( &self, scope: HostAdmissionScope, @@ -434,6 +438,7 @@ impl HostAdmissionTestRuntimeV1 { } #[doc(hidden)] + #[hotpath::measure(label = "daemon.host_admission.digest_session_domain", future = true)] pub async fn session_domain_sha256_for_test( &self, scope: HostAdmissionScope, @@ -455,6 +460,7 @@ impl HostAdmissionTestRuntimeV1 { } #[doc(hidden)] + #[hotpath::measure(label = "daemon.host_admission.replay_observations", future = true)] pub async fn replay_observations( &self, scope: HostAdmissionScope, @@ -493,6 +499,7 @@ impl HostAdmissionTestRuntimeV1 { } #[doc(hidden)] + #[hotpath::measure(label = "daemon.host_admission.count_temporal_rows", future = true)] pub async fn session_temporal_fixture_count_for_test( &self, scope: HostAdmissionScope, @@ -629,6 +636,7 @@ impl HostAdmissionTestRuntimeV1 { } #[doc(hidden)] + #[hotpath::measure(label = "daemon.host_admission.upsert_transcript_batch", future = true)] pub async fn upsert_transcript_batch_for_test( &self, scope: HostAdmissionScope, @@ -669,6 +677,7 @@ impl HostAdmissionTestRuntimeV1 { } #[doc(hidden)] + #[hotpath::measure(label = "daemon.host_admission.count_transcript_rows", future = true)] pub async fn transcript_store_counts_for_test( &self, scope: HostAdmissionScope, @@ -1030,6 +1039,7 @@ impl HostAdmissionTestRuntimeV1 { /// Initializes a project graph through this retained registered runtime. #[doc(hidden)] + #[hotpath::measure(label = "daemon.host_admission.init_project_graph", future = true)] pub async fn initialize_project_graph_for_test( &self, project_root: &Path, @@ -1073,6 +1083,7 @@ impl HostAdmissionTestRuntimeV1 { /// Reopens an existing project graph through this retained runtime. #[doc(hidden)] + #[hotpath::measure(label = "daemon.host_admission.open_project_graph", future = true)] pub async fn open_project_graph_for_test( &self, project_root: &Path, @@ -1094,6 +1105,7 @@ impl HostAdmissionTestRuntimeV1 { /// Opens one tracked branch through this retained registered runtime. #[doc(hidden)] + #[hotpath::measure(label = "daemon.host_admission.open_project_branch", future = true)] pub async fn open_project_branch_for_test( &self, project_root: &Path, @@ -1138,6 +1150,7 @@ impl HostAdmissionTestRuntimeV1 { /// Reopens an existing graph read-only without inferring authority. #[doc(hidden)] + #[hotpath::measure(label = "daemon.host_admission.open_graph_read_only", future = true)] pub async fn open_project_graph_read_only_for_test( &self, project_root: &Path, @@ -1245,6 +1258,7 @@ impl std::ops::Deref for ProjectScopedTestRuntimeV1 { } } +#[hotpath::measure(label = "daemon.host_admission.validate_authorities")] fn validate_registered_authorities( brain_id: &BrainId, profile_id: &UserProfileId, diff --git a/crates/tracedecay/src/mcp/project_route.rs b/crates/tracedecay/src/mcp/project_route.rs index 09fa9adb72..e37f26875c 100644 --- a/crates/tracedecay/src/mcp/project_route.rs +++ b/crates/tracedecay/src/mcp/project_route.rs @@ -53,6 +53,7 @@ impl std::fmt::Debug for ResolvedProjectRoute { } } +#[hotpath::measure(label = "mcp.project_route.resolve_registered", future = true)] pub(crate) async fn resolve_registered_project_route( context: ProjectRegistryContext, requested_path: &Path, @@ -192,6 +193,7 @@ impl HookProjectRouteCache { /// Evicts cached routes for a tombstoned project. The cached server lease /// is weak, but retaining the identity would make every later request fail /// against a retired project instead of allowing a newly resolved route. + #[hotpath::measure(label = "mcp.project_route.forget_project")] pub(crate) fn forget_project( &mut self, profile_id: &tracedecay_domain::UserProfileId, @@ -238,6 +240,7 @@ impl HookProjectRouteCache { .or(event.cwd.as_deref()) } + #[hotpath::measure(label = "mcp.project_route.observe_route")] pub(crate) fn observe_workspace_route( &mut self, event: &hook_events::HookEvent, @@ -277,6 +280,7 @@ impl HookProjectRouteCache { } } + #[hotpath::measure(label = "mcp.project_route.select_route")] pub(crate) fn workspace_route_for_arguments( &self, arguments: &Value, @@ -393,6 +397,7 @@ impl SharedHookProjectRouteCache { ) } + #[hotpath::measure(label = "mcp.project_route.snapshot_cache")] pub(crate) fn snapshot(&self) -> tracedecay_domain::errors::Result { let state = self .inner @@ -403,6 +408,7 @@ impl SharedHookProjectRouteCache { Ok(cache) } + #[hotpath::measure(label = "mcp.project_route.store_cache")] pub(crate) fn store( &self, cache: &HookProjectRouteCache, @@ -417,6 +423,7 @@ impl SharedHookProjectRouteCache { /// Refresh `target` from the shared cache with one clone under the lock, /// skipped entirely while `target` already carries the current shared /// generation (route updates are rare relative to tool calls). + #[hotpath::measure(label = "mcp.project_route.refresh_cache")] pub(crate) fn refresh_into( &self, target: &mut HookProjectRouteCache, @@ -435,6 +442,7 @@ impl SharedHookProjectRouteCache { Ok(()) } + #[hotpath::measure(label = "mcp.project_route.forget_project_shared")] pub(crate) fn forget_project( &self, profile_id: &tracedecay_domain::UserProfileId, @@ -458,6 +466,7 @@ pub(crate) fn arguments_have_structural_route_identity(arguments: &Value) -> boo mcp_route_thread_id(arguments).is_some() || mcp_analytics_session_id(arguments).is_some() } +#[hotpath::measure(label = "mcp.project_route.protect_structural_ids")] pub(crate) fn protect_tool_structural_ids(arguments: &mut Value) -> Result<(), ()> { const STRUCTURAL_ID_KEYS: &[&str] = &[ "session_id", diff --git a/crates/tracedecay/src/mcp/server.rs b/crates/tracedecay/src/mcp/server.rs index 86374636c1..8f8387c8ae 100644 --- a/crates/tracedecay/src/mcp/server.rs +++ b/crates/tracedecay/src/mcp/server.rs @@ -473,6 +473,7 @@ struct MountedProjectApplicationRetrievalV1 { } impl MountedProjectApplicationRetrievalV1 { + #[hotpath::measure(label = "mcp.server.retrieval_scope_check")] fn retrieval_for_scope( &self, expected_scope: &tracedecay_application::ResolvedScope, @@ -785,6 +786,7 @@ impl McpServer { context } + #[hotpath::measure(label = "mcp.server.construct", future = true)] pub(crate) async fn new_with_context(context: McpServerConstructionContext) -> Arc { let McpServerConstructionContext { cg, @@ -838,7 +840,12 @@ impl McpServer { } = context; let file_token_map = HashMap::new(); let response_handle_project_root = cg.project_root().to_path_buf(); - let persisted_tokens_saved = match cg.get_tokens_saved().await { + let persisted_tokens_saved = match hotpath::future!( + cg.get_tokens_saved(), + label = "mcp.server.read_tokens_baseline" + ) + .await + { Ok(persisted) => Some(persisted), Err(error) => { tracing::warn!( @@ -883,12 +890,15 @@ impl McpServer { let worktree_mismatch = { let project_root = cg.project_root().to_path_buf(); let scope_prefix = scope_prefix.clone(); - tokio::task::spawn_blocking(move || { - tracedecay_runtime_core::worktree::detect_scoped_worktree_index_mismatch( - &project_root, - scope_prefix.as_deref(), - ) - }) + hotpath::future!( + tokio::task::spawn_blocking(move || { + tracedecay_runtime_core::worktree::detect_scoped_worktree_index_mismatch( + &project_root, + scope_prefix.as_deref(), + ) + }), + label = "mcp.server.detect_worktree_mismatch" + ) .await .ok() .flatten() @@ -1197,6 +1207,7 @@ impl McpServer { self.scope_prefix.as_deref() } + #[hotpath::measure(label = "mcp.server.reconcile_automation", future = true)] pub(crate) async fn reconcile_automation_scheduler( &self, ) -> tracedecay_dashboard_api::AutomationSchedulerReconcileOutcome { @@ -1272,6 +1283,7 @@ impl McpServer { self.project_application_retrieval.is_some() } + #[hotpath::measure(label = "mcp.server.mount_work_evidence")] pub(crate) fn work_evidence_retrieval( &self, expected_scope: &tracedecay_application::ResolvedScope, @@ -1309,6 +1321,7 @@ impl McpServer { } } + #[hotpath::measure(label = "mcp.server.mount_retained_surfaces")] pub(crate) fn retained_surface_ports( &self, project_root: &Path, @@ -1348,6 +1361,7 @@ impl McpServer { self.cg.read().await.clone() } + #[hotpath::measure(label = "mcp.server.stats_snapshot", future = true)] pub async fn server_stats_json(&self) -> Value { let uptime = self.stats.started_at.elapsed(); let total_requests = self.stats.total_requests.load(Ordering::Relaxed); diff --git a/crates/tracedecay/src/mcp/server/ledger.rs b/crates/tracedecay/src/mcp/server/ledger.rs index 81c1a34a6a..338d509711 100644 --- a/crates/tracedecay/src/mcp/server/ledger.rs +++ b/crates/tracedecay/src/mcp/server/ledger.rs @@ -89,6 +89,7 @@ impl McpServer { /// Reads the upload policy from the daemon-retained desired configuration /// snapshot. There is deliberately no `config.toml` fallback: without the /// canonical authority, the upload decision is unavailable. + #[hotpath::measure(label = "mcp.ledger.read_upload_policy", future = true)] pub(super) async fn canonical_upload_enabled(&self) -> Result { let cg = self.cg_snapshot().await; let desired = cg @@ -107,6 +108,7 @@ impl McpServer { /// Estimates the raw-file token cost ("before") for the given file /// paths from the cached file-token map (indexed file bytes / 4). /// Pure lookup — persists nothing. + #[hotpath::measure(label = "mcp.ledger.estimate_raw_tokens")] pub(crate) fn estimate_raw_file_tokens(&self, file_paths: &[String]) -> u64 { if file_paths.is_empty() { return 0; @@ -136,6 +138,7 @@ impl McpServer { /// ledger-write path so the response never waits on them and tests can /// still await durability via [`Self::ledger_writes_settled`]. Shutdown /// persists the final counter independently. + #[hotpath::measure(label = "mcp.ledger.persist_token_accounting")] pub(crate) fn spawn_token_accounting_persist( &self, monitor_project_root: &Path, @@ -259,6 +262,7 @@ impl McpServer { /// Flushes pending tokens to the worldwide counter if at least 30 seconds /// have elapsed since the last flush. Best-effort, never blocks for long. + #[hotpath::measure(label = "mcp.ledger.flush_worldwide", future = true)] pub(crate) async fn maybe_flush_worldwide(&self) { let now = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) @@ -325,6 +329,7 @@ impl McpServer { } } + #[hotpath::measure(label = "mcp.ledger.record_error_analytics")] pub(crate) fn record_mcp_tool_error_analytics( &self, request: McpToolErrorAnalyticsRequest<'_>, @@ -378,6 +383,7 @@ impl McpServer { /// [`HostAdmissionOutcome`]. The durable admission sequence is carried as /// the event idempotency identity, so identical but distinct admissions /// remain distinct analytics rows. + #[hotpath::measure(label = "mcp.ledger.record_route_analytics")] pub(crate) fn record_hook_route_analytics( &self, project_root: &std::path::Path, @@ -424,6 +430,7 @@ impl McpServer { /// [`DEFAULT_SPAN_OBSERVATION_DEBOUNCE_SECS`](tracedecay_sessions::runtime::git_correlation::DEFAULT_SPAN_OBSERVATION_DEBOUNCE_SECS) /// (spans merge regardless, so a dropped observation only widens a span /// slightly less). + #[hotpath::measure(label = "mcp.ledger.record_span_observation")] pub(crate) fn record_hook_span_observation( self: &Arc, event: &hook_events::HookEvent, diff --git a/crates/tracedecay/src/profile_registry_maintenance.rs b/crates/tracedecay/src/profile_registry_maintenance.rs index ac53630e2d..f9e847a9fc 100644 --- a/crates/tracedecay/src/profile_registry_maintenance.rs +++ b/crates/tracedecay/src/profile_registry_maintenance.rs @@ -18,6 +18,7 @@ pub struct ProfileRegistryMaintenanceRuntime { impl ProfileRegistryMaintenanceRuntime { /// Opens an existing exact-final profile registry without creating one. + #[hotpath::measure(label = "daemon.profile_registry.open_existing", future = true)] pub async fn try_open_existing( profile_root: &Path, ) -> tracedecay_domain::errors::Result> { @@ -50,6 +51,7 @@ impl ProfileRegistryMaintenanceRuntime { Self::open(&profile_root).await.map(Some) } + #[hotpath::measure(label = "daemon.profile_registry.open", future = true)] pub async fn open(profile_root: &Path) -> tracedecay_domain::errors::Result { let identity = tracedecay_daemon_identity::profile_identity::load_existing(profile_root)?; let registry = @@ -61,6 +63,7 @@ impl ProfileRegistryMaintenanceRuntime { Ok(Self { profile_database }) } + #[hotpath::measure(label = "daemon.profile_registry.list_projects", future = true)] pub async fn registered_project_paths( &self, ) -> tracedecay_domain::errors::Result> { @@ -69,6 +72,7 @@ impl ProfileRegistryMaintenanceRuntime { .await } + #[hotpath::measure(label = "daemon.profile_registry.classify_storage", future = true)] pub async fn classify_project_storage( &self, project_root: &Path, @@ -95,6 +99,7 @@ impl ProfileRegistryMaintenanceRuntime { RegisteredGlobalDb::canonical_project_key(project_root) } + #[hotpath::measure(label = "daemon.profile_registry.retire_paths", future = true)] pub async fn delete_project_paths( &self, project_paths: &[PathBuf], @@ -106,6 +111,7 @@ impl ProfileRegistryMaintenanceRuntime { .await } + #[hotpath::measure(label = "daemon.profile_registry.apply_orphan_relink", future = true)] pub async fn apply_orphan_relink( &self, report: &RegistryOrphanRelinkReport, @@ -117,6 +123,7 @@ impl ProfileRegistryMaintenanceRuntime { .await } + #[hotpath::measure(label = "daemon.profile_registry.gc", future = true)] pub async fn registry_gc( &self, profile_root: &Path, diff --git a/crates/tracedecay/src/tracedecay/queries/meta.rs b/crates/tracedecay/src/tracedecay/queries/meta.rs index d38326a953..05e8643bf3 100644 --- a/crates/tracedecay/src/tracedecay/queries/meta.rs +++ b/crates/tracedecay/src/tracedecay/queries/meta.rs @@ -18,11 +18,13 @@ fn parse_counter(key: &'static str, value: Option) -> Result { impl TraceDecay { /// Returns the persisted tokens-saved counter. + #[hotpath::measure(label = "daemon.store_meta.read_tokens_saved", future = true)] pub async fn get_tokens_saved(&self) -> Result { parse_counter("tokens_saved", self.db.get_metadata("tokens_saved").await?) } /// Persists the tokens-saved counter to the database. + #[hotpath::measure(label = "daemon.store_meta.write_tokens_saved", future = true)] pub async fn set_tokens_saved(&self, value: u64) -> Result<()> { self.db .set_metadata("tokens_saved", &value.to_string()) @@ -33,6 +35,7 @@ impl TraceDecay { /// /// This is separate from the main `tokens_saved` counter and can be /// independently reset via [`Self::reset_local_counter`]. + #[hotpath::measure(label = "daemon.store_meta.read_local_counter", future = true)] pub async fn get_local_counter(&self) -> Result { parse_counter( "local_counter", @@ -41,11 +44,13 @@ impl TraceDecay { } /// Resets the project-local token counter to zero. + #[hotpath::measure(label = "daemon.store_meta.reset_local_counter", future = true)] pub async fn reset_local_counter(&self) -> Result<()> { self.db.set_metadata("local_counter", "0").await } /// Increments the project-local token counter by the given amount. + #[hotpath::measure(label = "daemon.store_meta.add_local_counter", future = true)] pub async fn add_local_counter(&self, delta: u64) -> Result<()> { let transaction = self.db.begin_write_transaction("add local counter").await?; let current = self.get_local_counter().await?; @@ -62,6 +67,7 @@ impl TraceDecay { } /// Checkpoints the WAL and closes the database connection. + #[hotpath::measure(label = "daemon.store_meta.checkpoint", future = true)] pub async fn checkpoint(&self) -> Result<()> { self.db.checkpoint().await } @@ -72,6 +78,7 @@ impl TraceDecay { } /// Run the quick integrity check and return the first problem row, if any. + #[hotpath::measure(label = "daemon.store_meta.quick_check", future = true)] pub(crate) async fn quick_check_report(&self) -> Result> { self.db.quick_check_report().await }