Skip to main content

canic_core/dto/
runtime.rs

1use crate::dto::prelude::*;
2use crate::ids::BuildNetwork;
3
4pub use crate::domain::runtime::{
5    FailureSeverity, HealthStatus, ReadinessStatus, RuntimeCheckStatus, RuntimeDiagnosticSeverity,
6    RuntimeFieldVisibility, RuntimeStateDomainStatus, RuntimeStatus, TimerExecutionOutcome,
7    TimerProcessCondition, TimerRegistrationStatus, TimerSchedulingMode,
8};
9
10pub const RUNTIME_INTROSPECTION_SCHEMA_VERSION: u32 = 3;
11
12//
13// CanicHealthStatus
14//
15
16#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
17pub struct CanicHealthStatus {
18    pub schema_version: u32,
19    pub status: HealthStatus,
20    pub observed_at_ns: Option<u64>,
21    pub checks: Vec<RuntimeCheck>,
22}
23
24//
25// CanicReadinessStatus
26//
27
28#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
29pub struct CanicReadinessStatus {
30    pub schema_version: u32,
31    pub role: Option<String>,
32    pub status: ReadinessStatus,
33    pub observed_at_ns: u64,
34    pub checks: Vec<RuntimeCheck>,
35    pub blockers: Vec<RuntimeDiagnostic>,
36    pub warnings: Vec<RuntimeDiagnostic>,
37}
38
39//
40// CanicRuntimeStatus
41//
42
43#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
44pub struct CanicRuntimeStatus {
45    pub schema_version: u32,
46    pub observed_at_ns: u64,
47    pub canister_id: Principal,
48    pub role: Option<String>,
49    pub root: Option<Principal>,
50    pub build_network: Option<BuildNetwork>,
51    pub build: RuntimeBuildInfo,
52    pub features: Vec<RuntimeFeatureStatus>,
53    pub topology: Option<RuntimeTopologyStatus>,
54    pub timers: Vec<CanisterTimerStatus>,
55    pub timer_inventory: RuntimeCheck,
56    pub state: Option<RuntimeStateSummary>,
57    pub auth: Option<RuntimeAuthStatusSummary>,
58    pub blob_storage: Option<RuntimeBlobStorageStatusSummary>,
59    pub receipt_capacity: Option<RuntimeReceiptCapacityStatus>,
60    pub recent_failures: Vec<RecentFailure>,
61    pub visibility: Vec<RuntimeVisibilityEntry>,
62    pub readiness: CanicReadinessStatus,
63    pub status: RuntimeStatus,
64}
65
66//
67// RuntimeBuildInfo
68//
69
70#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
71pub struct RuntimeBuildInfo {
72    pub package_name: String,
73    pub package_version: String,
74    pub canic_version: String,
75    pub canister_version: u64,
76}
77
78//
79// RuntimeCheck
80//
81
82#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
83pub struct RuntimeCheck {
84    pub category: String,
85    pub code: String,
86    pub status: RuntimeCheckStatus,
87    pub subject: String,
88    pub detail: String,
89    pub next: Option<String>,
90    pub source: String,
91}
92
93//
94// RuntimeDiagnostic
95//
96
97#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
98pub struct RuntimeDiagnostic {
99    pub category: String,
100    pub code: String,
101    pub severity: RuntimeDiagnosticSeverity,
102    pub subject: String,
103    pub detail: String,
104    pub next: Option<String>,
105    pub source: String,
106}
107
108//
109// RuntimeFeatureStatus
110//
111
112#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
113pub struct RuntimeFeatureStatus {
114    pub name: String,
115    pub enabled: bool,
116    pub visibility: RuntimeFieldVisibility,
117    pub source: String,
118}
119
120//
121// RuntimeTopologyStatus
122//
123
124#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
125pub struct RuntimeTopologyStatus {
126    pub root: Option<Principal>,
127    pub parent: Option<Principal>,
128    pub subnet: Option<Principal>,
129    pub source: String,
130}
131
132//
133// CanisterTimerStatus
134//
135
136/// One live or retained registration from the shared canister timer registry.
137///
138/// A transient `RemoveWhenStopped` registration is absent after reaching its
139/// terminal state; its final observation is therefore not retained here.
140#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
141pub struct CanisterTimerStatus {
142    pub name: String,
143    pub owner: String,
144    pub subsystem: String,
145    pub scheduling_mode: TimerSchedulingMode,
146    pub registration: TimerRegistrationStatus,
147    pub condition: TimerProcessCondition,
148    pub enabled: bool,
149    pub generation: Option<u64>,
150    pub next_due_at_ns: Option<u64>,
151    pub last_outcome: Option<TimerExecutionOutcome>,
152    pub last_work_count: u64,
153    pub last_success_at_ns: Option<u64>,
154    pub last_failure_at_ns: Option<u64>,
155    pub consecutive_expected_failures: u64,
156    pub schedules_since_runtime_start: u64,
157    pub executions_since_runtime_start: u64,
158    pub successes_since_runtime_start: u64,
159    pub expected_failures_since_runtime_start: u64,
160    pub invariant_failures_since_runtime_start: u64,
161    pub stale_callbacks_since_runtime_start: u64,
162    pub scheduler_performance: TimerCallbackPerformanceStatus,
163    pub work_performance: TimerCallbackPerformanceStatus,
164}
165
166//
167// TimerCallbackPerformanceStatus
168//
169
170/// Bounded, runtime-epoch observations for one timer callback role.
171///
172/// Samples cover the complete accepted shared-runtime callback path rather
173/// than isolating the consumer function from registry and completion work.
174#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
175pub struct TimerCallbackPerformanceStatus {
176    pub instruction_samples_since_runtime_start: u64,
177    pub instructions_latest: Option<u64>,
178    pub instructions_maximum: Option<u64>,
179    pub instructions_total_since_runtime_start: u64,
180    pub memory_page_samples_since_runtime_start: u64,
181    pub memory_pages_latest: Option<TimerMemoryPageSampleStatus>,
182    pub maximum_wasm_memory_growth_pages: Option<u64>,
183    pub maximum_stable_memory_growth_pages: Option<u64>,
184}
185
186//
187// TimerMemoryPageSampleStatus
188//
189
190/// Wasm and stable-memory page extents bracketing one completed callback.
191#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
192pub struct TimerMemoryPageSampleStatus {
193    pub start: TimerMemoryPageExtentStatus,
194    pub end: TimerMemoryPageExtentStatus,
195}
196
197//
198// TimerMemoryPageExtentStatus
199//
200
201/// Runtime high-water memory extents measured in 64-KiB pages.
202#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
203pub struct TimerMemoryPageExtentStatus {
204    pub wasm_pages: u64,
205    pub stable_pages: u64,
206}
207
208//
209// RuntimeStateSummary
210//
211
212#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
213pub struct RuntimeStateSummary {
214    pub manifest_schema_version: u32,
215    pub domains: Vec<RuntimeStateDomainSummary>,
216    pub total_stable_memory_pages: Option<u64>,
217}
218
219//
220// RuntimeStateDomainSummary
221//
222
223#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
224pub struct RuntimeStateDomainSummary {
225    pub domain: String,
226    pub version: u32,
227    pub storage: String,
228    pub memory_id: Option<u8>,
229    pub status: RuntimeStateDomainStatus,
230}
231
232//
233// RuntimeAuthStatusSummary
234//
235
236#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
237pub struct RuntimeAuthStatusSummary {
238    pub auth_features: Vec<RuntimeFeatureStatus>,
239}
240
241//
242// RuntimeBlobStorageStatusSummary
243//
244
245#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
246pub struct RuntimeBlobStorageStatusSummary {
247    pub blob_storage_features: Vec<RuntimeFeatureStatus>,
248}
249
250//
251// RuntimeReceiptCapacityStatus
252//
253
254#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
255pub struct RuntimeReceiptCapacityStatus {
256    pub status: RuntimeCheckStatus,
257    pub receipt_records: u64,
258    pub application_receipt_records: u64,
259    pub canic_owned_receipt_records: u64,
260    pub pending_application_receipt_records: u64,
261    pub terminal_application_receipt_records: u64,
262    pub receipt_record_limit: u64,
263    pub remaining_receipt_record_headroom: u64,
264    pub resource_total_records: u64,
265    pub resource_total_record_limit: u64,
266    pub remaining_resource_total_headroom: u64,
267    pub warning_headroom_threshold: u64,
268    pub reserved_terminal_slots: u64,
269    pub reserved_terminal_pages: u64,
270    pub next_terminal_eligibility_at_ns: Option<u64>,
271    pub source: String,
272}
273
274//
275// RecentFailure
276//
277
278#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
279pub struct RecentFailure {
280    pub occurred_at_ns: u64,
281    pub subsystem: String,
282    pub code: String,
283    pub severity: FailureSeverity,
284    pub summary: String,
285    pub correlation_id: Option<String>,
286    pub redacted: bool,
287}
288
289//
290// RuntimeVisibilityEntry
291//
292
293#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
294pub struct RuntimeVisibilityEntry {
295    pub field: String,
296    pub visibility: RuntimeFieldVisibility,
297}
298
299#[cfg(test)]
300mod tests {
301    use super::*;
302    use candid::{Decode, Encode};
303    use serde::Serialize;
304    use serde::de::DeserializeOwned;
305    use std::fmt::Debug;
306
307    #[test]
308    fn runtime_enums_roundtrip_candid_with_canonical_variant_labels() {
309        assert_enum_candid_contract(HealthStatus::Unknown);
310        assert_enum_candid_contract(ReadinessStatus::NotEvaluated);
311        assert_enum_candid_contract(RuntimeStatus::Failing);
312        assert_enum_candid_contract(TimerRegistrationStatus::Unregistered);
313        assert_enum_candid_contract(TimerProcessCondition::MissingRegistration);
314        assert_enum_candid_contract(TimerSchedulingMode::AfterCompletion);
315        assert_enum_candid_contract(TimerExecutionOutcome::InvariantFailure);
316        assert_enum_candid_contract(FailureSeverity::Critical);
317        assert_enum_candid_contract(RuntimeCheckStatus::NotEvaluated);
318        assert_enum_candid_contract(RuntimeDiagnosticSeverity::Unsupported);
319        assert_enum_candid_contract(RuntimeFieldVisibility::OperatorOnly);
320        assert_enum_candid_contract(RuntimeStateDomainStatus::NotEvaluated);
321    }
322
323    #[test]
324    fn runtime_enums_serialize_canonical_snake_case_labels() {
325        assert_enum_serde_contract(HealthStatus::Healthy, HealthStatus::Healthy.label());
326        assert_enum_serde_contract(HealthStatus::Degraded, HealthStatus::Degraded.label());
327        assert_enum_serde_contract(HealthStatus::Unhealthy, HealthStatus::Unhealthy.label());
328        assert_enum_serde_contract(HealthStatus::Unknown, HealthStatus::Unknown.label());
329
330        assert_enum_serde_contract(ReadinessStatus::Ready, ReadinessStatus::Ready.label());
331        assert_enum_serde_contract(ReadinessStatus::Degraded, ReadinessStatus::Degraded.label());
332        assert_enum_serde_contract(ReadinessStatus::NotReady, ReadinessStatus::NotReady.label());
333        assert_enum_serde_contract(
334            ReadinessStatus::NotEvaluated,
335            ReadinessStatus::NotEvaluated.label(),
336        );
337
338        assert_enum_serde_contract(RuntimeStatus::Ok, RuntimeStatus::Ok.label());
339        assert_enum_serde_contract(RuntimeStatus::Degraded, RuntimeStatus::Degraded.label());
340        assert_enum_serde_contract(RuntimeStatus::Failing, RuntimeStatus::Failing.label());
341        assert_enum_serde_contract(RuntimeStatus::Unknown, RuntimeStatus::Unknown.label());
342
343        assert_enum_serde_contract(
344            TimerRegistrationStatus::Unregistered,
345            TimerRegistrationStatus::Unregistered.label(),
346        );
347        assert_enum_serde_contract(
348            TimerProcessCondition::MissingRegistration,
349            TimerProcessCondition::MissingRegistration.label(),
350        );
351        assert_enum_serde_contract(
352            TimerSchedulingMode::AfterCompletion,
353            TimerSchedulingMode::AfterCompletion.label(),
354        );
355        assert_enum_serde_contract(
356            TimerExecutionOutcome::InvariantFailure,
357            TimerExecutionOutcome::InvariantFailure.label(),
358        );
359
360        assert_enum_serde_contract(FailureSeverity::Info, FailureSeverity::Info.label());
361        assert_enum_serde_contract(FailureSeverity::Warning, FailureSeverity::Warning.label());
362        assert_enum_serde_contract(FailureSeverity::Error, FailureSeverity::Error.label());
363        assert_enum_serde_contract(FailureSeverity::Critical, FailureSeverity::Critical.label());
364
365        assert_enum_serde_contract(RuntimeCheckStatus::Pass, RuntimeCheckStatus::Pass.label());
366        assert_enum_serde_contract(RuntimeCheckStatus::Warn, RuntimeCheckStatus::Warn.label());
367        assert_enum_serde_contract(RuntimeCheckStatus::Fail, RuntimeCheckStatus::Fail.label());
368        assert_enum_serde_contract(
369            RuntimeCheckStatus::NotEvaluated,
370            RuntimeCheckStatus::NotEvaluated.label(),
371        );
372
373        assert_enum_serde_contract(
374            RuntimeDiagnosticSeverity::Info,
375            RuntimeDiagnosticSeverity::Info.label(),
376        );
377        assert_enum_serde_contract(
378            RuntimeDiagnosticSeverity::Warning,
379            RuntimeDiagnosticSeverity::Warning.label(),
380        );
381        assert_enum_serde_contract(
382            RuntimeDiagnosticSeverity::Blocked,
383            RuntimeDiagnosticSeverity::Blocked.label(),
384        );
385        assert_enum_serde_contract(
386            RuntimeDiagnosticSeverity::Unsupported,
387            RuntimeDiagnosticSeverity::Unsupported.label(),
388        );
389
390        assert_enum_serde_contract(
391            RuntimeFieldVisibility::PublicSafe,
392            RuntimeFieldVisibility::PublicSafe.label(),
393        );
394        assert_enum_serde_contract(
395            RuntimeFieldVisibility::OperatorOnly,
396            RuntimeFieldVisibility::OperatorOnly.label(),
397        );
398        assert_enum_serde_contract(
399            RuntimeFieldVisibility::ControllerOnly,
400            RuntimeFieldVisibility::ControllerOnly.label(),
401        );
402        assert_enum_serde_contract(
403            RuntimeFieldVisibility::FeatureGated,
404            RuntimeFieldVisibility::FeatureGated.label(),
405        );
406        assert_enum_serde_contract(
407            RuntimeFieldVisibility::Disabled,
408            RuntimeFieldVisibility::Disabled.label(),
409        );
410
411        assert_enum_serde_contract(
412            RuntimeStateDomainStatus::Ok,
413            RuntimeStateDomainStatus::Ok.label(),
414        );
415        assert_enum_serde_contract(
416            RuntimeStateDomainStatus::Warning,
417            RuntimeStateDomainStatus::Warning.label(),
418        );
419        assert_enum_serde_contract(
420            RuntimeStateDomainStatus::Failing,
421            RuntimeStateDomainStatus::Failing.label(),
422        );
423        assert_enum_serde_contract(
424            RuntimeStateDomainStatus::NotEvaluated,
425            RuntimeStateDomainStatus::NotEvaluated.label(),
426        );
427    }
428
429    fn assert_enum_candid_contract<T>(value: T)
430    where
431        T: CandidType + Clone + Debug + DeserializeOwned + Eq,
432    {
433        let bytes = Encode!(&value).expect("encode runtime enum");
434        let decoded = Decode!(&bytes, T).expect("decode runtime enum");
435
436        assert_eq!(decoded, value);
437    }
438
439    fn assert_enum_serde_contract<T>(value: T, label: &str)
440    where
441        T: Clone + Debug + DeserializeOwned + Eq + Serialize,
442    {
443        let encoded = crate::cdk::serialize::serialize(&value).expect("encode runtime enum label");
444        let serialized_label: String =
445            crate::cdk::serialize::deserialize(&encoded).expect("decode runtime enum label");
446        assert_eq!(serialized_label, label);
447
448        let label_bytes =
449            crate::cdk::serialize::serialize(&label).expect("encode canonical runtime enum label");
450        let decoded: T = crate::cdk::serialize::deserialize(&label_bytes)
451            .expect("decode canonical runtime enum label");
452        assert_eq!(decoded, value);
453    }
454}