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}