Skip to main content

sleet/
response.rs

1//! Response types for one-shot subcommands run with `--format json`.
2//!
3//! These structs are the source of truth for `schema/cli.schema.json`;
4//! `tests/schema_sync.rs` regenerates it and fails if the two drift.
5//! Text rendering lives in `crate::render`.
6
7use schemars::JsonSchema;
8use serde::Serialize;
9
10use crate::config::{HumanDuration, Service};
11
12/// The subcommand response JSON Schema, pretty-printed.
13pub fn schema_json() -> String {
14    crate::schema_pretty::<Response>()
15}
16
17/// A response from any subcommand run with `--format json`, one variant
18/// per command. Exists to generate the single response schema: each
19/// command's response is a named definition under `$defs`, so consumers
20/// validate against e.g. `#/$defs/StatusResponse`.
21#[derive(Clone, Debug, Serialize, JsonSchema)]
22#[serde(untagged)]
23#[schemars(title = "sleet response")]
24pub enum Response {
25    /// `sleet status`.
26    Status(StatusResponse),
27    /// `sleet register`.
28    Register(RegisterResponse),
29    /// `sleet mirror sync`.
30    MirrorSync(MirrorSyncResponse),
31    /// `sleet mirror restore`.
32    MirrorRestore(MirrorRestoreResponse),
33}
34
35/// The `sleet status` response, derived from the fleet root: node
36/// liveness, roles, and versions from `nodes/`, registered databases
37/// from `dbs/`, and placement by computing the same rendezvous ranking
38/// the nodes do.
39#[derive(Clone, Debug, Serialize, JsonSchema)]
40#[schemars(title = "sleet status response")]
41pub struct StatusResponse {
42    /// Every fleet member with a heartbeat object.
43    pub nodes: Vec<NodeStatus>,
44
45    /// Registered databases and their service placement.
46    pub databases: Vec<DatabaseStatus>,
47
48    /// Per-target mirror lag; present only with `sleet status
49    /// --mirrors`.
50    #[serde(default, skip_serializing_if = "Vec::is_empty")]
51    pub mirrors: Vec<MirrorStatus>,
52
53    /// Fleet-level problems: registry entries that alias the same
54    /// database, services no live node offers, and the like.
55    #[serde(default, skip_serializing_if = "Vec::is_empty")]
56    pub warnings: Vec<String>,
57}
58
59/// One `(database, target)` mirror's lag, from the source and
60/// destination heads.
61#[derive(Clone, Debug, Serialize, JsonSchema)]
62pub struct MirrorStatus {
63    /// The source database's canonical URL.
64    pub database: String,
65
66    /// The target's name.
67    pub target: String,
68
69    /// The destination root the target maps this database to.
70    pub destination: String,
71
72    /// The source's latest manifest id.
73    #[serde(skip_serializing_if = "Option::is_none")]
74    pub source_manifest_id: Option<u64>,
75
76    /// The destination's latest manifest id (the watermark).
77    #[serde(skip_serializing_if = "Option::is_none")]
78    pub dest_manifest_id: Option<u64>,
79
80    /// Manifests the destination is behind.
81    #[serde(skip_serializing_if = "Option::is_none")]
82    pub manifests_behind: Option<u64>,
83
84    /// WAL ids the destination is behind.
85    #[serde(skip_serializing_if = "Option::is_none")]
86    pub wal_behind: Option<u64>,
87
88    /// Estimated seconds of lag: source and target sequence numbers
89    /// mapped through the source's sequence tracker.
90    #[serde(skip_serializing_if = "Option::is_none")]
91    pub seconds_behind: Option<u64>,
92
93    /// Why lag could not be read, if it could not.
94    #[serde(skip_serializing_if = "Option::is_none")]
95    pub error: Option<String>,
96}
97
98/// One fleet member.
99#[derive(Clone, Debug, Serialize, JsonSchema)]
100pub struct NodeStatus {
101    /// The node's id, from its heartbeat object name.
102    pub node_id: String,
103
104    /// Whether the heartbeat is younger than `heartbeat_timeout`.
105    pub live: bool,
106
107    /// Age of the heartbeat object.
108    pub heartbeat_age: HumanDuration,
109
110    /// Services the node offers, from its heartbeat object name.
111    pub services: Vec<Service>,
112
113    /// The sleet version the node runs, from the heartbeat body; absent
114    /// if the body was unreadable.
115    #[serde(skip_serializing_if = "Option::is_none")]
116    pub sleet_version: Option<String>,
117
118    /// The slatedb version the node runs, from the heartbeat body;
119    /// absent if the body was unreadable.
120    #[serde(skip_serializing_if = "Option::is_none")]
121    pub slatedb_version: Option<String>,
122}
123
124/// One registered database and its service placement.
125#[derive(Clone, Debug, Serialize, JsonSchema)]
126pub struct DatabaseStatus {
127    /// The database's canonical URL.
128    pub url: String,
129
130    /// Placement of each configured service.
131    pub services: Vec<ServicePlacement>,
132
133    /// Compaction queue depth from `.compactions`; present only with
134    /// `sleet status --compactions`.
135    #[serde(skip_serializing_if = "Option::is_none")]
136    pub queue: Option<QueueStatus>,
137}
138
139/// Compaction queue depth for one database.
140#[derive(Clone, Copy, Debug, Serialize, JsonSchema)]
141pub struct QueueStatus {
142    /// Jobs waiting for a worker.
143    pub claimable: u64,
144    /// Jobs a worker is executing.
145    pub running: u64,
146}
147
148/// Where one database service runs: the top of the service's rendezvous
149/// ranking. One node for `gc` and `compactor-coordinator`, the top
150/// `count` nodes for `compaction-workers`. Empty means no live node
151/// offers the service.
152#[derive(Clone, Debug, Serialize, JsonSchema)]
153pub struct ServicePlacement {
154    /// The placed service.
155    pub service: Service,
156
157    /// The owning nodes, best-ranked first.
158    pub nodes: Vec<String>,
159}
160
161/// The `sleet register` response.
162#[derive(Clone, Debug, Serialize, JsonSchema)]
163#[schemars(title = "sleet register response")]
164pub struct RegisterResponse {
165    /// The canonicalized database URL.
166    pub url: String,
167
168    /// The registry object written, relative to the fleet root.
169    pub file: String,
170
171    /// False if the database was already registered.
172    pub created: bool,
173}
174
175/// The `sleet mirror sync` response: one pass, plus the prune that
176/// follows it when retention is set.
177#[derive(Clone, Debug, Serialize, JsonSchema)]
178#[schemars(title = "sleet mirror sync response")]
179pub struct MirrorSyncResponse {
180    /// The source database's canonical URL.
181    pub database: String,
182
183    /// The target's name.
184    pub target: String,
185
186    /// The destination root.
187    pub destination: String,
188
189    /// The manifest id the destination head ended at.
190    pub head: u64,
191
192    /// False when the destination was already at the source's head.
193    pub committed: bool,
194
195    /// Manifests written to the destination.
196    pub manifests_committed: u64,
197
198    /// Data objects copied.
199    pub objects_copied: u64,
200
201    /// Data bytes copied; zero for the rclone copier.
202    pub bytes_copied: u64,
203
204    /// Manifests the prune deleted; zero without retention.
205    pub pruned_manifests: u64,
206
207    /// Data objects the prune deleted; zero without retention.
208    pub pruned_objects: u64,
209}
210
211/// The `sleet mirror restore` response.
212#[derive(Clone, Debug, Serialize, JsonSchema)]
213#[schemars(title = "sleet mirror restore response")]
214pub struct MirrorRestoreResponse {
215    /// The backup root restored from.
216    pub backup: String,
217
218    /// The destination root restored into.
219    pub destination: String,
220
221    /// The restore point committed as the destination's head.
222    pub manifest_id: u64,
223
224    /// Manifests committed.
225    pub manifests_committed: u64,
226
227    /// Data objects copied.
228    pub objects_copied: u64,
229
230    /// Data bytes copied.
231    pub bytes_copied: u64,
232}