dpm_model/run/view.rs
1//! What a run looks like when read: durable lifecycle beside derived observation.
2
3use super::{ActivityTally, LifecycleEvent, RunLink, RunRecord, RunState};
4use crate::{ActorId, WorkStatus};
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7
8/// What an observer can honestly say about a run at one clock reading.
9///
10/// The first five follow the last recorded transition. `Stale` and `Unknown` are never stored:
11/// they are derived from receipt times and attribution each time a view is computed, so a run
12/// that goes silent is never labelled finished, and one that resumes needs no repair.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
14#[serde(rename_all = "snake_case")]
15pub enum ObservedStatus {
16 /// Working, and heard from recently.
17 Working,
18 /// Waiting, and heard from recently.
19 Waiting,
20 /// Recorded as failed.
21 Failed,
22 /// Recorded as interrupted.
23 Interrupted,
24 /// Recorded as completed.
25 Completed,
26 /// Not finished according to its last transition, but nothing has been received for longer
27 /// than the staleness bound. Its real state is unknown.
28 Stale,
29 /// Cannot be judged here: it was recorded under another history than this store continues,
30 /// so whatever process it described is not known to be running in this one.
31 Unknown,
32}
33
34/// Whether a run was recorded in the history the project store now continues.
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
36#[serde(rename_all = "snake_case")]
37pub enum LineageStatus {
38 /// Recorded under the store's current lineage.
39 Current,
40 /// Recorded under another lineage, such as before a restore. Its attribution is unchanged
41 /// and it is never reattached to this history.
42 Foreign,
43}
44
45/// Why a run that is not finished no longer matches the task it executes.
46///
47/// Runs never move task ownership; a mismatch is reported so someone can decide what to do.
48#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
49#[serde(tag = "reason", rename_all = "snake_case")]
50pub enum Orphan {
51 /// The task is owned by someone other than the run's executor, or by no one.
52 OwnerChanged {
53 /// The task's current owner.
54 owner: Option<ActorId>,
55 },
56 /// The task is no longer being executed.
57 NotExecuting {
58 /// The task's current lifecycle.
59 status: WorkStatus,
60 },
61 /// The task is not in the plan this view was computed from.
62 WorkMissing,
63}
64
65/// Everything a store holds about one run, before any clock or plan is applied.
66#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
67pub struct RunSnapshot {
68 /// The run as started.
69 pub record: RunRecord,
70 /// The latest lifecycle fact.
71 pub last: LifecycleEvent,
72 /// What the run's activity amounts to.
73 pub activity: ActivityTally,
74 /// Project operations explicitly linked to the run.
75 pub operations: Vec<RunLink>,
76}
77
78/// A run with its derived observation, evaluated at one clock reading.
79#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
80pub struct RunView {
81 /// The run as started.
82 pub run: RunRecord,
83 /// Lifecycle truth: the last recorded transition.
84 pub state: RunState,
85 /// When that transition was recorded.
86 pub state_since: DateTime<Utc>,
87 /// Detail the reporter gave with it.
88 #[serde(default, skip_serializing_if = "Option::is_none")]
89 pub state_detail: Option<String>,
90 /// What can be said about the run now; see [`ObservedStatus`].
91 pub status: ObservedStatus,
92 /// The latest time DPM received anything from the run, from DPM's own clock.
93 pub last_receipt_at: DateTime<Utc>,
94 /// When a working or waiting run turns stale if nothing more arrives; the instant a view
95 /// should next be recomputed. Absent once the run is stale, unknown or finished.
96 #[serde(default, skip_serializing_if = "Option::is_none")]
97 pub stale_at: Option<DateTime<Utc>>,
98 /// Activity received, retained and latest.
99 pub activity: ActivityTally,
100 /// Whether the run belongs to the store's current history.
101 pub lineage: LineageStatus,
102 /// Present when the run is unfinished but its task no longer matches it.
103 #[serde(default, skip_serializing_if = "Option::is_none")]
104 pub orphan: Option<Orphan>,
105 /// Project operations explicitly linked to the run.
106 pub operations: Vec<RunLink>,
107 /// The clock reading this view was computed at.
108 pub evaluated_at: DateTime<Utc>,
109}