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